aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/block/dasd_eckd.c
diff options
context:
space:
mode:
authorStefan Haberland <sth@linux.vnet.ibm.com>2016-02-23 10:15:27 +0100
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2016-03-17 13:18:25 +0100
commit59a9ed5f87b6e396aed1f6bf7d6496f7ba66c37a (patch)
treea007b19d3731854629ac5985fc56239ea4f5716e /drivers/s390/block/dasd_eckd.c
parents390/cpum_sf: Fix cpu hotplug notifier transitions (diff)
downloadlinux-dev-59a9ed5f87b6e396aed1f6bf7d6496f7ba66c37a.tar.xz
linux-dev-59a9ed5f87b6e396aed1f6bf7d6496f7ba66c37a.zip
s390/dasd: reorder lcu and device lock
Reorder lcu and device lock to get rid of the error-prone trylock mechanism. The locking order is lcu lock -> device lock. This protects against changes to the lcu device lists and enables us to iterate over the devices, take the cdev lock and make changes to the device structures. The complicated part is the summary unit check handler that gets an interrupt on one device of the lcu that leads to structural changes of the whole lcu itself. This work needs to be done even if devices on the lcu disappear. So a device independent worker is used. The old approach tried to update some lcu structures and set up the lcu worker in the interrupt context with the device lock held. But this forced the lock order "cdev lock -> lcu lock" that made it hard to have the lcu lock held and iterate over all devices and change them. The new approach is to schedule a device specific worker that gets out of the interrupt context and rid of the device lock for summary unit checks. This worker is able to take the lcu lock and schedule the lcu worker that updates all devices. The time between interrupt and worker execution is no problem because the devices in the lcu reject all I/O in this time with an appropriate error. The dasd driver can deal with this situation and re-drive the I/O later on. Signed-off-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/block/dasd_eckd.c')
-rw-r--r--drivers/s390/block/dasd_eckd.c38
1 files changed, 29 insertions, 9 deletions
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 75c032dcf173..c1b4ae55e129 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1682,6 +1682,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
/* setup work queue for validate server*/
INIT_WORK(&device->kick_validate, dasd_eckd_do_validate_server);
+ /* setup work queue for summary unit check */
+ INIT_WORK(&device->suc_work, dasd_alias_handle_summary_unit_check);
if (!ccw_device_is_pathgroup(device->cdev)) {
dev_warn(&device->cdev->dev,
@@ -2549,14 +2551,6 @@ static void dasd_eckd_check_for_device_change(struct dasd_device *device,
device->state == DASD_STATE_ONLINE &&
!test_bit(DASD_FLAG_OFFLINE, &device->flags) &&
!test_bit(DASD_FLAG_SUSPENDED, &device->flags)) {
- /*
- * the state change could be caused by an alias
- * reassignment remove device from alias handling
- * to prevent new requests from being scheduled on
- * the wrong alias device
- */
- dasd_alias_remove_device(device);
-
/* schedule worker to reload device */
dasd_reload_device(device);
}
@@ -2571,7 +2565,27 @@ static void dasd_eckd_check_for_device_change(struct dasd_device *device,
/* summary unit check */
if ((sense[27] & DASD_SENSE_BIT_0) && (sense[7] == 0x0D) &&
(scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) {
- dasd_alias_handle_summary_unit_check(device, irb);
+ if (test_and_set_bit(DASD_FLAG_SUC, &device->flags)) {
+ DBF_DEV_EVENT(DBF_WARNING, device, "%s",
+ "eckd suc: device already notified");
+ return;
+ }
+ sense = dasd_get_sense(irb);
+ if (!sense) {
+ DBF_DEV_EVENT(DBF_WARNING, device, "%s",
+ "eckd suc: no reason code available");
+ clear_bit(DASD_FLAG_SUC, &device->flags);
+ return;
+
+ }
+ private->suc_reason = sense[8];
+ DBF_DEV_EVENT(DBF_NOTICE, device, "%s %x",
+ "eckd handle summary unit check: reason",
+ private->suc_reason);
+ dasd_get_device(device);
+ if (!schedule_work(&device->suc_work))
+ dasd_put_device(device);
+
return;
}
@@ -4495,6 +4509,12 @@ static int dasd_eckd_reload_device(struct dasd_device *device)
struct dasd_uid uid;
unsigned long flags;
+ /*
+ * remove device from alias handling to prevent new requests
+ * from being scheduled on the wrong alias device
+ */
+ dasd_alias_remove_device(device);
+
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
old_base = private->uid.base_unit_addr;
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);