diff options
Diffstat (limited to 'drivers/s390/block')
-rw-r--r-- | drivers/s390/block/dasd_alias.c | 62 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 37 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.h | 4 |
3 files changed, 89 insertions, 14 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. diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index e38a09b5feae..5819dc02a143 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1059,7 +1059,7 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav) /* * Valide storage server of current device. */ -static int dasd_eckd_validate_server(struct dasd_device *device) +static void dasd_eckd_validate_server(struct dasd_device *device) { int rc; struct dasd_eckd_private *private; @@ -1076,8 +1076,6 @@ static int dasd_eckd_validate_server(struct dasd_device *device) private = (struct dasd_eckd_private *) device->private; DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x " "returned rc=%d", private->uid.ssid, rc); - /* RE-Read Configuration Data */ - return dasd_eckd_read_conf(device); } /* @@ -1149,12 +1147,21 @@ dasd_eckd_check_characteristics(struct dasd_device *device) rc = is_known; goto out_err2; } + /* + * dasd_eckd_vaildate_server is done on the first device that + * is found for an LCU. All later other devices have to wait + * for it, so they will read the correct feature codes. + */ if (!is_known) { - /* new lcu found */ - rc = dasd_eckd_validate_server(device); /* will switch pav on */ - if (rc) - goto out_err3; - } + dasd_eckd_validate_server(device); + dasd_alias_lcu_setup_complete(device); + } else + dasd_alias_wait_for_lcu_setup(device); + + /* device may report different configuration data after LCU setup */ + rc = dasd_eckd_read_conf(device); + if (rc) + goto out_err3; /* Read Feature Codes */ dasd_eckd_read_features(device); @@ -3282,11 +3289,15 @@ int dasd_eckd_restore_device(struct dasd_device *device) if (is_known < 0) return is_known; if (!is_known) { - /* new lcu found */ - rc = dasd_eckd_validate_server(device); /* will switch pav on */ - if (rc) - goto out_err; - } + dasd_eckd_validate_server(device); + dasd_alias_lcu_setup_complete(device); + } else + dasd_alias_wait_for_lcu_setup(device); + + /* RE-Read Configuration Data */ + rc = dasd_eckd_read_conf(device); + if (rc) + goto out_err; /* Read Feature Codes */ dasd_eckd_read_features(device); diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index ad45bcac3ce4..864d53c04201 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -414,6 +414,7 @@ struct alias_lcu { struct summary_unit_check_work_data suc_data; struct read_uac_work_data ruac_data; struct dasd_ccw_req *rsu_cqr; + struct completion lcu_setup; }; struct alias_pav_group { @@ -460,5 +461,6 @@ int dasd_alias_remove_device(struct dasd_device *); struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *); void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *); void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *); - +void dasd_alias_lcu_setup_complete(struct dasd_device *); +void dasd_alias_wait_for_lcu_setup(struct dasd_device *); #endif /* DASD_ECKD_H */ |