diff options
Diffstat (limited to 'drivers/s390/block/dasd_eckd.c')
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 116 |
1 files changed, 103 insertions, 13 deletions
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 0cb23311685..5b1cd8d6e97 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c | |||
@@ -692,18 +692,20 @@ dasd_eckd_cdl_reclen(int recid) | |||
692 | /* | 692 | /* |
693 | * Generate device unique id that specifies the physical device. | 693 | * Generate device unique id that specifies the physical device. |
694 | */ | 694 | */ |
695 | static int dasd_eckd_generate_uid(struct dasd_device *device, | 695 | static int dasd_eckd_generate_uid(struct dasd_device *device) |
696 | struct dasd_uid *uid) | ||
697 | { | 696 | { |
698 | struct dasd_eckd_private *private; | 697 | struct dasd_eckd_private *private; |
698 | struct dasd_uid *uid; | ||
699 | int count; | 699 | int count; |
700 | unsigned long flags; | ||
700 | 701 | ||
701 | private = (struct dasd_eckd_private *) device->private; | 702 | private = (struct dasd_eckd_private *) device->private; |
702 | if (!private) | 703 | if (!private) |
703 | return -ENODEV; | 704 | return -ENODEV; |
704 | if (!private->ned || !private->gneq) | 705 | if (!private->ned || !private->gneq) |
705 | return -ENODEV; | 706 | return -ENODEV; |
706 | 707 | uid = &private->uid; | |
708 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); | ||
707 | memset(uid, 0, sizeof(struct dasd_uid)); | 709 | memset(uid, 0, sizeof(struct dasd_uid)); |
708 | memcpy(uid->vendor, private->ned->HDA_manufacturer, | 710 | memcpy(uid->vendor, private->ned->HDA_manufacturer, |
709 | sizeof(uid->vendor) - 1); | 711 | sizeof(uid->vendor) - 1); |
@@ -726,9 +728,25 @@ static int dasd_eckd_generate_uid(struct dasd_device *device, | |||
726 | private->vdsneq->uit[count]); | 728 | private->vdsneq->uit[count]); |
727 | } | 729 | } |
728 | } | 730 | } |
731 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); | ||
729 | return 0; | 732 | return 0; |
730 | } | 733 | } |
731 | 734 | ||
735 | static int dasd_eckd_get_uid(struct dasd_device *device, struct dasd_uid *uid) | ||
736 | { | ||
737 | struct dasd_eckd_private *private; | ||
738 | unsigned long flags; | ||
739 | |||
740 | if (device->private) { | ||
741 | private = (struct dasd_eckd_private *)device->private; | ||
742 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); | ||
743 | *uid = private->uid; | ||
744 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); | ||
745 | return 0; | ||
746 | } | ||
747 | return -EINVAL; | ||
748 | } | ||
749 | |||
732 | static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, | 750 | static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, |
733 | void *rcd_buffer, | 751 | void *rcd_buffer, |
734 | struct ciw *ciw, __u8 lpm) | 752 | struct ciw *ciw, __u8 lpm) |
@@ -1088,6 +1106,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device) | |||
1088 | { | 1106 | { |
1089 | struct dasd_eckd_private *private; | 1107 | struct dasd_eckd_private *private; |
1090 | struct dasd_block *block; | 1108 | struct dasd_block *block; |
1109 | struct dasd_uid temp_uid; | ||
1091 | int is_known, rc; | 1110 | int is_known, rc; |
1092 | int readonly; | 1111 | int readonly; |
1093 | 1112 | ||
@@ -1124,13 +1143,13 @@ dasd_eckd_check_characteristics(struct dasd_device *device) | |||
1124 | if (rc) | 1143 | if (rc) |
1125 | goto out_err1; | 1144 | goto out_err1; |
1126 | 1145 | ||
1127 | /* Generate device unique id and register in devmap */ | 1146 | /* Generate device unique id */ |
1128 | rc = dasd_eckd_generate_uid(device, &private->uid); | 1147 | rc = dasd_eckd_generate_uid(device); |
1129 | if (rc) | 1148 | if (rc) |
1130 | goto out_err1; | 1149 | goto out_err1; |
1131 | dasd_set_uid(device->cdev, &private->uid); | ||
1132 | 1150 | ||
1133 | if (private->uid.type == UA_BASE_DEVICE) { | 1151 | dasd_eckd_get_uid(device, &temp_uid); |
1152 | if (temp_uid.type == UA_BASE_DEVICE) { | ||
1134 | block = dasd_alloc_block(); | 1153 | block = dasd_alloc_block(); |
1135 | if (IS_ERR(block)) { | 1154 | if (IS_ERR(block)) { |
1136 | DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", | 1155 | DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", |
@@ -1451,6 +1470,7 @@ static int dasd_eckd_ready_to_online(struct dasd_device *device) | |||
1451 | 1470 | ||
1452 | static int dasd_eckd_online_to_ready(struct dasd_device *device) | 1471 | static int dasd_eckd_online_to_ready(struct dasd_device *device) |
1453 | { | 1472 | { |
1473 | cancel_work_sync(&device->reload_device); | ||
1454 | return dasd_alias_remove_device(device); | 1474 | return dasd_alias_remove_device(device); |
1455 | }; | 1475 | }; |
1456 | 1476 | ||
@@ -1709,10 +1729,27 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, | |||
1709 | { | 1729 | { |
1710 | char mask; | 1730 | char mask; |
1711 | char *sense = NULL; | 1731 | char *sense = NULL; |
1732 | struct dasd_eckd_private *private; | ||
1712 | 1733 | ||
1734 | private = (struct dasd_eckd_private *) device->private; | ||
1713 | /* first of all check for state change pending interrupt */ | 1735 | /* first of all check for state change pending interrupt */ |
1714 | mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; | 1736 | mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; |
1715 | if ((scsw_dstat(&irb->scsw) & mask) == mask) { | 1737 | if ((scsw_dstat(&irb->scsw) & mask) == mask) { |
1738 | /* for alias only and not in offline processing*/ | ||
1739 | if (!device->block && private->lcu && | ||
1740 | !test_bit(DASD_FLAG_OFFLINE, &device->flags)) { | ||
1741 | /* | ||
1742 | * the state change could be caused by an alias | ||
1743 | * reassignment remove device from alias handling | ||
1744 | * to prevent new requests from being scheduled on | ||
1745 | * the wrong alias device | ||
1746 | */ | ||
1747 | dasd_alias_remove_device(device); | ||
1748 | |||
1749 | /* schedule worker to reload device */ | ||
1750 | dasd_reload_device(device); | ||
1751 | } | ||
1752 | |||
1716 | dasd_generic_handle_state_change(device); | 1753 | dasd_generic_handle_state_change(device); |
1717 | return; | 1754 | return; |
1718 | } | 1755 | } |
@@ -3259,7 +3296,7 @@ static void dasd_eckd_dump_sense(struct dasd_device *device, | |||
3259 | dasd_eckd_dump_sense_ccw(device, req, irb); | 3296 | dasd_eckd_dump_sense_ccw(device, req, irb); |
3260 | } | 3297 | } |
3261 | 3298 | ||
3262 | int dasd_eckd_pm_freeze(struct dasd_device *device) | 3299 | static int dasd_eckd_pm_freeze(struct dasd_device *device) |
3263 | { | 3300 | { |
3264 | /* | 3301 | /* |
3265 | * the device should be disconnected from our LCU structure | 3302 | * the device should be disconnected from our LCU structure |
@@ -3272,7 +3309,7 @@ int dasd_eckd_pm_freeze(struct dasd_device *device) | |||
3272 | return 0; | 3309 | return 0; |
3273 | } | 3310 | } |
3274 | 3311 | ||
3275 | int dasd_eckd_restore_device(struct dasd_device *device) | 3312 | static int dasd_eckd_restore_device(struct dasd_device *device) |
3276 | { | 3313 | { |
3277 | struct dasd_eckd_private *private; | 3314 | struct dasd_eckd_private *private; |
3278 | struct dasd_eckd_characteristics temp_rdc_data; | 3315 | struct dasd_eckd_characteristics temp_rdc_data; |
@@ -3287,15 +3324,16 @@ int dasd_eckd_restore_device(struct dasd_device *device) | |||
3287 | if (rc) | 3324 | if (rc) |
3288 | goto out_err; | 3325 | goto out_err; |
3289 | 3326 | ||
3290 | /* Generate device unique id and register in devmap */ | 3327 | dasd_eckd_get_uid(device, &temp_uid); |
3291 | rc = dasd_eckd_generate_uid(device, &private->uid); | 3328 | /* Generate device unique id */ |
3292 | dasd_get_uid(device->cdev, &temp_uid); | 3329 | rc = dasd_eckd_generate_uid(device); |
3330 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); | ||
3293 | if (memcmp(&private->uid, &temp_uid, sizeof(struct dasd_uid)) != 0) | 3331 | if (memcmp(&private->uid, &temp_uid, sizeof(struct dasd_uid)) != 0) |
3294 | dev_err(&device->cdev->dev, "The UID of the DASD has " | 3332 | dev_err(&device->cdev->dev, "The UID of the DASD has " |
3295 | "changed\n"); | 3333 | "changed\n"); |
3334 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); | ||
3296 | if (rc) | 3335 | if (rc) |
3297 | goto out_err; | 3336 | goto out_err; |
3298 | dasd_set_uid(device->cdev, &private->uid); | ||
3299 | 3337 | ||
3300 | /* register lcu with alias handling, enable PAV if this is a new lcu */ | 3338 | /* register lcu with alias handling, enable PAV if this is a new lcu */ |
3301 | is_known = dasd_alias_make_device_known_to_lcu(device); | 3339 | is_known = dasd_alias_make_device_known_to_lcu(device); |
@@ -3336,6 +3374,56 @@ out_err: | |||
3336 | return -1; | 3374 | return -1; |
3337 | } | 3375 | } |
3338 | 3376 | ||
3377 | static int dasd_eckd_reload_device(struct dasd_device *device) | ||
3378 | { | ||
3379 | struct dasd_eckd_private *private; | ||
3380 | int rc, old_base; | ||
3381 | char print_uid[60]; | ||
3382 | struct dasd_uid uid; | ||
3383 | unsigned long flags; | ||
3384 | |||
3385 | private = (struct dasd_eckd_private *) device->private; | ||
3386 | |||
3387 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); | ||
3388 | old_base = private->uid.base_unit_addr; | ||
3389 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); | ||
3390 | |||
3391 | /* Read Configuration Data */ | ||
3392 | rc = dasd_eckd_read_conf(device); | ||
3393 | if (rc) | ||
3394 | goto out_err; | ||
3395 | |||
3396 | rc = dasd_eckd_generate_uid(device); | ||
3397 | if (rc) | ||
3398 | goto out_err; | ||
3399 | /* | ||
3400 | * update unit address configuration and | ||
3401 | * add device to alias management | ||
3402 | */ | ||
3403 | dasd_alias_update_add_device(device); | ||
3404 | |||
3405 | dasd_eckd_get_uid(device, &uid); | ||
3406 | |||
3407 | if (old_base != uid.base_unit_addr) { | ||
3408 | if (strlen(uid.vduit) > 0) | ||
3409 | snprintf(print_uid, sizeof(print_uid), | ||
3410 | "%s.%s.%04x.%02x.%s", uid.vendor, uid.serial, | ||
3411 | uid.ssid, uid.base_unit_addr, uid.vduit); | ||
3412 | else | ||
3413 | snprintf(print_uid, sizeof(print_uid), | ||
3414 | "%s.%s.%04x.%02x", uid.vendor, uid.serial, | ||
3415 | uid.ssid, uid.base_unit_addr); | ||
3416 | |||
3417 | dev_info(&device->cdev->dev, | ||
3418 | "An Alias device was reassigned to a new base device " | ||
3419 | "with UID: %s\n", print_uid); | ||
3420 | } | ||
3421 | return 0; | ||
3422 | |||
3423 | out_err: | ||
3424 | return -1; | ||
3425 | } | ||
3426 | |||
3339 | static struct ccw_driver dasd_eckd_driver = { | 3427 | static struct ccw_driver dasd_eckd_driver = { |
3340 | .name = "dasd-eckd", | 3428 | .name = "dasd-eckd", |
3341 | .owner = THIS_MODULE, | 3429 | .owner = THIS_MODULE, |
@@ -3389,6 +3477,8 @@ static struct dasd_discipline dasd_eckd_discipline = { | |||
3389 | .ioctl = dasd_eckd_ioctl, | 3477 | .ioctl = dasd_eckd_ioctl, |
3390 | .freeze = dasd_eckd_pm_freeze, | 3478 | .freeze = dasd_eckd_pm_freeze, |
3391 | .restore = dasd_eckd_restore_device, | 3479 | .restore = dasd_eckd_restore_device, |
3480 | .reload = dasd_eckd_reload_device, | ||
3481 | .get_uid = dasd_eckd_get_uid, | ||
3392 | }; | 3482 | }; |
3393 | 3483 | ||
3394 | static int __init | 3484 | static int __init |