aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/block
diff options
context:
space:
mode:
authorStefan Haberland <stefan.haberland@de.ibm.com>2011-12-27 05:27:27 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2011-12-27 05:27:14 -0500
commitb206181d636d416fde48c7f493d7ac5d935b57e3 (patch)
treed28c27ee31d1fbfc6cfb3b3abf01c465f2c2607a /drivers/s390/block
parente58b0d902f7c7c407bae9c8bc8d90fa1d06184c5 (diff)
[S390] dasd: add sanity check to detect path connection error
Prevents possible data corruption by detecting cabling error. Therefor read and compare the UID for all available channel paths. Signed-off-by: Stefan Haberland <stefan.haberland@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/block')
-rw-r--r--drivers/s390/block/dasd_eckd.c409
1 files changed, 300 insertions, 109 deletions
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 6ab29680586a..763f1bd9605a 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -752,24 +752,13 @@ dasd_eckd_cdl_reclen(int recid)
752 return sizes_trk0[recid]; 752 return sizes_trk0[recid];
753 return LABEL_SIZE; 753 return LABEL_SIZE;
754} 754}
755 755/* create unique id from private structure. */
756/* 756static void create_uid(struct dasd_eckd_private *private)
757 * Generate device unique id that specifies the physical device.
758 */
759static int dasd_eckd_generate_uid(struct dasd_device *device)
760{ 757{
761 struct dasd_eckd_private *private;
762 struct dasd_uid *uid;
763 int count; 758 int count;
764 unsigned long flags; 759 struct dasd_uid *uid;
765 760
766 private = (struct dasd_eckd_private *) device->private;
767 if (!private)
768 return -ENODEV;
769 if (!private->ned || !private->gneq)
770 return -ENODEV;
771 uid = &private->uid; 761 uid = &private->uid;
772 spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
773 memset(uid, 0, sizeof(struct dasd_uid)); 762 memset(uid, 0, sizeof(struct dasd_uid));
774 memcpy(uid->vendor, private->ned->HDA_manufacturer, 763 memcpy(uid->vendor, private->ned->HDA_manufacturer,
775 sizeof(uid->vendor) - 1); 764 sizeof(uid->vendor) - 1);
@@ -792,6 +781,23 @@ static int dasd_eckd_generate_uid(struct dasd_device *device)
792 private->vdsneq->uit[count]); 781 private->vdsneq->uit[count]);
793 } 782 }
794 } 783 }
784}
785
786/*
787 * Generate device unique id that specifies the physical device.
788 */
789static int dasd_eckd_generate_uid(struct dasd_device *device)
790{
791 struct dasd_eckd_private *private;
792 unsigned long flags;
793
794 private = (struct dasd_eckd_private *) device->private;
795 if (!private)
796 return -ENODEV;
797 if (!private->ned || !private->gneq)
798 return -ENODEV;
799 spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
800 create_uid(private);
795 spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 801 spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
796 return 0; 802 return 0;
797} 803}
@@ -811,6 +817,21 @@ static int dasd_eckd_get_uid(struct dasd_device *device, struct dasd_uid *uid)
811 return -EINVAL; 817 return -EINVAL;
812} 818}
813 819
820/*
821 * compare device UID with data of a given dasd_eckd_private structure
822 * return 0 for match
823 */
824static int dasd_eckd_compare_path_uid(struct dasd_device *device,
825 struct dasd_eckd_private *private)
826{
827 struct dasd_uid device_uid;
828
829 create_uid(private);
830 dasd_eckd_get_uid(device, &device_uid);
831
832 return memcmp(&device_uid, &private->uid, sizeof(struct dasd_uid));
833}
834
814static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device, 835static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device,
815 struct dasd_ccw_req *cqr, 836 struct dasd_ccw_req *cqr,
816 __u8 *rcd_buffer, 837 __u8 *rcd_buffer,
@@ -1005,59 +1026,120 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
1005 int conf_len, conf_data_saved; 1026 int conf_len, conf_data_saved;
1006 int rc; 1027 int rc;
1007 __u8 lpm, opm; 1028 __u8 lpm, opm;
1008 struct dasd_eckd_private *private; 1029 struct dasd_eckd_private *private, path_private;
1009 struct dasd_path *path_data; 1030 struct dasd_path *path_data;
1031 struct dasd_uid *uid;
1032 char print_path_uid[60], print_device_uid[60];
1010 1033
1011 private = (struct dasd_eckd_private *) device->private; 1034 private = (struct dasd_eckd_private *) device->private;
1012 path_data = &device->path_data; 1035 path_data = &device->path_data;
1013 opm = ccw_device_get_path_mask(device->cdev); 1036 opm = ccw_device_get_path_mask(device->cdev);
1014 lpm = 0x80;
1015 conf_data_saved = 0; 1037 conf_data_saved = 0;
1016 /* get configuration data per operational path */ 1038 /* get configuration data per operational path */
1017 for (lpm = 0x80; lpm; lpm>>= 1) { 1039 for (lpm = 0x80; lpm; lpm>>= 1) {
1018 if (lpm & opm) { 1040 if (!(lpm & opm))
1019 rc = dasd_eckd_read_conf_lpm(device, &conf_data, 1041 continue;
1020 &conf_len, lpm); 1042 rc = dasd_eckd_read_conf_lpm(device, &conf_data,
1021 if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ 1043 &conf_len, lpm);
1022 DBF_EVENT_DEVID(DBF_WARNING, device->cdev, 1044 if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */
1023 "Read configuration data returned " 1045 DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
1024 "error %d", rc); 1046 "Read configuration data returned "
1025 return rc; 1047 "error %d", rc);
1026 } 1048 return rc;
1027 if (conf_data == NULL) { 1049 }
1028 DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 1050 if (conf_data == NULL) {
1029 "No configuration data " 1051 DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
1030 "retrieved"); 1052 "No configuration data "
1031 /* no further analysis possible */ 1053 "retrieved");
1032 path_data->opm |= lpm; 1054 /* no further analysis possible */
1033 continue; /* no error */ 1055 path_data->opm |= lpm;
1056 continue; /* no error */
1057 }
1058 /* save first valid configuration data */
1059 if (!conf_data_saved) {
1060 kfree(private->conf_data);
1061 private->conf_data = conf_data;
1062 private->conf_len = conf_len;
1063 if (dasd_eckd_identify_conf_parts(private)) {
1064 private->conf_data = NULL;
1065 private->conf_len = 0;
1066 kfree(conf_data);
1067 continue;
1034 } 1068 }
1035 /* save first valid configuration data */ 1069 /*
1036 if (!conf_data_saved) { 1070 * build device UID that other path data
1037 kfree(private->conf_data); 1071 * can be compared to it
1038 private->conf_data = conf_data; 1072 */
1039 private->conf_len = conf_len; 1073 dasd_eckd_generate_uid(device);
1040 if (dasd_eckd_identify_conf_parts(private)) { 1074 conf_data_saved++;
1041 private->conf_data = NULL; 1075 } else {
1042 private->conf_len = 0; 1076 path_private.conf_data = conf_data;
1043 kfree(conf_data); 1077 path_private.conf_len = DASD_ECKD_RCD_DATA_SIZE;
1044 continue; 1078 if (dasd_eckd_identify_conf_parts(
1045 } 1079 &path_private)) {
1046 conf_data_saved++; 1080 path_private.conf_data = NULL;
1081 path_private.conf_len = 0;
1082 kfree(conf_data);
1083 continue;
1047 } 1084 }
1048 switch (dasd_eckd_path_access(conf_data, conf_len)) { 1085
1049 case 0x02: 1086 if (dasd_eckd_compare_path_uid(
1050 path_data->npm |= lpm; 1087 device, &path_private)) {
1051 break; 1088 uid = &path_private.uid;
1052 case 0x03: 1089 if (strlen(uid->vduit) > 0)
1053 path_data->ppm |= lpm; 1090 snprintf(print_path_uid,
1054 break; 1091 sizeof(print_path_uid),
1092 "%s.%s.%04x.%02x.%s",
1093 uid->vendor, uid->serial,
1094 uid->ssid, uid->real_unit_addr,
1095 uid->vduit);
1096 else
1097 snprintf(print_path_uid,
1098 sizeof(print_path_uid),
1099 "%s.%s.%04x.%02x",
1100 uid->vendor, uid->serial,
1101 uid->ssid,
1102 uid->real_unit_addr);
1103 uid = &private->uid;
1104 if (strlen(uid->vduit) > 0)
1105 snprintf(print_device_uid,
1106 sizeof(print_device_uid),
1107 "%s.%s.%04x.%02x.%s",
1108 uid->vendor, uid->serial,
1109 uid->ssid, uid->real_unit_addr,
1110 uid->vduit);
1111 else
1112 snprintf(print_device_uid,
1113 sizeof(print_device_uid),
1114 "%s.%s.%04x.%02x",
1115 uid->vendor, uid->serial,
1116 uid->ssid,
1117 uid->real_unit_addr);
1118 dev_err(&device->cdev->dev,
1119 "Not all channel paths lead to "
1120 "the same device, path %02X leads to "
1121 "device %s instead of %s\n", lpm,
1122 print_path_uid, print_device_uid);
1123 return -EINVAL;
1055 } 1124 }
1056 path_data->opm |= lpm; 1125
1057 if (conf_data != private->conf_data) 1126 path_private.conf_data = NULL;
1058 kfree(conf_data); 1127 path_private.conf_len = 0;
1059 } 1128 }
1129 switch (dasd_eckd_path_access(conf_data, conf_len)) {
1130 case 0x02:
1131 path_data->npm |= lpm;
1132 break;
1133 case 0x03:
1134 path_data->ppm |= lpm;
1135 break;
1136 }
1137 path_data->opm |= lpm;
1138
1139 if (conf_data != private->conf_data)
1140 kfree(conf_data);
1060 } 1141 }
1142
1061 return 0; 1143 return 0;
1062} 1144}
1063 1145
@@ -1090,12 +1172,61 @@ static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm)
1090 return 0; 1172 return 0;
1091} 1173}
1092 1174
1175static int rebuild_device_uid(struct dasd_device *device,
1176 struct path_verification_work_data *data)
1177{
1178 struct dasd_eckd_private *private;
1179 struct dasd_path *path_data;
1180 __u8 lpm, opm;
1181 int rc;
1182
1183 rc = -ENODEV;
1184 private = (struct dasd_eckd_private *) device->private;
1185 path_data = &device->path_data;
1186 opm = device->path_data.opm;
1187
1188 for (lpm = 0x80; lpm; lpm >>= 1) {
1189 if (!(lpm & opm))
1190 continue;
1191 memset(&data->rcd_buffer, 0, sizeof(data->rcd_buffer));
1192 memset(&data->cqr, 0, sizeof(data->cqr));
1193 data->cqr.cpaddr = &data->ccw;
1194 rc = dasd_eckd_read_conf_immediately(device, &data->cqr,
1195 data->rcd_buffer,
1196 lpm);
1197
1198 if (rc) {
1199 if (rc == -EOPNOTSUPP) /* -EOPNOTSUPP is ok */
1200 continue;
1201 DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
1202 "Read configuration data "
1203 "returned error %d", rc);
1204 break;
1205 }
1206 memcpy(private->conf_data, data->rcd_buffer,
1207 DASD_ECKD_RCD_DATA_SIZE);
1208 if (dasd_eckd_identify_conf_parts(private)) {
1209 rc = -ENODEV;
1210 } else /* first valid path is enough */
1211 break;
1212 }
1213
1214 if (!rc)
1215 rc = dasd_eckd_generate_uid(device);
1216
1217 return rc;
1218}
1219
1093static void do_path_verification_work(struct work_struct *work) 1220static void do_path_verification_work(struct work_struct *work)
1094{ 1221{
1095 struct path_verification_work_data *data; 1222 struct path_verification_work_data *data;
1096 struct dasd_device *device; 1223 struct dasd_device *device;
1224 struct dasd_eckd_private path_private;
1225 struct dasd_uid *uid;
1226 __u8 path_rcd_buf[DASD_ECKD_RCD_DATA_SIZE];
1097 __u8 lpm, opm, npm, ppm, epm; 1227 __u8 lpm, opm, npm, ppm, epm;
1098 unsigned long flags; 1228 unsigned long flags;
1229 char print_uid[60];
1099 int rc; 1230 int rc;
1100 1231
1101 data = container_of(work, struct path_verification_work_data, worker); 1232 data = container_of(work, struct path_verification_work_data, worker);
@@ -1112,64 +1243,129 @@ static void do_path_verification_work(struct work_struct *work)
1112 ppm = 0; 1243 ppm = 0;
1113 epm = 0; 1244 epm = 0;
1114 for (lpm = 0x80; lpm; lpm >>= 1) { 1245 for (lpm = 0x80; lpm; lpm >>= 1) {
1115 if (lpm & data->tbvpm) { 1246 if (!(lpm & data->tbvpm))
1116 memset(data->rcd_buffer, 0, sizeof(data->rcd_buffer)); 1247 continue;
1117 memset(&data->cqr, 0, sizeof(data->cqr)); 1248 memset(&data->rcd_buffer, 0, sizeof(data->rcd_buffer));
1118 data->cqr.cpaddr = &data->ccw; 1249 memset(&data->cqr, 0, sizeof(data->cqr));
1119 rc = dasd_eckd_read_conf_immediately(device, &data->cqr, 1250 data->cqr.cpaddr = &data->ccw;
1120 data->rcd_buffer, 1251 rc = dasd_eckd_read_conf_immediately(device, &data->cqr,
1121 lpm); 1252 data->rcd_buffer,
1122 if (!rc) { 1253 lpm);
1123 switch (dasd_eckd_path_access(data->rcd_buffer, 1254 if (!rc) {
1124 DASD_ECKD_RCD_DATA_SIZE)) { 1255 switch (dasd_eckd_path_access(data->rcd_buffer,
1125 case 0x02: 1256 DASD_ECKD_RCD_DATA_SIZE)
1126 npm |= lpm; 1257 ) {
1127 break; 1258 case 0x02:
1128 case 0x03: 1259 npm |= lpm;
1129 ppm |= lpm; 1260 break;
1130 break; 1261 case 0x03:
1131 } 1262 ppm |= lpm;
1132 opm |= lpm; 1263 break;
1133 } else if (rc == -EOPNOTSUPP) { 1264 }
1134 DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 1265 opm |= lpm;
1135 "path verification: No configuration " 1266 } else if (rc == -EOPNOTSUPP) {
1136 "data retrieved"); 1267 DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
1137 opm |= lpm; 1268 "path verification: No configuration "
1138 } else if (rc == -EAGAIN) { 1269 "data retrieved");
1139 DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 1270 opm |= lpm;
1271 } else if (rc == -EAGAIN) {
1272 DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
1140 "path verification: device is stopped," 1273 "path verification: device is stopped,"
1141 " try again later"); 1274 " try again later");
1142 epm |= lpm; 1275 epm |= lpm;
1143 } else { 1276 } else {
1144 dev_warn(&device->cdev->dev, 1277 dev_warn(&device->cdev->dev,
1145 "Reading device feature codes failed " 1278 "Reading device feature codes failed "
1146 "(rc=%d) for new path %x\n", rc, lpm); 1279 "(rc=%d) for new path %x\n", rc, lpm);
1147 continue; 1280 continue;
1148 } 1281 }
1149 if (verify_fcx_max_data(device, lpm)) { 1282 if (verify_fcx_max_data(device, lpm)) {
1283 opm &= ~lpm;
1284 npm &= ~lpm;
1285 ppm &= ~lpm;
1286 continue;
1287 }
1288
1289 /*
1290 * save conf_data for comparison after
1291 * rebuild_device_uid may have changed
1292 * the original data
1293 */
1294 memcpy(&path_rcd_buf, data->rcd_buffer,
1295 DASD_ECKD_RCD_DATA_SIZE);
1296 path_private.conf_data = (void *) &path_rcd_buf;
1297 path_private.conf_len = DASD_ECKD_RCD_DATA_SIZE;
1298 if (dasd_eckd_identify_conf_parts(&path_private)) {
1299 path_private.conf_data = NULL;
1300 path_private.conf_len = 0;
1301 continue;
1302 }
1303
1304 /*
1305 * compare path UID with device UID only if at least
1306 * one valid path is left
1307 * in other case the device UID may have changed and
1308 * the first working path UID will be used as device UID
1309 */
1310 if (device->path_data.opm &&
1311 dasd_eckd_compare_path_uid(device, &path_private)) {
1312 /*
1313 * the comparison was not successful
1314 * rebuild the device UID with at least one
1315 * known path in case a z/VM hyperswap command
1316 * has changed the device
1317 *
1318 * after this compare again
1319 *
1320 * if either the rebuild or the recompare fails
1321 * the path can not be used
1322 */
1323 if (rebuild_device_uid(device, data) ||
1324 dasd_eckd_compare_path_uid(
1325 device, &path_private)) {
1326 uid = &path_private.uid;
1327 if (strlen(uid->vduit) > 0)
1328 snprintf(print_uid, sizeof(print_uid),
1329 "%s.%s.%04x.%02x.%s",
1330 uid->vendor, uid->serial,
1331 uid->ssid, uid->real_unit_addr,
1332 uid->vduit);
1333 else
1334 snprintf(print_uid, sizeof(print_uid),
1335 "%s.%s.%04x.%02x",
1336 uid->vendor, uid->serial,
1337 uid->ssid,
1338 uid->real_unit_addr);
1339 dev_err(&device->cdev->dev,
1340 "The newly added channel path %02X "
1341 "will not be used because it leads "
1342 "to a different device %s\n",
1343 lpm, print_uid);
1150 opm &= ~lpm; 1344 opm &= ~lpm;
1151 npm &= ~lpm; 1345 npm &= ~lpm;
1152 ppm &= ~lpm; 1346 ppm &= ~lpm;
1347 continue;
1153 } 1348 }
1154 } 1349 }
1350
1351 /*
1352 * There is a small chance that a path is lost again between
1353 * above path verification and the following modification of
1354 * the device opm mask. We could avoid that race here by using
1355 * yet another path mask, but we rather deal with this unlikely
1356 * situation in dasd_start_IO.
1357 */
1358 spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
1359 if (!device->path_data.opm && opm) {
1360 device->path_data.opm = opm;
1361 dasd_generic_path_operational(device);
1362 } else
1363 device->path_data.opm |= opm;
1364 device->path_data.npm |= npm;
1365 device->path_data.ppm |= ppm;
1366 device->path_data.tbvpm |= epm;
1367 spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
1155 } 1368 }
1156 /*
1157 * There is a small chance that a path is lost again between
1158 * above path verification and the following modification of
1159 * the device opm mask. We could avoid that race here by using
1160 * yet another path mask, but we rather deal with this unlikely
1161 * situation in dasd_start_IO.
1162 */
1163 spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
1164 if (!device->path_data.opm && opm) {
1165 device->path_data.opm = opm;
1166 dasd_generic_path_operational(device);
1167 } else
1168 device->path_data.opm |= opm;
1169 device->path_data.npm |= npm;
1170 device->path_data.ppm |= ppm;
1171 device->path_data.tbvpm |= epm;
1172 spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
1173 1369
1174 dasd_put_device(device); 1370 dasd_put_device(device);
1175 if (data->isglobal) 1371 if (data->isglobal)
@@ -1441,11 +1637,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
1441 device->default_expires = value; 1637 device->default_expires = value;
1442 } 1638 }
1443 1639
1444 /* Generate device unique id */
1445 rc = dasd_eckd_generate_uid(device);
1446 if (rc)
1447 goto out_err1;
1448
1449 dasd_eckd_get_uid(device, &temp_uid); 1640 dasd_eckd_get_uid(device, &temp_uid);
1450 if (temp_uid.type == UA_BASE_DEVICE) { 1641 if (temp_uid.type == UA_BASE_DEVICE) {
1451 block = dasd_alloc_block(); 1642 block = dasd_alloc_block();