diff options
Diffstat (limited to 'drivers/s390/block/dasd_alias.c')
| -rw-r--r-- | drivers/s390/block/dasd_alias.c | 337 |
1 files changed, 160 insertions, 177 deletions
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index a2597e683e79..f7e768d8ca76 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * PAV alias management for the DASD ECKD discipline * @@ -5,20 +6,12 @@ * Author(s): Stefan Weinhuber <wein@de.ibm.com> */ -#define KMSG_COMPONENT "dasd-eckd" - #include <linux/list.h> #include <linux/slab.h> #include <asm/ebcdic.h> #include "dasd_int.h" #include "dasd_eckd.h" -#ifdef PRINTK_HEADER -#undef PRINTK_HEADER -#endif /* PRINTK_HEADER */ -#define PRINTK_HEADER "dasd(eckd):" - - /* * General concept of alias management: * - PAV and DASD alias management is specific to the eckd discipline. @@ -58,7 +51,7 @@ static struct alias_server *_find_server(struct dasd_uid *uid) && !strncmp(pos->uid.serial, uid->serial, sizeof(uid->serial))) return pos; - }; + } return NULL; } @@ -69,7 +62,7 @@ static struct alias_lcu *_find_lcu(struct alias_server *server, list_for_each_entry(pos, &server->lculist, lcu) { if (pos->uid.ssid == uid->ssid) return pos; - }; + } return NULL; } @@ -97,7 +90,7 @@ static struct alias_pav_group *_find_group(struct alias_lcu *lcu, if (pos->uid.base_unit_addr == search_unit_addr && !strncmp(pos->uid.vduit, uid->vduit, sizeof(uid->vduit))) return pos; - }; + } return NULL; } @@ -185,14 +178,12 @@ static void _free_lcu(struct alias_lcu *lcu) */ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) { - struct dasd_eckd_private *private; + struct dasd_eckd_private *private = device->private; unsigned long flags; struct alias_server *server, *newserver; struct alias_lcu *lcu, *newlcu; struct dasd_uid uid; - private = (struct dasd_eckd_private *) device->private; - device->discipline->get_uid(device, &uid); spin_lock_irqsave(&aliastree.lock, flags); server = _find_server(&uid); @@ -244,28 +235,28 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) */ void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device) { - struct dasd_eckd_private *private; + struct dasd_eckd_private *private = device->private; unsigned long flags; struct alias_lcu *lcu; struct alias_server *server; int was_pending; struct dasd_uid uid; - private = (struct dasd_eckd_private *) device->private; lcu = private->lcu; /* nothing to do if already disconnected */ if (!lcu) return; device->discipline->get_uid(device, &uid); spin_lock_irqsave(&lcu->lock, flags); - list_del_init(&device->alias_list); /* make sure that the workers don't use this device */ if (device == lcu->suc_data.device) { spin_unlock_irqrestore(&lcu->lock, flags); cancel_work_sync(&lcu->suc_data.worker); spin_lock_irqsave(&lcu->lock, flags); - if (device == lcu->suc_data.device) + if (device == lcu->suc_data.device) { + dasd_put_device(device); lcu->suc_data.device = NULL; + } } was_pending = 0; if (device == lcu->ruac_data.device) { @@ -273,14 +264,17 @@ void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device) was_pending = 1; cancel_delayed_work_sync(&lcu->ruac_data.dwork); spin_lock_irqsave(&lcu->lock, flags); - if (device == lcu->ruac_data.device) + if (device == lcu->ruac_data.device) { + dasd_put_device(device); lcu->ruac_data.device = NULL; + } } private->lcu = NULL; spin_unlock_irqrestore(&lcu->lock, flags); spin_lock_irqsave(&aliastree.lock, flags); spin_lock(&lcu->lock); + list_del_init(&device->alias_list); if (list_empty(&lcu->grouplist) && list_empty(&lcu->active_devices) && list_empty(&lcu->inactive_devices)) { @@ -312,31 +306,21 @@ static int _add_device_to_lcu(struct alias_lcu *lcu, struct dasd_device *pos) { - struct dasd_eckd_private *private; + struct dasd_eckd_private *private = device->private; struct alias_pav_group *group; struct dasd_uid uid; - unsigned long flags; - - private = (struct dasd_eckd_private *) device->private; - /* only lock if not already locked */ - if (device != pos) - spin_lock_irqsave_nested(get_ccwdev_lock(device->cdev), flags, - CDEV_NESTED_SECOND); + spin_lock(get_ccwdev_lock(device->cdev)); private->uid.type = lcu->uac->unit[private->uid.real_unit_addr].ua_type; private->uid.base_unit_addr = lcu->uac->unit[private->uid.real_unit_addr].base_ua; uid = private->uid; - - if (device != pos) - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); - + spin_unlock(get_ccwdev_lock(device->cdev)); /* if we have no PAV anyway, we don't need to bother with PAV groups */ if (lcu->pav == NO_PAV) { list_move(&device->alias_list, &lcu->active_devices); return 0; } - group = _find_group(lcu, &uid); if (!group) { group = kzalloc(sizeof(*group), GFP_ATOMIC); @@ -366,10 +350,9 @@ static int _add_device_to_lcu(struct alias_lcu *lcu, static void _remove_device_from_lcu(struct alias_lcu *lcu, struct dasd_device *device) { - struct dasd_eckd_private *private; + struct dasd_eckd_private *private = device->private; struct alias_pav_group *group; - private = (struct dasd_eckd_private *) device->private; list_move(&device->alias_list, &lcu->inactive_devices); group = private->pavgroup; if (!group) @@ -392,6 +375,20 @@ suborder_not_supported(struct dasd_ccw_req *cqr) char msg_format; char msg_no; + /* + * intrc values ENODEV, ENOLINK and EPERM + * will be optained from sleep_on to indicate that no + * IO operation can be started + */ + if (cqr->intrc == -ENODEV) + return 1; + + if (cqr->intrc == -ENOLINK) + return 1; + + if (cqr->intrc == -EPERM) + return 1; + sense = dasd_get_sense(&cqr->irb); if (!sense) return 0; @@ -416,9 +413,9 @@ static int read_unit_address_configuration(struct dasd_device *device, int rc; unsigned long flags; - cqr = dasd_kmalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, (sizeof(struct dasd_psf_prssd_data)), - device); + device, NULL); if (IS_ERR(cqr)) return PTR_ERR(cqr); cqr->startdev = device; @@ -438,7 +435,7 @@ static int read_unit_address_configuration(struct dasd_device *device, ccw->cmd_code = DASD_ECKD_CCW_PSF; ccw->count = sizeof(struct dasd_psf_prssd_data); ccw->flags |= CCW_FLAG_CC; - ccw->cda = (__u32)(addr_t) prssdp; + ccw->cda = virt_to_dma32(prssdp); /* Read Subsystem Data - feature codes */ memset(lcu->uac, 0, sizeof(*(lcu->uac))); @@ -446,7 +443,7 @@ static int read_unit_address_configuration(struct dasd_device *device, ccw++; ccw->cmd_code = DASD_ECKD_CCW_RSSD; ccw->count = sizeof(*(lcu->uac)); - ccw->cda = (__u32)(addr_t) lcu->uac; + ccw->cda = virt_to_dma32(lcu->uac); cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; @@ -456,17 +453,21 @@ static int read_unit_address_configuration(struct dasd_device *device, lcu->flags &= ~NEED_UAC_UPDATE; spin_unlock_irqrestore(&lcu->lock, flags); - do { - rc = dasd_sleep_on(cqr); - if (rc && suborder_not_supported(cqr)) - return -EOPNOTSUPP; - } while (rc && (cqr->retries > 0)); - if (rc) { + rc = dasd_sleep_on(cqr); + if (!rc) + goto out; + + if (suborder_not_supported(cqr)) { + /* suborder not supported or device unusable for IO */ + rc = -EOPNOTSUPP; + } else { + /* IO failed but should be retried */ spin_lock_irqsave(&lcu->lock, flags); lcu->flags |= NEED_UAC_UPDATE; spin_unlock_irqrestore(&lcu->lock, flags); } - dasd_kfree_request(cqr, cqr->memdev); +out: + dasd_sfree_request(cqr, cqr->memdev); return rc; } @@ -483,13 +484,13 @@ static int _lcu_update(struct dasd_device *refdev, struct alias_lcu *lcu) list_for_each_entry_safe(device, tempdev, &pavgroup->baselist, alias_list) { list_move(&device->alias_list, &lcu->active_devices); - private = (struct dasd_eckd_private *) device->private; + private = device->private; private->pavgroup = NULL; } list_for_each_entry_safe(device, tempdev, &pavgroup->aliaslist, alias_list) { list_move(&device->alias_list, &lcu->active_devices); - private = (struct dasd_eckd_private *) device->private; + private = device->private; private->pavgroup = NULL; } list_del(&pavgroup->group); @@ -501,10 +502,15 @@ static int _lcu_update(struct dasd_device *refdev, struct alias_lcu *lcu) if (rc) return rc; - /* need to take cdev lock before lcu lock */ - spin_lock_irqsave_nested(get_ccwdev_lock(refdev->cdev), flags, - CDEV_NESTED_FIRST); - spin_lock(&lcu->lock); + spin_lock_irqsave(&lcu->lock, flags); + /* + * there is another update needed skip the remaining handling + * the data might already be outdated + * but especially do not add the device to an LCU with pending + * update + */ + if (lcu->flags & NEED_UAC_UPDATE) + goto out; lcu->pav = NO_PAV; for (i = 0; i < MAX_DEVICES_PER_LCU; ++i) { switch (lcu->uac->unit[i].ua_type) { @@ -523,8 +529,8 @@ static int _lcu_update(struct dasd_device *refdev, struct alias_lcu *lcu) alias_list) { _add_device_to_lcu(lcu, device, refdev); } - spin_unlock(&lcu->lock); - spin_unlock_irqrestore(get_ccwdev_lock(refdev->cdev), flags); +out: + spin_unlock_irqrestore(&lcu->lock, flags); return 0; } @@ -549,8 +555,10 @@ static void lcu_update_work(struct work_struct *work) if ((rc && (rc != -EOPNOTSUPP)) || (lcu->flags & NEED_UAC_UPDATE)) { DBF_DEV_EVENT(DBF_WARNING, device, "could not update" " alias data in lcu (rc = %d), retry later", rc); - schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ); + if (!schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ)) + dasd_put_device(device); } else { + dasd_put_device(device); lcu->ruac_data.device = NULL; lcu->flags &= ~UPDATE_PENDING; } @@ -593,25 +601,32 @@ static int _schedule_lcu_update(struct alias_lcu *lcu, */ if (!usedev) return -EINVAL; + dasd_get_device(usedev); lcu->ruac_data.device = usedev; - schedule_delayed_work(&lcu->ruac_data.dwork, 0); + if (!schedule_delayed_work(&lcu->ruac_data.dwork, 0)) + dasd_put_device(usedev); return 0; } int dasd_alias_add_device(struct dasd_device *device) { - struct dasd_eckd_private *private; - struct alias_lcu *lcu; + struct dasd_eckd_private *private = device->private; + __u8 uaddr = private->uid.real_unit_addr; + struct alias_lcu *lcu = private->lcu; unsigned long flags; int rc; - private = (struct dasd_eckd_private *) device->private; - lcu = private->lcu; rc = 0; - - /* need to take cdev lock before lcu lock */ - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - spin_lock(&lcu->lock); + spin_lock_irqsave(&lcu->lock, flags); + /* + * Check if device and lcu type differ. If so, the uac data may be + * outdated and needs to be updated. + */ + if (private->uid.type != lcu->uac->unit[uaddr].ua_type) { + lcu->flags |= UPDATE_PENDING; + DBF_DEV_EVENT(DBF_WARNING, device, "%s", + "uid type mismatch - trigger rescan"); + } if (!(lcu->flags & UPDATE_PENDING)) { rc = _add_device_to_lcu(lcu, device, device); if (rc) @@ -619,29 +634,27 @@ int dasd_alias_add_device(struct dasd_device *device) } if (lcu->flags & UPDATE_PENDING) { list_move(&device->alias_list, &lcu->active_devices); + private->pavgroup = NULL; _schedule_lcu_update(lcu, device); } - spin_unlock(&lcu->lock); - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + spin_unlock_irqrestore(&lcu->lock, flags); return rc; } int dasd_alias_update_add_device(struct dasd_device *device) { - struct dasd_eckd_private *private; - private = (struct dasd_eckd_private *) device->private; + struct dasd_eckd_private *private = device->private; + private->lcu->flags |= UPDATE_PENDING; return dasd_alias_add_device(device); } int dasd_alias_remove_device(struct dasd_device *device) { - struct dasd_eckd_private *private; - struct alias_lcu *lcu; + struct dasd_eckd_private *private = device->private; + struct alias_lcu *lcu = private->lcu; unsigned long flags; - private = (struct dasd_eckd_private *) device->private; - lcu = private->lcu; /* nothing to do if already removed */ if (!lcu) return 0; @@ -653,17 +666,13 @@ int dasd_alias_remove_device(struct dasd_device *device) struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device) { - + struct dasd_eckd_private *alias_priv, *private = base_device->private; + struct alias_lcu *lcu = private->lcu; struct dasd_device *alias_device; struct alias_pav_group *group; - struct alias_lcu *lcu; - struct dasd_eckd_private *private, *alias_priv; unsigned long flags; - private = (struct dasd_eckd_private *) base_device->private; - group = private->pavgroup; - lcu = private->lcu; - if (!group || !lcu) + if (!lcu) return NULL; if (lcu->pav == NO_PAV || lcu->flags & (NEED_UAC_UPDATE | UPDATE_PENDING)) @@ -680,6 +689,11 @@ struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device) } spin_lock_irqsave(&lcu->lock, flags); + group = private->pavgroup; + if (!group) { + spin_unlock_irqrestore(&lcu->lock, flags); + return NULL; + } alias_device = group->next; if (!alias_device) { if (list_empty(&group->aliaslist)) { @@ -698,8 +712,9 @@ struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device) group->next = list_first_entry(&alias_device->alias_list, struct dasd_device, alias_list); spin_unlock_irqrestore(&lcu->lock, flags); - alias_priv = (struct dasd_eckd_private *) alias_device->private; - if ((alias_priv->count < private->count) && !alias_device->stopped) + alias_priv = alias_device->private; + if ((alias_priv->count < private->count) && !alias_device->stopped && + !test_bit(DASD_FLAG_OFFLINE, &alias_device->flags)) return alias_device; else return NULL; @@ -718,13 +733,13 @@ static int reset_summary_unit_check(struct alias_lcu *lcu, struct ccw1 *ccw; cqr = lcu->rsu_cqr; - strncpy((char *) &cqr->magic, "ECKD", 4); + memcpy((char *) &cqr->magic, "ECKD", 4); ASCEBC((char *) &cqr->magic, 4); ccw = cqr->cpaddr; ccw->cmd_code = DASD_ECKD_CCW_RSCK; - ccw->flags = 0 ; + ccw->flags = CCW_FLAG_SLI; ccw->count = 16; - ccw->cda = (__u32)(addr_t) cqr->data; + ccw->cda = virt_to_dma32(cqr->data); ((char *)cqr->data)[0] = reason; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); @@ -745,30 +760,19 @@ static void _restart_all_base_devices_on_lcu(struct alias_lcu *lcu) struct alias_pav_group *pavgroup; struct dasd_device *device; struct dasd_eckd_private *private; - unsigned long flags; /* active and inactive list can contain alias as well as base devices */ list_for_each_entry(device, &lcu->active_devices, alias_list) { - private = (struct dasd_eckd_private *) device->private; - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - if (private->uid.type != UA_BASE_DEVICE) { - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), - flags); + private = device->private; + if (private->uid.type != UA_BASE_DEVICE) continue; - } - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); dasd_schedule_block_bh(device->block); dasd_schedule_device_bh(device); } list_for_each_entry(device, &lcu->inactive_devices, alias_list) { - private = (struct dasd_eckd_private *) device->private; - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - if (private->uid.type != UA_BASE_DEVICE) { - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), - flags); + private = device->private; + if (private->uid.type != UA_BASE_DEVICE) continue; - } - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); dasd_schedule_block_bh(device->block); dasd_schedule_device_bh(device); } @@ -785,7 +789,6 @@ static void flush_all_alias_devices_on_lcu(struct alias_lcu *lcu) struct alias_pav_group *pavgroup; struct dasd_device *device, *temp; struct dasd_eckd_private *private; - int rc; unsigned long flags; LIST_HEAD(active); @@ -803,7 +806,7 @@ static void flush_all_alias_devices_on_lcu(struct alias_lcu *lcu) spin_lock_irqsave(&lcu->lock, flags); list_for_each_entry_safe(device, temp, &lcu->active_devices, alias_list) { - private = (struct dasd_eckd_private *) device->private; + private = device->private; if (private->uid.type == UA_BASE_DEVICE) continue; list_move(&device->alias_list, &active); @@ -816,51 +819,48 @@ static void flush_all_alias_devices_on_lcu(struct alias_lcu *lcu) device = list_first_entry(&active, struct dasd_device, alias_list); spin_unlock_irqrestore(&lcu->lock, flags); - rc = dasd_flush_device_queue(device); + dasd_flush_device_queue(device); spin_lock_irqsave(&lcu->lock, flags); /* * only move device around if it wasn't moved away while we * were waiting for the flush */ if (device == list_first_entry(&active, - struct dasd_device, alias_list)) + struct dasd_device, alias_list)) { list_move(&device->alias_list, &lcu->active_devices); + private = device->private; + private->pavgroup = NULL; + } } spin_unlock_irqrestore(&lcu->lock, flags); } -static void __stop_device_on_lcu(struct dasd_device *device, - struct dasd_device *pos) -{ - /* If pos == device then device is already locked! */ - if (pos == device) { - dasd_device_set_stop_bits(pos, DASD_STOPPED_SU); - return; - } - spin_lock(get_ccwdev_lock(pos->cdev)); - dasd_device_set_stop_bits(pos, DASD_STOPPED_SU); - spin_unlock(get_ccwdev_lock(pos->cdev)); -} - -/* - * This function is called in interrupt context, so the - * cdev lock for device is already locked! - */ -static void _stop_all_devices_on_lcu(struct alias_lcu *lcu, - struct dasd_device *device) +static void _stop_all_devices_on_lcu(struct alias_lcu *lcu) { struct alias_pav_group *pavgroup; - struct dasd_device *pos; + struct dasd_device *device; - list_for_each_entry(pos, &lcu->active_devices, alias_list) - __stop_device_on_lcu(device, pos); - list_for_each_entry(pos, &lcu->inactive_devices, alias_list) - __stop_device_on_lcu(device, pos); + list_for_each_entry(device, &lcu->active_devices, alias_list) { + spin_lock(get_ccwdev_lock(device->cdev)); + dasd_device_set_stop_bits(device, DASD_STOPPED_SU); + spin_unlock(get_ccwdev_lock(device->cdev)); + } + list_for_each_entry(device, &lcu->inactive_devices, alias_list) { + spin_lock(get_ccwdev_lock(device->cdev)); + dasd_device_set_stop_bits(device, DASD_STOPPED_SU); + spin_unlock(get_ccwdev_lock(device->cdev)); + } list_for_each_entry(pavgroup, &lcu->grouplist, group) { - list_for_each_entry(pos, &pavgroup->baselist, alias_list) - __stop_device_on_lcu(device, pos); - list_for_each_entry(pos, &pavgroup->aliaslist, alias_list) - __stop_device_on_lcu(device, pos); + list_for_each_entry(device, &pavgroup->baselist, alias_list) { + spin_lock(get_ccwdev_lock(device->cdev)); + dasd_device_set_stop_bits(device, DASD_STOPPED_SU); + spin_unlock(get_ccwdev_lock(device->cdev)); + } + list_for_each_entry(device, &pavgroup->aliaslist, alias_list) { + spin_lock(get_ccwdev_lock(device->cdev)); + dasd_device_set_stop_bits(device, DASD_STOPPED_SU); + spin_unlock(get_ccwdev_lock(device->cdev)); + } } } @@ -868,32 +868,27 @@ static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu) { struct alias_pav_group *pavgroup; struct dasd_device *device; - unsigned long flags; list_for_each_entry(device, &lcu->active_devices, alias_list) { - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + spin_lock(get_ccwdev_lock(device->cdev)); dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + spin_unlock(get_ccwdev_lock(device->cdev)); } - list_for_each_entry(device, &lcu->inactive_devices, alias_list) { - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + spin_lock(get_ccwdev_lock(device->cdev)); dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + spin_unlock(get_ccwdev_lock(device->cdev)); } - list_for_each_entry(pavgroup, &lcu->grouplist, group) { list_for_each_entry(device, &pavgroup->baselist, alias_list) { - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + spin_lock(get_ccwdev_lock(device->cdev)); dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), - flags); + spin_unlock(get_ccwdev_lock(device->cdev)); } list_for_each_entry(device, &pavgroup->aliaslist, alias_list) { - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + spin_lock(get_ccwdev_lock(device->cdev)); dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), - flags); + spin_unlock(get_ccwdev_lock(device->cdev)); } } } @@ -926,45 +921,26 @@ static void summary_unit_check_handling_work(struct work_struct *work) /* 3. read new alias configuration */ _schedule_lcu_update(lcu, device); lcu->suc_data.device = NULL; + dasd_put_device(device); spin_unlock_irqrestore(&lcu->lock, flags); } -/* - * note: this will be called from int handler context (cdev locked) - */ -void dasd_alias_handle_summary_unit_check(struct dasd_device *device, - struct irb *irb) +void dasd_alias_handle_summary_unit_check(struct work_struct *work) { + struct dasd_device *device = container_of(work, struct dasd_device, + suc_work); + struct dasd_eckd_private *private = device->private; struct alias_lcu *lcu; - char reason; - struct dasd_eckd_private *private; - char *sense; - - private = (struct dasd_eckd_private *) device->private; - - sense = dasd_get_sense(irb); - if (sense) { - reason = sense[8]; - DBF_DEV_EVENT(DBF_NOTICE, device, "%s %x", - "eckd handle summary unit check: reason", reason); - } else { - DBF_DEV_EVENT(DBF_WARNING, device, "%s", - "eckd handle summary unit check:" - " no reason code available"); - return; - } + unsigned long flags; lcu = private->lcu; if (!lcu) { DBF_DEV_EVENT(DBF_WARNING, device, "%s", "device not ready to handle summary" " unit check (no lcu structure)"); - return; + goto out; } - spin_lock(&lcu->lock); - _stop_all_devices_on_lcu(lcu, device); - /* prepare for lcu_update */ - private->lcu->flags |= NEED_UAC_UPDATE | UPDATE_PENDING; + spin_lock_irqsave(&lcu->lock, flags); /* If this device is about to be removed just return and wait for * the next interrupt on a different device */ @@ -972,19 +948,26 @@ void dasd_alias_handle_summary_unit_check(struct dasd_device *device, DBF_DEV_EVENT(DBF_WARNING, device, "%s", "device is in offline processing," " don't do summary unit check handling"); - spin_unlock(&lcu->lock); - return; + goto out_unlock; } if (lcu->suc_data.device) { /* already scheduled or running */ DBF_DEV_EVENT(DBF_WARNING, device, "%s", "previous instance of summary unit check worker" " still pending"); - spin_unlock(&lcu->lock); - return ; + goto out_unlock; } - lcu->suc_data.reason = reason; + _stop_all_devices_on_lcu(lcu); + /* prepare for lcu_update */ + lcu->flags |= NEED_UAC_UPDATE | UPDATE_PENDING; + lcu->suc_data.reason = private->suc_reason; lcu->suc_data.device = device; - spin_unlock(&lcu->lock); - schedule_work(&lcu->suc_data.worker); + dasd_get_device(device); + if (!schedule_work(&lcu->suc_data.worker)) + dasd_put_device(device); +out_unlock: + spin_unlock_irqrestore(&lcu->lock, flags); +out: + clear_bit(DASD_FLAG_SUC, &device->flags); + dasd_put_device(device); }; |
