aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/block/dasd_alias.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/block/dasd_alias.c')
-rw-r--r--drivers/s390/block/dasd_alias.c62
1 files changed, 62 insertions, 0 deletions
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c
index cdc6c049c353..fd1231738ef4 100644
--- a/drivers/s390/block/dasd_alias.c
+++ b/drivers/s390/block/dasd_alias.c
@@ -152,6 +152,7 @@ static struct alias_lcu *_allocate_lcu(struct dasd_uid *uid)
INIT_WORK(&lcu->suc_data.worker, summary_unit_check_handling_work);
INIT_DELAYED_WORK(&lcu->ruac_data.dwork, lcu_update_work);
spin_lock_init(&lcu->lock);
+ init_completion(&lcu->lcu_setup);
return lcu;
out_err4:
@@ -240,6 +241,67 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device)
}
/*
+ * The first device to be registered on an LCU will have to do
+ * some additional setup steps to configure that LCU on the
+ * storage server. All further devices should wait with their
+ * initialization until the first device is done.
+ * To synchronize this work, the first device will call
+ * dasd_alias_lcu_setup_complete when it is done, and all
+ * other devices will wait for it with dasd_alias_wait_for_lcu_setup.
+ */
+void dasd_alias_lcu_setup_complete(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private;
+ unsigned long flags;
+ struct alias_server *server;
+ struct alias_lcu *lcu;
+ struct dasd_uid *uid;
+
+ private = (struct dasd_eckd_private *) device->private;
+ uid = &private->uid;
+ lcu = NULL;
+ spin_lock_irqsave(&aliastree.lock, flags);
+ server = _find_server(uid);
+ if (server)
+ lcu = _find_lcu(server, uid);
+ spin_unlock_irqrestore(&aliastree.lock, flags);
+ if (!lcu) {
+ DBF_EVENT_DEVID(DBF_ERR, device->cdev,
+ "could not find lcu for %04x %02x",
+ uid->ssid, uid->real_unit_addr);
+ WARN_ON(1);
+ return;
+ }
+ complete_all(&lcu->lcu_setup);
+}
+
+void dasd_alias_wait_for_lcu_setup(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private;
+ unsigned long flags;
+ struct alias_server *server;
+ struct alias_lcu *lcu;
+ struct dasd_uid *uid;
+
+ private = (struct dasd_eckd_private *) device->private;
+ uid = &private->uid;
+ lcu = NULL;
+ spin_lock_irqsave(&aliastree.lock, flags);
+ server = _find_server(uid);
+ if (server)
+ lcu = _find_lcu(server, uid);
+ spin_unlock_irqrestore(&aliastree.lock, flags);
+ if (!lcu) {
+ DBF_EVENT_DEVID(DBF_ERR, device->cdev,
+ "could not find lcu for %04x %02x",
+ uid->ssid, uid->real_unit_addr);
+ WARN_ON(1);
+ return;
+ }
+ wait_for_completion(&lcu->lcu_setup);
+}
+
+/*
* This function removes a device from the scope of alias management.
* The complicated part is to make sure that it is not in use by
* any of the workers. If necessary cancel the work.