diff options
author | Stefan Haberland <stefan.haberland@de.ibm.com> | 2011-12-27 05:27:27 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2011-12-27 05:27:14 -0500 |
commit | b206181d636d416fde48c7f493d7ac5d935b57e3 (patch) | |
tree | d28c27ee31d1fbfc6cfb3b3abf01c465f2c2607a /drivers/s390/block | |
parent | e58b0d902f7c7c407bae9c8bc8d90fa1d06184c5 (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.c | 409 |
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 | /* | 756 | static void create_uid(struct dasd_eckd_private *private) |
757 | * Generate device unique id that specifies the physical device. | ||
758 | */ | ||
759 | static 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 | */ | ||
789 | static 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 | */ | ||
824 | static 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 | |||
814 | static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device, | 835 | static 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 | ||
1175 | static 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 | |||
1093 | static void do_path_verification_work(struct work_struct *work) | 1220 | static 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(); |