From 48e4c385c5f54626651cca027afe242439281899 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:15 +0100 Subject: [S390] cio: fix double free in case of probe failure io_subchannel_probe() frees memory for sch->private which is later freed again when io_subchannel_remove() is called. Fix this problem by removing the cleanup in io_subchannel_probe(). Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 2490b741e16a..55f997308e42 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1292,7 +1292,7 @@ static int io_subchannel_probe(struct subchannel *sch) sch->private = kzalloc(sizeof(struct io_subchannel_private), GFP_KERNEL | GFP_DMA); if (!sch->private) - goto out_err; + goto out_schedule; /* * First check if a fitting device may be found amongst the * disconnected devices or in the orphanage. @@ -1317,7 +1317,7 @@ static int io_subchannel_probe(struct subchannel *sch) } cdev = io_subchannel_create_ccwdev(sch); if (IS_ERR(cdev)) - goto out_err; + goto out_schedule; rc = io_subchannel_recog(cdev, sch); if (rc) { spin_lock_irqsave(sch->lock, flags); @@ -1325,9 +1325,7 @@ static int io_subchannel_probe(struct subchannel *sch) spin_unlock_irqrestore(sch->lock, flags); } return 0; -out_err: - kfree(sch->private); - sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); + out_schedule: io_subchannel_schedule_removal(sch); return 0; @@ -1341,13 +1339,14 @@ io_subchannel_remove (struct subchannel *sch) cdev = sch_get_cdev(sch); if (!cdev) - return 0; + goto out_free; /* Set ccw device to not operational and drop reference. */ spin_lock_irqsave(cdev->ccwlock, flags); sch_set_cdev(sch, NULL); cdev->private->state = DEV_STATE_NOT_OPER; spin_unlock_irqrestore(cdev->ccwlock, flags); ccw_device_unregister(cdev); +out_free: kfree(sch->private); sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); return 0; -- cgit v1.2.3 From 60e4dac1abdf49ccdb7545ec406325f08423d848 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:16 +0100 Subject: [S390] cio: fix repeat setting of cdev parent association sch_create_and_recog_new_device() associates a parent subchannel with its ccw device child even though this is already done by the subsequently called io_subchannel_recog(). Also make sure io_subchannel_recog() sets the association under lock. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 55f997308e42..0efecefdb83a 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -888,9 +888,6 @@ static void sch_create_and_recog_new_device(struct subchannel *sch) css_sch_device_unregister(sch); return; } - spin_lock_irq(sch->lock); - sch_set_cdev(sch, cdev); - spin_unlock_irq(sch->lock); /* Start recognition for the new ccw device. */ if (io_subchannel_recog(cdev, sch)) { spin_lock_irq(sch->lock); @@ -1107,7 +1104,6 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) int rc; struct ccw_device_private *priv; - sch_set_cdev(sch, cdev); cdev->ccwlock = sch->lock; /* Init private data. */ @@ -1125,6 +1121,7 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) /* Start async. device sensing. */ spin_lock_irq(sch->lock); + sch_set_cdev(sch, cdev); rc = ccw_device_recognition(cdev); spin_unlock_irq(sch->lock); if (rc) { -- cgit v1.2.3 From 5d6e6b6f6f3eac10a7f5a15e961bac3b36824d9d Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:17 +0100 Subject: [S390] cio: introduce parent-initiated device move Change the initiative to update subchannel-ccw device associations to the subchannel: when there is an indication that the internal association no longer reflects the current hardware state, mark each affected subchannel as requiring attention. Once processing reaches a subchannel, determine the correct association for that subchannel at that time and perform the necessary device_move operations. This change fixes problems with the previous approach which would leave devices in an inconsistent state when a new hardware change occurred while a device_move was already scheduled. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/css.c | 9 +- drivers/s390/cio/device.c | 513 +++++++++++++++--------------------------- drivers/s390/cio/device_fsm.c | 8 +- 3 files changed, 192 insertions(+), 338 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 91c25706fa83..b4df5a56cfe2 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -376,8 +376,8 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) /* Unusable - ignore. */ return 0; } - CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, " - "slow path.\n", schid.ssid, schid.sch_no, CIO_OPER); + CIO_MSG_EVENT(4, "event: sch 0.%x.%04x, new\n", schid.ssid, + schid.sch_no); return css_probe_device(schid); } @@ -394,6 +394,10 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) "Got subchannel machine check but " "no sch_event handler provided.\n"); } + if (ret != 0 && ret != -EAGAIN) { + CIO_MSG_EVENT(2, "eval: sch 0.%x.%04x, rc=%d\n", + sch->schid.ssid, sch->schid.sch_no, ret); + } return ret; } @@ -684,6 +688,7 @@ static int __init setup_css(int nr) css->pseudo_subchannel->dev.parent = &css->device; css->pseudo_subchannel->dev.release = css_subchannel_release; dev_set_name(&css->pseudo_subchannel->dev, "defunct"); + mutex_init(&css->pseudo_subchannel->reg_mutex); ret = cio_create_sch_lock(css->pseudo_subchannel); if (ret) { kfree(css->pseudo_subchannel); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 0efecefdb83a..6097763f1035 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -673,57 +673,19 @@ static int ccw_device_register(struct ccw_device *cdev) return ret; } -struct match_data { - struct ccw_dev_id dev_id; - struct ccw_device * sibling; -}; - -static int -match_devno(struct device * dev, void * data) -{ - struct match_data * d = data; - struct ccw_device * cdev; - - cdev = to_ccwdev(dev); - if ((cdev->private->state == DEV_STATE_DISCONNECTED) && - !ccw_device_is_orphan(cdev) && - ccw_dev_id_is_equal(&cdev->private->dev_id, &d->dev_id) && - (cdev != d->sibling)) - return 1; - return 0; -} - -static struct ccw_device * get_disc_ccwdev_by_dev_id(struct ccw_dev_id *dev_id, - struct ccw_device *sibling) +static int match_dev_id(struct device *dev, void *data) { - struct device *dev; - struct match_data data; - - data.dev_id = *dev_id; - data.sibling = sibling; - dev = bus_find_device(&ccw_bus_type, NULL, &data, match_devno); - - return dev ? to_ccwdev(dev) : NULL; -} - -static int match_orphan(struct device *dev, void *data) -{ - struct ccw_dev_id *dev_id; - struct ccw_device *cdev; + struct ccw_device *cdev = to_ccwdev(dev); + struct ccw_dev_id *dev_id = data; - dev_id = data; - cdev = to_ccwdev(dev); return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id); } -static struct ccw_device * -get_orphaned_ccwdev_by_dev_id(struct channel_subsystem *css, - struct ccw_dev_id *dev_id) +static struct ccw_device *get_ccwdev_by_dev_id(struct ccw_dev_id *dev_id) { struct device *dev; - dev = device_find_child(&css->pseudo_subchannel->dev, dev_id, - match_orphan); + dev = bus_find_device(&ccw_bus_type, NULL, dev_id, match_dev_id); return dev ? to_ccwdev(dev) : NULL; } @@ -808,75 +770,6 @@ static struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch) static int io_subchannel_recog(struct ccw_device *, struct subchannel *); -static void sch_attach_device(struct subchannel *sch, - struct ccw_device *cdev) -{ - css_update_ssd_info(sch); - spin_lock_irq(sch->lock); - sch_set_cdev(sch, cdev); - cdev->private->schid = sch->schid; - cdev->ccwlock = sch->lock; - ccw_device_trigger_reprobe(cdev); - spin_unlock_irq(sch->lock); -} - -static void sch_attach_disconnected_device(struct subchannel *sch, - struct ccw_device *cdev) -{ - struct subchannel *other_sch; - int ret; - - /* Get reference for new parent. */ - if (!get_device(&sch->dev)) - return; - other_sch = to_subchannel(cdev->dev.parent); - /* Note: device_move() changes cdev->dev.parent */ - ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); - if (ret) { - CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed " - "(ret=%d)!\n", cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, ret); - /* Put reference for new parent. */ - put_device(&sch->dev); - return; - } - sch_set_cdev(other_sch, NULL); - /* No need to keep a subchannel without ccw device around. */ - css_sch_device_unregister(other_sch); - sch_attach_device(sch, cdev); - /* Put reference for old parent. */ - put_device(&other_sch->dev); -} - -static void sch_attach_orphaned_device(struct subchannel *sch, - struct ccw_device *cdev) -{ - int ret; - struct subchannel *pseudo_sch; - - /* Get reference for new parent. */ - if (!get_device(&sch->dev)) - return; - pseudo_sch = to_subchannel(cdev->dev.parent); - /* - * Try to move the ccw device to its new subchannel. - * Note: device_move() changes cdev->dev.parent - */ - ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); - if (ret) { - CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage " - "failed (ret=%d)!\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, ret); - /* Put reference for new parent. */ - put_device(&sch->dev); - return; - } - sch_attach_device(sch, cdev); - /* Put reference on pseudo subchannel. */ - put_device(&pseudo_sch->dev); -} - static void sch_create_and_recog_new_device(struct subchannel *sch) { struct ccw_device *cdev; @@ -901,70 +794,6 @@ static void sch_create_and_recog_new_device(struct subchannel *sch) } } - -void ccw_device_move_to_orphanage(struct work_struct *work) -{ - struct ccw_device_private *priv; - struct ccw_device *cdev; - struct ccw_device *replacing_cdev; - struct subchannel *sch; - int ret; - struct channel_subsystem *css; - struct ccw_dev_id dev_id; - - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; - sch = to_subchannel(cdev->dev.parent); - css = to_css(sch->dev.parent); - dev_id.devno = sch->schib.pmcw.dev; - dev_id.ssid = sch->schid.ssid; - - /* Increase refcount for pseudo subchannel. */ - get_device(&css->pseudo_subchannel->dev); - /* - * Move the orphaned ccw device to the orphanage so the replacing - * ccw device can take its place on the subchannel. - * Note: device_move() changes cdev->dev.parent - */ - ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev, - DPM_ORDER_NONE); - if (ret) { - CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed " - "(ret=%d)!\n", cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, ret); - /* Decrease refcount for pseudo subchannel again. */ - put_device(&css->pseudo_subchannel->dev); - return; - } - cdev->ccwlock = css->pseudo_subchannel->lock; - /* - * Search for the replacing ccw device - * - among the disconnected devices - * - in the orphanage - */ - replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev); - if (replacing_cdev) { - sch_attach_disconnected_device(sch, replacing_cdev); - /* Release reference from get_disc_ccwdev_by_dev_id() */ - put_device(&replacing_cdev->dev); - /* Release reference of subchannel from old cdev. */ - put_device(&sch->dev); - return; - } - replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id); - if (replacing_cdev) { - sch_attach_orphaned_device(sch, replacing_cdev); - /* Release reference from get_orphaned_ccwdev_by_dev_id() */ - put_device(&replacing_cdev->dev); - /* Release reference of subchannel from old cdev. */ - put_device(&sch->dev); - return; - } - sch_create_and_recog_new_device(sch); - /* Release reference of subchannel from old cdev. */ - put_device(&sch->dev); -} - /* * Register recognized device. */ @@ -1131,53 +960,56 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) return rc; } -static void ccw_device_move_to_sch(struct work_struct *work) +static int ccw_device_move_to_sch(struct ccw_device *cdev, + struct subchannel *sch) { - struct ccw_device_private *priv; + struct subchannel *old_sch; int rc; - struct subchannel *sch; - struct ccw_device *cdev; - struct subchannel *former_parent; - priv = container_of(work, struct ccw_device_private, kick_work); - sch = priv->sch; - cdev = priv->cdev; - former_parent = to_subchannel(cdev->dev.parent); - /* Get reference for new parent. */ + old_sch = to_subchannel(cdev->dev.parent); + /* Obtain child reference for new parent. */ if (!get_device(&sch->dev)) - return; + return -ENODEV; mutex_lock(&sch->reg_mutex); - /* - * Try to move the ccw device to its new subchannel. - * Note: device_move() changes cdev->dev.parent - */ rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); mutex_unlock(&sch->reg_mutex); if (rc) { - CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to subchannel " - "0.%x.%04x failed (ret=%d)!\n", + CIO_MSG_EVENT(0, "device_move(0.%x.%04x,0.%x.%04x)=%d\n", cdev->private->dev_id.ssid, cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, rc); - css_sch_device_unregister(sch); - /* Put reference for new parent again. */ + sch->schib.pmcw.dev, rc); + /* Release child reference for new parent. */ put_device(&sch->dev); - goto out; + return rc; } - if (!sch_is_pseudo_sch(former_parent)) { - spin_lock_irq(former_parent->lock); - sch_set_cdev(former_parent, NULL); - spin_unlock_irq(former_parent->lock); - css_sch_device_unregister(former_parent); - /* Reset intparm to zeroes. */ - former_parent->config.intparm = 0; - cio_commit_config(former_parent); + /* Clean up old subchannel. */ + if (!sch_is_pseudo_sch(old_sch)) { + spin_lock_irq(old_sch->lock); + sch_set_cdev(old_sch, NULL); + cio_disable_subchannel(old_sch); + spin_unlock_irq(old_sch->lock); + css_schedule_eval(old_sch->schid); } - sch_attach_device(sch, cdev); -out: - /* Put reference for old parent. */ - put_device(&former_parent->dev); - put_device(&cdev->dev); + /* Release child reference for old parent. */ + put_device(&old_sch->dev); + /* Initialize new subchannel. */ + spin_lock_irq(sch->lock); + cdev->private->schid = sch->schid; + cdev->ccwlock = sch->lock; + if (!sch_is_pseudo_sch(sch)) + sch_set_cdev(sch, cdev); + spin_unlock_irq(sch->lock); + if (!sch_is_pseudo_sch(sch)) + css_update_ssd_info(sch); + return 0; +} + +static int ccw_device_move_to_orph(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct channel_subsystem *css = to_css(sch->dev.parent); + + return ccw_device_move_to_sch(cdev, css->pseudo_subchannel); } static void io_subchannel_irq(struct subchannel *sch) @@ -1244,8 +1076,6 @@ static int io_subchannel_probe(struct subchannel *sch) { struct ccw_device *cdev; int rc; - unsigned long flags; - struct ccw_dev_id dev_id; if (cio_is_console(sch->schid)) { rc = sysfs_create_group(&sch->dev.kobj, @@ -1290,37 +1120,7 @@ static int io_subchannel_probe(struct subchannel *sch) GFP_KERNEL | GFP_DMA); if (!sch->private) goto out_schedule; - /* - * First check if a fitting device may be found amongst the - * disconnected devices or in the orphanage. - */ - dev_id.devno = sch->schib.pmcw.dev; - dev_id.ssid = sch->schid.ssid; - cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL); - if (!cdev) - cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent), - &dev_id); - if (cdev) { - /* - * Schedule moving the device until when we have a registered - * subchannel to move to and succeed the probe. We can - * unregister later again, when the probe is through. - */ - cdev->private->sch = sch; - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_move_to_sch); - queue_work(slow_path_wq, &cdev->private->kick_work); - return 0; - } - cdev = io_subchannel_create_ccwdev(sch); - if (IS_ERR(cdev)) - goto out_schedule; - rc = io_subchannel_recog(cdev, sch); - if (rc) { - spin_lock_irqsave(sch->lock, flags); - io_subchannel_recog_done(cdev); - spin_unlock_irqrestore(sch->lock, flags); - } + css_schedule_eval(sch->schid); return 0; out_schedule: @@ -1349,16 +1149,6 @@ out_free: return 0; } -static int io_subchannel_notify(struct subchannel *sch, int event) -{ - struct ccw_device *cdev; - - cdev = sch_get_cdev(sch); - if (!cdev) - return 0; - return ccw_device_notify(cdev, event); -} - static void io_subchannel_verify(struct subchannel *sch) { struct ccw_device *cdev; @@ -1482,19 +1272,6 @@ io_subchannel_shutdown(struct subchannel *sch) cio_disable_subchannel(sch); } -static int io_subchannel_get_status(struct subchannel *sch) -{ - struct schib schib; - - if (stsch(sch->schid, &schib) || !schib.pmcw.dnv) - return CIO_GONE; - if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) - return CIO_REVALIDATE; - if (!sch->lpm) - return CIO_NO_PATH; - return CIO_OPER; -} - static int device_is_disconnected(struct ccw_device *cdev) { if (!cdev) @@ -1626,91 +1403,165 @@ void ccw_device_set_notoper(struct ccw_device *cdev) cdev->private->state = DEV_STATE_NOT_OPER; } -static int io_subchannel_sch_event(struct subchannel *sch, int slow) +enum io_sch_action { + IO_SCH_UNREG, + IO_SCH_ORPH_UNREG, + IO_SCH_ATTACH, + IO_SCH_UNREG_ATTACH, + IO_SCH_ORPH_ATTACH, + IO_SCH_REPROBE, + IO_SCH_VERIFY, + IO_SCH_DISC, + IO_SCH_NOP, +}; + +static enum io_sch_action sch_get_action(struct subchannel *sch) +{ + struct ccw_device *cdev; + + cdev = sch_get_cdev(sch); + if (cio_update_schib(sch)) { + /* Not operational. */ + if (!cdev) + return IO_SCH_UNREG; + if (!ccw_device_notify(cdev, CIO_GONE)) + return IO_SCH_UNREG; + return IO_SCH_ORPH_UNREG; + } + /* Operational. */ + if (!cdev) + return IO_SCH_ATTACH; + if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { + if (!ccw_device_notify(cdev, CIO_GONE)) + return IO_SCH_UNREG_ATTACH; + return IO_SCH_ORPH_ATTACH; + } + if ((sch->schib.pmcw.pam & sch->opm) == 0) { + if (!ccw_device_notify(cdev, CIO_NO_PATH)) + return IO_SCH_UNREG; + return IO_SCH_DISC; + } + if (device_is_disconnected(cdev)) + return IO_SCH_REPROBE; + if (cdev->online) + return IO_SCH_VERIFY; + return IO_SCH_NOP; +} + +/** + * io_subchannel_sch_event - process subchannel event + * @sch: subchannel + * @process: non-zero if function is called in process context + * + * An unspecified event occurred for this subchannel. Adjust data according + * to the current operational state of the subchannel and device. Return + * zero when the event has been handled sufficiently or -EAGAIN when this + * function should be called again in process context. + */ +static int io_subchannel_sch_event(struct subchannel *sch, int process) { - int event, ret, disc; unsigned long flags; - enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE, DISC } action; struct ccw_device *cdev; + struct ccw_dev_id dev_id; + enum io_sch_action action; + int rc = -EAGAIN; spin_lock_irqsave(sch->lock, flags); + if (!device_is_registered(&sch->dev)) + goto out_unlock; + action = sch_get_action(sch); + CIO_MSG_EVENT(2, "event: sch 0.%x.%04x, process=%d, action=%d\n", + sch->schid.ssid, sch->schid.sch_no, process, + action); + /* Perform immediate actions while holding the lock. */ cdev = sch_get_cdev(sch); - disc = device_is_disconnected(cdev); - if (disc && slow) { - /* Disconnected devices are evaluated directly only.*/ - spin_unlock_irqrestore(sch->lock, flags); - return 0; - } - /* No interrupt after machine check - kill pending timers. */ - if (cdev) - ccw_device_set_timeout(cdev, 0); - if (!disc && !slow) { - /* Non-disconnected devices are evaluated on the slow path. */ - spin_unlock_irqrestore(sch->lock, flags); - return -EAGAIN; + switch (action) { + case IO_SCH_REPROBE: + /* Trigger device recognition. */ + ccw_device_trigger_reprobe(cdev); + rc = 0; + goto out_unlock; + case IO_SCH_VERIFY: + /* Trigger path verification. */ + io_subchannel_verify(sch); + rc = 0; + goto out_unlock; + case IO_SCH_DISC: + ccw_device_set_disconnected(cdev); + rc = 0; + goto out_unlock; + case IO_SCH_ORPH_UNREG: + case IO_SCH_ORPH_ATTACH: + ccw_device_set_disconnected(cdev); + break; + case IO_SCH_UNREG_ATTACH: + case IO_SCH_UNREG: + if (cdev) + ccw_device_set_notoper(cdev); + break; + case IO_SCH_NOP: + rc = 0; + goto out_unlock; + default: + break; } - event = io_subchannel_get_status(sch); - CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n", - sch->schid.ssid, sch->schid.sch_no, event, - disc ? "disconnected" : "normal", - slow ? "slow" : "fast"); - /* Analyze subchannel status. */ - action = NONE; - switch (event) { - case CIO_NO_PATH: - if (disc) { - /* Check if paths have become available. */ - action = REPROBE; - break; - } - /* fall through */ - case CIO_GONE: - /* Ask driver what to do with device. */ - if (io_subchannel_notify(sch, event)) - action = DISC; - else - action = UNREGISTER; + spin_unlock_irqrestore(sch->lock, flags); + /* All other actions require process context. */ + if (!process) + goto out; + /* Handle attached ccw device. */ + switch (action) { + case IO_SCH_ORPH_UNREG: + case IO_SCH_ORPH_ATTACH: + /* Move ccw device to orphanage. */ + rc = ccw_device_move_to_orph(cdev); + if (rc) + goto out; break; - case CIO_REVALIDATE: - /* Device will be removed, so no notify necessary. */ - if (disc) - /* Reprobe because immediate unregister might block. */ - action = REPROBE; - else - action = UNREGISTER_PROBE; + case IO_SCH_UNREG_ATTACH: + /* Unregister ccw device. */ + ccw_device_unregister(cdev); break; - case CIO_OPER: - if (disc) - /* Get device operational again. */ - action = REPROBE; + default: break; } - /* Perform action. */ - ret = 0; + /* Handle subchannel. */ switch (action) { - case UNREGISTER: - case UNREGISTER_PROBE: - ccw_device_set_notoper(cdev); - /* Unregister device (will use subchannel lock). */ - spin_unlock_irqrestore(sch->lock, flags); + case IO_SCH_ORPH_UNREG: + case IO_SCH_UNREG: css_sch_device_unregister(sch); - spin_lock_irqsave(sch->lock, flags); break; - case REPROBE: + case IO_SCH_ORPH_ATTACH: + case IO_SCH_UNREG_ATTACH: + case IO_SCH_ATTACH: + dev_id.ssid = sch->schid.ssid; + dev_id.devno = sch->schib.pmcw.dev; + cdev = get_ccwdev_by_dev_id(&dev_id); + if (!cdev) { + sch_create_and_recog_new_device(sch); + break; + } + rc = ccw_device_move_to_sch(cdev, sch); + if (rc) { + /* Release reference from get_ccwdev_by_dev_id() */ + put_device(&cdev->dev); + goto out; + } + spin_lock_irqsave(sch->lock, flags); ccw_device_trigger_reprobe(cdev); - break; - case DISC: - ccw_device_set_disconnected(cdev); + spin_unlock_irqrestore(sch->lock, flags); + /* Release reference from get_ccwdev_by_dev_id() */ + put_device(&cdev->dev); break; default: break; } - spin_unlock_irqrestore(sch->lock, flags); - /* Probe if necessary. */ - if (action == UNREGISTER_PROBE) - ret = css_probe_device(sch->schid); + return 0; - return ret; +out_unlock: + spin_unlock_irqrestore(sch->lock, flags); +out: + return rc; } #ifdef CONFIG_CCW_CONSOLE diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index b9613d7df9ef..d1e05f44fb6f 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -1072,11 +1072,9 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev) /* We should also udate ssd info, but this has to wait. */ /* Check if this is another device which appeared on the same sch. */ - if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_move_to_orphanage); - queue_work(slow_path_wq, &cdev->private->kick_work); - } else + if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) + css_schedule_eval(sch->schid); + else ccw_device_start_id(cdev, 0); } -- cgit v1.2.3 From 390935acac21f3ea1a130bdca8eb9397cb293643 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:18 +0100 Subject: [S390] cio: introduce subchannel todos Ensure that current and future users of sch->work do not overwrite each other by introducing a single mechanism for delayed subchannel work. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cio.h | 8 +++++++- drivers/s390/cio/css.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/s390/cio/css.h | 3 +++ drivers/s390/cio/device.c | 23 +++++------------------ 4 files changed, 63 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 2e43558c704b..bf7f80f5a330 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -68,6 +68,11 @@ struct schib { __u8 mda[4]; /* model dependent area */ } __attribute__ ((packed,aligned(4))); +enum sch_todo { + SCH_TODO_NOTHING, + SCH_TODO_UNREG, +}; + /* subchannel data structure used by I/O subroutines */ struct subchannel { struct subchannel_id schid; @@ -95,7 +100,8 @@ struct subchannel { struct device dev; /* entry in device tree */ struct css_driver *driver; void *private; /* private per subchannel type data */ - struct work_struct work; + enum sch_todo todo; + struct work_struct todo_work; struct schib_config config; } __attribute__ ((aligned(8))); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index b4df5a56cfe2..92ff88ac1107 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -133,6 +133,8 @@ out: return rc; } +static void css_sch_todo(struct work_struct *work); + static struct subchannel * css_alloc_subchannel(struct subchannel_id schid) { @@ -147,6 +149,7 @@ css_alloc_subchannel(struct subchannel_id schid) kfree(sch); return ERR_PTR(ret); } + INIT_WORK(&sch->todo_work, css_sch_todo); return sch; } @@ -190,6 +193,51 @@ void css_sch_device_unregister(struct subchannel *sch) } EXPORT_SYMBOL_GPL(css_sch_device_unregister); +static void css_sch_todo(struct work_struct *work) +{ + struct subchannel *sch; + enum sch_todo todo; + + sch = container_of(work, struct subchannel, todo_work); + /* Find out todo. */ + spin_lock_irq(sch->lock); + todo = sch->todo; + CIO_MSG_EVENT(4, "sch_todo: sch=0.%x.%04x, todo=%d\n", sch->schid.ssid, + sch->schid.sch_no, todo); + sch->todo = SCH_TODO_NOTHING; + spin_unlock_irq(sch->lock); + /* Perform todo. */ + if (todo == SCH_TODO_UNREG) + css_sch_device_unregister(sch); + /* Release workqueue ref. */ + put_device(&sch->dev); +} + +/** + * css_sched_sch_todo - schedule a subchannel operation + * @sch: subchannel + * @todo: todo + * + * Schedule the operation identified by @todo to be performed on the slow path + * workqueue. Do nothing if another operation with higher priority is already + * scheduled. Needs to be called with subchannel lock held. + */ +void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo) +{ + CIO_MSG_EVENT(4, "sch_todo: sched sch=0.%x.%04x todo=%d\n", + sch->schid.ssid, sch->schid.sch_no, todo); + if (sch->todo >= todo) + return; + /* Get workqueue ref. */ + if (!get_device(&sch->dev)) + return; + sch->todo = todo; + if (!queue_work(slow_path_wq, &sch->todo_work)) { + /* Already queued, release workqueue ref. */ + put_device(&sch->dev); + } +} + static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw) { int i; diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 68d6b0bf151c..fe84b92cde60 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -11,6 +11,8 @@ #include #include +#include "cio.h" + /* * path grouping stuff */ @@ -151,4 +153,5 @@ int css_sch_is_valid(struct schib *); extern struct workqueue_struct *slow_path_wq; void css_wait_for_slow_path(void); +void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo); #endif diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 6097763f1035..0dcfc0ee3d81 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1051,23 +1051,6 @@ static void io_subchannel_init_fields(struct subchannel *sch) io_subchannel_init_config(sch); } -static void io_subchannel_do_unreg(struct work_struct *work) -{ - struct subchannel *sch; - - sch = container_of(work, struct subchannel, work); - css_sch_device_unregister(sch); - put_device(&sch->dev); -} - -/* Schedule unregister if we have no cdev. */ -static void io_subchannel_schedule_removal(struct subchannel *sch) -{ - get_device(&sch->dev); - INIT_WORK(&sch->work, io_subchannel_do_unreg); - queue_work(slow_path_wq, &sch->work); -} - /* * Note: We always return 0 so that we bind to the device even on error. * This is needed so that our remove function is called on unregister. @@ -1124,7 +1107,9 @@ static int io_subchannel_probe(struct subchannel *sch) return 0; out_schedule: - io_subchannel_schedule_removal(sch); + spin_lock_irq(sch->lock); + css_sched_sch_todo(sch, SCH_TODO_UNREG); + spin_unlock_irq(sch->lock); return 0; } @@ -1469,6 +1454,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) spin_lock_irqsave(sch->lock, flags); if (!device_is_registered(&sch->dev)) goto out_unlock; + if (work_pending(&sch->todo_work)) + goto out_unlock; action = sch_get_action(sch); CIO_MSG_EVENT(2, "event: sch 0.%x.%04x, process=%d, action=%d\n", sch->schid.ssid, sch->schid.sch_no, process, -- cgit v1.2.3 From 37de53bb52908726c18fc84515792a5b2f454532 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:19 +0100 Subject: [S390] cio: introduce ccw device todos Introduce a central mechanism for performing delayed ccw device work to ensure that different types of work do not overwrite each other. Prioritization ensures that the most important work is always performed while less important tasks are either obsoleted or repeated later. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 203 +++++++++++++++++++++--------------------- drivers/s390/cio/device.h | 3 +- drivers/s390/cio/device_fsm.c | 28 ++---- drivers/s390/cio/io_sch.h | 12 ++- 4 files changed, 119 insertions(+), 127 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 0dcfc0ee3d81..167446785d19 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -306,47 +306,6 @@ static void ccw_device_unregister(struct ccw_device *cdev) } } -static void ccw_device_remove_orphan_cb(struct work_struct *work) -{ - struct ccw_device_private *priv; - struct ccw_device *cdev; - - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; - ccw_device_unregister(cdev); - /* Release cdev reference for workqueue processing. */ - put_device(&cdev->dev); -} - -static void -ccw_device_remove_disconnected(struct ccw_device *cdev) -{ - unsigned long flags; - - /* - * Forced offline in disconnected state means - * 'throw away device'. - */ - if (ccw_device_is_orphan(cdev)) { - /* - * Deregister ccw device. - * Unfortunately, we cannot do this directly from the - * attribute method. - */ - /* Get cdev reference for workqueue processing. */ - if (!get_device(&cdev->dev)) - return; - spin_lock_irqsave(cdev->ccwlock, flags); - cdev->private->state = DEV_STATE_NOT_OPER; - spin_unlock_irqrestore(cdev->ccwlock, flags); - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_remove_orphan_cb); - queue_work(slow_path_wq, &cdev->private->kick_work); - } else - /* Deregister subchannel, which will kill the ccw device. */ - ccw_device_schedule_sch_unregister(cdev); -} - /** * ccw_device_set_offline() - disable a ccw device for I/O * @cdev: target ccw device @@ -494,9 +453,11 @@ error: static int online_store_handle_offline(struct ccw_device *cdev) { - if (cdev->private->state == DEV_STATE_DISCONNECTED) - ccw_device_remove_disconnected(cdev); - else if (cdev->online && cdev->drv && cdev->drv->set_offline) + if (cdev->private->state == DEV_STATE_DISCONNECTED) { + spin_lock_irq(cdev->ccwlock); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL); + spin_unlock_irq(cdev->ccwlock); + } else if (cdev->online && cdev->drv && cdev->drv->set_offline) return ccw_device_set_offline(cdev); return 0; } @@ -690,17 +651,10 @@ static struct ccw_device *get_ccwdev_by_dev_id(struct ccw_dev_id *dev_id) return dev ? to_ccwdev(dev) : NULL; } -void ccw_device_do_unbind_bind(struct work_struct *work) +static void ccw_device_do_unbind_bind(struct ccw_device *cdev) { - struct ccw_device_private *priv; - struct ccw_device *cdev; - struct subchannel *sch; int ret; - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; - sch = to_subchannel(cdev->dev.parent); - if (test_bit(1, &cdev->private->registered)) { device_release_driver(&cdev->dev); ret = device_attach(&cdev->dev); @@ -735,6 +689,8 @@ static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch) return ERR_PTR(-ENOMEM); } +static void ccw_device_todo(struct work_struct *work); + static int io_subchannel_initialize_dev(struct subchannel *sch, struct ccw_device *cdev) { @@ -742,7 +698,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch, atomic_set(&cdev->private->onoff, 0); cdev->dev.parent = &sch->dev; cdev->dev.release = ccw_device_release; - INIT_WORK(&cdev->private->kick_work, NULL); + INIT_WORK(&cdev->private->todo_work, ccw_device_todo); cdev->dev.groups = ccwdev_attr_groups; /* Do first half of device_register. */ device_initialize(&cdev->dev); @@ -797,17 +753,12 @@ static void sch_create_and_recog_new_device(struct subchannel *sch) /* * Register recognized device. */ -static void -io_subchannel_register(struct work_struct *work) +static void io_subchannel_register(struct ccw_device *cdev) { - struct ccw_device_private *priv; - struct ccw_device *cdev; struct subchannel *sch; int ret; unsigned long flags; - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); /* * Check if subchannel is still registered. It may have become @@ -859,41 +810,23 @@ out: cdev->private->flags.recog_done = 1; wake_up(&cdev->private->wait_q); out_err: - /* Release reference for workqueue processing. */ - put_device(&cdev->dev); if (atomic_dec_and_test(&ccw_device_init_count)) wake_up(&ccw_device_init_wq); } -static void ccw_device_call_sch_unregister(struct work_struct *work) +static void ccw_device_call_sch_unregister(struct ccw_device *cdev) { - struct ccw_device_private *priv; - struct ccw_device *cdev; struct subchannel *sch; - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; /* Get subchannel reference for local processing. */ if (!get_device(cdev->dev.parent)) return; sch = to_subchannel(cdev->dev.parent); css_sch_device_unregister(sch); - /* Release cdev reference for workqueue processing.*/ - put_device(&cdev->dev); /* Release subchannel reference for local processing. */ put_device(&sch->dev); } -void ccw_device_schedule_sch_unregister(struct ccw_device *cdev) -{ - /* Get cdev reference for workqueue processing. */ - if (!get_device(&cdev->dev)) - return; - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_call_sch_unregister); - queue_work(slow_path_wq, &cdev->private->kick_work); -} - /* * subchannel recognition done. Called from the state machine. */ @@ -909,7 +842,8 @@ io_subchannel_recog_done(struct ccw_device *cdev) /* Device did not respond in time. */ case DEV_STATE_NOT_OPER: cdev->private->flags.recog_done = 1; - ccw_device_schedule_sch_unregister(cdev); + /* Remove device found not operational. */ + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); if (atomic_dec_and_test(&ccw_device_init_count)) wake_up(&ccw_device_init_wq); break; @@ -918,11 +852,7 @@ io_subchannel_recog_done(struct ccw_device *cdev) * We can't register the device in interrupt context so * we schedule a work item. */ - if (!get_device(&cdev->dev)) - break; - PREPARE_WORK(&cdev->private->kick_work, - io_subchannel_register); - queue_work(slow_path_wq, &cdev->private->kick_work); + ccw_device_sched_todo(cdev, CDEV_TODO_REGISTER); break; } } @@ -1333,20 +1263,16 @@ static void ccw_device_schedule_recovery(void) static int purge_fn(struct device *dev, void *data) { struct ccw_device *cdev = to_ccwdev(dev); - struct ccw_device_private *priv = cdev->private; - int unreg; + struct ccw_dev_id *id = &cdev->private->dev_id; spin_lock_irq(cdev->ccwlock); - unreg = is_blacklisted(priv->dev_id.ssid, priv->dev_id.devno) && - (priv->state == DEV_STATE_OFFLINE); + if (is_blacklisted(id->ssid, id->devno) && + (cdev->private->state == DEV_STATE_OFFLINE)) { + CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", id->ssid, + id->devno); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); + } spin_unlock_irq(cdev->ccwlock); - if (!unreg) - goto out; - CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid, - priv->dev_id.devno); - ccw_device_schedule_sch_unregister(cdev); - -out: /* Abort loop in case of pending signal. */ if (signal_pending(current)) return -EINTR; @@ -1456,12 +1382,14 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) goto out_unlock; if (work_pending(&sch->todo_work)) goto out_unlock; + cdev = sch_get_cdev(sch); + if (cdev && work_pending(&cdev->private->todo_work)) + goto out_unlock; action = sch_get_action(sch); CIO_MSG_EVENT(2, "event: sch 0.%x.%04x, process=%d, action=%d\n", sch->schid.ssid, sch->schid.sch_no, process, action); /* Perform immediate actions while holding the lock. */ - cdev = sch_get_cdev(sch); switch (action) { case IO_SCH_REPROBE: /* Trigger device recognition. */ @@ -1753,7 +1681,7 @@ static int ccw_device_pm_prepare(struct device *dev) { struct ccw_device *cdev = to_ccwdev(dev); - if (work_pending(&cdev->private->kick_work)) + if (work_pending(&cdev->private->todo_work)) return -EAGAIN; /* Fail while device is being set online/offline. */ if (atomic_read(&cdev->private->onoff)) @@ -1874,7 +1802,7 @@ static int resume_handle_boxed(struct ccw_device *cdev) cdev->private->state = DEV_STATE_BOXED; if (ccw_device_notify(cdev, CIO_BOXED)) return 0; - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); return -ENODEV; } @@ -1883,7 +1811,7 @@ static int resume_handle_disc(struct ccw_device *cdev) cdev->private->state = DEV_STATE_DISCONNECTED; if (ccw_device_notify(cdev, CIO_GONE)) return 0; - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); return -ENODEV; } @@ -1928,9 +1856,7 @@ static int ccw_device_pm_restore(struct device *dev) /* check if the device type has changed */ if (!ccw_device_test_sense_data(cdev)) { ccw_device_update_sense_data(cdev); - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_do_unbind_bind); - queue_work(ccw_device_work, &cdev->private->kick_work); + ccw_device_sched_todo(cdev, CDEV_TODO_REBIND); ret = -ENODEV; goto out_unlock; } @@ -1974,7 +1900,7 @@ out_disc_unlock: goto out_restore; out_unreg_unlock: - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL); ret = -ENODEV; out_unlock: spin_unlock_irq(sch->lock); @@ -2039,6 +1965,77 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev) return sch->schid; } +static void ccw_device_todo(struct work_struct *work) +{ + struct ccw_device_private *priv; + struct ccw_device *cdev; + struct subchannel *sch; + enum cdev_todo todo; + + priv = container_of(work, struct ccw_device_private, todo_work); + cdev = priv->cdev; + sch = to_subchannel(cdev->dev.parent); + /* Find out todo. */ + spin_lock_irq(cdev->ccwlock); + todo = priv->todo; + priv->todo = CDEV_TODO_NOTHING; + CIO_MSG_EVENT(4, "cdev_todo: cdev=0.%x.%04x todo=%d\n", + priv->dev_id.ssid, priv->dev_id.devno, todo); + spin_unlock_irq(cdev->ccwlock); + /* Perform todo. */ + switch (todo) { + case CDEV_TODO_ENABLE_CMF: + cmf_reenable(cdev); + break; + case CDEV_TODO_REBIND: + ccw_device_do_unbind_bind(cdev); + break; + case CDEV_TODO_REGISTER: + io_subchannel_register(cdev); + break; + case CDEV_TODO_UNREG_EVAL: + if (!sch_is_pseudo_sch(sch)) + css_schedule_eval(sch->schid); + /* fall-through */ + case CDEV_TODO_UNREG: + if (sch_is_pseudo_sch(sch)) + ccw_device_unregister(cdev); + else + ccw_device_call_sch_unregister(cdev); + break; + default: + break; + } + /* Release workqueue ref. */ + put_device(&cdev->dev); +} + +/** + * ccw_device_sched_todo - schedule ccw device operation + * @cdev: ccw device + * @todo: todo + * + * Schedule the operation identified by @todo to be performed on the slow path + * workqueue. Do nothing if another operation with higher priority is already + * scheduled. Needs to be called with ccwdev lock held. + */ +void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo) +{ + CIO_MSG_EVENT(4, "cdev_todo: sched cdev=0.%x.%04x todo=%d\n", + cdev->private->dev_id.ssid, cdev->private->dev_id.devno, + todo); + if (cdev->private->todo >= todo) + return; + cdev->private->todo = todo; + /* Get workqueue ref. */ + if (!get_device(&cdev->dev)) + return; + if (!queue_work(slow_path_wq, &cdev->private->todo_work)) { + /* Already queued, release workqueue ref. */ + put_device(&cdev->dev); + } +} + MODULE_LICENSE("GPL"); EXPORT_SYMBOL(ccw_device_set_online); EXPORT_SYMBOL(ccw_device_set_offline); diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 246c6482842c..adaa27efc59e 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -81,8 +81,6 @@ void io_subchannel_init_config(struct subchannel *sch); int ccw_device_cancel_halt_clear(struct ccw_device *); -void ccw_device_do_unbind_bind(struct work_struct *); -void ccw_device_move_to_orphanage(struct work_struct *); int ccw_device_is_orphan(struct ccw_device *); int ccw_device_recognition(struct ccw_device *); @@ -92,6 +90,7 @@ void ccw_device_update_sense_data(struct ccw_device *); int ccw_device_test_sense_data(struct ccw_device *); void ccw_device_schedule_sch_unregister(struct ccw_device *); int ccw_purge_blacklisted(void); +void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo); /* Function prototypes for device status and basic sense stuff. */ void ccw_device_accumulate_irb(struct ccw_device *, struct irb *); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index d1e05f44fb6f..b163743bf586 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -289,9 +289,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) wake_up(&cdev->private->wait_q); } else { ccw_device_update_sense_data(cdev); - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_do_unbind_bind); - queue_work(ccw_device_work, &cdev->private->kick_work); + ccw_device_sched_todo(cdev, CDEV_TODO_REBIND); } return; case DEV_STATE_BOXED: @@ -343,28 +341,16 @@ int ccw_device_notify(struct ccw_device *cdev, int event) return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0; } -static void cmf_reenable_delayed(struct work_struct *work) -{ - struct ccw_device_private *priv; - struct ccw_device *cdev; - - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; - cmf_reenable(cdev); -} - static void ccw_device_oper_notify(struct ccw_device *cdev) { if (ccw_device_notify(cdev, CIO_OPER)) { /* Reenable channel measurements, if needed. */ - PREPARE_WORK(&cdev->private->kick_work, cmf_reenable_delayed); - queue_work(ccw_device_work, &cdev->private->kick_work); + ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF); return; } /* Driver doesn't want device back. */ ccw_device_set_notoper(cdev); - PREPARE_WORK(&cdev->private->kick_work, ccw_device_do_unbind_bind); - queue_work(ccw_device_work, &cdev->private->kick_work); + ccw_device_sched_todo(cdev, CDEV_TODO_REBIND); } /* @@ -392,14 +378,14 @@ ccw_device_done(struct ccw_device *cdev, int state) CIO_MSG_EVENT(0, "Boxed device %04x on subchannel %04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (cdev->online && !ccw_device_notify(cdev, CIO_BOXED)) - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); cdev->private->flags.donotify = 0; break; case DEV_STATE_NOT_OPER: CIO_MSG_EVENT(0, "Device %04x gone on subchannel %04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (!ccw_device_notify(cdev, CIO_GONE)) - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); else ccw_device_set_disconnected(cdev); cdev->private->flags.donotify = 0; @@ -409,7 +395,7 @@ ccw_device_done(struct ccw_device *cdev, int state) "%04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (!ccw_device_notify(cdev, CIO_NO_PATH)) - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); else ccw_device_set_disconnected(cdev); cdev->private->flags.donotify = 0; @@ -751,7 +737,7 @@ static void ccw_device_generic_notoper(struct ccw_device *cdev, enum dev_event dev_event) { if (!ccw_device_notify(cdev, CIO_GONE)) - ccw_device_schedule_sch_unregister(cdev); + ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); else ccw_device_set_disconnected(cdev); } diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index 0b8f381bd20e..b770e4202131 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -82,6 +82,15 @@ struct senseid { struct ciw ciw[MAX_CIWS]; /* variable # of CIWs */ } __attribute__ ((packed, aligned(4))); +enum cdev_todo { + CDEV_TODO_NOTHING, + CDEV_TODO_ENABLE_CMF, + CDEV_TODO_REBIND, + CDEV_TODO_REGISTER, + CDEV_TODO_UNREG, + CDEV_TODO_UNREG_EVAL, +}; + struct ccw_device_private { struct ccw_device *cdev; struct subchannel *sch; @@ -115,7 +124,8 @@ struct ccw_device_private { struct senseid senseid; /* SenseID info */ struct pgid pgid[8]; /* path group IDs per chpid*/ struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */ - struct work_struct kick_work; + struct work_struct todo_work; + enum cdev_todo todo; wait_queue_head_t wait_q; struct timer_list timer; void *cmb; /* measurement information */ -- cgit v1.2.3 From a7ae2c02f5ab7080646a4cc6c01065ae9decad54 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:20 +0100 Subject: [S390] cio: inform user when online/offline processing fails Print a warning message in case a ccw device enters boxed or not operational state during online/offline processing. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 167446785d19..ee3450667687 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -7,6 +7,10 @@ * Cornelia Huck (cornelia.huck@de.ibm.com) * Martin Schwidefsky (schwidefsky@de.ibm.com) */ + +#define KMSG_COMPONENT "cio" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include #include #include @@ -347,6 +351,14 @@ int ccw_device_set_offline(struct ccw_device *cdev) spin_unlock_irq(cdev->ccwlock); wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) || cdev->private->state == DEV_STATE_DISCONNECTED)); + /* Inform the user if set offline failed. */ + if (cdev->private->state == DEV_STATE_BOXED) { + pr_warning("%s: The device entered boxed state while " + "being set offline\n", dev_name(&cdev->dev)); + } else if (cdev->private->state == DEV_STATE_NOT_OPER) { + pr_warning("%s: The device stopped operating while " + "being set offline\n", dev_name(&cdev->dev)); + } /* Give up reference from ccw_device_set_online(). */ put_device(&cdev->dev); return 0; @@ -407,6 +419,16 @@ int ccw_device_set_online(struct ccw_device *cdev) if ((cdev->private->state != DEV_STATE_ONLINE) && (cdev->private->state != DEV_STATE_W4SENSE)) { spin_unlock_irq(cdev->ccwlock); + /* Inform the user that set online failed. */ + if (cdev->private->state == DEV_STATE_BOXED) { + pr_warning("%s: Setting the device online failed " + "because it is boxed\n", + dev_name(&cdev->dev)); + } else if (cdev->private->state == DEV_STATE_NOT_OPER) { + pr_warning("%s: Setting the device online failed " + "because it is not operational\n", + dev_name(&cdev->dev)); + } /* Give up online reference since onlining failed. */ put_device(&cdev->dev); return -ENODEV; -- cgit v1.2.3 From 736b5db895eb900c108fe9e9b1659c171481169e Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:21 +0100 Subject: [S390] cio: handle error during device recognition consistently Remove the return code from ccw_device_recognition and handle recognition errors through the existing callback ccw_device_recog_done to reduce cleanup code duplication. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 57 ++++++------------------------------------- drivers/s390/cio/device.h | 2 +- drivers/s390/cio/device_fsm.c | 20 +++++---------- 3 files changed, 15 insertions(+), 64 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index ee3450667687..7ad6bfb2e55e 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -486,18 +486,9 @@ static int online_store_handle_offline(struct ccw_device *cdev) static int online_store_recog_and_online(struct ccw_device *cdev) { - int ret; - /* Do device recognition, if needed. */ if (cdev->private->state == DEV_STATE_BOXED) { - ret = ccw_device_recognition(cdev); - if (ret) { - CIO_MSG_EVENT(0, "Couldn't start recognition " - "for device 0.%x.%04x (ret=%d)\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, ret); - return ret; - } + ccw_device_recognition(cdev); wait_event(cdev->private->wait_q, cdev->private->flags.recog_done); if (cdev->private->state != DEV_STATE_OFFLINE) @@ -746,7 +737,7 @@ static struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch) return cdev; } -static int io_subchannel_recog(struct ccw_device *, struct subchannel *); +static void io_subchannel_recog(struct ccw_device *, struct subchannel *); static void sch_create_and_recog_new_device(struct subchannel *sch) { @@ -760,16 +751,7 @@ static void sch_create_and_recog_new_device(struct subchannel *sch) return; } /* Start recognition for the new ccw device. */ - if (io_subchannel_recog(cdev, sch)) { - spin_lock_irq(sch->lock); - sch_set_cdev(sch, NULL); - spin_unlock_irq(sch->lock); - css_sch_device_unregister(sch); - /* Put reference from io_subchannel_create_ccwdev(). */ - put_device(&sch->dev); - /* Give up initial reference. */ - put_device(&cdev->dev); - } + io_subchannel_recog(cdev, sch); } /* @@ -879,10 +861,8 @@ io_subchannel_recog_done(struct ccw_device *cdev) } } -static int -io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) +static void io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) { - int rc; struct ccw_device_private *priv; cdev->ccwlock = sch->lock; @@ -903,13 +883,8 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) /* Start async. device sensing. */ spin_lock_irq(sch->lock); sch_set_cdev(sch, cdev); - rc = ccw_device_recognition(cdev); + ccw_device_recognition(cdev); spin_unlock_irq(sch->lock); - if (rc) { - if (atomic_dec_and_test(&ccw_device_init_count)) - wake_up(&ccw_device_init_wq); - } - return rc; } static int ccw_device_move_to_sch(struct ccw_device *cdev, @@ -1528,10 +1503,7 @@ static int ccw_device_console_enable(struct ccw_device *cdev, sch->driver = &io_subchannel_driver; /* Initialize the ccw_device structure. */ cdev->dev.parent= &sch->dev; - rc = io_subchannel_recog(cdev, sch); - if (rc) - return rc; - + io_subchannel_recog(cdev, sch); /* Now wait for the async. recognition to come to an end. */ spin_lock_irq(cdev->ccwlock); while (!dev_fsm_final_state(cdev)) @@ -1547,7 +1519,7 @@ static int ccw_device_console_enable(struct ccw_device *cdev, rc = 0; out_unlock: spin_unlock_irq(cdev->ccwlock); - return 0; + return rc; } struct ccw_device * @@ -1789,7 +1761,6 @@ static int ccw_device_pm_thaw(struct device *dev) static void __ccw_device_pm_restore(struct ccw_device *cdev) { struct subchannel *sch = to_subchannel(cdev->dev.parent); - int ret; if (cio_is_console(sch->schid)) goto out; @@ -1799,22 +1770,10 @@ static void __ccw_device_pm_restore(struct ccw_device *cdev) */ spin_lock_irq(sch->lock); cdev->private->flags.resuming = 1; - ret = ccw_device_recognition(cdev); + ccw_device_recognition(cdev); spin_unlock_irq(sch->lock); - if (ret) { - CIO_MSG_EVENT(0, "Couldn't start recognition for device " - "0.%x.%04x (ret=%d)\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, ret); - spin_lock_irq(sch->lock); - cdev->private->state = DEV_STATE_DISCONNECTED; - spin_unlock_irq(sch->lock); - /* notify driver after the resume cb */ - goto out; - } wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev) || cdev->private->state == DEV_STATE_DISCONNECTED); - out: cdev->private->flags.resuming = 0; } diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index adaa27efc59e..78662e05d317 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -83,7 +83,7 @@ int ccw_device_cancel_halt_clear(struct ccw_device *); int ccw_device_is_orphan(struct ccw_device *); -int ccw_device_recognition(struct ccw_device *); +void ccw_device_recognition(struct ccw_device *); int ccw_device_online(struct ccw_device *); int ccw_device_offline(struct ccw_device *); void ccw_device_update_sense_data(struct ccw_device *); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index b163743bf586..83adb919648f 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -498,20 +498,9 @@ ccw_device_sense_pgid_done(struct ccw_device *cdev, int err) /* * Start device recognition. */ -int -ccw_device_recognition(struct ccw_device *cdev) +void ccw_device_recognition(struct ccw_device *cdev) { - struct subchannel *sch; - int ret; - - sch = to_subchannel(cdev->dev.parent); - ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); - if (ret != 0) - /* Couldn't enable the subchannel for i/o. Sick device. */ - return ret; - - /* After 60s the device recognition is considered to have failed. */ - ccw_device_set_timeout(cdev, 60*HZ); + struct subchannel *sch = to_subchannel(cdev->dev.parent); /* * We used to start here with a sense pgid to find out whether a device @@ -523,8 +512,11 @@ ccw_device_recognition(struct ccw_device *cdev) */ cdev->private->flags.recog_done = 0; cdev->private->state = DEV_STATE_SENSE_ID; + if (cio_enable_subchannel(sch, (u32) (addr_t) sch)) { + ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER); + return; + } ccw_device_sense_id_start(cdev); - return 0; } /* -- cgit v1.2.3 From 7c4d964fa4e857d6fb6b63159a898a5c63c173bf Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:22 +0100 Subject: [S390] cio: handle error during path verification consistently Handle verification errors consistently through the existing callback ccw_device_done to reduce cleanup code duplication. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_fsm.c | 19 +++++++++---------- drivers/s390/cio/device_pgid.c | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 83adb919648f..a70c46c8b4bc 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -549,9 +549,8 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) sch = to_subchannel(cdev->dev.parent); /* Update schib - pom may have changed. */ if (cio_update_schib(sch)) { - cdev->private->flags.donotify = 0; - ccw_device_done(cdev, DEV_STATE_NOT_OPER); - return; + err = -ENODEV; + goto callback; } /* Update lpm with verified path mask. */ sch->lpm = sch->vpm; @@ -561,9 +560,8 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) ccw_device_verify_start(cdev); return; } +callback: switch (err) { - case -EOPNOTSUPP: /* path grouping not supported, just set online. */ - cdev->private->options.pgroup = 0; case 0: ccw_device_done(cdev, DEV_STATE_ONLINE); /* Deliver fake irb to device driver, if needed. */ @@ -586,14 +584,15 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) cdev->private->flags.donotify = 0; ccw_device_done(cdev, DEV_STATE_BOXED); break; + case -EACCES: + /* Reset oper notify indication after verify error. */ + cdev->private->flags.donotify = 0; + ccw_device_done(cdev, DEV_STATE_DISCONNECTED); + break; default: /* Reset oper notify indication after verify error. */ cdev->private->flags.donotify = 0; - if (cdev->online) { - ccw_device_set_timeout(cdev, 0); - dev_fsm_event(cdev, DEV_EVENT_NOTOPER); - } else - ccw_device_done(cdev, DEV_STATE_NOT_OPER); + ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } } diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index fc5ca1dd52b3..cb27bd4cc231 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -423,7 +423,7 @@ __ccw_device_verify_start(struct ccw_device *cdev) /* Permanent path failure, try next. */ } /* Done with all paths. */ - ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV); + ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -EACCES); } /* -- cgit v1.2.3 From 1f5bd3848bfc56de4c32ef6971a6a966776204bb Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:23 +0100 Subject: [S390] cio: ensure proper locking during device recognition Device recognition needs to be started with the ccw device lock held to prevent race conditions between I/O starting and interrupt reception. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 7ad6bfb2e55e..afa362ce9e85 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -488,7 +488,9 @@ static int online_store_recog_and_online(struct ccw_device *cdev) { /* Do device recognition, if needed. */ if (cdev->private->state == DEV_STATE_BOXED) { + spin_lock_irq(cdev->ccwlock); ccw_device_recognition(cdev); + spin_unlock_irq(cdev->ccwlock); wait_event(cdev->private->wait_q, cdev->private->flags.recog_done); if (cdev->private->state != DEV_STATE_OFFLINE) -- cgit v1.2.3 From 16b9a0571da4ee5cd15ca75e871722b0b5aee64d Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:24 +0100 Subject: [S390] cio: dont panic in non-fatal conditions Remove the call to BUG() for situations which are unexpected but do not cause actual problems. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_fsm.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index a70c46c8b4bc..c397442ab3d8 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -1055,14 +1055,14 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev) ccw_device_start_id(cdev, 0); } -static void -ccw_device_offline_irq(struct ccw_device *cdev, enum dev_event dev_event) +static void ccw_device_disabled_irq(struct ccw_device *cdev, + enum dev_event dev_event) { struct subchannel *sch; sch = to_subchannel(cdev->dev.parent); /* - * An interrupt in state offline means a previous disable was not + * An interrupt in a disabled state means a previous disable was not * successful - should not happen, but we try to disable again. */ cio_disable_subchannel(sch); @@ -1124,26 +1124,13 @@ ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event) { } -/* - * Bug operation action. - */ -static void -ccw_device_bug(struct ccw_device *cdev, enum dev_event dev_event) -{ - CIO_MSG_EVENT(0, "Internal state [%i][%i] not handled for device " - "0.%x.%04x\n", cdev->private->state, dev_event, - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno); - BUG(); -} - /* * device statemachine */ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_STATE_NOT_OPER] = { [DEV_EVENT_NOTOPER] = ccw_device_nop, - [DEV_EVENT_INTERRUPT] = ccw_device_bug, + [DEV_EVENT_INTERRUPT] = ccw_device_disabled_irq, [DEV_EVENT_TIMEOUT] = ccw_device_nop, [DEV_EVENT_VERIFY] = ccw_device_nop, }, @@ -1161,7 +1148,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { }, [DEV_STATE_OFFLINE] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_offline_irq, + [DEV_EVENT_INTERRUPT] = ccw_device_disabled_irq, [DEV_EVENT_TIMEOUT] = ccw_device_nop, [DEV_EVENT_VERIFY] = ccw_device_offline_verify, }, @@ -1218,7 +1205,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_STATE_DISCONNECTED] = { [DEV_EVENT_NOTOPER] = ccw_device_nop, [DEV_EVENT_INTERRUPT] = ccw_device_start_id, - [DEV_EVENT_TIMEOUT] = ccw_device_bug, + [DEV_EVENT_TIMEOUT] = ccw_device_nop, [DEV_EVENT_VERIFY] = ccw_device_start_id, }, [DEV_STATE_DISCONNECTED_SENSE_ID] = { -- cgit v1.2.3 From e1f0fbd655539b0093738f58d57db83a0ac2dd6c Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:25 +0100 Subject: [S390] cio: consistent infrastructure for internal I/O requests Reduce code duplication by introducing a central infrastructure to perform an internal I/O operation on a CCW device. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/Makefile | 2 +- drivers/s390/cio/ccwreq.c | 327 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/s390/cio/device.h | 8 ++ drivers/s390/cio/io_sch.h | 50 +++++++ 4 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 drivers/s390/cio/ccwreq.c (limited to 'drivers') diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index fa4c9662f65e..d033414f7599 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -3,7 +3,7 @@ # obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o \ - fcx.o itcw.o crw.o + fcx.o itcw.o crw.o ccwreq.o ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device_id.o device_pgid.o device_status.o obj-y += ccw_device.o cmf.o diff --git a/drivers/s390/cio/ccwreq.c b/drivers/s390/cio/ccwreq.c new file mode 100644 index 000000000000..a6e205a384b2 --- /dev/null +++ b/drivers/s390/cio/ccwreq.c @@ -0,0 +1,327 @@ +/* + * Handling of internal CCW device requests. + * + * Copyright IBM Corp. 2009 + * Author(s): Peter Oberparleiter + */ + +#include +#include +#include +#include + +#include "io_sch.h" +#include "cio.h" +#include "device.h" +#include "cio_debug.h" + +/** + * lpm_adjust - adjust path mask + * @lpm: path mask to adjust + * @mask: mask of available paths + * + * Shift @lpm right until @lpm and @mask have at least one bit in common or + * until @lpm is zero. Return the resulting lpm. + */ +int lpm_adjust(int lpm, int mask) +{ + while (lpm && ((lpm & mask) == 0)) + lpm >>= 1; + return lpm; +} + +/* + * Adjust path mask to use next path and reset retry count. Return resulting + * path mask. + */ +static u16 ccwreq_next_path(struct ccw_device *cdev) +{ + struct ccw_request *req = &cdev->private->req; + + req->retries = req->maxretries; + req->mask = lpm_adjust(req->mask >>= 1, req->lpm); + + return req->mask; +} + +/* + * Clean up device state and report to callback. + */ +static void ccwreq_stop(struct ccw_device *cdev, int rc) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + if (req->done) + return; + req->done = 1; + ccw_device_set_timeout(cdev, 0); + memset(&cdev->private->irb, 0, sizeof(struct irb)); + sch->lpm = sch->schib.pmcw.pam; + if (rc && rc != -ENODEV && req->drc) + rc = req->drc; + req->callback(cdev, req->data, rc); +} + +/* + * (Re-)Start the operation until retries and paths are exhausted. + */ +static void ccwreq_do(struct ccw_device *cdev) +{ + struct ccw_request *req = &cdev->private->req; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw1 *cp = req->cp; + int rc = -EACCES; + + while (req->mask) { + if (req->retries-- == 0) { + /* Retries exhausted, try next path. */ + ccwreq_next_path(cdev); + continue; + } + /* Perform start function. */ + sch->lpm = 0xff; + memset(&cdev->private->irb, 0, sizeof(struct irb)); + rc = cio_start(sch, cp, req->mask); + if (rc == 0) { + /* I/O started successfully. */ + ccw_device_set_timeout(cdev, req->timeout); + return; + } + if (rc == -ENODEV) { + /* Permanent device error. */ + break; + } + if (rc == -EACCES) { + /* Permant path error. */ + ccwreq_next_path(cdev); + continue; + } + /* Temporary improper status. */ + rc = cio_clear(sch); + if (rc) + break; + return; + } + ccwreq_stop(cdev, rc); +} + +/** + * ccw_request_start - perform I/O request + * @cdev: ccw device + * + * Perform the I/O request specified by cdev->req. + */ +void ccw_request_start(struct ccw_device *cdev) +{ + struct ccw_request *req = &cdev->private->req; + + req->mask = 0x80; + req->retries = req->maxretries; + req->mask = lpm_adjust(req->mask, req->lpm); + req->drc = 0; + req->done = 0; + req->cancel = 0; + if (!req->mask) + goto out_nopath; + ccwreq_do(cdev); + return; + +out_nopath: + ccwreq_stop(cdev, -EACCES); +} + +/** + * ccw_request_cancel - cancel running I/O request + * @cdev: ccw device + * + * Cancel the I/O request specified by cdev->req. Return non-zero if request + * has already finished, zero otherwise. + */ +int ccw_request_cancel(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + int rc; + + if (req->done) + return 1; + req->cancel = 1; + rc = cio_clear(sch); + if (rc) + ccwreq_stop(cdev, rc); + return 0; +} + +/* + * Return the status of the internal I/O started on the specified ccw device. + * Perform BASIC SENSE if required. + */ +static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb) +{ + struct irb *irb = &cdev->private->irb; + struct cmd_scsw *scsw = &irb->scsw.cmd; + + /* Perform BASIC SENSE if needed. */ + if (ccw_device_accumulate_and_sense(cdev, lcirb)) + return IO_RUNNING; + /* Check for halt/clear interrupt. */ + if (scsw->fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) + return IO_KILLED; + /* Check for path error. */ + if (scsw->cc == 3 || scsw->pno) + return IO_PATH_ERROR; + /* Handle BASIC SENSE data. */ + if (irb->esw.esw0.erw.cons) { + CIO_TRACE_EVENT(2, "sensedata"); + CIO_HEX_EVENT(2, &cdev->private->dev_id, + sizeof(struct ccw_dev_id)); + CIO_HEX_EVENT(2, &cdev->private->irb.ecw, SENSE_MAX_COUNT); + /* Check for command reject. */ + if (irb->ecw[0] & SNS0_CMD_REJECT) + return IO_REJECTED; + /* Assume that unexpected SENSE data implies an error. */ + return IO_STATUS_ERROR; + } + /* Check for channel errors. */ + if (scsw->cstat != 0) + return IO_STATUS_ERROR; + /* Check for device errors. */ + if (scsw->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) + return IO_STATUS_ERROR; + /* Check for final state. */ + if (!(scsw->dstat & DEV_STAT_DEV_END)) + return IO_RUNNING; + /* Check for other improper status. */ + if (scsw->cc == 1 && (scsw->stctl & SCSW_STCTL_ALERT_STATUS)) + return IO_STATUS_ERROR; + return IO_DONE; +} + +/* + * Log ccw request status. + */ +static void ccwreq_log_status(struct ccw_device *cdev, enum io_status status) +{ + struct ccw_request *req = &cdev->private->req; + struct { + struct ccw_dev_id dev_id; + u16 retries; + u8 lpm; + u8 status; + } __attribute__ ((packed)) data; + data.dev_id = cdev->private->dev_id; + data.retries = req->retries; + data.lpm = req->mask; + data.status = (u8) status; + CIO_TRACE_EVENT(2, "reqstat"); + CIO_HEX_EVENT(2, &data, sizeof(data)); +} + +/** + * ccw_request_handler - interrupt handler for I/O request procedure. + * @cdev: ccw device + * + * Handle interrupt during I/O request procedure. + */ +void ccw_request_handler(struct ccw_device *cdev) +{ + struct ccw_request *req = &cdev->private->req; + struct irb *irb = (struct irb *) __LC_IRB; + enum io_status status; + int rc = -EOPNOTSUPP; + + /* Check status of I/O request. */ + status = ccwreq_status(cdev, irb); + if (req->filter) + status = req->filter(cdev, req->data, irb, status); + if (status != IO_RUNNING) + ccw_device_set_timeout(cdev, 0); + if (status != IO_DONE && status != IO_RUNNING) + ccwreq_log_status(cdev, status); + switch (status) { + case IO_DONE: + break; + case IO_RUNNING: + return; + case IO_REJECTED: + goto err; + case IO_PATH_ERROR: + goto out_next_path; + case IO_STATUS_ERROR: + goto out_restart; + case IO_KILLED: + /* Check if request was cancelled on purpose. */ + if (req->cancel) { + rc = -EIO; + goto err; + } + goto out_restart; + } + /* Check back with request initiator. */ + if (!req->check) + goto out; + switch (req->check(cdev, req->data)) { + case 0: + break; + case -EAGAIN: + goto out_restart; + case -EACCES: + goto out_next_path; + default: + goto err; + } +out: + ccwreq_stop(cdev, 0); + return; + +out_next_path: + /* Try next path and restart I/O. */ + if (!ccwreq_next_path(cdev)) { + rc = -EACCES; + goto err; + } +out_restart: + /* Restart. */ + ccwreq_do(cdev); + return; +err: + ccwreq_stop(cdev, rc); +} + + +/** + * ccw_request_timeout - timeout handler for I/O request procedure + * @cdev: ccw device + * + * Handle timeout during I/O request procedure. + */ +void ccw_request_timeout(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + int rc; + + if (!ccwreq_next_path(cdev)) { + /* set the final return code for this request */ + req->drc = -ETIME; + } + rc = cio_clear(sch); + if (rc) + goto err; + return; + +err: + ccwreq_stop(cdev, rc); +} + +/** + * ccw_request_notoper - notoper handler for I/O request procedure + * @cdev: ccw device + * + * Handle timeout during I/O request procedure. + */ +void ccw_request_notoper(struct ccw_device *cdev) +{ + ccwreq_stop(cdev, -ENODEV); +} diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 78662e05d317..f3c8a2a8c3a4 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -98,6 +98,14 @@ void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *); int ccw_device_accumulate_and_sense(struct ccw_device *, struct irb *); int ccw_device_do_sense(struct ccw_device *, struct irb *); +/* Function prototype for internal request handling. */ +int lpm_adjust(int lpm, int mask); +void ccw_request_start(struct ccw_device *); +int ccw_request_cancel(struct ccw_device *cdev); +void ccw_request_handler(struct ccw_device *cdev); +void ccw_request_timeout(struct ccw_device *cdev); +void ccw_request_notoper(struct ccw_device *cdev); + /* Function prototypes for sense id stuff. */ void ccw_device_sense_id_start(struct ccw_device *); void ccw_device_sense_id_irq(struct ccw_device *, enum dev_event); diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index b770e4202131..f9ff7683e242 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -1,7 +1,10 @@ #ifndef S390_IO_SCH_H #define S390_IO_SCH_H +#include #include +#include +#include "css.h" /* * command-mode operation request block @@ -67,6 +70,52 @@ struct io_subchannel_private { #define MAX_CIWS 8 +/* + * Possible status values for a CCW request's I/O. + */ +enum io_status { + IO_DONE, + IO_RUNNING, + IO_STATUS_ERROR, + IO_PATH_ERROR, + IO_REJECTED, + IO_KILLED +}; + +/** + * ccw_request - Internal CCW request. + * @cp: channel program to start + * @timeout: maximum allowable time in jiffies between start I/O and interrupt + * @maxretries: number of retries per I/O operation and path + * @lpm: mask of paths to use + * @check: optional callback that determines if results are final + * @filter: optional callback to adjust request status based on IRB data + * @callback: final callback + * @data: user-defined pointer passed to all callbacks + * @mask: current path mask + * @retries: current number of retries + * @drc: delayed return code + * @cancel: non-zero if request was cancelled + * @done: non-zero if request was finished + */ +struct ccw_request { + struct ccw1 *cp; + unsigned long timeout; + u16 maxretries; + u8 lpm; + int (*check)(struct ccw_device *, void *); + enum io_status (*filter)(struct ccw_device *, void *, struct irb *, + enum io_status); + void (*callback)(struct ccw_device *, void *, int); + void *data; + /* These fields are used internally. */ + u8 mask; + u16 retries; + int drc; + int cancel:1; + int done:1; +} __attribute__((packed)); + /* * sense-id response buffer layout */ @@ -99,6 +148,7 @@ struct ccw_device_private { unsigned long registered; struct ccw_dev_id dev_id; /* device id */ struct subchannel_id schid; /* subchannel number */ + struct ccw_request req; /* internal I/O request */ u8 imask; /* lpm mask for SNID/SID/SPGID */ int iretry; /* retry counter SNID/SID/SPGID */ struct { -- cgit v1.2.3 From 39f5360b3d68a8e96d280481d9c442e7c005c317 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:26 +0100 Subject: [S390] cio: use ccw request infrastructure for sense id Use the newly introduced ccw request infrastructure to implement the sense id operation. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.h | 1 - drivers/s390/cio/device_fsm.c | 66 ++------ drivers/s390/cio/device_id.c | 375 +++++++++++++++--------------------------- 3 files changed, 154 insertions(+), 288 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index f3c8a2a8c3a4..2815798c4165 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -108,7 +108,6 @@ void ccw_request_notoper(struct ccw_device *cdev); /* Function prototypes for sense id stuff. */ void ccw_device_sense_id_start(struct ccw_device *); -void ccw_device_sense_id_irq(struct ccw_device *, enum dev_event); void ccw_device_sense_id_done(struct ccw_device *, int); /* Function prototypes for path grouping stuff. */ diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index c397442ab3d8..6247d07d395e 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -229,7 +229,6 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) sch = to_subchannel(cdev->dev.parent); - ccw_device_set_timeout(cdev, 0); cio_disable_subchannel(sch); /* * Now that we tried recognition, we have performed device selection @@ -263,22 +262,10 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) } switch (state) { case DEV_STATE_NOT_OPER: - CIO_MSG_EVENT(2, "SenseID : unknown device %04x on " - "subchannel 0.%x.%04x\n", - cdev->private->dev_id.devno, - sch->schid.ssid, sch->schid.sch_no); break; case DEV_STATE_OFFLINE: if (!cdev->online) { ccw_device_update_sense_data(cdev); - /* Issue device info message. */ - CIO_MSG_EVENT(4, "SenseID : device 0.%x.%04x reports: " - "CU Type/Mod = %04X/%02X, Dev Type/Mod " - "= %04X/%02X\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, - cdev->id.cu_type, cdev->id.cu_model, - cdev->id.dev_type, cdev->id.dev_model); break; } cdev->private->state = DEV_STATE_OFFLINE; @@ -293,10 +280,6 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) } return; case DEV_STATE_BOXED: - CIO_MSG_EVENT(0, "SenseID : boxed device %04x on " - " subchannel 0.%x.%04x\n", - cdev->private->dev_id.devno, - sch->schid.ssid, sch->schid.sch_no); if (cdev->id.cu_type != 0) { /* device was recognized before */ cdev->private->flags.recog_done = 1; cdev->private->state = DEV_STATE_BOXED; @@ -520,27 +503,25 @@ void ccw_device_recognition(struct ccw_device *cdev) } /* - * Handle timeout in device recognition. + * Handle events for states that use the ccw request infrastructure. */ -static void -ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event) +static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e) { - int ret; - - ret = ccw_device_cancel_halt_clear(cdev); - switch (ret) { - case 0: - ccw_device_recog_done(cdev, DEV_STATE_BOXED); + switch (e) { + case DEV_EVENT_NOTOPER: + ccw_request_notoper(cdev); break; - case -ENODEV: - ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER); + case DEV_EVENT_INTERRUPT: + ccw_request_handler(cdev); + break; + case DEV_EVENT_TIMEOUT: + ccw_request_timeout(cdev); break; default: - ccw_device_set_timeout(cdev, 3*HZ); + break; } } - void ccw_device_verify_done(struct ccw_device *cdev, int err) { @@ -712,15 +693,6 @@ ccw_device_onoff_timeout(struct ccw_device *cdev, enum dev_event dev_event) } } -/* - * Handle not oper event in device recognition. - */ -static void -ccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event) -{ - ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER); -} - /* * Handle not operational event in non-special state. */ @@ -1015,10 +987,6 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event) if (cio_enable_subchannel(sch, (u32)(addr_t)sch) != 0) /* Couldn't enable the subchannel for i/o. Sick device. */ return; - - /* After 60s the device recognition is considered to have failed. */ - ccw_device_set_timeout(cdev, 60*HZ); - cdev->private->state = DEV_STATE_DISCONNECTED_SENSE_ID; ccw_device_sense_id_start(cdev); } @@ -1141,9 +1109,9 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_SENSE_ID] = { - [DEV_EVENT_NOTOPER] = ccw_device_recog_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_sense_id_irq, - [DEV_EVENT_TIMEOUT] = ccw_device_recog_timeout, + [DEV_EVENT_NOTOPER] = ccw_device_request_event, + [DEV_EVENT_INTERRUPT] = ccw_device_request_event, + [DEV_EVENT_TIMEOUT] = ccw_device_request_event, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_OFFLINE] = { @@ -1209,9 +1177,9 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_VERIFY] = ccw_device_start_id, }, [DEV_STATE_DISCONNECTED_SENSE_ID] = { - [DEV_EVENT_NOTOPER] = ccw_device_recog_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_sense_id_irq, - [DEV_EVENT_TIMEOUT] = ccw_device_recog_timeout, + [DEV_EVENT_NOTOPER] = ccw_device_request_event, + [DEV_EVENT_INTERRUPT] = ccw_device_request_event, + [DEV_EVENT_TIMEOUT] = ccw_device_request_event, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_CMFCHANGE] = { diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c index 1bdaa614e34f..4728644ed85c 100644 --- a/drivers/s390/cio/device_id.c +++ b/drivers/s390/cio/device_id.c @@ -1,40 +1,39 @@ /* - * drivers/s390/cio/device_id.c + * CCW device SENSE ID I/O handling. * - * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation - * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) - * Martin Schwidefsky (schwidefsky@de.ibm.com) - * - * Sense ID functions. + * Copyright IBM Corp. 2002,2009 + * Author(s): Cornelia Huck + * Martin Schwidefsky + * Peter Oberparleiter */ -#include -#include #include - +#include +#include +#include #include -#include +#include #include -#include #include #include "cio.h" #include "cio_debug.h" -#include "css.h" #include "device.h" -#include "ioasm.h" #include "io_sch.h" +#define SENSE_ID_RETRIES 5 +#define SENSE_ID_TIMEOUT (10 * HZ) +#define SENSE_ID_MIN_LEN 4 +#define SENSE_ID_BASIC_LEN 7 + /** - * vm_vdev_to_cu_type - Convert vm virtual device into control unit type - * for certain devices. - * @class: virtual device class - * @type: virtual device type + * diag210_to_senseid - convert diag 0x210 data to sense id information + * @senseid: sense id + * @diag: diag 0x210 data * - * Returns control unit type if a match was made or %0xffff otherwise. + * Return 0 on success, non-zero otherwise. */ -static int vm_vdev_to_cu_type(int class, int type) +static int diag210_to_senseid(struct senseid *senseid, struct diag210 *diag) { static struct { int class, type, cu_type; @@ -71,253 +70,153 @@ static int vm_vdev_to_cu_type(int class, int type) }; int i; - for (i = 0; i < ARRAY_SIZE(vm_devices); i++) - if (class == vm_devices[i].class && type == vm_devices[i].type) - return vm_devices[i].cu_type; + /* Special case for osa devices. */ + if (diag->vrdcvcla == 0x02 && diag->vrdcvtyp == 0x20) { + senseid->cu_type = 0x3088; + senseid->cu_model = 0x60; + senseid->reserved = 0xff; + return 0; + } + for (i = 0; i < ARRAY_SIZE(vm_devices); i++) { + if (diag->vrdcvcla == vm_devices[i].class && + diag->vrdcvtyp == vm_devices[i].type) { + senseid->cu_type = vm_devices[i].cu_type; + senseid->reserved = 0xff; + return 0; + } + } - return 0xffff; + return -ENODEV; } /** - * diag_get_dev_info - retrieve device information via DIAG X'210' - * @devno: device number - * @ps: pointer to sense ID data area + * diag_get_dev_info - retrieve device information via diag 0x210 + * @cdev: ccw device * * Returns zero on success, non-zero otherwise. */ -static int diag_get_dev_info(u16 devno, struct senseid *ps) +static int diag210_get_dev_info(struct ccw_device *cdev) { + struct ccw_dev_id *dev_id = &cdev->private->dev_id; + struct senseid *senseid = &cdev->private->senseid; struct diag210 diag_data; - int ccode; - - CIO_TRACE_EVENT (4, "VMvdinf"); - - diag_data = (struct diag210) { - .vrdcdvno = devno, - .vrdclen = sizeof (diag_data), - }; - - ccode = diag210 (&diag_data); - if ((ccode == 0) || (ccode == 2)) { - ps->reserved = 0xff; - - /* Special case for osa devices. */ - if (diag_data.vrdcvcla == 0x02 && diag_data.vrdcvtyp == 0x20) { - ps->cu_type = 0x3088; - ps->cu_model = 0x60; - return 0; - } - ps->cu_type = vm_vdev_to_cu_type(diag_data.vrdcvcla, - diag_data.vrdcvtyp); - if (ps->cu_type != 0xffff) - return 0; - } - - CIO_MSG_EVENT(0, "DIAG X'210' for device %04X returned (cc = %d):" - "vdev class : %02X, vdev type : %04X \n ... " - "rdev class : %02X, rdev type : %04X, " - "rdev model: %02X\n", - devno, ccode, - diag_data.vrdcvcla, diag_data.vrdcvtyp, - diag_data.vrdcrccl, diag_data.vrdccrty, - diag_data.vrdccrmd); - + int rc; + + if (dev_id->ssid != 0) + return -ENODEV; + memset(&diag_data, 0, sizeof(diag_data)); + diag_data.vrdcdvno = dev_id->devno; + diag_data.vrdclen = sizeof(diag_data); + rc = diag210(&diag_data); + CIO_TRACE_EVENT(4, "diag210"); + CIO_HEX_EVENT(4, &rc, sizeof(rc)); + CIO_HEX_EVENT(4, &diag_data, sizeof(diag_data)); + if (rc != 0 && rc != 2) + goto err_failed; + if (diag210_to_senseid(senseid, &diag_data)) + goto err_unknown; + return 0; + +err_unknown: + CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: unknown diag210 data\n", + dev_id->ssid, dev_id->devno); + return -ENODEV; +err_failed: + CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: diag210 failed (rc=%d)\n", + dev_id->ssid, dev_id->devno, rc); return -ENODEV; } /* - * Start Sense ID helper function. - * Try to obtain the 'control unit'/'device type' information - * associated with the subchannel. + * Initialize SENSE ID data. */ -static int -__ccw_device_sense_id_start(struct ccw_device *cdev) -{ - struct subchannel *sch; - struct ccw1 *ccw; - int ret; - - sch = to_subchannel(cdev->dev.parent); - /* Setup sense channel program. */ - ccw = cdev->private->iccws; - ccw->cmd_code = CCW_CMD_SENSE_ID; - ccw->cda = (__u32) __pa (&cdev->private->senseid); - ccw->count = sizeof (struct senseid); - ccw->flags = CCW_FLAG_SLI; - - /* Reset device status. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); - - /* Try on every path. */ - ret = -ENODEV; - while (cdev->private->imask != 0) { - cdev->private->senseid.cu_type = 0xFFFF; - if ((sch->opm & cdev->private->imask) != 0 && - cdev->private->iretry > 0) { - cdev->private->iretry--; - /* Reset internal retry indication. */ - cdev->private->flags.intretry = 0; - ret = cio_start (sch, cdev->private->iccws, - cdev->private->imask); - /* ret is 0, -EBUSY, -EACCES or -ENODEV */ - if (ret != -EACCES) - return ret; - } - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - } - return ret; -} - -void -ccw_device_sense_id_start(struct ccw_device *cdev) +static void snsid_init(struct ccw_device *cdev) { - int ret; - - memset (&cdev->private->senseid, 0, sizeof (struct senseid)); - cdev->private->imask = 0x80; - cdev->private->iretry = 5; - ret = __ccw_device_sense_id_start(cdev); - if (ret && ret != -EBUSY) - ccw_device_sense_id_done(cdev, ret); + cdev->private->flags.esid = 0; + memset(&cdev->private->senseid, 0, sizeof(cdev->private->senseid)); + cdev->private->senseid.cu_type = 0xffff; } /* - * Called from interrupt context to check if a valid answer - * to Sense ID was received. + * Check for complete SENSE ID data. */ -static int -ccw_device_check_sense_id(struct ccw_device *cdev) +static int snsid_check(struct ccw_device *cdev, void *data) { - struct subchannel *sch; - struct irb *irb; - - sch = to_subchannel(cdev->dev.parent); - irb = &cdev->private->irb; - - /* Check the error cases. */ - if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { - /* Retry Sense ID if requested. */ - if (cdev->private->flags.intretry) { - cdev->private->flags.intretry = 0; - return -EAGAIN; - } - return -ETIME; - } - if (irb->esw.esw0.erw.cons && (irb->ecw[0] & SNS0_CMD_REJECT)) { - /* - * if the device doesn't support the SenseID - * command further retries wouldn't help ... - * NB: We don't check here for intervention required like we - * did before, because tape devices with no tape inserted - * may present this status *in conjunction with* the - * sense id information. So, for intervention required, - * we use the "whack it until it talks" strategy... - */ - CIO_MSG_EVENT(0, "SenseID : device %04x on Subchannel " - "0.%x.%04x reports cmd reject\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no); + struct cmd_scsw *scsw = &cdev->private->irb.scsw.cmd; + int len = sizeof(struct senseid) - scsw->count; + + /* Check for incomplete SENSE ID data. */ + if (len < SENSE_ID_MIN_LEN) + goto out_restart; + if (cdev->private->senseid.cu_type == 0xffff) + goto out_restart; + /* Check for incompatible SENSE ID data. */ + if (cdev->private->senseid.reserved != 0xff) return -EOPNOTSUPP; - } - if (irb->esw.esw0.erw.cons) { - CIO_MSG_EVENT(2, "SenseID : UC on dev 0.%x.%04x, " - "lpum %02X, cnt %02d, sns :" - " %02X%02X%02X%02X %02X%02X%02X%02X ...\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, - irb->esw.esw0.sublog.lpum, - irb->esw.esw0.erw.scnt, - irb->ecw[0], irb->ecw[1], - irb->ecw[2], irb->ecw[3], - irb->ecw[4], irb->ecw[5], - irb->ecw[6], irb->ecw[7]); - return -EAGAIN; - } - if (irb->scsw.cmd.cc == 3) { - u8 lpm; + /* Check for extended-identification information. */ + if (len > SENSE_ID_BASIC_LEN) + cdev->private->flags.esid = 1; + return 0; - lpm = to_io_private(sch)->orb.cmd.lpm; - if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0) - CIO_MSG_EVENT(4, "SenseID : path %02X for device %04x " - "on subchannel 0.%x.%04x is " - "'not operational'\n", lpm, - cdev->private->dev_id.devno, - sch->schid.ssid, sch->schid.sch_no); - return -EACCES; - } - - /* Did we get a proper answer ? */ - if (irb->scsw.cmd.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF && - cdev->private->senseid.reserved == 0xFF) { - if (irb->scsw.cmd.count < sizeof(struct senseid) - 8) - cdev->private->flags.esid = 1; - return 0; /* Success */ - } - - /* Hmm, whatever happened, try again. */ - CIO_MSG_EVENT(2, "SenseID : start_IO() for device %04x on " - "subchannel 0.%x.%04x returns status %02X%02X\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, - irb->scsw.cmd.dstat, irb->scsw.cmd.cstat); +out_restart: + snsid_init(cdev); return -EAGAIN; } /* - * Got interrupt for Sense ID. + * Process SENSE ID request result. */ -void -ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event) +static void snsid_callback(struct ccw_device *cdev, void *data, int rc) { - struct subchannel *sch; - struct irb *irb; - int ret; - - sch = to_subchannel(cdev->dev.parent); - irb = (struct irb *) __LC_IRB; - /* Retry sense id, if needed. */ - if (irb->scsw.cmd.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if ((irb->scsw.cmd.cc == 1) || !irb->scsw.cmd.actl) { - ret = __ccw_device_sense_id_start(cdev); - if (ret && ret != -EBUSY) - ccw_device_sense_id_done(cdev, ret); + struct ccw_dev_id *id = &cdev->private->dev_id; + struct senseid *senseid = &cdev->private->senseid; + int vm = 0; + + if (rc && MACHINE_IS_VM) { + /* Try diag 0x210 fallback on z/VM. */ + snsid_init(cdev); + if (diag210_get_dev_info(cdev) == 0) { + rc = 0; + vm = 1; } - return; } - if (ccw_device_accumulate_and_sense(cdev, irb) != 0) - return; - ret = ccw_device_check_sense_id(cdev); - memset(&cdev->private->irb, 0, sizeof(struct irb)); - switch (ret) { - /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN or -EACCES */ - case 0: /* Sense id succeeded. */ - case -ETIME: /* Sense id stopped by timeout. */ - ccw_device_sense_id_done(cdev, ret); - break; - case -EACCES: /* channel is not operational. */ - sch->lpm &= ~cdev->private->imask; - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - /* fall through. */ - case -EAGAIN: /* try again. */ - ret = __ccw_device_sense_id_start(cdev); - if (ret == 0 || ret == -EBUSY) - break; - /* fall through. */ - default: /* Sense ID failed. Try asking VM. */ - if (MACHINE_IS_VM) - ret = diag_get_dev_info(cdev->private->dev_id.devno, - &cdev->private->senseid); - else - /* - * If we can't couldn't identify the device type we - * consider the device "not operational". - */ - ret = -ENODEV; + CIO_MSG_EVENT(2, "snsid: device 0.%x.%04x: rc=%d %04x/%02x " + "%04x/%02x%s\n", id->ssid, id->devno, rc, + senseid->cu_type, senseid->cu_model, senseid->dev_type, + senseid->dev_model, vm ? " (diag210)" : ""); + ccw_device_sense_id_done(cdev, rc); +} - ccw_device_sense_id_done(cdev, ret); - break; - } +/** + * ccw_device_sense_id_start - perform SENSE ID + * @cdev: ccw device + * + * Execute a SENSE ID channel program on @cdev to update its sense id + * information. When finished, call ccw_device_sense_id_done with a + * return code specifying the result. + */ +void ccw_device_sense_id_start(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + + CIO_TRACE_EVENT(4, "snsid"); + CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); + /* Data setup. */ + snsid_init(cdev); + /* Channel program setup. */ + cp->cmd_code = CCW_CMD_SENSE_ID; + cp->cda = (u32) (addr_t) &cdev->private->senseid; + cp->count = sizeof(struct senseid); + cp->flags = CCW_FLAG_SLI; + /* Request setup. */ + memset(req, 0, sizeof(*req)); + req->cp = cp; + req->timeout = SENSE_ID_TIMEOUT; + req->maxretries = SENSE_ID_RETRIES; + req->lpm = sch->schib.pmcw.pam & sch->opm; + req->check = snsid_check; + req->callback = snsid_callback; + ccw_request_start(cdev); } -- cgit v1.2.3 From 9679baaf85b6e4dc662160bbbca344287ea6580d Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:27 +0100 Subject: [S390] cio: use ccw request infrastructure for pgid Use the newly introduced ccw request infrastructure to implement pgid related operations: sense pgid, set pgid and disband pg. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 3 - drivers/s390/cio/device.h | 3 - drivers/s390/cio/device_fsm.c | 100 +---- drivers/s390/cio/device_pgid.c | 876 +++++++++++++++++------------------------ drivers/s390/cio/io_sch.h | 5 +- 5 files changed, 383 insertions(+), 604 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index afa362ce9e85..6a9ac85065ed 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -957,9 +957,6 @@ void io_subchannel_init_config(struct subchannel *sch) { memset(&sch->config, 0, sizeof(sch->config)); sch->config.csense = 1; - /* Use subchannel mp mode when there is more than 1 installed CHPID. */ - if ((sch->schib.pmcw.pim & (sch->schib.pmcw.pim - 1)) != 0) - sch->config.mp = 1; } static void io_subchannel_init_fields(struct subchannel *sch) diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 2815798c4165..ac6f55b4b74c 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -112,15 +112,12 @@ void ccw_device_sense_id_done(struct ccw_device *, int); /* Function prototypes for path grouping stuff. */ void ccw_device_sense_pgid_start(struct ccw_device *); -void ccw_device_sense_pgid_irq(struct ccw_device *, enum dev_event); void ccw_device_sense_pgid_done(struct ccw_device *, int); void ccw_device_verify_start(struct ccw_device *); -void ccw_device_verify_irq(struct ccw_device *, enum dev_event); void ccw_device_verify_done(struct ccw_device *, int); void ccw_device_disband_start(struct ccw_device *); -void ccw_device_disband_irq(struct ccw_device *, enum dev_event); void ccw_device_disband_done(struct ccw_device *, int); int ccw_device_call_handler(struct ccw_device *); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 6247d07d395e..c7439f5500f8 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -394,58 +394,6 @@ ccw_device_done(struct ccw_device *cdev, int state) wake_up(&cdev->private->wait_q); } -static int cmp_pgid(struct pgid *p1, struct pgid *p2) -{ - char *c1; - char *c2; - - c1 = (char *)p1; - c2 = (char *)p2; - - return memcmp(c1 + 1, c2 + 1, sizeof(struct pgid) - 1); -} - -static void __ccw_device_get_common_pgid(struct ccw_device *cdev) -{ - int i; - int last; - - last = 0; - for (i = 0; i < 8; i++) { - if (cdev->private->pgid[i].inf.ps.state1 == SNID_STATE1_RESET) - /* No PGID yet */ - continue; - if (cdev->private->pgid[last].inf.ps.state1 == - SNID_STATE1_RESET) { - /* First non-zero PGID */ - last = i; - continue; - } - if (cmp_pgid(&cdev->private->pgid[i], - &cdev->private->pgid[last]) == 0) - /* Non-conflicting PGIDs */ - continue; - - /* PGID mismatch, can't pathgroup. */ - CIO_MSG_EVENT(0, "SNID - pgid mismatch for device " - "0.%x.%04x, can't pathgroup\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno); - cdev->private->options.pgroup = 0; - return; - } - if (cdev->private->pgid[last].inf.ps.state1 == - SNID_STATE1_RESET) - /* No previous pgid found */ - memcpy(&cdev->private->pgid[0], - &channel_subsystems[0]->global_pgid, - sizeof(struct pgid)); - else - /* Use existing pgid */ - memcpy(&cdev->private->pgid[0], &cdev->private->pgid[last], - sizeof(struct pgid)); -} - /* * Function called from device_pgid.c after sense path ground has completed. */ @@ -457,12 +405,8 @@ ccw_device_sense_pgid_done(struct ccw_device *cdev, int err) sch = to_subchannel(cdev->dev.parent); switch (err) { case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */ - cdev->private->options.pgroup = 0; - break; case 0: /* success */ case -EACCES: /* partial success, some paths not operational */ - /* Check if all pgids are equal or 0. */ - __ccw_device_get_common_pgid(cdev); break; case -ETIME: /* Sense path group id stopped by timeout. */ case -EUSERS: /* device is reserved for someone else. */ @@ -474,7 +418,6 @@ ccw_device_sense_pgid_done(struct ccw_device *cdev, int err) } /* Start Path Group verification. */ cdev->private->state = DEV_STATE_VERIFY; - cdev->private->flags.doverify = 0; ccw_device_verify_start(cdev); } @@ -537,7 +480,6 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) sch->lpm = sch->vpm; /* Repeat path verification? */ if (cdev->private->flags.doverify) { - cdev->private->flags.doverify = 0; ccw_device_verify_start(cdev); return; } @@ -602,7 +544,6 @@ ccw_device_online(struct ccw_device *cdev) if (!cdev->private->options.pgroup) { /* Start initial path verification. */ cdev->private->state = DEV_STATE_VERIFY; - cdev->private->flags.doverify = 0; ccw_device_verify_start(cdev); return 0; } @@ -624,7 +565,6 @@ ccw_device_disband_done(struct ccw_device *cdev, int err) break; default: cdev->private->flags.donotify = 0; - dev_fsm_event(cdev, DEV_EVENT_NOTOPER); ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } @@ -672,27 +612,6 @@ ccw_device_offline(struct ccw_device *cdev) return 0; } -/* - * Handle timeout in device online/offline process. - */ -static void -ccw_device_onoff_timeout(struct ccw_device *cdev, enum dev_event dev_event) -{ - int ret; - - ret = ccw_device_cancel_halt_clear(cdev); - switch (ret) { - case 0: - ccw_device_done(cdev, DEV_STATE_BOXED); - break; - case -ENODEV: - ccw_device_done(cdev, DEV_STATE_NOT_OPER); - break; - default: - ccw_device_set_timeout(cdev, 3*HZ); - } -} - /* * Handle not operational event in non-special state. */ @@ -751,7 +670,6 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event) } /* Device is idle, we can do the path verification. */ cdev->private->state = DEV_STATE_VERIFY; - cdev->private->flags.doverify = 0; ccw_device_verify_start(cdev); } @@ -1103,9 +1021,9 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_SENSE_PGID] = { - [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_sense_pgid_irq, - [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, + [DEV_EVENT_NOTOPER] = ccw_device_request_event, + [DEV_EVENT_INTERRUPT] = ccw_device_request_event, + [DEV_EVENT_TIMEOUT] = ccw_device_request_event, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_SENSE_ID] = { @@ -1121,9 +1039,9 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_VERIFY] = ccw_device_offline_verify, }, [DEV_STATE_VERIFY] = { - [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_verify_irq, - [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, + [DEV_EVENT_NOTOPER] = ccw_device_request_event, + [DEV_EVENT_INTERRUPT] = ccw_device_request_event, + [DEV_EVENT_TIMEOUT] = ccw_device_request_event, [DEV_EVENT_VERIFY] = ccw_device_delay_verify, }, [DEV_STATE_ONLINE] = { @@ -1139,9 +1057,9 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_VERIFY] = ccw_device_online_verify, }, [DEV_STATE_DISBAND_PGID] = { - [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_disband_irq, - [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, + [DEV_EVENT_NOTOPER] = ccw_device_request_event, + [DEV_EVENT_INTERRUPT] = ccw_device_request_event, + [DEV_EVENT_TIMEOUT] = ccw_device_request_event, [DEV_EVENT_VERIFY] = ccw_device_nop, }, [DEV_STATE_BOXED] = { diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index cb27bd4cc231..ce493144b054 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -1,594 +1,460 @@ /* - * drivers/s390/cio/device_pgid.c + * CCW device PGID and path verification I/O handling. * - * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation - * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) - * Martin Schwidefsky (schwidefsky@de.ibm.com) - * - * Path Group ID functions. + * Copyright IBM Corp. 2002,2009 + * Author(s): Cornelia Huck + * Martin Schwidefsky + * Peter Oberparleiter */ -#include -#include - +#include +#include +#include +#include +#include #include #include -#include -#include #include "cio.h" #include "cio_debug.h" -#include "css.h" #include "device.h" -#include "ioasm.h" #include "io_sch.h" +#define PGID_RETRIES 5 +#define PGID_TIMEOUT (10 * HZ) + /* - * Helper function called from interrupt context to decide whether an - * operation should be tried again. + * Process path verification data and report result. */ -static int __ccw_device_should_retry(union scsw *scsw) +static void verify_done(struct ccw_device *cdev, int rc) { - /* CC is only valid if start function bit is set. */ - if ((scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && scsw->cmd.cc == 1) - return 1; - /* No more activity. For sense and set PGID we stubbornly try again. */ - if (!scsw->cmd.actl) - return 1; - return 0; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_dev_id *id = &cdev->private->dev_id; + int mpath = !cdev->private->flags.pgid_single; + int pgroup = cdev->private->options.pgroup; + + if (rc) + goto out; + /* Ensure consistent multipathing state at device and channel. */ + if (sch->config.mp != mpath) { + sch->config.mp = mpath; + rc = cio_commit_config(sch); + } +out: + CIO_MSG_EVENT(2, "vrfy: device 0.%x.%04x: rc=%d pgroup=%d mpath=%d " + "vpm=%02x\n", id->ssid, id->devno, rc, pgroup, mpath, + sch->vpm); + ccw_device_verify_done(cdev, rc); } /* - * Start Sense Path Group ID helper function. Used in ccw_device_recog - * and ccw_device_sense_pgid. + * Create channel program to perform a NOOP. */ -static int -__ccw_device_sense_pgid_start(struct ccw_device *cdev) +static void nop_build_cp(struct ccw_device *cdev) { - struct subchannel *sch; - struct ccw1 *ccw; - int ret; - int i; - - sch = to_subchannel(cdev->dev.parent); - /* Return if we already checked on all paths. */ - if (cdev->private->imask == 0) - return (sch->lpm == 0) ? -ENODEV : -EACCES; - i = 8 - ffs(cdev->private->imask); - - /* Setup sense path group id channel program. */ - ccw = cdev->private->iccws; - ccw->cmd_code = CCW_CMD_SENSE_PGID; - ccw->count = sizeof (struct pgid); - ccw->flags = CCW_FLAG_SLI; - - /* Reset device status. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); - /* Try on every path. */ - ret = -ENODEV; - while (cdev->private->imask != 0) { - /* Try every path multiple times. */ - ccw->cda = (__u32) __pa (&cdev->private->pgid[i]); - if (cdev->private->iretry > 0) { - cdev->private->iretry--; - /* Reset internal retry indication. */ - cdev->private->flags.intretry = 0; - ret = cio_start (sch, cdev->private->iccws, - cdev->private->imask); - /* ret is 0, -EBUSY, -EACCES or -ENODEV */ - if (ret != -EACCES) - return ret; - CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel " - "0.%x.%04x, lpm %02X, became 'not " - "operational'\n", - cdev->private->dev_id.devno, - sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - - } - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - i++; - } - - return ret; + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + + cp->cmd_code = CCW_CMD_NOOP; + cp->cda = 0; + cp->count = 0; + cp->flags = CCW_FLAG_SLI; + req->cp = cp; } -void -ccw_device_sense_pgid_start(struct ccw_device *cdev) +/* + * Perform NOOP on a single path. + */ +static void nop_do(struct ccw_device *cdev) { - int ret; - - /* Set a timeout of 60s */ - ccw_device_set_timeout(cdev, 60*HZ); - - cdev->private->state = DEV_STATE_SENSE_PGID; - cdev->private->imask = 0x80; - cdev->private->iretry = 5; - memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid)); - ret = __ccw_device_sense_pgid_start(cdev); - if (ret && ret != -EBUSY) - ccw_device_sense_pgid_done(cdev, ret); + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + /* Adjust lpm. */ + req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm); + if (!req->lpm) + goto out_nopath; + nop_build_cp(cdev); + ccw_request_start(cdev); + return; + +out_nopath: + verify_done(cdev, sch->vpm ? 0 : -EACCES); } /* - * Called from interrupt context to check if a valid answer - * to Sense Path Group ID was received. + * Adjust NOOP I/O status. */ -static int -__ccw_device_check_sense_pgid(struct ccw_device *cdev) +static enum io_status nop_filter(struct ccw_device *cdev, void *data, + struct irb *irb, enum io_status status) { - struct subchannel *sch; - struct irb *irb; - int i; - - sch = to_subchannel(cdev->dev.parent); - irb = &cdev->private->irb; - if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { - /* Retry Sense PGID if requested. */ - if (cdev->private->flags.intretry) { - cdev->private->flags.intretry = 0; - return -EAGAIN; - } - return -ETIME; - } - if (irb->esw.esw0.erw.cons && - (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) { - /* - * If the device doesn't support the Sense Path Group ID - * command further retries wouldn't help ... - */ - return -EOPNOTSUPP; - } - if (irb->esw.esw0.erw.cons) { - CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, " - "lpum %02X, cnt %02d, sns : " - "%02X%02X%02X%02X %02X%02X%02X%02X ...\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, - irb->esw.esw0.sublog.lpum, - irb->esw.esw0.erw.scnt, - irb->ecw[0], irb->ecw[1], - irb->ecw[2], irb->ecw[3], - irb->ecw[4], irb->ecw[5], - irb->ecw[6], irb->ecw[7]); - return -EAGAIN; - } - if (irb->scsw.cmd.cc == 3) { - u8 lpm; - - lpm = to_io_private(sch)->orb.cmd.lpm; - CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x," - " lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, lpm); - return -EACCES; - } - i = 8 - ffs(cdev->private->imask); - if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) { - CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x " - "is reserved by someone else\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no); - return -EUSERS; - } - return 0; + /* Only subchannel status might indicate a path error. */ + if (status == IO_STATUS_ERROR && irb->scsw.cmd.cstat == 0) + return IO_DONE; + return status; } /* - * Got interrupt for Sense Path Group ID. + * Process NOOP request result for a single path. */ -void -ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event) +static void nop_callback(struct ccw_device *cdev, void *data, int rc) { - struct subchannel *sch; - struct irb *irb; - int ret; - - irb = (struct irb *) __LC_IRB; - - if (irb->scsw.cmd.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if (__ccw_device_should_retry(&irb->scsw)) { - ret = __ccw_device_sense_pgid_start(cdev); - if (ret && ret != -EBUSY) - ccw_device_sense_pgid_done(cdev, ret); - } - return; - } - if (ccw_device_accumulate_and_sense(cdev, irb) != 0) - return; - sch = to_subchannel(cdev->dev.parent); - ret = __ccw_device_check_sense_pgid(cdev); - memset(&cdev->private->irb, 0, sizeof(struct irb)); - switch (ret) { - /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */ - case -EOPNOTSUPP: /* Sense Path Group ID not supported */ - ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP); - break; - case -ETIME: /* Sense path group id stopped by timeout. */ - ccw_device_sense_pgid_done(cdev, -ETIME); - break; - case -EACCES: /* channel is not operational. */ - sch->lpm &= ~cdev->private->imask; - /* Fall through. */ - case 0: /* Sense Path Group ID successful. */ - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - /* Fall through. */ - case -EAGAIN: /* Try again. */ - ret = __ccw_device_sense_pgid_start(cdev); - if (ret != 0 && ret != -EBUSY) - ccw_device_sense_pgid_done(cdev, ret); - break; - case -EUSERS: /* device is reserved for someone else. */ - ccw_device_sense_pgid_done(cdev, -EUSERS); - break; - } + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + if (rc == 0) + sch->vpm |= req->lpm; + else if (rc != -EACCES) + goto err; + req->lpm >>= 1; + nop_do(cdev); + return; + +err: + verify_done(cdev, rc); } /* - * Path Group ID helper function. + * Create channel program to perform SET PGID on a single path. */ -static int -__ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) +static void spid_build_cp(struct ccw_device *cdev, u8 fn) { - struct subchannel *sch; - struct ccw1 *ccw; - int ret; - - sch = to_subchannel(cdev->dev.parent); - - /* Setup sense path group id channel program. */ - cdev->private->pgid[0].inf.fc = func; - ccw = cdev->private->iccws; - if (cdev->private->flags.pgid_single) - cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH; - else - cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH; - ccw->cmd_code = CCW_CMD_SET_PGID; - ccw->cda = (__u32) __pa (&cdev->private->pgid[0]); - ccw->count = sizeof (struct pgid); - ccw->flags = CCW_FLAG_SLI; - - /* Reset device status. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); - - /* Try multiple times. */ - ret = -EACCES; - if (cdev->private->iretry > 0) { - cdev->private->iretry--; - /* Reset internal retry indication. */ - cdev->private->flags.intretry = 0; - ret = cio_start (sch, cdev->private->iccws, - cdev->private->imask); - /* We expect an interrupt in case of success or busy - * indication. */ - if ((ret == 0) || (ret == -EBUSY)) - return ret; - } - /* PGID command failed on this path. */ - CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel " - "0.%x.%04x, lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - return ret; + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + int i = 8 - ffs(req->lpm); + struct pgid *pgid = &cdev->private->pgid[i]; + + pgid->inf.fc = fn; + cp->cmd_code = CCW_CMD_SET_PGID; + cp->cda = (u32) (addr_t) pgid; + cp->count = sizeof(*pgid); + cp->flags = CCW_FLAG_SLI; + req->cp = cp; } /* - * Helper function to send a nop ccw down a path. + * Perform establish/resign SET PGID on a single path. */ -static int __ccw_device_do_nop(struct ccw_device *cdev) +static void spid_do(struct ccw_device *cdev) { - struct subchannel *sch; - struct ccw1 *ccw; - int ret; - - sch = to_subchannel(cdev->dev.parent); - - /* Setup nop channel program. */ - ccw = cdev->private->iccws; - ccw->cmd_code = CCW_CMD_NOOP; - ccw->cda = 0; - ccw->count = 0; - ccw->flags = CCW_FLAG_SLI; - - /* Reset device status. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); - - /* Try multiple times. */ - ret = -EACCES; - if (cdev->private->iretry > 0) { - cdev->private->iretry--; - /* Reset internal retry indication. */ - cdev->private->flags.intretry = 0; - ret = cio_start (sch, cdev->private->iccws, - cdev->private->imask); - /* We expect an interrupt in case of success or busy - * indication. */ - if ((ret == 0) || (ret == -EBUSY)) - return ret; - } - /* nop command failed on this path. */ - CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel " - "0.%x.%04x, lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - return ret; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + u8 fn; + + /* Adjust lpm if paths are not set in pam. */ + req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam); + if (!req->lpm) + goto out_nopath; + /* Channel program setup. */ + if (req->lpm & sch->opm) + fn = SPID_FUNC_ESTABLISH; + else + fn = SPID_FUNC_RESIGN; + if (!cdev->private->flags.pgid_single) + fn |= SPID_FUNC_MULTI_PATH; + spid_build_cp(cdev, fn); + ccw_request_start(cdev); + return; + +out_nopath: + verify_done(cdev, sch->vpm ? 0 : -EACCES); } +static void verify_start(struct ccw_device *cdev); /* - * Called from interrupt context to check if a valid answer - * to Set Path Group ID was received. + * Process SET PGID request result for a single path. */ -static int -__ccw_device_check_pgid(struct ccw_device *cdev) +static void spid_callback(struct ccw_device *cdev, void *data, int rc) { - struct subchannel *sch; - struct irb *irb; - - sch = to_subchannel(cdev->dev.parent); - irb = &cdev->private->irb; - if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { - /* Retry Set PGID if requested. */ - if (cdev->private->flags.intretry) { - cdev->private->flags.intretry = 0; - return -EAGAIN; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + switch (rc) { + case 0: + sch->vpm |= req->lpm & sch->opm; + break; + case -EACCES: + break; + case -EOPNOTSUPP: + if (!cdev->private->flags.pgid_single) { + /* Try without multipathing. */ + cdev->private->flags.pgid_single = 1; + goto out_restart; } - return -ETIME; - } - if (irb->esw.esw0.erw.cons) { - if (irb->ecw[0] & SNS0_CMD_REJECT) - return -EOPNOTSUPP; - /* Hmm, whatever happened, try again. */ - CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, " - "cnt %02d, " - "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, - irb->esw.esw0.erw.scnt, - irb->ecw[0], irb->ecw[1], - irb->ecw[2], irb->ecw[3], - irb->ecw[4], irb->ecw[5], - irb->ecw[6], irb->ecw[7]); - return -EAGAIN; - } - if (irb->scsw.cmd.cc == 3) { - CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel 0.%x.%04x," - " lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - return -EACCES; + /* Try without pathgrouping. */ + cdev->private->options.pgroup = 0; + goto out_restart; + default: + goto err; } - return 0; + req->lpm >>= 1; + spid_do(cdev); + return; + +out_restart: + verify_start(cdev); + return; +err: + verify_done(cdev, rc); +} + +static int pgid_cmp(struct pgid *p1, struct pgid *p2) +{ + return memcmp((char *) p1 + 1, (char *) p2 + 1, + sizeof(struct pgid) - 1); } /* - * Called from interrupt context to check the path status after a nop has - * been send. + * Determine pathgroup state from PGID data. */ -static int __ccw_device_check_nop(struct ccw_device *cdev) +static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, + int *mismatch, int *reserved, int *reset) { - struct subchannel *sch; - struct irb *irb; - - sch = to_subchannel(cdev->dev.parent); - irb = &cdev->private->irb; - if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { - /* Retry NOP if requested. */ - if (cdev->private->flags.intretry) { - cdev->private->flags.intretry = 0; - return -EAGAIN; + struct pgid *pgid = &cdev->private->pgid[0]; + struct pgid *first = NULL; + int lpm; + int i; + + *mismatch = 0; + *reserved = 0; + *reset = 0; + for (i = 0, lpm = 0x80; i < 8; i++, pgid++, lpm >>= 1) { + if ((cdev->private->pgid_valid_mask & lpm) == 0) + continue; + if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE) + *reserved = 1; + if (pgid->inf.ps.state1 == SNID_STATE1_RESET) { + /* A PGID was reset. */ + *reset = 1; + continue; } - return -ETIME; - } - if (irb->scsw.cmd.cc == 3) { - CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel 0.%x.%04x," - " lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - return -EACCES; + if (!first) { + first = pgid; + continue; + } + if (pgid_cmp(pgid, first) != 0) + *mismatch = 1; } - return 0; + if (!first) + first = &channel_subsystems[0]->global_pgid; + *p = first; } -static void -__ccw_device_verify_start(struct ccw_device *cdev) +static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) { - struct subchannel *sch; - __u8 func; - int ret; - - sch = to_subchannel(cdev->dev.parent); - /* Repeat for all paths. */ - for (; cdev->private->imask; cdev->private->imask >>= 1, - cdev->private->iretry = 5) { - if ((cdev->private->imask & sch->schib.pmcw.pam) == 0) - /* Path not available, try next. */ - continue; - if (cdev->private->options.pgroup) { - if (sch->opm & cdev->private->imask) - func = SPID_FUNC_ESTABLISH; - else - func = SPID_FUNC_RESIGN; - ret = __ccw_device_do_pgid(cdev, func); - } else - ret = __ccw_device_do_nop(cdev); - /* We expect an interrupt in case of success or busy - * indication. */ - if (ret == 0 || ret == -EBUSY) - return; - /* Permanent path failure, try next. */ + int i; + + for (i = 0; i < 8; i++) + memcpy(&cdev->private->pgid[i], pgid, sizeof(struct pgid)); +} + +/* + * Process SENSE PGID data and report result. + */ +static void snid_done(struct ccw_device *cdev, int rc) +{ + struct ccw_dev_id *id = &cdev->private->dev_id; + struct pgid *pgid; + int mismatch = 0; + int reserved = 0; + int reset = 0; + + if (rc) + goto out; + pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset); + if (!mismatch) { + pgid_fill(cdev, pgid); + cdev->private->flags.pgid_rdy = 1; } - /* Done with all paths. */ - ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -EACCES); + if (reserved) + rc = -EUSERS; +out: + CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x mism=%d " + "rsvd=%d reset=%d\n", id->ssid, id->devno, rc, + cdev->private->pgid_valid_mask, mismatch, reserved, + reset); + ccw_device_sense_pgid_done(cdev, rc); } - + /* - * Got interrupt for Set Path Group ID. + * Create channel program to perform a SENSE PGID on a single path. */ -void -ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event) +static void snid_build_cp(struct ccw_device *cdev) { - struct subchannel *sch; - struct irb *irb; - int ret; + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + int i = 8 - ffs(req->lpm); + + /* Channel program setup. */ + cp->cmd_code = CCW_CMD_SENSE_PGID; + cp->cda = (u32) (addr_t) &cdev->private->pgid[i]; + cp->count = sizeof(struct pgid); + cp->flags = CCW_FLAG_SLI; + req->cp = cp; +} - irb = (struct irb *) __LC_IRB; +/* + * Perform SENSE PGID on a single path. + */ +static void snid_do(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + /* Adjust lpm if paths are not set in pam. */ + req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam); + if (!req->lpm) + goto out_nopath; + snid_build_cp(cdev); + ccw_request_start(cdev); + return; + +out_nopath: + snid_done(cdev, cdev->private->pgid_valid_mask ? 0 : -EACCES); +} - if (irb->scsw.cmd.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if (__ccw_device_should_retry(&irb->scsw)) - __ccw_device_verify_start(cdev); - return; - } - if (ccw_device_accumulate_and_sense(cdev, irb) != 0) - return; - sch = to_subchannel(cdev->dev.parent); - if (cdev->private->options.pgroup) - ret = __ccw_device_check_pgid(cdev); - else - ret = __ccw_device_check_nop(cdev); - memset(&cdev->private->irb, 0, sizeof(struct irb)); +/* + * Process SENSE PGID request result for single path. + */ +static void snid_callback(struct ccw_device *cdev, void *data, int rc) +{ + struct ccw_request *req = &cdev->private->req; + + if (rc == 0) + cdev->private->pgid_valid_mask |= req->lpm; + else if (rc != -EACCES) + goto err; + req->lpm >>= 1; + snid_do(cdev); + return; + +err: + snid_done(cdev, rc); +} - switch (ret) { - /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ - case 0: - /* Path verification ccw finished successfully, update lpm. */ - sch->vpm |= sch->opm & cdev->private->imask; - /* Go on with next path. */ - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - __ccw_device_verify_start(cdev); - break; - case -EOPNOTSUPP: - /* - * One of those strange devices which claim to be able - * to do multipathing but not for Set Path Group ID. - */ - if (cdev->private->flags.pgid_single) - cdev->private->options.pgroup = 0; - else - cdev->private->flags.pgid_single = 1; - /* Retry */ - sch->vpm = 0; - cdev->private->imask = 0x80; - cdev->private->iretry = 5; - /* fall through. */ - case -EAGAIN: /* Try again. */ - __ccw_device_verify_start(cdev); - break; - case -ETIME: /* Set path group id stopped by timeout. */ - ccw_device_verify_done(cdev, -ETIME); - break; - case -EACCES: /* channel is not operational. */ - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - __ccw_device_verify_start(cdev); - break; - } +/** + * ccw_device_sense_pgid_start - perform SENSE PGID + * @cdev: ccw device + * + * Execute a SENSE PGID channel program on each path to @cdev to update its + * PGID information. When finished, call ccw_device_sense_id_done with a + * return code specifying the result. + */ +void ccw_device_sense_pgid_start(struct ccw_device *cdev) +{ + struct ccw_request *req = &cdev->private->req; + + CIO_TRACE_EVENT(4, "snid"); + CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); + /* Initialize PGID data. */ + memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid)); + cdev->private->flags.pgid_rdy = 0; + cdev->private->pgid_valid_mask = 0; + /* Initialize request data. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->callback = snid_callback; + req->lpm = 0x80; + snid_do(cdev); } -void -ccw_device_verify_start(struct ccw_device *cdev) +/* + * Perform path verification. + */ +static void verify_start(struct ccw_device *cdev) { struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; - cdev->private->flags.pgid_single = 0; - cdev->private->imask = 0x80; - cdev->private->iretry = 5; - - /* Start with empty vpm. */ sch->vpm = 0; - - /* Get current pam. */ - if (cio_update_schib(sch)) { - ccw_device_verify_done(cdev, -ENODEV); - return; + /* Initialize request data. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->lpm = 0x80; + if (cdev->private->options.pgroup) { + req->callback = spid_callback; + spid_do(cdev); + } else { + req->filter = nop_filter; + req->callback = nop_callback; + nop_do(cdev); } - /* After 60s path verification is considered to have failed. */ - ccw_device_set_timeout(cdev, 60*HZ); - __ccw_device_verify_start(cdev); } -static void -__ccw_device_disband_start(struct ccw_device *cdev) +/** + * ccw_device_verify_start - perform path verification + * @cdev: ccw device + * + * Perform an I/O on each available channel path to @cdev to determine which + * paths are operational. The resulting path mask is stored in sch->vpm. + * If device options specify pathgrouping, establish a pathgroup for the + * operational paths. When finished, call ccw_device_verify_done with a + * return code specifying the result. + */ +void ccw_device_verify_start(struct ccw_device *cdev) { - struct subchannel *sch; - int ret; - - sch = to_subchannel(cdev->dev.parent); - while (cdev->private->imask != 0) { - if (sch->lpm & cdev->private->imask) { - ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND); - if (ret == 0) - return; - } - cdev->private->iretry = 5; - cdev->private->imask >>= 1; - } - ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV); + CIO_TRACE_EVENT(4, "vrfy"); + CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); + if (!cdev->private->flags.pgid_rdy) { + /* No pathgrouping possible. */ + cdev->private->options.pgroup = 0; + cdev->private->flags.pgid_single = 1; + } else + cdev->private->flags.pgid_single = 0; + cdev->private->flags.doverify = 0; + verify_start(cdev); } /* - * Got interrupt for Unset Path Group ID. + * Process disband SET PGID request result. */ -void -ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event) +static void disband_callback(struct ccw_device *cdev, void *data, int rc) { - struct subchannel *sch; - struct irb *irb; - int ret; - - irb = (struct irb *) __LC_IRB; - - if (irb->scsw.cmd.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if (__ccw_device_should_retry(&irb->scsw)) - __ccw_device_disband_start(cdev); - return; - } - if (ccw_device_accumulate_and_sense(cdev, irb) != 0) - return; - sch = to_subchannel(cdev->dev.parent); - ret = __ccw_device_check_pgid(cdev); - memset(&cdev->private->irb, 0, sizeof(struct irb)); - switch (ret) { - /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ - case 0: /* disband successful. */ - ccw_device_disband_done(cdev, ret); - break; - case -EOPNOTSUPP: - /* - * One of those strange devices which claim to be able - * to do multipathing but not for Unset Path Group ID. - */ - cdev->private->flags.pgid_single = 1; - /* fall through. */ - case -EAGAIN: /* Try again. */ - __ccw_device_disband_start(cdev); - break; - case -ETIME: /* Set path group id stopped by timeout. */ - ccw_device_disband_done(cdev, -ETIME); - break; - case -EACCES: /* channel is not operational. */ - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - __ccw_device_disband_start(cdev); - break; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_dev_id *id = &cdev->private->dev_id; + + if (rc) + goto out; + /* Ensure consistent multipathing state at device and channel. */ + cdev->private->flags.pgid_single = 1; + if (sch->config.mp) { + sch->config.mp = 0; + rc = cio_commit_config(sch); } +out: + CIO_MSG_EVENT(0, "disb: device 0.%x.%04x: rc=%d\n", id->ssid, id->devno, + rc); + ccw_device_disband_done(cdev, rc); } -void -ccw_device_disband_start(struct ccw_device *cdev) +/** + * ccw_device_disband_start - disband pathgroup + * @cdev: ccw device + * + * Execute a SET PGID channel program on @cdev to disband a previously + * established pathgroup. When finished, call ccw_device_disband_done with + * a return code specifying the result. + */ +void ccw_device_disband_start(struct ccw_device *cdev) { - /* After 60s disbanding is considered to have failed. */ - ccw_device_set_timeout(cdev, 60*HZ); - - cdev->private->flags.pgid_single = 0; - cdev->private->iretry = 5; - cdev->private->imask = 0x80; - __ccw_device_disband_start(cdev); + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + u8 fn; + + CIO_TRACE_EVENT(4, "disb"); + CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); + /* Request setup. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->lpm = sch->schib.pmcw.pam & sch->opm; + req->callback = disband_callback; + fn = SPID_FUNC_DISBAND; + if (!cdev->private->flags.pgid_single) + fn |= SPID_FUNC_MULTI_PATH; + spid_build_cp(cdev, fn); + ccw_request_start(cdev); } diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index f9ff7683e242..78b5ad980cf3 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -149,8 +149,8 @@ struct ccw_device_private { struct ccw_dev_id dev_id; /* device id */ struct subchannel_id schid; /* subchannel number */ struct ccw_request req; /* internal I/O request */ - u8 imask; /* lpm mask for SNID/SID/SPGID */ - int iretry; /* retry counter SNID/SID/SPGID */ + int iretry; + u8 pgid_valid_mask; /* mask of valid PGIDs */ struct { unsigned int fast:1; /* post with "channel end" */ unsigned int repall:1; /* report every interrupt status */ @@ -167,6 +167,7 @@ struct ccw_device_private { unsigned int fake_irb:1; /* deliver faked irb */ unsigned int intretry:1; /* retry internal operation */ unsigned int resuming:1; /* recognition while resume */ + unsigned int pgid_rdy:1; /* pgids are ready */ } __attribute__((packed)) flags; unsigned long intparm; /* user interruption parameter */ struct qdio_irq *qdio_data; -- cgit v1.2.3 From 350e91207bc9c6a464c22b9e0e30d21dfc07efe3 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:28 +0100 Subject: [S390] cio: allow setting not-operational devices offline Accept a request for setting a not-operational device offline. This way, users can remove devices from Linux which would otherwise remain unusable until reboot. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 6a9ac85065ed..9af864f615b0 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -529,11 +529,10 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, int force, ret; unsigned long i; - if ((cdev->private->state != DEV_STATE_OFFLINE && - cdev->private->state != DEV_STATE_ONLINE && - cdev->private->state != DEV_STATE_BOXED && - cdev->private->state != DEV_STATE_DISCONNECTED) || - atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) + if (!dev_fsm_final_state(cdev) && + cdev->private->state != DEV_STATE_DISCONNECTED) + return -EAGAIN; + if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) return -EAGAIN; if (cdev->drv && !try_module_get(cdev->drv->owner)) { -- cgit v1.2.3 From 4257aaecffab77bad43e12057f56a5590b360f9f Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:29 +0100 Subject: [S390] cio: remove intretry flag After changing all internal I/O functions to use the newly introduced ccw request infrastructure, retries are handled automatically after a clear operation. Therefore remove the internal retry flag and associated code. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 58 ++++++++++++---------------------------- drivers/s390/cio/device.h | 1 - drivers/s390/cio/device_fsm.c | 27 ------------------- drivers/s390/cio/device_ops.c | 3 +-- drivers/s390/cio/device_status.c | 3 --- drivers/s390/cio/io_sch.h | 1 - 6 files changed, 18 insertions(+), 75 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 9af864f615b0..e24b9b1d1022 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1068,36 +1068,6 @@ static void io_subchannel_verify(struct subchannel *sch) dev_fsm_event(cdev, DEV_EVENT_VERIFY); } -static int check_for_io_on_path(struct subchannel *sch, int mask) -{ - if (cio_update_schib(sch)) - return 0; - if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask) - return 1; - return 0; -} - -static void terminate_internal_io(struct subchannel *sch, - struct ccw_device *cdev) -{ - if (cio_clear(sch)) { - /* Recheck device in case clear failed. */ - sch->lpm = 0; - if (cdev->online) - dev_fsm_event(cdev, DEV_EVENT_VERIFY); - else - css_schedule_eval(sch->schid); - return; - } - cdev->private->state = DEV_STATE_CLEAR_VERIFY; - /* Request retry of internal operation. */ - cdev->private->flags.intretry = 1; - /* Call handler. */ - if (cdev->handler) - cdev->handler(cdev, cdev->private->intparm, - ERR_PTR(-EIO)); -} - static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask) { struct ccw_device *cdev; @@ -1105,18 +1075,24 @@ static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask) cdev = sch_get_cdev(sch); if (!cdev) return; - if (check_for_io_on_path(sch, mask)) { - if (cdev->private->state == DEV_STATE_ONLINE) - ccw_device_kill_io(cdev); - else { - terminate_internal_io(sch, cdev); - /* Re-start path verification. */ - dev_fsm_event(cdev, DEV_EVENT_VERIFY); - } - } else - /* trigger path verification. */ - dev_fsm_event(cdev, DEV_EVENT_VERIFY); + if (cio_update_schib(sch)) + goto err; + /* Check for I/O on path. */ + if (scsw_actl(&sch->schib.scsw) == 0 || sch->schib.pmcw.lpum != mask) + goto out; + if (cdev->private->state == DEV_STATE_ONLINE) { + ccw_device_kill_io(cdev); + goto out; + } + if (cio_clear(sch)) + goto err; +out: + /* Trigger path verification. */ + dev_fsm_event(cdev, DEV_EVENT_VERIFY); + return; +err: + dev_fsm_event(cdev, DEV_EVENT_NOTOPER); } static int io_subchannel_chp_event(struct subchannel *sch, diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index ac6f55b4b74c..4e1775cf9739 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -21,7 +21,6 @@ enum dev_state { DEV_STATE_DISBAND_PGID, DEV_STATE_BOXED, /* states to wait for i/o completion before doing something */ - DEV_STATE_CLEAR_VERIFY, DEV_STATE_TIMEOUT_KILL, DEV_STATE_QUIESCE, /* special states for devices gone not operational */ diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index c7439f5500f8..349d8c52c0d0 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -771,12 +771,6 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event) */ if (scsw_fctl(&irb->scsw) & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) { - /* Retry Basic Sense if requested. */ - if (cdev->private->flags.intretry) { - cdev->private->flags.intretry = 0; - ccw_device_do_sense(cdev, irb); - return; - } cdev->private->flags.dosense = 0; memset(&cdev->private->irb, 0, sizeof(struct irb)); ccw_device_accumulate_irb(cdev, irb); @@ -799,21 +793,6 @@ call_handler: ccw_device_online_verify(cdev, 0); } -static void -ccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event) -{ - struct irb *irb; - - irb = (struct irb *) __LC_IRB; - /* Accumulate status. We don't do basic sense. */ - ccw_device_accumulate_irb(cdev, irb); - /* Remember to clear irb to avoid residuals. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); - /* Try to start delayed device verification. */ - ccw_device_online_verify(cdev, 0); - /* Note: Don't call handler for cio initiated clear! */ -} - static void ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event) { @@ -1069,12 +1048,6 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_VERIFY] = ccw_device_nop, }, /* states to wait for i/o completion before doing something */ - [DEV_STATE_CLEAR_VERIFY] = { - [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_clear_verify, - [DEV_EVENT_TIMEOUT] = ccw_device_nop, - [DEV_EVENT_VERIFY] = ccw_device_nop, - }, [DEV_STATE_TIMEOUT_KILL] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, [DEV_EVENT_INTERRUPT] = ccw_device_killing_irq, diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 2d0efee8a290..5ab90ec42318 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -167,8 +167,7 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, return -EINVAL; if (cdev->private->state == DEV_STATE_NOT_OPER) return -ENODEV; - if (cdev->private->state == DEV_STATE_VERIFY || - cdev->private->state == DEV_STATE_CLEAR_VERIFY) { + if (cdev->private->state == DEV_STATE_VERIFY) { /* Remember to fake irb when finished. */ if (!cdev->private->flags.fake_irb) { cdev->private->flags.fake_irb = 1; diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c index 5814dbee2410..66d8066ef22a 100644 --- a/drivers/s390/cio/device_status.c +++ b/drivers/s390/cio/device_status.c @@ -336,9 +336,6 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb) sense_ccw->count = SENSE_MAX_COUNT; sense_ccw->flags = CCW_FLAG_SLI; - /* Reset internal retry indication. */ - cdev->private->flags.intretry = 0; - rc = cio_start(sch, sense_ccw, 0xff); if (rc == -ENODEV || rc == -EACCES) dev_fsm_event(cdev, DEV_EVENT_VERIFY); diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index 78b5ad980cf3..8942dc092d0a 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -165,7 +165,6 @@ struct ccw_device_private { unsigned int donotify:1; /* call notify function */ unsigned int recog_done:1; /* dev. recog. complete */ unsigned int fake_irb:1; /* deliver faked irb */ - unsigned int intretry:1; /* retry internal operation */ unsigned int resuming:1; /* recognition while resume */ unsigned int pgid_rdy:1; /* pgids are ready */ } __attribute__((packed)) flags; -- cgit v1.2.3 From 454e1fa1ebae7cff707b2e3f12b775c263c8408b Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:30 +0100 Subject: [S390] cio: split PGID settings and status Split setting (driver wants feature enabled) and status (feature setup was successful) for PGID related ccw device features so that setup errors can be detected. Previously, incorrectly handled setup errors could in rare cases lead to erratic I/O behavior and permanently unusuable devices. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/ccwdev.h | 4 ++++ drivers/s390/block/dasd.c | 7 ------- drivers/s390/block/dasd_eckd.c | 12 +++++++++++- drivers/s390/char/tape_core.c | 3 ++- drivers/s390/cio/device_fsm.c | 2 +- drivers/s390/cio/device_ops.c | 27 +++++++++++++++++++++++++++ drivers/s390/cio/device_pgid.c | 33 ++++++++++++++++++++------------- drivers/s390/cio/io_sch.h | 4 +++- 8 files changed, 68 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/arch/s390/include/asm/ccwdev.h b/arch/s390/include/asm/ccwdev.h index 2a5419551176..f4bd346a52d3 100644 --- a/arch/s390/include/asm/ccwdev.h +++ b/arch/s390/include/asm/ccwdev.h @@ -142,6 +142,8 @@ struct ccw1; extern int ccw_device_set_options_mask(struct ccw_device *, unsigned long); extern int ccw_device_set_options(struct ccw_device *, unsigned long); extern void ccw_device_clear_options(struct ccw_device *, unsigned long); +int ccw_device_is_pathgroup(struct ccw_device *cdev); +int ccw_device_is_multipath(struct ccw_device *cdev); /* Allow for i/o completion notification after primary interrupt status. */ #define CCWDEV_EARLY_NOTIFICATION 0x0001 @@ -151,6 +153,8 @@ extern void ccw_device_clear_options(struct ccw_device *, unsigned long); #define CCWDEV_DO_PATHGROUP 0x0004 /* Allow forced onlining of boxed devices. */ #define CCWDEV_ALLOW_FORCE 0x0008 +/* Try to use multipath mode. */ +#define CCWDEV_DO_MULTIPATH 0x0010 extern int ccw_device_start(struct ccw_device *, struct ccw1 *, unsigned long, __u8, unsigned long); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index aaccc8ecfa8f..58ffbd1d04a1 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -2208,13 +2208,6 @@ int dasd_generic_probe(struct ccw_device *cdev, { int ret; - ret = ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); - if (ret) { - DBF_EVENT(DBF_WARNING, - "dasd_generic_probe: could not set ccw-device options " - "for %s\n", dev_name(&cdev->dev)); - return ret; - } ret = dasd_add_sysfs_files(cdev); if (ret) { DBF_EVENT(DBF_WARNING, diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 417b97cd3f94..a8ec0731609e 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -86,7 +86,8 @@ dasd_eckd_probe (struct ccw_device *cdev) int ret; /* set ECKD specific ccw-device options */ - ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE); + ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE | + CCWDEV_DO_PATHGROUP | CCWDEV_DO_MULTIPATH); if (ret) { DBF_EVENT(DBF_WARNING, "dasd_eckd_probe: could not set ccw-device options " @@ -1090,6 +1091,15 @@ dasd_eckd_check_characteristics(struct dasd_device *device) struct dasd_block *block; int is_known, rc; + if (!ccw_device_is_pathgroup(device->cdev)) { + dev_warn(&device->cdev->dev, + "A channel path group could not be established\n"); + return -EIO; + } + if (!ccw_device_is_multipath(device->cdev)) { + dev_info(&device->cdev->dev, + "The DASD is not operating in multipath mode\n"); + } private = (struct dasd_eckd_private *) device->private; if (!private) { private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA); diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 5cd31e071647..27503a778fcb 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -579,7 +579,8 @@ tape_generic_probe(struct ccw_device *cdev) device = tape_alloc_device(); if (IS_ERR(device)) return -ENODEV; - ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); + ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP | + CCWDEV_DO_MULTIPATH); ret = sysfs_create_group(&cdev->dev.kobj, &tape_attr_group); if (ret) { tape_put_device(device); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 349d8c52c0d0..d6e315dc0f98 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -601,7 +601,7 @@ ccw_device_offline(struct ccw_device *cdev) if (cdev->private->state != DEV_STATE_ONLINE) return -EINVAL; /* Are we doing path grouping? */ - if (!cdev->private->options.pgroup) { + if (!cdev->private->flags.pgroup) { /* No, set state offline immediately. */ ccw_device_done(cdev, DEV_STATE_OFFLINE); return 0; diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 5ab90ec42318..d4be16acebe4 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -46,6 +46,7 @@ int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) cdev->private->options.repall = (flags & CCWDEV_REPORT_ALL) != 0; cdev->private->options.pgroup = (flags & CCWDEV_DO_PATHGROUP) != 0; cdev->private->options.force = (flags & CCWDEV_ALLOW_FORCE) != 0; + cdev->private->options.mpath = (flags & CCWDEV_DO_MULTIPATH) != 0; return 0; } @@ -74,6 +75,7 @@ int ccw_device_set_options(struct ccw_device *cdev, unsigned long flags) cdev->private->options.repall |= (flags & CCWDEV_REPORT_ALL) != 0; cdev->private->options.pgroup |= (flags & CCWDEV_DO_PATHGROUP) != 0; cdev->private->options.force |= (flags & CCWDEV_ALLOW_FORCE) != 0; + cdev->private->options.mpath |= (flags & CCWDEV_DO_MULTIPATH) != 0; return 0; } @@ -90,8 +92,33 @@ void ccw_device_clear_options(struct ccw_device *cdev, unsigned long flags) cdev->private->options.repall &= (flags & CCWDEV_REPORT_ALL) == 0; cdev->private->options.pgroup &= (flags & CCWDEV_DO_PATHGROUP) == 0; cdev->private->options.force &= (flags & CCWDEV_ALLOW_FORCE) == 0; + cdev->private->options.mpath &= (flags & CCWDEV_DO_MULTIPATH) == 0; } +/** + * ccw_device_is_pathgroup - determine if paths to this device are grouped + * @cdev: ccw device + * + * Return non-zero if there is a path group, zero otherwise. + */ +int ccw_device_is_pathgroup(struct ccw_device *cdev) +{ + return cdev->private->flags.pgroup; +} +EXPORT_SYMBOL(ccw_device_is_pathgroup); + +/** + * ccw_device_is_multipath - determine if device is operating in multipath mode + * @cdev: ccw device + * + * Return non-zero if device is operating in multipath mode, zero otherwise. + */ +int ccw_device_is_multipath(struct ccw_device *cdev) +{ + return cdev->private->flags.mpath; +} +EXPORT_SYMBOL(ccw_device_is_multipath); + /** * ccw_device_clear() - terminate I/O request processing * @cdev: target ccw device diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index ce493144b054..3323042ba755 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -30,8 +30,8 @@ static void verify_done(struct ccw_device *cdev, int rc) { struct subchannel *sch = to_subchannel(cdev->dev.parent); struct ccw_dev_id *id = &cdev->private->dev_id; - int mpath = !cdev->private->flags.pgid_single; - int pgroup = cdev->private->options.pgroup; + int mpath = cdev->private->flags.mpath; + int pgroup = cdev->private->flags.pgroup; if (rc) goto out; @@ -150,7 +150,7 @@ static void spid_do(struct ccw_device *cdev) fn = SPID_FUNC_ESTABLISH; else fn = SPID_FUNC_RESIGN; - if (!cdev->private->flags.pgid_single) + if (cdev->private->flags.mpath) fn |= SPID_FUNC_MULTI_PATH; spid_build_cp(cdev, fn); ccw_request_start(cdev); @@ -177,13 +177,13 @@ static void spid_callback(struct ccw_device *cdev, void *data, int rc) case -EACCES: break; case -EOPNOTSUPP: - if (!cdev->private->flags.pgid_single) { + if (cdev->private->flags.mpath) { /* Try without multipathing. */ - cdev->private->flags.pgid_single = 1; + cdev->private->flags.mpath = 0; goto out_restart; } /* Try without pathgrouping. */ - cdev->private->options.pgroup = 0; + cdev->private->flags.pgroup = 0; goto out_restart; default: goto err; @@ -374,7 +374,7 @@ static void verify_start(struct ccw_device *cdev) req->timeout = PGID_TIMEOUT; req->maxretries = PGID_RETRIES; req->lpm = 0x80; - if (cdev->private->options.pgroup) { + if (cdev->private->flags.pgroup) { req->callback = spid_callback; spid_do(cdev); } else { @@ -400,10 +400,17 @@ void ccw_device_verify_start(struct ccw_device *cdev) CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); if (!cdev->private->flags.pgid_rdy) { /* No pathgrouping possible. */ - cdev->private->options.pgroup = 0; - cdev->private->flags.pgid_single = 1; - } else - cdev->private->flags.pgid_single = 0; + cdev->private->flags.pgroup = 0; + cdev->private->flags.mpath = 0; + } else { + /* + * Initialize pathgroup and multipath state with target values. + * They may change in the course of path verification. + */ + cdev->private->flags.pgroup = cdev->private->options.pgroup; + cdev->private->flags.mpath = cdev->private->options.mpath; + + } cdev->private->flags.doverify = 0; verify_start(cdev); } @@ -419,7 +426,7 @@ static void disband_callback(struct ccw_device *cdev, void *data, int rc) if (rc) goto out; /* Ensure consistent multipathing state at device and channel. */ - cdev->private->flags.pgid_single = 1; + cdev->private->flags.mpath = 0; if (sch->config.mp) { sch->config.mp = 0; rc = cio_commit_config(sch); @@ -453,7 +460,7 @@ void ccw_device_disband_start(struct ccw_device *cdev) req->lpm = sch->schib.pmcw.pam & sch->opm; req->callback = disband_callback; fn = SPID_FUNC_DISBAND; - if (!cdev->private->flags.pgid_single) + if (cdev->private->flags.mpath) fn |= SPID_FUNC_MULTI_PATH; spid_build_cp(cdev, fn); ccw_request_start(cdev); diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index 8942dc092d0a..b387c80d1888 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -156,9 +156,9 @@ struct ccw_device_private { unsigned int repall:1; /* report every interrupt status */ unsigned int pgroup:1; /* do path grouping */ unsigned int force:1; /* allow forced online */ + unsigned int mpath:1; /* do multipathing */ } __attribute__ ((packed)) options; struct { - unsigned int pgid_single:1; /* use single path for Set PGID */ unsigned int esid:1; /* Ext. SenseID supported by HW */ unsigned int dosense:1; /* delayed SENSE required */ unsigned int doverify:1; /* delayed path verification */ @@ -167,6 +167,8 @@ struct ccw_device_private { unsigned int fake_irb:1; /* deliver faked irb */ unsigned int resuming:1; /* recognition while resume */ unsigned int pgid_rdy:1; /* pgids are ready */ + unsigned int pgroup:1; /* pathgroup is set up */ + unsigned int mpath:1; /* multipathing is set up */ } __attribute__((packed)) flags; unsigned long intparm; /* user interruption parameter */ struct qdio_irq *qdio_data; -- cgit v1.2.3 From 52ef0608e3ee4a511725e443c4b572fece22b353 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:31 +0100 Subject: [S390] cio: use sense-pgid operation for path verification Set-pgid operations fail for some device types under z/VM for which the hypervisor has already set the pgid. Also reserved devices or changed pgids are not correctly recognized. Fix these problems by using a combination of sense-pgid and set-pgid and by also accepting pre-defined pgid settings. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.h | 3 - drivers/s390/cio/device_fsm.c | 41 ++---------- drivers/s390/cio/device_pgid.c | 148 ++++++++++++++++++++++++++--------------- drivers/s390/cio/io_sch.h | 1 - 4 files changed, 99 insertions(+), 94 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 4e1775cf9739..2df519bb877e 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -110,9 +110,6 @@ void ccw_device_sense_id_start(struct ccw_device *); void ccw_device_sense_id_done(struct ccw_device *, int); /* Function prototypes for path grouping stuff. */ -void ccw_device_sense_pgid_start(struct ccw_device *); -void ccw_device_sense_pgid_done(struct ccw_device *, int); - void ccw_device_verify_start(struct ccw_device *); void ccw_device_verify_done(struct ccw_device *, int); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index d6e315dc0f98..8d565ff85e43 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -394,33 +394,6 @@ ccw_device_done(struct ccw_device *cdev, int state) wake_up(&cdev->private->wait_q); } -/* - * Function called from device_pgid.c after sense path ground has completed. - */ -void -ccw_device_sense_pgid_done(struct ccw_device *cdev, int err) -{ - struct subchannel *sch; - - sch = to_subchannel(cdev->dev.parent); - switch (err) { - case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */ - case 0: /* success */ - case -EACCES: /* partial success, some paths not operational */ - break; - case -ETIME: /* Sense path group id stopped by timeout. */ - case -EUSERS: /* device is reserved for someone else. */ - ccw_device_done(cdev, DEV_STATE_BOXED); - return; - default: - ccw_device_done(cdev, DEV_STATE_NOT_OPER); - return; - } - /* Start Path Group verification. */ - cdev->private->state = DEV_STATE_VERIFY; - ccw_device_verify_start(cdev); -} - /* * Start device recognition. */ @@ -503,6 +476,7 @@ callback: } break; case -ETIME: + case -EUSERS: /* Reset oper notify indication after verify error. */ cdev->private->flags.donotify = 0; ccw_device_done(cdev, DEV_STATE_BOXED); @@ -540,16 +514,9 @@ ccw_device_online(struct ccw_device *cdev) dev_fsm_event(cdev, DEV_EVENT_NOTOPER); return ret; } - /* Do we want to do path grouping? */ - if (!cdev->private->options.pgroup) { - /* Start initial path verification. */ - cdev->private->state = DEV_STATE_VERIFY; - ccw_device_verify_start(cdev); - return 0; - } - /* Do a SensePGID first. */ - cdev->private->state = DEV_STATE_SENSE_PGID; - ccw_device_sense_pgid_start(cdev); + /* Start initial path verification. */ + cdev->private->state = DEV_STATE_VERIFY; + ccw_device_verify_start(cdev); return 0; } diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 3323042ba755..4d54abd82b8c 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -141,8 +141,8 @@ static void spid_do(struct ccw_device *cdev) struct ccw_request *req = &cdev->private->req; u8 fn; - /* Adjust lpm if paths are not set in pam. */ - req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam); + /* Use next available path that is not already in correct state. */ + req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & ~sch->vpm); if (!req->lpm) goto out_nopath; /* Channel program setup. */ @@ -199,6 +199,19 @@ err: verify_done(cdev, rc); } +static void spid_start(struct ccw_device *cdev) +{ + struct ccw_request *req = &cdev->private->req; + + /* Initialize request data. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->lpm = 0x80; + req->callback = spid_callback; + spid_do(cdev); +} + static int pgid_cmp(struct pgid *p1, struct pgid *p2) { return memcmp((char *) p1 + 1, (char *) p2 + 1, @@ -241,6 +254,40 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, *p = first; } +static u8 pgid_to_vpm(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct pgid *pgid; + int i; + int lpm; + u8 vpm = 0; + + /* Set VPM bits for paths which are already in the target state. */ + for (i = 0; i < 8; i++) { + lpm = 0x80 >> i; + if ((cdev->private->pgid_valid_mask & lpm) == 0) + continue; + pgid = &cdev->private->pgid[i]; + if (sch->opm & lpm) { + if (pgid->inf.ps.state1 != SNID_STATE1_GROUPED) + continue; + } else { + if (pgid->inf.ps.state1 != SNID_STATE1_UNGROUPED) + continue; + } + if (cdev->private->flags.mpath) { + if (pgid->inf.ps.state3 != SNID_STATE3_MULTI_PATH) + continue; + } else { + if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH) + continue; + } + vpm |= lpm; + } + + return vpm; +} + static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) { int i; @@ -255,6 +302,7 @@ static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) static void snid_done(struct ccw_device *cdev, int rc) { struct ccw_dev_id *id = &cdev->private->dev_id; + struct subchannel *sch = to_subchannel(cdev->dev.parent); struct pgid *pgid; int mismatch = 0; int reserved = 0; @@ -263,18 +311,38 @@ static void snid_done(struct ccw_device *cdev, int rc) if (rc) goto out; pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset); - if (!mismatch) { - pgid_fill(cdev, pgid); - cdev->private->flags.pgid_rdy = 1; - } if (reserved) rc = -EUSERS; + else if (mismatch) + rc = -EOPNOTSUPP; + else { + sch->vpm = pgid_to_vpm(cdev); + pgid_fill(cdev, pgid); + } out: - CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x mism=%d " - "rsvd=%d reset=%d\n", id->ssid, id->devno, rc, - cdev->private->pgid_valid_mask, mismatch, reserved, - reset); - ccw_device_sense_pgid_done(cdev, rc); + CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x " + "mism=%d rsvd=%d reset=%d\n", id->ssid, id->devno, rc, + cdev->private->pgid_valid_mask, sch->vpm, mismatch, + reserved, reset); + switch (rc) { + case 0: + /* Anything left to do? */ + if (sch->vpm == sch->schib.pmcw.pam) { + verify_done(cdev, sch->vpm == 0 ? -EACCES : 0); + return; + } + /* Perform path-grouping. */ + spid_start(cdev); + break; + case -EOPNOTSUPP: + /* Path-grouping not supported. */ + cdev->private->flags.pgroup = 0; + cdev->private->flags.mpath = 0; + verify_start(cdev); + break; + default: + verify_done(cdev, rc); + } } /* @@ -333,33 +401,6 @@ err: snid_done(cdev, rc); } -/** - * ccw_device_sense_pgid_start - perform SENSE PGID - * @cdev: ccw device - * - * Execute a SENSE PGID channel program on each path to @cdev to update its - * PGID information. When finished, call ccw_device_sense_id_done with a - * return code specifying the result. - */ -void ccw_device_sense_pgid_start(struct ccw_device *cdev) -{ - struct ccw_request *req = &cdev->private->req; - - CIO_TRACE_EVENT(4, "snid"); - CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); - /* Initialize PGID data. */ - memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid)); - cdev->private->flags.pgid_rdy = 0; - cdev->private->pgid_valid_mask = 0; - /* Initialize request data. */ - memset(req, 0, sizeof(*req)); - req->timeout = PGID_TIMEOUT; - req->maxretries = PGID_RETRIES; - req->callback = snid_callback; - req->lpm = 0x80; - snid_do(cdev); -} - /* * Perform path verification. */ @@ -367,6 +408,7 @@ static void verify_start(struct ccw_device *cdev) { struct subchannel *sch = to_subchannel(cdev->dev.parent); struct ccw_request *req = &cdev->private->req; + struct ccw_dev_id *devid = &cdev->private->dev_id; sch->vpm = 0; /* Initialize request data. */ @@ -375,9 +417,13 @@ static void verify_start(struct ccw_device *cdev) req->maxretries = PGID_RETRIES; req->lpm = 0x80; if (cdev->private->flags.pgroup) { - req->callback = spid_callback; - spid_do(cdev); + CIO_TRACE_EVENT(4, "snid"); + CIO_HEX_EVENT(4, devid, sizeof(*devid)); + req->callback = snid_callback; + snid_do(cdev); } else { + CIO_TRACE_EVENT(4, "nop"); + CIO_HEX_EVENT(4, devid, sizeof(*devid)); req->filter = nop_filter; req->callback = nop_callback; nop_do(cdev); @@ -398,19 +444,15 @@ void ccw_device_verify_start(struct ccw_device *cdev) { CIO_TRACE_EVENT(4, "vrfy"); CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); - if (!cdev->private->flags.pgid_rdy) { - /* No pathgrouping possible. */ - cdev->private->flags.pgroup = 0; - cdev->private->flags.mpath = 0; - } else { - /* - * Initialize pathgroup and multipath state with target values. - * They may change in the course of path verification. - */ - cdev->private->flags.pgroup = cdev->private->options.pgroup; - cdev->private->flags.mpath = cdev->private->options.mpath; - - } + /* Initialize PGID data. */ + memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid)); + cdev->private->pgid_valid_mask = 0; + /* + * Initialize pathgroup and multipath state with target values. + * They may change in the course of path verification. + */ + cdev->private->flags.pgroup = cdev->private->options.pgroup; + cdev->private->flags.mpath = cdev->private->options.mpath; cdev->private->flags.doverify = 0; verify_start(cdev); } diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index b387c80d1888..0559479073cc 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -166,7 +166,6 @@ struct ccw_device_private { unsigned int recog_done:1; /* dev. recog. complete */ unsigned int fake_irb:1; /* deliver faked irb */ unsigned int resuming:1; /* recognition while resume */ - unsigned int pgid_rdy:1; /* pgids are ready */ unsigned int pgroup:1; /* pathgroup is set up */ unsigned int mpath:1; /* multipathing is set up */ } __attribute__((packed)) flags; -- cgit v1.2.3 From d7d12ef2befac4fed0dccaddff11338b654804df Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:32 +0100 Subject: [S390] cio: make steal lock procedure more robust An Unconditional Reserve + Release operation (steal lock) for a boxed device may fail when encountering special error cases (e.g. unit checks or path errors). Fix this by using the more robust ccw_request infrastructure for performing the steal lock CCW program. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.h | 4 ++ drivers/s390/cio/device_fsm.c | 55 ++++++++++---------- drivers/s390/cio/device_ops.c | 112 +++++++++++++++++++---------------------- drivers/s390/cio/device_pgid.c | 52 +++++++++++++++++++ 4 files changed, 134 insertions(+), 89 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 2df519bb877e..bcfe13e42638 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -28,6 +28,7 @@ enum dev_state { DEV_STATE_DISCONNECTED_SENSE_ID, DEV_STATE_CMFCHANGE, DEV_STATE_CMFUPDATE, + DEV_STATE_STEAL_LOCK, /* last element! */ NR_DEV_STATES }; @@ -116,6 +117,9 @@ void ccw_device_verify_done(struct ccw_device *, int); void ccw_device_disband_start(struct ccw_device *); void ccw_device_disband_done(struct ccw_device *, int); +void ccw_device_stlck_start(struct ccw_device *, void *, void *, void *); +void ccw_device_stlck_done(struct ccw_device *, void *, int); + int ccw_device_call_handler(struct ccw_device *); int ccw_device_stlck(struct ccw_device *); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 8d565ff85e43..7d42417bc2c7 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -640,6 +640,23 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event) ccw_device_verify_start(cdev); } +/* + * Handle path verification event in boxed state. + */ +static void ccw_device_boxed_verify(struct ccw_device *cdev, + enum dev_event dev_event) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + + if (cdev->online) { + if (cio_enable_subchannel(sch, (u32) (addr_t) sch)) + ccw_device_done(cdev, DEV_STATE_NOT_OPER); + else + ccw_device_online_verify(cdev, dev_event); + } else + css_schedule_eval(sch->schid); +} + /* * Got an interrupt for a normal io (state online). */ @@ -816,32 +833,6 @@ ccw_device_delay_verify(struct ccw_device *cdev, enum dev_event dev_event) cdev->private->flags.doverify = 1; } -static void -ccw_device_stlck_done(struct ccw_device *cdev, enum dev_event dev_event) -{ - struct irb *irb; - - switch (dev_event) { - case DEV_EVENT_INTERRUPT: - irb = (struct irb *) __LC_IRB; - /* Check for unsolicited interrupt. */ - if ((scsw_stctl(&irb->scsw) == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) && - (!scsw_cc(&irb->scsw))) - /* FIXME: we should restart stlck here, but this - * is extremely unlikely ... */ - goto out_wakeup; - - ccw_device_accumulate_irb(cdev, irb); - /* We don't care about basic sense etc. */ - break; - default: /* timeout */ - break; - } -out_wakeup: - wake_up(&cdev->private->wait_q); -} - static void ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event) { @@ -1010,9 +1001,9 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { }, [DEV_STATE_BOXED] = { [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, - [DEV_EVENT_INTERRUPT] = ccw_device_stlck_done, - [DEV_EVENT_TIMEOUT] = ccw_device_stlck_done, - [DEV_EVENT_VERIFY] = ccw_device_nop, + [DEV_EVENT_INTERRUPT] = ccw_device_nop, + [DEV_EVENT_TIMEOUT] = ccw_device_nop, + [DEV_EVENT_VERIFY] = ccw_device_boxed_verify, }, /* states to wait for i/o completion before doing something */ [DEV_STATE_TIMEOUT_KILL] = { @@ -1052,6 +1043,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_TIMEOUT] = ccw_device_update_cmfblock, [DEV_EVENT_VERIFY] = ccw_device_update_cmfblock, }, + [DEV_STATE_STEAL_LOCK] = { + [DEV_EVENT_NOTOPER] = ccw_device_request_event, + [DEV_EVENT_INTERRUPT] = ccw_device_request_event, + [DEV_EVENT_TIMEOUT] = ccw_device_request_event, + [DEV_EVENT_VERIFY] = ccw_device_nop, + }, }; EXPORT_SYMBOL_GPL(ccw_device_set_timeout); diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index d4be16acebe4..6da84543dfe9 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -504,74 +505,65 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev) return sch->lpm; } -/* - * Try to break the lock on a boxed device. - */ -int -ccw_device_stlck(struct ccw_device *cdev) -{ - void *buf, *buf2; - unsigned long flags; - struct subchannel *sch; - int ret; +struct stlck_data { + struct completion done; + int rc; +}; - if (!cdev) - return -ENODEV; +void ccw_device_stlck_done(struct ccw_device *cdev, void *data, int rc) +{ + struct stlck_data *sdata = data; - if (cdev->drv && !cdev->private->options.force) - return -EINVAL; + sdata->rc = rc; + complete(&sdata->done); +} - sch = to_subchannel(cdev->dev.parent); - - CIO_TRACE_EVENT(2, "stl lock"); - CIO_TRACE_EVENT(2, dev_name(&cdev->dev)); +/* + * Perform unconditional reserve + release. + */ +int ccw_device_stlck(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct stlck_data data; + u8 *buffer; + int rc; - buf = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL); - if (!buf) - return -ENOMEM; - buf2 = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL); - if (!buf2) { - kfree(buf); - return -ENOMEM; + /* Check if steal lock operation is valid for this device. */ + if (cdev->drv) { + if (!cdev->private->options.force) + return -EINVAL; } - spin_lock_irqsave(sch->lock, flags); - ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); - if (ret) - goto out_unlock; - /* - * Setup ccw. We chain an unconditional reserve and a release so we - * only break the lock. - */ - cdev->private->iccws[0].cmd_code = CCW_CMD_STLCK; - cdev->private->iccws[0].cda = (__u32) __pa(buf); - cdev->private->iccws[0].count = 32; - cdev->private->iccws[0].flags = CCW_FLAG_CC; - cdev->private->iccws[1].cmd_code = CCW_CMD_RELEASE; - cdev->private->iccws[1].cda = (__u32) __pa(buf2); - cdev->private->iccws[1].count = 32; - cdev->private->iccws[1].flags = 0; - ret = cio_start(sch, cdev->private->iccws, 0); - if (ret) { - cio_disable_subchannel(sch); //FIXME: return code? + buffer = kzalloc(64, GFP_DMA | GFP_KERNEL); + if (!buffer) + return -ENOMEM; + init_completion(&data.done); + data.rc = -EIO; + spin_lock_irq(sch->lock); + rc = cio_enable_subchannel(sch, (u32) (addr_t) sch); + if (rc) goto out_unlock; + /* Perform operation. */ + cdev->private->state = DEV_STATE_STEAL_LOCK, + ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]); + spin_unlock_irq(sch->lock); + /* Wait for operation to finish. */ + if (wait_for_completion_interruptible(&data.done)) { + /* Got a signal. */ + spin_lock_irq(sch->lock); + ccw_request_cancel(cdev); + spin_unlock_irq(sch->lock); + wait_for_completion(&data.done); } - cdev->private->irb.scsw.cmd.actl |= SCSW_ACTL_START_PEND; - spin_unlock_irqrestore(sch->lock, flags); - wait_event(cdev->private->wait_q, - cdev->private->irb.scsw.cmd.actl == 0); - spin_lock_irqsave(sch->lock, flags); - cio_disable_subchannel(sch); //FIXME: return code? - if ((cdev->private->irb.scsw.cmd.dstat != - (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) || - (cdev->private->irb.scsw.cmd.cstat != 0)) - ret = -EIO; - /* Clear irb. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); + rc = data.rc; + /* Check results. */ + spin_lock_irq(sch->lock); + cio_disable_subchannel(sch); + cdev->private->state = DEV_STATE_BOXED; out_unlock: - kfree(buf); - kfree(buf2); - spin_unlock_irqrestore(sch->lock, flags); - return ret; + spin_unlock_irq(sch->lock); + kfree(buffer); + + return rc; } void *ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no) diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 4d54abd82b8c..5bcefeaff744 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -507,3 +507,55 @@ void ccw_device_disband_start(struct ccw_device *cdev) spid_build_cp(cdev, fn); ccw_request_start(cdev); } + +static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2) +{ + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + + cp[0].cmd_code = CCW_CMD_STLCK; + cp[0].cda = (u32) (addr_t) buf1; + cp[0].count = 32; + cp[0].flags = CCW_FLAG_CC; + cp[1].cmd_code = CCW_CMD_RELEASE; + cp[1].cda = (u32) (addr_t) buf2; + cp[1].count = 32; + cp[1].flags = 0; + req->cp = cp; +} + +static void stlck_callback(struct ccw_device *cdev, void *data, int rc) +{ + ccw_device_stlck_done(cdev, data, rc); +} + +/** + * ccw_device_stlck_start - perform unconditional release + * @cdev: ccw device + * @data: data pointer to be passed to ccw_device_stlck_done + * @buf1: data pointer used in channel program + * @buf2: data pointer used in channel program + * + * Execute a channel program on @cdev to release an existing PGID reservation. + * When finished, call ccw_device_stlck_done with a return code specifying the + * result. + */ +void ccw_device_stlck_start(struct ccw_device *cdev, void *data, void *buf1, + void *buf2) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + CIO_TRACE_EVENT(4, "stlck"); + CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); + /* Request setup. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->lpm = sch->schib.pmcw.pam & sch->opm; + req->data = data; + req->callback = stlck_callback; + stlck_build_cp(cdev, buf1, buf2); + ccw_request_start(cdev); +} + -- cgit v1.2.3 From 7d253b9a1aaf5192808e641659f4feb122faa536 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 7 Dec 2009 12:51:33 +0100 Subject: [S390] cio: remove registered flag from ccw_device_private We used to maintain a "registered" flag in our ccw_device_private structure. This patch removes the "registered" flag and converts all users of it to device_is_registered which has the exact same meaning. Note: The usage the atomic operation test_and_clear_bit is replaced by the non-atomic if (device_is_registered()) device_del(). This will not do harm, since we serialize calls to ccw_device_unregister with a single-threaded workqueue. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 11 +++-------- drivers/s390/cio/io_sch.h | 1 - 2 files changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e24b9b1d1022..f4401ede768a 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -303,7 +303,7 @@ int ccw_device_is_orphan(struct ccw_device *cdev) static void ccw_device_unregister(struct ccw_device *cdev) { - if (test_and_clear_bit(1, &cdev->private->registered)) { + if (device_is_registered(&cdev->dev)) { device_del(&cdev->dev); /* Release reference from device_initialize(). */ put_device(&cdev->dev); @@ -640,12 +640,7 @@ static int ccw_device_register(struct ccw_device *cdev) cdev->private->dev_id.devno); if (ret) return ret; - ret = device_add(dev); - if (ret) - return ret; - - set_bit(1, &cdev->private->registered); - return ret; + return device_add(dev); } static int match_dev_id(struct device *dev, void *data) @@ -669,7 +664,7 @@ static void ccw_device_do_unbind_bind(struct ccw_device *cdev) { int ret; - if (test_bit(1, &cdev->private->registered)) { + if (device_is_registered(&cdev->dev)) { device_release_driver(&cdev->dev); ret = device_attach(&cdev->dev); WARN_ON(ret == -ENODEV); diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index 0559479073cc..ca1063d6b505 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -145,7 +145,6 @@ struct ccw_device_private { struct subchannel *sch; int state; /* device state */ atomic_t onoff; - unsigned long registered; struct ccw_dev_id dev_id; /* device id */ struct subchannel_id schid; /* subchannel number */ struct ccw_request req; /* internal I/O request */ -- cgit v1.2.3 From 24a1872d6411c7cce82c0888a4fbea23e993e051 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 7 Dec 2009 12:51:34 +0100 Subject: [S390] cio: add per device initialization status flag The function ccw_device_unregister has to ensure to remove all references obtained by device_add and device_initialize. Unfortunately it gets called for devices which are 1) uninitialized, 2) initialized but unregistered, and 3) registered devices. To distinguish 1) and 2) this patch introduces a new flag "initialized", which is 1 as long as we hold the initial device reference. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 6 ++++++ drivers/s390/cio/io_sch.h | 1 + 2 files changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index f4401ede768a..e8cb99a63cc6 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -304,7 +304,11 @@ int ccw_device_is_orphan(struct ccw_device *cdev) static void ccw_device_unregister(struct ccw_device *cdev) { if (device_is_registered(&cdev->dev)) { + /* Undo device_add(). */ device_del(&cdev->dev); + } + if (cdev->private->flags.initialized) { + cdev->private->flags.initialized = 0; /* Release reference from device_initialize(). */ put_device(&cdev->dev); } @@ -716,6 +720,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch, put_device(&cdev->dev); return -ENODEV; } + cdev->private->flags.initialized = 1; return 0; } @@ -998,6 +1003,7 @@ static int io_subchannel_probe(struct subchannel *sch) cdev = sch_get_cdev(sch); cdev->dev.groups = ccwdev_attr_groups; device_initialize(&cdev->dev); + cdev->private->flags.initialized = 1; ccw_device_register(cdev); /* * Check if the device is already online. If it is diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index ca1063d6b505..dbc69a5a043e 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -167,6 +167,7 @@ struct ccw_device_private { unsigned int resuming:1; /* recognition while resume */ unsigned int pgroup:1; /* pathgroup is set up */ unsigned int mpath:1; /* multipathing is set up */ + unsigned int initialized:1; /* set if initial reference held */ } __attribute__((packed)) flags; unsigned long intparm; /* user interruption parameter */ struct qdio_irq *qdio_data; -- cgit v1.2.3 From 56e6b796fe9b99287648fc5686aae00106b37bab Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 7 Dec 2009 12:51:35 +0100 Subject: [S390] cio: fix quiesce state DEV_STATE_QUIESCE is used to stop all IO on a busy subchannel. This patch fixes the following problems related to the QUIESCE state: * Fix a potential race condition which could occur when the resulting state was DEV_STATE_OFFLINE. * Add missing locking around cio_disable_subchannel, ccw_device_cancel_halt_clear and the cdev's handler. * Loop until we know for sure that the subchannel is disabled. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 35 +++++++++++++++++++---------------- drivers/s390/cio/device_fsm.c | 17 ++++------------- 2 files changed, 23 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e8cb99a63cc6..2b50f93b7fef 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1130,33 +1130,36 @@ static int io_subchannel_chp_event(struct subchannel *sch, return 0; } -static void -io_subchannel_shutdown(struct subchannel *sch) +static void io_subchannel_shutdown(struct subchannel *sch) { struct ccw_device *cdev; int ret; + spin_lock_irq(sch->lock); cdev = sch_get_cdev(sch); - if (cio_is_console(sch->schid)) - return; + goto out_unlock; if (!sch->schib.pmcw.ena) - /* Nothing to do. */ - return; + goto out_unlock; ret = cio_disable_subchannel(sch); if (ret != -EBUSY) - /* Subchannel is disabled, we're done. */ - return; - cdev->private->state = DEV_STATE_QUIESCE; + goto out_unlock; if (cdev->handler) - cdev->handler(cdev, cdev->private->intparm, - ERR_PTR(-EIO)); - ret = ccw_device_cancel_halt_clear(cdev); - if (ret == -EBUSY) { - ccw_device_set_timeout(cdev, HZ/10); - wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); + cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); + while (ret == -EBUSY) { + cdev->private->state = DEV_STATE_QUIESCE; + ret = ccw_device_cancel_halt_clear(cdev); + if (ret == -EBUSY) { + ccw_device_set_timeout(cdev, HZ/10); + spin_unlock_irq(sch->lock); + wait_event(cdev->private->wait_q, + cdev->private->state != DEV_STATE_QUIESCE); + spin_lock_irq(sch->lock); + } + ret = cio_disable_subchannel(sch); } - cio_disable_subchannel(sch); +out_unlock: + spin_unlock_irq(sch->lock); } static int device_is_disconnected(struct ccw_device *cdev) diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 7d42417bc2c7..862b89ef3ee6 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -911,10 +911,7 @@ static void ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event) { ccw_device_set_timeout(cdev, 0); - if (dev_event == DEV_EVENT_NOTOPER) - cdev->private->state = DEV_STATE_NOT_OPER; - else - cdev->private->state = DEV_STATE_OFFLINE; + cdev->private->state = DEV_STATE_NOT_OPER; wake_up(&cdev->private->wait_q); } @@ -924,17 +921,11 @@ ccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event) int ret; ret = ccw_device_cancel_halt_clear(cdev); - switch (ret) { - case 0: - cdev->private->state = DEV_STATE_OFFLINE; - wake_up(&cdev->private->wait_q); - break; - case -ENODEV: + if (ret == -EBUSY) { + ccw_device_set_timeout(cdev, HZ/10); + } else { cdev->private->state = DEV_STATE_NOT_OPER; wake_up(&cdev->private->wait_q); - break; - default: - ccw_device_set_timeout(cdev, HZ/10); } } -- cgit v1.2.3 From ec64333c3a9bc52e34d79cff23acf4e5764b1353 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 7 Dec 2009 12:51:36 +0100 Subject: [S390] cio: handle failed disable_subchannel after device recognition Handle a failing cio_disable_subchannel at the end of our device recognition as if the recognition itself failed. This way subsequent registration steps do not need to handle enabled subchannels. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_fsm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 862b89ef3ee6..ae760658a131 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -229,7 +229,8 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) sch = to_subchannel(cdev->dev.parent); - cio_disable_subchannel(sch); + if (cio_disable_subchannel(sch)) + state = DEV_STATE_NOT_OPER; /* * Now that we tried recognition, we have performed device selection * through ssch() and the path information is up to date. -- cgit v1.2.3 From 0c609fca243d456af014e92ad1caca045072dfe8 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 7 Dec 2009 12:51:37 +0100 Subject: [S390] cio: handle busy subchannel in ccw_device_move_to_sch Try to disable the old subchannel before we ask the driver core to move the attached device to a new parent. This way we can use the QUIESCE state during shutdown which prevents a possible use after free situation in some error cases. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 2b50f93b7fef..af500aac24ef 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -892,12 +892,27 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev, struct subchannel *sch) { struct subchannel *old_sch; - int rc; + int rc, old_enabled = 0; old_sch = to_subchannel(cdev->dev.parent); /* Obtain child reference for new parent. */ if (!get_device(&sch->dev)) return -ENODEV; + + if (!sch_is_pseudo_sch(old_sch)) { + spin_lock_irq(old_sch->lock); + old_enabled = old_sch->schib.pmcw.ena; + rc = 0; + if (old_enabled) + rc = cio_disable_subchannel(old_sch); + spin_unlock_irq(old_sch->lock); + if (rc == -EBUSY) { + /* Release child reference for new parent. */ + put_device(&sch->dev); + return rc; + } + } + mutex_lock(&sch->reg_mutex); rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV); mutex_unlock(&sch->reg_mutex); @@ -906,6 +921,12 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev, cdev->private->dev_id.ssid, cdev->private->dev_id.devno, sch->schid.ssid, sch->schib.pmcw.dev, rc); + if (old_enabled) { + /* Try to reenable the old subchannel. */ + spin_lock_irq(old_sch->lock); + cio_enable_subchannel(old_sch, (u32)(addr_t)old_sch); + spin_unlock_irq(old_sch->lock); + } /* Release child reference for new parent. */ put_device(&sch->dev); return rc; @@ -914,7 +935,6 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev, if (!sch_is_pseudo_sch(old_sch)) { spin_lock_irq(old_sch->lock); sch_set_cdev(old_sch, NULL); - cio_disable_subchannel(old_sch); spin_unlock_irq(old_sch->lock); css_schedule_eval(old_sch->schid); } -- cgit v1.2.3 From 6e9a0f67deeca90c433ac40b887cee8da3bdcea2 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 7 Dec 2009 12:51:38 +0100 Subject: [S390] cio: quiesce subchannel in io_subchannel_remove Ensure that there will be no more interrupts for an unregistered device by using the same quiesce and disable loop as in io_subchannel_shutdown. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index af500aac24ef..bd6e8cf77fad 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1059,6 +1059,8 @@ out_schedule: return 0; } +static void io_subchannel_quiesce(struct subchannel *); + static int io_subchannel_remove (struct subchannel *sch) { @@ -1068,6 +1070,7 @@ io_subchannel_remove (struct subchannel *sch) cdev = sch_get_cdev(sch); if (!cdev) goto out_free; + io_subchannel_quiesce(sch); /* Set ccw device to not operational and drop reference. */ spin_lock_irqsave(cdev->ccwlock, flags); sch_set_cdev(sch, NULL); @@ -1150,7 +1153,7 @@ static int io_subchannel_chp_event(struct subchannel *sch, return 0; } -static void io_subchannel_shutdown(struct subchannel *sch) +static void io_subchannel_quiesce(struct subchannel *sch) { struct ccw_device *cdev; int ret; @@ -1182,6 +1185,11 @@ out_unlock: spin_unlock_irq(sch->lock); } +static void io_subchannel_shutdown(struct subchannel *sch) +{ + io_subchannel_quiesce(sch); +} + static int device_is_disconnected(struct ccw_device *cdev) { if (!cdev) -- cgit v1.2.3 From 7a8ad1001c51bba0507ee08cb4323d8ddcb07c70 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 7 Dec 2009 12:51:39 +0100 Subject: [S390] cio: change locking in io_subchannel_remove IO subchannels are always unregistered in process context, so use spin_lock_irq in the corresponding remove callback. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index bd6e8cf77fad..dc97cb9f227f 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1065,17 +1065,16 @@ static int io_subchannel_remove (struct subchannel *sch) { struct ccw_device *cdev; - unsigned long flags; cdev = sch_get_cdev(sch); if (!cdev) goto out_free; io_subchannel_quiesce(sch); /* Set ccw device to not operational and drop reference. */ - spin_lock_irqsave(cdev->ccwlock, flags); + spin_lock_irq(cdev->ccwlock); sch_set_cdev(sch, NULL); cdev->private->state = DEV_STATE_NOT_OPER; - spin_unlock_irqrestore(cdev->ccwlock, flags); + spin_unlock_irq(cdev->ccwlock); ccw_device_unregister(cdev); out_free: kfree(sch->private); -- cgit v1.2.3 From de1b04388f63cbddf91d9f6c50c29be7232881ca Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:40 +0100 Subject: [S390] cio: improve error recovery for internal I/Os Improve error recovery for internal I/Os by repeating each I/O 256 times per path to cope with long-running non-permanent error conditions. Also retry each path twice to cope with link flapping, i.e. single paths becoming unavailable in the order in which they are tried. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/ccwreq.c | 7 ++++--- drivers/s390/cio/device_id.c | 2 +- drivers/s390/cio/device_pgid.c | 2 +- drivers/s390/cio/io_sch.h | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/ccwreq.c b/drivers/s390/cio/ccwreq.c index a6e205a384b2..9509e3860934 100644 --- a/drivers/s390/cio/ccwreq.c +++ b/drivers/s390/cio/ccwreq.c @@ -82,7 +82,7 @@ static void ccwreq_do(struct ccw_device *cdev) /* Perform start function. */ sch->lpm = 0xff; memset(&cdev->private->irb, 0, sizeof(struct irb)); - rc = cio_start(sch, cp, req->mask); + rc = cio_start(sch, cp, (u8) req->mask); if (rc == 0) { /* I/O started successfully. */ ccw_device_set_timeout(cdev, req->timeout); @@ -116,7 +116,8 @@ void ccw_request_start(struct ccw_device *cdev) { struct ccw_request *req = &cdev->private->req; - req->mask = 0x80; + /* Try all paths twice to counter link flapping. */ + req->mask = 0x8080; req->retries = req->maxretries; req->mask = lpm_adjust(req->mask, req->lpm); req->drc = 0; @@ -212,7 +213,7 @@ static void ccwreq_log_status(struct ccw_device *cdev, enum io_status status) } __attribute__ ((packed)) data; data.dev_id = cdev->private->dev_id; data.retries = req->retries; - data.lpm = req->mask; + data.lpm = (u8) req->mask; data.status = (u8) status; CIO_TRACE_EVENT(2, "reqstat"); CIO_HEX_EVENT(2, &data, sizeof(data)); diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c index 4728644ed85c..78a0b43862c5 100644 --- a/drivers/s390/cio/device_id.c +++ b/drivers/s390/cio/device_id.c @@ -21,7 +21,7 @@ #include "device.h" #include "io_sch.h" -#define SENSE_ID_RETRIES 5 +#define SENSE_ID_RETRIES 256 #define SENSE_ID_TIMEOUT (10 * HZ) #define SENSE_ID_MIN_LEN 4 #define SENSE_ID_BASIC_LEN 7 diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 5bcefeaff744..aad188e43b4f 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -20,7 +20,7 @@ #include "device.h" #include "io_sch.h" -#define PGID_RETRIES 5 +#define PGID_RETRIES 256 #define PGID_TIMEOUT (10 * HZ) /* diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index dbc69a5a043e..d72ae4c93af9 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -109,7 +109,7 @@ struct ccw_request { void (*callback)(struct ccw_device *, void *, int); void *data; /* These fields are used internally. */ - u8 mask; + u16 mask; u16 retries; int drc; int cancel:1; -- cgit v1.2.3 From d40f7b75a23d1e59b6ec9d6701231fd4c6992ac6 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 7 Dec 2009 12:51:41 +0100 Subject: [S390] cio: dont unregister a busy device in ccw_device_set_offline If we detect a busy subchannel after the driver's set_offline callback returned in ccw_device_set_offline, the current behavior is to unregister the device, which may lead to undesired consequences. Change this to just quiesce the subchannel and go on with the offline processing. Note: This is no excuse for not fixing these drivers - after the set_offline callback they should have no running IO! Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index dc97cb9f227f..9fecfb4223a8 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -314,6 +314,8 @@ static void ccw_device_unregister(struct ccw_device *cdev) } } +static void io_subchannel_quiesce(struct subchannel *); + /** * ccw_device_set_offline() - disable a ccw device for I/O * @cdev: target ccw device @@ -327,7 +329,8 @@ static void ccw_device_unregister(struct ccw_device *cdev) */ int ccw_device_set_offline(struct ccw_device *cdev) { - int ret; + struct subchannel *sch; + int ret, state; if (!cdev) return -ENODEV; @@ -341,6 +344,7 @@ int ccw_device_set_offline(struct ccw_device *cdev) } cdev->online = 0; spin_lock_irq(cdev->ccwlock); + sch = to_subchannel(cdev->dev.parent); /* Wait until a final state or DISCONNECTED is reached */ while (!dev_fsm_final_state(cdev) && cdev->private->state != DEV_STATE_DISCONNECTED) { @@ -349,9 +353,21 @@ int ccw_device_set_offline(struct ccw_device *cdev) cdev->private->state == DEV_STATE_DISCONNECTED)); spin_lock_irq(cdev->ccwlock); } - ret = ccw_device_offline(cdev); - if (ret) - goto error; + do { + ret = ccw_device_offline(cdev); + if (!ret) + break; + CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device " + "0.%x.%04x\n", ret, cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); + if (ret != -EBUSY) + goto error; + state = cdev->private->state; + spin_unlock_irq(cdev->ccwlock); + io_subchannel_quiesce(sch); + spin_lock_irq(cdev->ccwlock); + cdev->private->state = state; + } while (ret == -EBUSY); spin_unlock_irq(cdev->ccwlock); wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) || cdev->private->state == DEV_STATE_DISCONNECTED)); @@ -368,9 +384,6 @@ int ccw_device_set_offline(struct ccw_device *cdev) return 0; error: - CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device 0.%x.%04x\n", - ret, cdev->private->dev_id.ssid, - cdev->private->dev_id.devno); cdev->private->state = DEV_STATE_OFFLINE; dev_fsm_event(cdev, DEV_EVENT_NOTOPER); spin_unlock_irq(cdev->ccwlock); @@ -1059,8 +1072,6 @@ out_schedule: return 0; } -static void io_subchannel_quiesce(struct subchannel *); - static int io_subchannel_remove (struct subchannel *sch) { -- cgit v1.2.3 From 22825ab7693fd29769518a0d25ba43c01a50092a Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Mon, 7 Dec 2009 12:51:48 +0100 Subject: [S390] dasd: support DIAG access for read-only devices When a DASD device is used with the DIAG discipline, the DIAG initialization will indicate success or error with a respective return code. So far we have interpreted a return code of 4 as error, but it actually means that the initialization was successful, but the device is read-only. To allow read-only devices to be used with DIAG we need to accept a return code of 4 as success. Re-initialization of the DIAG access is also part of the DIAG error recovery. If we find that the access mode of a device has been changed from writable to read-only while the device was in use, we print an error message. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_diag.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 4e49b4a6c880..8174ec921769 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -145,6 +145,15 @@ dasd_diag_erp(struct dasd_device *device) mdsk_term_io(device); rc = mdsk_init_io(device, device->block->bp_block, 0, NULL); + if (rc == 4) { + if (!(device->features & DASD_FEATURE_READONLY)) { + dev_warn(&device->cdev->dev, + "The access mode of a DIAG device changed" + " to read-only"); + device->features |= DASD_FEATURE_READONLY; + } + rc = 0; + } if (rc) dev_warn(&device->cdev->dev, "DIAG ERP failed with " "rc=%d\n", rc); @@ -433,16 +442,20 @@ dasd_diag_check_device(struct dasd_device *device) for (sb = 512; sb < bsize; sb = sb << 1) block->s2b_shift++; rc = mdsk_init_io(device, block->bp_block, 0, NULL); - if (rc) { + if (rc && (rc != 4)) { dev_warn(&device->cdev->dev, "DIAG initialization " "failed with rc=%d\n", rc); rc = -EIO; } else { + if (rc == 4) + device->features |= DASD_FEATURE_READONLY; dev_info(&device->cdev->dev, - "New DASD with %ld byte/block, total size %ld KB\n", + "New DASD with %ld byte/block, total size %ld KB%s\n", (unsigned long) block->bp_block, (unsigned long) (block->blocks << - block->s2b_shift) >> 1); + block->s2b_shift) >> 1, + (rc == 4) ? ", read-only device" : ""); + rc = 0; } out_label: free_page((long) label); -- cgit v1.2.3 From 779c27820a6bd53523a34551aa6004045a060dcf Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Mon, 7 Dec 2009 12:51:49 +0100 Subject: [S390] dasd: remove dead code the todclk.h header file is dead code. Remove it. Signed-off-by: Christian Borntraeger Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/todclk.h | 23 ----------------------- drivers/s390/block/dasd.c | 1 - drivers/s390/block/dasd_3990_erp.c | 1 - drivers/s390/block/dasd_diag.c | 1 - drivers/s390/block/dasd_eckd.c | 1 - drivers/s390/block/dasd_fba.c | 1 - 6 files changed, 28 deletions(-) delete mode 100644 arch/s390/include/asm/todclk.h (limited to 'drivers') diff --git a/arch/s390/include/asm/todclk.h b/arch/s390/include/asm/todclk.h deleted file mode 100644 index c7f62055488a..000000000000 --- a/arch/s390/include/asm/todclk.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * File...........: linux/include/asm/todclk.h - * Author(s)......: Holger Smolinski - * Bugreports.to..: - * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 - * - * History of changes (starts July 2000) - */ - -#ifndef __ASM_TODCLK_H -#define __ASM_TODCLK_H - -#ifdef __KERNEL__ - -#define TOD_uSEC (0x1000ULL) -#define TOD_mSEC (1000 * TOD_uSEC) -#define TOD_SEC (1000 * TOD_mSEC) -#define TOD_MIN (60 * TOD_SEC) -#define TOD_HOUR (60 * TOD_MIN) - -#endif - -#endif diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 58ffbd1d04a1..329115a4d4b3 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -24,7 +24,6 @@ #include #include #include -#include #include /* This is ugly... */ diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index e8ff7b0c961d..316eb1256a99 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -12,7 +12,6 @@ #include #include #include -#include #define PRINTK_HEADER "dasd_erp(3990): " diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 8174ec921769..f64d0db881b4 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index a8ec0731609e..5b909e6ad978 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index f245377e8e27..d1a07e4c0e78 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include "dasd_int.h" -- cgit v1.2.3 From 626350b63ef2cd447023d3dc2a34eaa7ca01bfff Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Mon, 7 Dec 2009 12:51:50 +0100 Subject: [S390] dasd: enable prefix independent of pav support The DASD device driver needs to explicitly enable the prefix command on the storage server, before it can be used. Originally we enabled this command along with others only if we wanted to support PAV. However, today we require this command for other features like High Performance FICON as well, so we need to always enable prefix. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 5b909e6ad978..abb2ec836ee8 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1012,9 +1012,9 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device, } psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data; psf_ssc_data->order = PSF_ORDER_SSC; - psf_ssc_data->suborder = 0x40; + psf_ssc_data->suborder = 0xc0; if (enable_pav) { - psf_ssc_data->suborder |= 0x88; + psf_ssc_data->suborder |= 0x08; psf_ssc_data->reserved[0] = 0x88; } ccw = cqr->cpaddr; -- cgit v1.2.3 From eb6e199bef288611157b8198c25d12b32bf058d0 Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Mon, 7 Dec 2009 12:51:51 +0100 Subject: [S390] dasd: improve error recovery for internal I/O Most of the error conditions reported by a FICON storage server indicate situations which can be recovered. Sometimes the host just needs to retry an I/O request, but sometimes the recovery is more complex and requires the device driver to wait, choose a different path, etc. The DASD device driver has a fully featured error recovery for normal block layer I/O, but not for internal I/O request which are for example used during the device bring up. This can lead to situations where the IPL of a system fails because DASD devices are not properly recognized. This patch will extend the internal I/O handling to use the existing error recovery procedures. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 207 +++++++++++++++++++++++++++---------- drivers/s390/block/dasd_3990_erp.c | 46 ++++++--- drivers/s390/block/dasd_alias.c | 15 +-- drivers/s390/block/dasd_eckd.c | 72 ++++++++++--- drivers/s390/block/dasd_int.h | 3 + drivers/s390/block/dasd_ioctl.c | 4 +- 6 files changed, 253 insertions(+), 94 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 329115a4d4b3..4f211c175b55 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -63,6 +63,7 @@ static void do_restore_device(struct work_struct *); static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *); static void dasd_device_timeout(unsigned long); static void dasd_block_timeout(unsigned long); +static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *); /* * SECTION: Operations on the device structure. @@ -959,7 +960,7 @@ static void dasd_device_timeout(unsigned long ptr) device = (struct dasd_device *) ptr; spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); /* re-activate request queue */ - device->stopped &= ~DASD_STOPPED_PENDING; + dasd_device_remove_stop_bits(device, DASD_STOPPED_PENDING); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); dasd_schedule_device_bh(device); } @@ -1022,7 +1023,7 @@ void dasd_generic_handle_state_change(struct dasd_device *device) /* First of all start sense subsystem status request. */ dasd_eer_snss(device); - device->stopped &= ~DASD_STOPPED_PENDING; + dasd_device_remove_stop_bits(device, DASD_STOPPED_PENDING); dasd_schedule_device_bh(device); if (device->block) dasd_schedule_block_bh(device->block); @@ -1404,6 +1405,20 @@ void dasd_schedule_device_bh(struct dasd_device *device) tasklet_hi_schedule(&device->tasklet); } +void dasd_device_set_stop_bits(struct dasd_device *device, int bits) +{ + device->stopped |= bits; +} +EXPORT_SYMBOL_GPL(dasd_device_set_stop_bits); + +void dasd_device_remove_stop_bits(struct dasd_device *device, int bits) +{ + device->stopped &= ~bits; + if (!device->stopped) + wake_up(&generic_waitq); +} +EXPORT_SYMBOL_GPL(dasd_device_remove_stop_bits); + /* * Queue a request to the head of the device ccw_queue. * Start the I/O if possible. @@ -1464,58 +1479,135 @@ static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr) } /* - * Queue a request to the tail of the device ccw_queue and wait for - * it's completion. + * checks if error recovery is necessary, returns 1 if yes, 0 otherwise. */ -int dasd_sleep_on(struct dasd_ccw_req *cqr) +static int __dasd_sleep_on_erp(struct dasd_ccw_req *cqr) { struct dasd_device *device; - int rc; + dasd_erp_fn_t erp_fn; + if (cqr->status == DASD_CQR_FILLED) + return 0; device = cqr->startdev; + if (test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) { + if (cqr->status == DASD_CQR_TERMINATED) { + device->discipline->handle_terminated_request(cqr); + return 1; + } + if (cqr->status == DASD_CQR_NEED_ERP) { + erp_fn = device->discipline->erp_action(cqr); + erp_fn(cqr); + return 1; + } + if (cqr->status == DASD_CQR_FAILED) + dasd_log_sense(cqr, &cqr->irb); + if (cqr->refers) { + __dasd_process_erp(device, cqr); + return 1; + } + } + return 0; +} - cqr->callback = dasd_wakeup_cb; - cqr->callback_data = (void *) &generic_waitq; - dasd_add_request_tail(cqr); - wait_event(generic_waitq, _wait_for_wakeup(cqr)); +static int __dasd_sleep_on_loop_condition(struct dasd_ccw_req *cqr) +{ + if (test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) { + if (cqr->refers) /* erp is not done yet */ + return 1; + return ((cqr->status != DASD_CQR_DONE) && + (cqr->status != DASD_CQR_FAILED)); + } else + return (cqr->status == DASD_CQR_FILLED); +} - if (cqr->status == DASD_CQR_DONE) +static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) +{ + struct dasd_device *device; + int rc; + struct list_head ccw_queue; + struct dasd_ccw_req *cqr; + + INIT_LIST_HEAD(&ccw_queue); + maincqr->status = DASD_CQR_FILLED; + device = maincqr->startdev; + list_add(&maincqr->blocklist, &ccw_queue); + for (cqr = maincqr; __dasd_sleep_on_loop_condition(cqr); + cqr = list_first_entry(&ccw_queue, + struct dasd_ccw_req, blocklist)) { + + if (__dasd_sleep_on_erp(cqr)) + continue; + if (cqr->status != DASD_CQR_FILLED) /* could be failed */ + continue; + + /* Non-temporary stop condition will trigger fail fast */ + if (device->stopped & ~DASD_STOPPED_PENDING && + test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && + (!dasd_eer_enabled(device))) { + cqr->status = DASD_CQR_FAILED; + continue; + } + + /* Don't try to start requests if device is stopped */ + if (interruptible) { + rc = wait_event_interruptible( + generic_waitq, !(device->stopped)); + if (rc == -ERESTARTSYS) { + cqr->status = DASD_CQR_FAILED; + maincqr->intrc = rc; + continue; + } + } else + wait_event(generic_waitq, !(device->stopped)); + + cqr->callback = dasd_wakeup_cb; + cqr->callback_data = (void *) &generic_waitq; + dasd_add_request_tail(cqr); + if (interruptible) { + rc = wait_event_interruptible( + generic_waitq, _wait_for_wakeup(cqr)); + if (rc == -ERESTARTSYS) { + dasd_cancel_req(cqr); + /* wait (non-interruptible) for final status */ + wait_event(generic_waitq, + _wait_for_wakeup(cqr)); + cqr->status = DASD_CQR_FAILED; + maincqr->intrc = rc; + continue; + } + } else + wait_event(generic_waitq, _wait_for_wakeup(cqr)); + } + + maincqr->endclk = get_clock(); + if ((maincqr->status != DASD_CQR_DONE) && + (maincqr->intrc != -ERESTARTSYS)) + dasd_log_sense(maincqr, &maincqr->irb); + if (maincqr->status == DASD_CQR_DONE) rc = 0; - else if (cqr->intrc) - rc = cqr->intrc; + else if (maincqr->intrc) + rc = maincqr->intrc; else rc = -EIO; return rc; } +/* + * Queue a request to the tail of the device ccw_queue and wait for + * it's completion. + */ +int dasd_sleep_on(struct dasd_ccw_req *cqr) +{ + return _dasd_sleep_on(cqr, 0); +} + /* * Queue a request to the tail of the device ccw_queue and wait * interruptible for it's completion. */ int dasd_sleep_on_interruptible(struct dasd_ccw_req *cqr) { - struct dasd_device *device; - int rc; - - device = cqr->startdev; - cqr->callback = dasd_wakeup_cb; - cqr->callback_data = (void *) &generic_waitq; - dasd_add_request_tail(cqr); - rc = wait_event_interruptible(generic_waitq, _wait_for_wakeup(cqr)); - if (rc == -ERESTARTSYS) { - dasd_cancel_req(cqr); - /* wait (non-interruptible) for final status */ - wait_event(generic_waitq, _wait_for_wakeup(cqr)); - cqr->intrc = rc; - } - - if (cqr->status == DASD_CQR_DONE) - rc = 0; - else if (cqr->intrc) - rc = cqr->intrc; - else - rc = -EIO; - return rc; + return _dasd_sleep_on(cqr, 1); } /* @@ -1629,7 +1721,7 @@ static void dasd_block_timeout(unsigned long ptr) block = (struct dasd_block *) ptr; spin_lock_irqsave(get_ccwdev_lock(block->base->cdev), flags); /* re-activate request queue */ - block->base->stopped &= ~DASD_STOPPED_PENDING; + dasd_device_remove_stop_bits(block->base, DASD_STOPPED_PENDING); spin_unlock_irqrestore(get_ccwdev_lock(block->base->cdev), flags); dasd_schedule_block_bh(block); } @@ -1656,11 +1748,10 @@ void dasd_block_clear_timer(struct dasd_block *block) /* * Process finished error recovery ccw. */ -static inline void __dasd_block_process_erp(struct dasd_block *block, - struct dasd_ccw_req *cqr) +static void __dasd_process_erp(struct dasd_device *device, + struct dasd_ccw_req *cqr) { dasd_erp_fn_t erp_fn; - struct dasd_device *device = block->base; if (cqr->status == DASD_CQR_DONE) DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful"); @@ -1724,9 +1815,12 @@ static void __dasd_process_request_queue(struct dasd_block *block) */ if (!list_empty(&block->ccw_queue)) break; - spin_lock_irqsave(get_ccwdev_lock(basedev->cdev), flags); - basedev->stopped |= DASD_STOPPED_PENDING; - spin_unlock_irqrestore(get_ccwdev_lock(basedev->cdev), flags); + spin_lock_irqsave( + get_ccwdev_lock(basedev->cdev), flags); + dasd_device_set_stop_bits(basedev, + DASD_STOPPED_PENDING); + spin_unlock_irqrestore( + get_ccwdev_lock(basedev->cdev), flags); dasd_block_set_timer(block, HZ/2); break; } @@ -1812,7 +1906,7 @@ restart: cqr->status = DASD_CQR_FILLED; cqr->retries = 255; spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); - base->stopped |= DASD_STOPPED_QUIESCE; + dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE); spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); goto restart; @@ -1820,7 +1914,7 @@ restart: /* Process finished ERP request. */ if (cqr->refers) { - __dasd_block_process_erp(block, cqr); + __dasd_process_erp(base, cqr); goto restart; } @@ -1951,7 +2045,7 @@ restart_cb: /* Process finished ERP request. */ if (cqr->refers) { spin_lock_bh(&block->queue_lock); - __dasd_block_process_erp(block, cqr); + __dasd_process_erp(block->base, cqr); spin_unlock_bh(&block->queue_lock); /* restart list_for_xx loop since dasd_process_erp * might remove multiple elements */ @@ -2410,16 +2504,16 @@ int dasd_generic_notify(struct ccw_device *cdev, int event) cqr->status = DASD_CQR_QUEUED; cqr->retries++; } - device->stopped |= DASD_STOPPED_DC_WAIT; + dasd_device_set_stop_bits(device, DASD_STOPPED_DC_WAIT); dasd_device_clear_timer(device); dasd_schedule_device_bh(device); ret = 1; break; case CIO_OPER: /* FIXME: add a sanity check. */ - device->stopped &= ~DASD_STOPPED_DC_WAIT; + dasd_device_remove_stop_bits(device, DASD_STOPPED_DC_WAIT); if (device->stopped & DASD_UNRESUMED_PM) { - device->stopped &= ~DASD_UNRESUMED_PM; + dasd_device_remove_stop_bits(device, DASD_UNRESUMED_PM); dasd_restore_device(device); ret = 1; break; @@ -2444,7 +2538,7 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev) if (IS_ERR(device)) return PTR_ERR(device); /* disallow new I/O */ - device->stopped |= DASD_STOPPED_PM; + dasd_device_set_stop_bits(device, DASD_STOPPED_PM); /* clear active requests */ INIT_LIST_HEAD(&freeze_queue); spin_lock_irq(get_ccwdev_lock(cdev)); @@ -2496,14 +2590,18 @@ int dasd_generic_restore_device(struct ccw_device *cdev) return PTR_ERR(device); /* allow new IO again */ - device->stopped &= ~DASD_STOPPED_PM; - device->stopped &= ~DASD_UNRESUMED_PM; + dasd_device_remove_stop_bits(device, + (DASD_STOPPED_PM | DASD_UNRESUMED_PM)); dasd_schedule_device_bh(device); - if (device->discipline->restore) + /* + * call discipline restore function + * if device is stopped do nothing e.g. for disconnected devices + */ + if (device->discipline->restore && !(device->stopped)) rc = device->discipline->restore(device); - if (rc) + if (rc || device->stopped) /* * if the resume failed for the DASD we put it in * an UNRESUMED stop state @@ -2553,8 +2651,7 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, cqr->startdev = device; cqr->memdev = device; cqr->expires = 10*HZ; - clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); - cqr->retries = 2; + cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; return cqr; diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 316eb1256a99..44796ba4eb9b 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -69,8 +69,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status) * processing until the started timer has expired or an related * interrupt was received. */ -static void -dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) +static void dasd_3990_erp_block_queue(struct dasd_ccw_req *erp, int expires) { struct dasd_device *device = erp->startdev; @@ -80,10 +79,13 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) "blocking request queue for %is", expires/HZ); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped |= DASD_STOPPED_PENDING; + dasd_device_set_stop_bits(device, DASD_STOPPED_PENDING); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); erp->status = DASD_CQR_FILLED; - dasd_block_set_timer(device->block, expires); + if (erp->block) + dasd_block_set_timer(erp->block, expires); + else + dasd_device_set_timer(device, expires); } /* @@ -242,9 +244,13 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) * DESCRIPTION * Setup ERP to do the ERP action 1 (see Reference manual). * Repeat the operation on a different channel path. - * If all alternate paths have been tried, the request is posted with a - * permanent error. - * Note: duplex handling is not implemented (yet). + * As deviation from the recommended recovery action, we reset the path mask + * after we have tried each path and go through all paths a second time. + * This will cover situations where only one path at a time is actually down, + * but all paths fail and recover just with the same sequence and timing as + * we try to use them (flapping links). + * If all alternate paths have been tried twice, the request is posted with + * a permanent error. * * PARAMETER * erp pointer to the current ERP @@ -253,17 +259,25 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) * erp pointer to the ERP * */ -static struct dasd_ccw_req * -dasd_3990_erp_action_1(struct dasd_ccw_req * erp) +static struct dasd_ccw_req *dasd_3990_erp_action_1_sec(struct dasd_ccw_req *erp) { + erp->function = dasd_3990_erp_action_1_sec; + dasd_3990_erp_alternate_path(erp); + return erp; +} +static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp) +{ erp->function = dasd_3990_erp_action_1; - dasd_3990_erp_alternate_path(erp); - + if (erp->status == DASD_CQR_FAILED) { + erp->status = DASD_CQR_FILLED; + erp->retries = 10; + erp->lpm = LPM_ANYPATH; + erp->function = dasd_3990_erp_action_1_sec; + } return erp; - -} /* end dasd_3990_erp_action_1 */ +} /* end dasd_3990_erp_action_1(b) */ /* * DASD_3990_ERP_ACTION_4 @@ -2294,6 +2308,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) return cqr; } + ccw = cqr->cpaddr; if (cqr->cpmode == 1) { /* make a shallow copy of the original tcw but set new tsb */ erp->cpmode = 1; @@ -2302,6 +2317,9 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) tsb = (struct tsb *) &tcw[1]; *tcw = *((struct tcw *)cqr->cpaddr); tcw->tsb = (long)tsb; + } else if (ccw->cmd_code == DASD_ECKD_CCW_PSF) { + /* PSF cannot be chained from NOOP/TIC */ + erp->cpaddr = cqr->cpaddr; } else { /* initialize request with default TIC to current ERP/CQR */ ccw = erp->cpaddr; @@ -2486,6 +2504,8 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) erp = dasd_3990_erp_action_1(erp); + } else if (erp->function == dasd_3990_erp_action_1_sec) { + erp = dasd_3990_erp_action_1_sec(erp); } else if (erp->function == dasd_3990_erp_action_5) { /* retries have not been successful */ diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index 70a008c00522..cdc6c049c353 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -755,11 +755,11 @@ static void __stop_device_on_lcu(struct dasd_device *device, { /* If pos == device then device is already locked! */ if (pos == device) { - pos->stopped |= DASD_STOPPED_SU; + dasd_device_set_stop_bits(pos, DASD_STOPPED_SU); return; } spin_lock(get_ccwdev_lock(pos->cdev)); - pos->stopped |= DASD_STOPPED_SU; + dasd_device_set_stop_bits(pos, DASD_STOPPED_SU); spin_unlock(get_ccwdev_lock(pos->cdev)); } @@ -793,26 +793,26 @@ static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu) list_for_each_entry(device, &lcu->active_devices, alias_list) { spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped &= ~DASD_STOPPED_SU; + dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } list_for_each_entry(device, &lcu->inactive_devices, alias_list) { spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped &= ~DASD_STOPPED_SU; + dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } 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); - device->stopped &= ~DASD_STOPPED_SU; + dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } list_for_each_entry(device, &pavgroup->aliaslist, alias_list) { spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped &= ~DASD_STOPPED_SU; + dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } @@ -836,7 +836,8 @@ static void summary_unit_check_handling_work(struct work_struct *work) /* 2. reset summary unit check */ spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped &= ~(DASD_STOPPED_SU | DASD_STOPPED_PENDING); + dasd_device_remove_stop_bits(device, + (DASD_STOPPED_SU | DASD_STOPPED_PENDING)); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); reset_summary_unit_check(lcu, device, suc_data->reason); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index abb2ec836ee8..39ffc84712f0 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -77,6 +77,11 @@ MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids); static struct ccw_driver dasd_eckd_driver; /* see below */ +#define INIT_CQR_OK 0 +#define INIT_CQR_UNFORMATTED 1 +#define INIT_CQR_ERROR 2 + + /* initial attempt at a probe function. this can be simplified once * the other detection code is gone */ static int @@ -749,8 +754,7 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, cqr->block = NULL; cqr->expires = 10*HZ; cqr->lpm = lpm; - clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); - cqr->retries = 2; + cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; return cqr; @@ -949,8 +953,7 @@ static int dasd_eckd_read_features(struct dasd_device *device) cqr->startdev = device; cqr->memdev = device; cqr->block = NULL; - clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); - cqr->retries = 5; + cqr->retries = 256; cqr->expires = 10 * HZ; /* Prepare for Read Subsystem Data */ @@ -1025,6 +1028,7 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device, cqr->startdev = device; cqr->memdev = device; cqr->block = NULL; + cqr->retries = 256; cqr->expires = 10*HZ; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; @@ -1068,6 +1072,7 @@ static int dasd_eckd_validate_server(struct dasd_device *device) else enable_pav = 1; rc = dasd_eckd_psf_ssc(device, enable_pav); + /* may be requested feature is not available on server, * therefore just report error and go ahead */ private = (struct dasd_eckd_private *) device->private; @@ -1265,12 +1270,29 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) cqr->block = NULL; cqr->startdev = device; cqr->memdev = device; - cqr->retries = 0; + cqr->retries = 255; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } +/* differentiate between 'no record found' and any other error */ +static int dasd_eckd_analysis_evaluation(struct dasd_ccw_req *init_cqr) +{ + char *sense; + if (init_cqr->status == DASD_CQR_DONE) + return INIT_CQR_OK; + else if (init_cqr->status == DASD_CQR_NEED_ERP || + init_cqr->status == DASD_CQR_FAILED) { + sense = dasd_get_sense(&init_cqr->irb); + if (sense && (sense[1] & SNS1_NO_REC_FOUND)) + return INIT_CQR_UNFORMATTED; + else + return INIT_CQR_ERROR; + } else + return INIT_CQR_ERROR; +} + /* * This is the callback function for the init_analysis cqr. It saves * the status of the initial analysis ccw before it frees it and kicks @@ -1278,21 +1300,20 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) * dasd_eckd_do_analysis again (if the devices has not been marked * for deletion in the meantime). */ -static void -dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data) +static void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, + void *data) { struct dasd_eckd_private *private; struct dasd_device *device; device = init_cqr->startdev; private = (struct dasd_eckd_private *) device->private; - private->init_cqr_status = init_cqr->status; + private->init_cqr_status = dasd_eckd_analysis_evaluation(init_cqr); dasd_sfree_request(init_cqr, device); dasd_kick_device(device); } -static int -dasd_eckd_start_analysis(struct dasd_block *block) +static int dasd_eckd_start_analysis(struct dasd_block *block) { struct dasd_eckd_private *private; struct dasd_ccw_req *init_cqr; @@ -1304,27 +1325,44 @@ dasd_eckd_start_analysis(struct dasd_block *block) init_cqr->callback = dasd_eckd_analysis_callback; init_cqr->callback_data = NULL; init_cqr->expires = 5*HZ; + /* first try without ERP, so we can later handle unformatted + * devices as special case + */ + clear_bit(DASD_CQR_FLAGS_USE_ERP, &init_cqr->flags); + init_cqr->retries = 0; dasd_add_request_head(init_cqr); return -EAGAIN; } -static int -dasd_eckd_end_analysis(struct dasd_block *block) +static int dasd_eckd_end_analysis(struct dasd_block *block) { struct dasd_device *device; struct dasd_eckd_private *private; struct eckd_count *count_area; unsigned int sb, blk_per_trk; int status, i; + struct dasd_ccw_req *init_cqr; device = block->base; private = (struct dasd_eckd_private *) device->private; status = private->init_cqr_status; private->init_cqr_status = -1; - if (status != DASD_CQR_DONE) { - dev_warn(&device->cdev->dev, - "The DASD is not formatted\n"); + if (status == INIT_CQR_ERROR) { + /* try again, this time with full ERP */ + init_cqr = dasd_eckd_analysis_ccw(device); + dasd_sleep_on(init_cqr); + status = dasd_eckd_analysis_evaluation(init_cqr); + dasd_sfree_request(init_cqr, device); + } + + if (status == INIT_CQR_UNFORMATTED) { + dev_warn(&device->cdev->dev, "The DASD is not formatted\n"); return -EMEDIUMTYPE; + } else if (status == INIT_CQR_ERROR) { + dev_err(&device->cdev->dev, + "Detecting the DASD disk layout failed because " + "of an I/O error\n"); + return -EIO; } private->uses_cdl = 1; @@ -1616,8 +1654,7 @@ dasd_eckd_format_device(struct dasd_device * device, } fcp->startdev = device; fcp->memdev = device; - clear_bit(DASD_CQR_FLAGS_USE_ERP, &fcp->flags); - fcp->retries = 5; /* set retry counter to enable default ERP */ + fcp->retries = 256; fcp->buildclk = get_clock(); fcp->status = DASD_CQR_FILLED; return fcp; @@ -2699,6 +2736,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp) cqr->startdev = device; cqr->memdev = device; cqr->retries = 0; + clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); cqr->expires = 10 * HZ; /* Prepare for Read Subsystem Data */ diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 8afd9fa00875..8c2ea045188e 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -595,6 +595,9 @@ int dasd_generic_restore_device(struct ccw_device *); int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int); char *dasd_get_sense(struct irb *); +void dasd_device_set_stop_bits(struct dasd_device *, int); +void dasd_device_remove_stop_bits(struct dasd_device *, int); + /* externals in dasd_devmap.c */ extern int dasd_max_devindex; extern int dasd_probeonly; diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index f756a1b0c57a..478bcdb90b6f 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -101,7 +101,7 @@ static int dasd_ioctl_quiesce(struct dasd_block *block) pr_info("%s: The DASD has been put in the quiesce " "state\n", dev_name(&base->cdev->dev)); spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); - base->stopped |= DASD_STOPPED_QUIESCE; + dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE); spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); return 0; } @@ -122,7 +122,7 @@ static int dasd_ioctl_resume(struct dasd_block *block) pr_info("%s: I/O operations have been resumed " "on the DASD\n", dev_name(&base->cdev->dev)); spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); - base->stopped &= ~DASD_STOPPED_QUIESCE; + dasd_device_remove_stop_bits(base, DASD_STOPPED_QUIESCE); spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); dasd_schedule_block_bh(block); -- cgit v1.2.3 From b8ed5dd54895647c2690575aad6f07748c2c618a Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Mon, 7 Dec 2009 12:51:52 +0100 Subject: [S390] dasd: remove strings from s390dbf Remove strings from s390 debugfeature entries that could lead to a crash when the data is read from dbf because the strings do not exist any more. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 22 ++++++++++----------- drivers/s390/block/dasd_eckd.c | 44 ++++++++++++++++++------------------------ drivers/s390/block/dasd_fba.c | 10 ++++------ drivers/s390/block/dasd_int.h | 10 ++++++++++ 4 files changed, 44 insertions(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 4f211c175b55..fdb2e7c14506 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -994,10 +994,9 @@ static void dasd_handle_killed_request(struct ccw_device *cdev, return; cqr = (struct dasd_ccw_req *) intparm; if (cqr->status != DASD_CQR_IN_IO) { - DBF_EVENT(DBF_DEBUG, - "invalid status in handle_killed_request: " - "bus_id %s, status %02x", - dev_name(&cdev->dev), cqr->status); + DBF_EVENT_DEVID(DBF_DEBUG, cdev, + "invalid status in handle_killed_request: " + "%02x", cqr->status); return; } @@ -1045,12 +1044,13 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, case -EIO: break; case -ETIMEDOUT: - DBF_EVENT(DBF_WARNING, "%s(%s): request timed out\n", - __func__, dev_name(&cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s: " + "request timed out\n", __func__); break; default: - DBF_EVENT(DBF_WARNING, "%s(%s): unknown error %ld\n", - __func__, dev_name(&cdev->dev), PTR_ERR(irb)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s: " + "unknown error %ld\n", __func__, + PTR_ERR(irb)); } dasd_handle_killed_request(cdev, intparm); return; @@ -2303,9 +2303,9 @@ int dasd_generic_probe(struct ccw_device *cdev, ret = dasd_add_sysfs_files(cdev); if (ret) { - DBF_EVENT(DBF_WARNING, - "dasd_generic_probe: could not add sysfs entries " - "for %s\n", dev_name(&cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", + "dasd_generic_probe: could not add " + "sysfs entries"); return ret; } cdev->handler = &dasd_int_handler; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 39ffc84712f0..e38a09b5feae 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -93,9 +93,9 @@ dasd_eckd_probe (struct ccw_device *cdev) ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE | CCWDEV_DO_PATHGROUP | CCWDEV_DO_MULTIPATH); if (ret) { - DBF_EVENT(DBF_WARNING, - "dasd_eckd_probe: could not set ccw-device options " - "for %s\n", dev_name(&cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", + "dasd_eckd_probe: could not set " + "ccw-device options"); return ret; } ret = dasd_generic_probe(cdev, &dasd_eckd_discipline); @@ -889,16 +889,15 @@ static int dasd_eckd_read_conf(struct dasd_device *device) rc = dasd_eckd_read_conf_lpm(device, &conf_data, &conf_len, lpm); if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ - DBF_EVENT(DBF_WARNING, + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "Read configuration data returned " - "error %d for device: %s", rc, - dev_name(&device->cdev->dev)); + "error %d", rc); return rc; } if (conf_data == NULL) { - DBF_EVENT(DBF_WARNING, "No configuration " - "data retrieved for device: %s", - dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "No configuration data " + "retrieved"); continue; /* no error */ } /* save first valid configuration data */ @@ -945,9 +944,8 @@ static int dasd_eckd_read_features(struct dasd_device *device) sizeof(struct dasd_rssd_features)), device); if (IS_ERR(cqr)) { - DBF_EVENT(DBF_WARNING, "Could not allocate initialization " - "request for device: %s", - dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not " + "allocate initialization request"); return PTR_ERR(cqr); } cqr->startdev = device; @@ -1076,10 +1074,8 @@ static int dasd_eckd_validate_server(struct dasd_device *device) /* may be requested feature is not available on server, * therefore just report error and go ahead */ private = (struct dasd_eckd_private *) device->private; - DBF_EVENT(DBF_WARNING, "PSF-SSC on storage subsystem %s.%s.%04x " - "returned rc=%d for device: %s", - private->uid.vendor, private->uid.serial, - private->uid.ssid, rc, dev_name(&device->cdev->dev)); + 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); } @@ -1137,9 +1133,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device) if (private->uid.type == UA_BASE_DEVICE) { block = dasd_alloc_block(); if (IS_ERR(block)) { - DBF_EVENT(DBF_WARNING, "could not allocate dasd " - "block structure for device: %s", - dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "could not allocate dasd " + "block structure"); rc = PTR_ERR(block); goto out_err1; } @@ -1167,9 +1163,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device) rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, &private->rdc_data, 64); if (rc) { - DBF_EVENT(DBF_WARNING, - "Read device characteristics failed, rc=%d for " - "device: %s", rc, dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, + "Read device characteristic failed, rc=%d", rc); goto out_err3; } /* find the vaild cylinder size */ @@ -3300,9 +3295,8 @@ int dasd_eckd_restore_device(struct dasd_device *device) rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, &temp_rdc_data, 64); if (rc) { - DBF_EVENT(DBF_WARNING, - "Read device characteristics failed, rc=%d for " - "device: %s", rc, dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, + "Read device characteristic failed, rc=%d", rc); goto out_err; } spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index d1a07e4c0e78..0f152444ac77 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -140,9 +140,8 @@ dasd_fba_check_characteristics(struct dasd_device *device) } block = dasd_alloc_block(); if (IS_ERR(block)) { - DBF_EVENT(DBF_WARNING, "could not allocate dasd block " - "structure for device: %s", - dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", "could not allocate " + "dasd block structure"); device->private = NULL; kfree(private); return PTR_ERR(block); @@ -154,9 +153,8 @@ dasd_fba_check_characteristics(struct dasd_device *device) rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC, &private->rdc_data, 32); if (rc) { - DBF_EVENT(DBF_WARNING, "Read device characteristics returned " - "error %d for device: %s", - rc, dev_name(&device->cdev->dev)); + DBF_EVENT_DEVID(DBF_WARNING, cdev, "Read device " + "characteristics returned error %d", rc); device->block = NULL; dasd_free_block(block); device->private = NULL; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 8c2ea045188e..e4c2143dabf6 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -108,6 +108,16 @@ do { \ d_data); \ } while(0) +#define DBF_EVENT_DEVID(d_level, d_cdev, d_str, d_data...) \ +do { \ + struct ccw_dev_id __dev_id; \ + ccw_device_get_id(d_cdev, &__dev_id); \ + debug_sprintf_event(dasd_debug_area, \ + d_level, \ + "0.%x.%04x " d_str "\n", \ + __dev_id.ssid, __dev_id.devno, d_data); \ +} while (0) + #define DBF_EXC(d_level, d_str, d_data...)\ do { \ debug_sprintf_exception(dasd_debug_area, \ -- cgit v1.2.3 From f4ac1d0255748fe0f8e128a26b1c29490cae5c08 Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Mon, 7 Dec 2009 12:51:53 +0100 Subject: [S390] dasd: let device initialization wait for LCU setup The first DASD that is set online for a specific logical control unit has to do certain setup steps on the storage server to make full use of it, for example it will enable PAV. The features and characteristics reported by the storage server will depend on this setup, so all other devices on the same LCU will need to wait for the setup to be finished. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_alias.c | 62 +++++++++++++++++++++++++++++++++++++++++ drivers/s390/block/dasd_eckd.c | 37 +++++++++++++++--------- drivers/s390/block/dasd_eckd.h | 4 ++- 3 files changed, 89 insertions(+), 14 deletions(-) (limited to 'drivers') 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: @@ -239,6 +240,67 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) return is_lcu_known; } +/* + * 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 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 */ -- cgit v1.2.3 From 468ffddf19c1417947cac931c240b0d600e4b5bf Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Mon, 7 Dec 2009 12:51:54 +0100 Subject: [S390] zcrypt: initialize ap_messages for cex3 exploitation AP messages need to be initialized, before they will be used. Values will be zeroized. This will be needed later when introducing support for the special commands. Signed-off-by: Felix Beck Signed-off-by: Ralph Wuerthner Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.h | 11 +++++++++++ drivers/s390/crypto/zcrypt_cex2a.c | 2 ++ drivers/s390/crypto/zcrypt_pcica.c | 2 ++ drivers/s390/crypto/zcrypt_pcicc.c | 2 ++ drivers/s390/crypto/zcrypt_pcixcc.c | 5 +++++ 5 files changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index a35362241805..fcf2497556dd 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -167,6 +167,17 @@ struct ap_message { .dev_type=(dt), \ .match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE, +/** + * ap_init_message() - Initialize ap_message. + * Initialize a message before using. Otherwise this might result in + * unexpected behaviour. + */ +static inline void ap_init_message(struct ap_message *ap_msg) +{ + ap_msg->psmid = 0; + ap_msg->length = 0; +} + /* * Note: don't use ap_send/ap_recv after using ap_queue_message * for the first time. Otherwise the ap message queue will get diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c index 326ea08f67c9..ad61a6a59374 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -298,6 +298,7 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev, struct completion work; int rc; + ap_init_message(&ap_msg); ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -335,6 +336,7 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev, struct completion work; int rc; + ap_init_message(&ap_msg); ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c index 17ba81b58c78..e78df3671caf 100644 --- a/drivers/s390/crypto/zcrypt_pcica.c +++ b/drivers/s390/crypto/zcrypt_pcica.c @@ -281,6 +281,7 @@ static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev, struct completion work; int rc; + ap_init_message(&ap_msg); ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -318,6 +319,7 @@ static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev, struct completion work; int rc; + ap_init_message(&ap_msg); ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c index f4b0c4795434..a23726a0735c 100644 --- a/drivers/s390/crypto/zcrypt_pcicc.c +++ b/drivers/s390/crypto/zcrypt_pcicc.c @@ -483,6 +483,7 @@ static long zcrypt_pcicc_modexpo(struct zcrypt_device *zdev, struct completion work; int rc; + ap_init_message(&ap_msg); ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -521,6 +522,7 @@ static long zcrypt_pcicc_modexpo_crt(struct zcrypt_device *zdev, struct completion work; int rc; + ap_init_message(&ap_msg); ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 5677b40e4ac0..11ca6dc99b23 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -688,6 +688,7 @@ static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev, }; int rc; + ap_init_message(&ap_msg); ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -727,6 +728,7 @@ static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev, }; int rc; + ap_init_message(&ap_msg); ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -766,6 +768,7 @@ static long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev, }; int rc; + ap_init_message(&ap_msg); ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -805,6 +808,7 @@ static long zcrypt_pcixcc_rng(struct zcrypt_device *zdev, }; int rc; + ap_init_message(&ap_msg); ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; @@ -972,6 +976,7 @@ static int zcrypt_pcixcc_rng_supported(struct ap_device *ap_dev) } __attribute__((packed)) *reply; int rc, i; + ap_init_message(&ap_msg); ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; -- cgit v1.2.3 From a6a5d73a56540b5e59dff83bc8f2b2725591346a Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Mon, 7 Dec 2009 12:51:55 +0100 Subject: [S390] zcrypt: special command support for cex3 exploitation Support for special command is implemented in the AP Bus in the NQAP function __ap_send. This is extended for a further parameter special. When set, the special bit, in GR0 will be set. Therefor the ap_message struct is extended for a further bit. Thus calling functions of __ap_send can use the special parameter in ap_message to give to __ap_send. Affected is in the first place ap_queue_message, which is called by the actual card driver. The second part of this support is that the card driver for the CEX3C needs to set this special bit, when an according CPRB is sent to the driver. Signed-off-by: Felix Beck Signed-off-by: Ralph Wuerthner Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 20 +++++++++++++++----- drivers/s390/crypto/ap_bus.h | 3 +++ drivers/s390/crypto/zcrypt_pcixcc.c | 5 +++++ 3 files changed, 23 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 1294876bf7b4..21077f4b8c50 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -282,6 +282,7 @@ static int ap_queue_enable_interruption(ap_qid_t qid, void *ind) * @psmid: The program supplied message identifier * @msg: The message text * @length: The message length + * @special: Special Bit * * Returns AP queue status structure. * Condition code 1 on NQAP can't happen because the L bit is 1. @@ -289,7 +290,8 @@ static int ap_queue_enable_interruption(ap_qid_t qid, void *ind) * because a segment boundary was reached. The NQAP is repeated. */ static inline struct ap_queue_status -__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) +__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length, + unsigned int special) { typedef struct { char _[length]; } msgblock; register unsigned long reg0 asm ("0") = qid | 0x40000000UL; @@ -299,6 +301,9 @@ __ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32); register unsigned long reg5 asm ("5") = (unsigned int) psmid; + if (special == 1) + reg0 |= 0x400000UL; + asm volatile ( "0: .long 0xb2ad0042\n" /* DQAP */ " brc 2,0b" @@ -312,13 +317,15 @@ int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) { struct ap_queue_status status; - status = __ap_send(qid, psmid, msg, length); + status = __ap_send(qid, psmid, msg, length, 0); switch (status.response_code) { case AP_RESPONSE_NORMAL: return 0; case AP_RESPONSE_Q_FULL: case AP_RESPONSE_RESET_IN_PROGRESS: return -EBUSY; + case AP_RESPONSE_REQ_FAC_NOT_INST: + return -EINVAL; default: /* Device is gone. */ return -ENODEV; } @@ -1008,7 +1015,7 @@ static int ap_probe_device_type(struct ap_device *ap_dev) } status = __ap_send(ap_dev->qid, 0x0102030405060708ULL, - msg, sizeof(msg)); + msg, sizeof(msg), 0); if (status.response_code != AP_RESPONSE_NORMAL) { rc = -ENODEV; goto out_free; @@ -1243,7 +1250,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags) /* Start the next request on the queue. */ ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list); status = __ap_send(ap_dev->qid, ap_msg->psmid, - ap_msg->message, ap_msg->length); + ap_msg->message, ap_msg->length, ap_msg->special); switch (status.response_code) { case AP_RESPONSE_NORMAL: atomic_inc(&ap_poll_requests); @@ -1261,6 +1268,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags) *flags |= 2; break; case AP_RESPONSE_MESSAGE_TOO_BIG: + case AP_RESPONSE_REQ_FAC_NOT_INST: return -EINVAL; default: return -ENODEV; @@ -1302,7 +1310,8 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms if (list_empty(&ap_dev->requestq) && ap_dev->queue_count < ap_dev->queue_depth) { status = __ap_send(ap_dev->qid, ap_msg->psmid, - ap_msg->message, ap_msg->length); + ap_msg->message, ap_msg->length, + ap_msg->special); switch (status.response_code) { case AP_RESPONSE_NORMAL: list_add_tail(&ap_msg->list, &ap_dev->pendingq); @@ -1317,6 +1326,7 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms ap_dev->requestq_count++; ap_dev->total_request_count++; return -EBUSY; + case AP_RESPONSE_REQ_FAC_NOT_INST: case AP_RESPONSE_MESSAGE_TOO_BIG: ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL)); return -EINVAL; diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index fcf2497556dd..c37466713b32 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -87,6 +87,7 @@ struct ap_queue_status { #define AP_RESPONSE_INDEX_TOO_BIG 0x11 #define AP_RESPONSE_NO_FIRST_PART 0x13 #define AP_RESPONSE_MESSAGE_TOO_BIG 0x15 +#define AP_RESPONSE_REQ_FAC_NOT_INST 0x16 /* * Known device types @@ -161,6 +162,7 @@ struct ap_message { size_t length; /* Message length. */ void *private; /* ap driver private pointer. */ + unsigned int special:1; /* Used for special commands. */ }; #define AP_DEVICE(dt) \ @@ -176,6 +178,7 @@ static inline void ap_init_message(struct ap_message *ap_msg) { ap_msg->psmid = 0; ap_msg->length = 0; + ap_msg->special = 0; } /* diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 11ca6dc99b23..addaaddaf994 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -326,6 +326,11 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev, function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len; memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code)); + if (memcmp(function_code, "US", 2) == 0) + ap_msg->special = 1; + else + ap_msg->special = 0; + /* copy data block */ if (xcRB->request_data_length && copy_from_user(req_data, xcRB->request_data_address, -- cgit v1.2.3 From ffda4f719866bb10a8edb7de3e6bf564ff7c3de4 Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Mon, 7 Dec 2009 12:51:56 +0100 Subject: [S390] zcrypt: add support for cex3 device types This patch renames the CEX2C2 and CEX2A2 types to CEX3 device types. Signed-off-by: Felix Beck Signed-off-by: Ralph Wuerthner Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.h | 4 ++-- drivers/s390/crypto/zcrypt_api.c | 9 +++++++-- drivers/s390/crypto/zcrypt_cex2a.c | 2 +- drivers/s390/crypto/zcrypt_pcixcc.c | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index c37466713b32..4785d07cd447 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -97,8 +97,8 @@ struct ap_queue_status { #define AP_DEVICE_TYPE_PCIXCC 5 #define AP_DEVICE_TYPE_CEX2A 6 #define AP_DEVICE_TYPE_CEX2C 7 -#define AP_DEVICE_TYPE_CEX2A2 8 -#define AP_DEVICE_TYPE_CEX2C2 9 +#define AP_DEVICE_TYPE_CEX3A 8 +#define AP_DEVICE_TYPE_CEX3C 9 /* * AP reset flag states diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 65b6a96afe6b..fe214770f2e7 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -1009,6 +1009,10 @@ static int zcrypt_status_read(char *resp_buff, char **start, off_t offset, zcrypt_count_type(ZCRYPT_CEX2C)); len += sprintf(resp_buff + len, "CEX2A count: %d\n", zcrypt_count_type(ZCRYPT_CEX2A)); + len += sprintf(resp_buff + len, "CEX3C count: %d\n", + zcrypt_count_type(ZCRYPT_CEX3C)); + len += sprintf(resp_buff + len, "CEX3A count: %d\n", + zcrypt_count_type(ZCRYPT_CEX3A)); len += sprintf(resp_buff + len, "requestq count: %d\n", zcrypt_requestq_count()); len += sprintf(resp_buff + len, "pendingq count: %d\n", @@ -1017,7 +1021,7 @@ static int zcrypt_status_read(char *resp_buff, char **start, off_t offset, atomic_read(&zcrypt_open_count)); zcrypt_status_mask(workarea); len += sprinthx("Online devices: 1=PCICA 2=PCICC 3=PCIXCC(MCL2) " - "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A", + "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A 7=CEX3C 8=CEX3A", resp_buff+len, workarea, AP_DEVICES); zcrypt_qdepth_mask(workarea); len += sprinthx("Waiting work element counts", @@ -1095,8 +1099,9 @@ static int zcrypt_status_write(struct file *file, const char __user *buffer, * '0' for no device, '1' for PCICA, '2' for PCICC, * '3' for PCIXCC_MCL2, '4' for PCIXCC_MCL3, * '5' for CEX2C and '6' for CEX2A' + * '7' for CEX3C and '8' for CEX3A */ - if (*ptr >= '0' && *ptr <= '6') + if (*ptr >= '0' && *ptr <= '8') j++; else if (*ptr == 'd' || *ptr == 'D') zcrypt_disable_card(j++); diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c index ad61a6a59374..d25b6dfed2e0 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -49,7 +49,7 @@ static struct ap_device_id zcrypt_cex2a_ids[] = { { AP_DEVICE(AP_DEVICE_TYPE_CEX2A) }, - { AP_DEVICE(AP_DEVICE_TYPE_CEX2A2) }, + { AP_DEVICE(AP_DEVICE_TYPE_CEX3A) }, { /* end of list */ }, }; diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index addaaddaf994..cdaa401f20cd 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -72,7 +72,7 @@ struct response_type { static struct ap_device_id zcrypt_pcixcc_ids[] = { { AP_DEVICE(AP_DEVICE_TYPE_PCIXCC) }, { AP_DEVICE(AP_DEVICE_TYPE_CEX2C) }, - { AP_DEVICE(AP_DEVICE_TYPE_CEX2C2) }, + { AP_DEVICE(AP_DEVICE_TYPE_CEX3C) }, { /* end of list */ }, }; -- cgit v1.2.3 From 8e89b6be76927751af5608490158100974b339e6 Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Mon, 7 Dec 2009 12:51:57 +0100 Subject: [S390] zcrypt: use definitions for cex3 New definitions are added for CEX3 device types. They will be set in the according probe functions. CEX3 device types will be handled in the same modules as CEX2 device types. In the first step they are the same as CEX2 types, but they can be adjusted for further characteristics of CEX3 easily. Signed-off-by: Felix Beck Signed-off-by: Ralph Wuerthner Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/zcrypt_api.h | 2 ++ drivers/s390/crypto/zcrypt_cex2a.c | 71 ++++++++++++++++++++++++------------- drivers/s390/crypto/zcrypt_pcixcc.c | 22 ++++++++++-- 3 files changed, 67 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h index 1d1ec74dadb2..8e7ffbf2466c 100644 --- a/drivers/s390/crypto/zcrypt_api.h +++ b/drivers/s390/crypto/zcrypt_api.h @@ -71,6 +71,8 @@ struct ica_z90_status { #define ZCRYPT_PCIXCC_MCL3 4 #define ZCRYPT_CEX2C 5 #define ZCRYPT_CEX2A 6 +#define ZCRYPT_CEX3C 7 +#define ZCRYPT_CEX3A 8 /** * Large random numbers are pulled in 4096 byte chunks from the crypto cards diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c index d25b6dfed2e0..a90b76079fa9 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -39,13 +39,20 @@ #define CEX2A_MIN_MOD_SIZE 1 /* 8 bits */ #define CEX2A_MAX_MOD_SIZE 256 /* 2048 bits */ +#define CEX3A_MIN_MOD_SIZE CEX2A_MIN_MOD_SIZE +#define CEX3A_MAX_MOD_SIZE CEX2A_MAX_MOD_SIZE #define CEX2A_SPEED_RATING 970 +#define CEX3A_SPEED_RATING 1100 #define CEX2A_MAX_MESSAGE_SIZE 0x390 /* sizeof(struct type50_crb2_msg) */ #define CEX2A_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */ +#define CEX3A_MAX_MESSAGE_SIZE CEX2A_MAX_MESSAGE_SIZE +#define CEX3A_MAX_RESPONSE_SIZE CEX2A_MAX_RESPONSE_SIZE + #define CEX2A_CLEANUP_TIME (15*HZ) +#define CEX3A_CLEANUP_TIME CEX2A_CLEANUP_TIME static struct ap_device_id zcrypt_cex2a_ids[] = { { AP_DEVICE(AP_DEVICE_TYPE_CEX2A) }, @@ -375,31 +382,45 @@ static struct zcrypt_ops zcrypt_cex2a_ops = { */ static int zcrypt_cex2a_probe(struct ap_device *ap_dev) { - struct zcrypt_device *zdev; - int rc; - - zdev = zcrypt_device_alloc(CEX2A_MAX_RESPONSE_SIZE); - if (!zdev) - return -ENOMEM; - zdev->ap_dev = ap_dev; - zdev->ops = &zcrypt_cex2a_ops; - zdev->online = 1; - zdev->user_space_type = ZCRYPT_CEX2A; - zdev->type_string = "CEX2A"; - zdev->min_mod_size = CEX2A_MIN_MOD_SIZE; - zdev->max_mod_size = CEX2A_MAX_MOD_SIZE; - zdev->short_crt = 1; - zdev->speed_rating = CEX2A_SPEED_RATING; - ap_dev->reply = &zdev->reply; - ap_dev->private = zdev; - rc = zcrypt_device_register(zdev); - if (rc) - goto out_free; - return 0; - -out_free: - ap_dev->private = NULL; - zcrypt_device_free(zdev); + struct zcrypt_device *zdev = NULL; + int rc = 0; + + switch (ap_dev->device_type) { + case AP_DEVICE_TYPE_CEX2A: + zdev = zcrypt_device_alloc(CEX2A_MAX_RESPONSE_SIZE); + if (!zdev) + return -ENOMEM; + zdev->user_space_type = ZCRYPT_CEX2A; + zdev->type_string = "CEX2A"; + zdev->min_mod_size = CEX2A_MIN_MOD_SIZE; + zdev->max_mod_size = CEX2A_MAX_MOD_SIZE; + zdev->short_crt = 1; + zdev->speed_rating = CEX2A_SPEED_RATING; + break; + case AP_DEVICE_TYPE_CEX3A: + zdev = zcrypt_device_alloc(CEX3A_MAX_RESPONSE_SIZE); + if (!zdev) + return -ENOMEM; + zdev->user_space_type = ZCRYPT_CEX3A; + zdev->type_string = "CEX3A"; + zdev->min_mod_size = CEX3A_MIN_MOD_SIZE; + zdev->max_mod_size = CEX3A_MAX_MOD_SIZE; + zdev->short_crt = 1; + zdev->speed_rating = CEX3A_SPEED_RATING; + break; + } + if (zdev != NULL) { + zdev->ap_dev = ap_dev; + zdev->ops = &zcrypt_cex2a_ops; + zdev->online = 1; + ap_dev->reply = &zdev->reply; + ap_dev->private = zdev; + rc = zcrypt_device_register(zdev); + } + if (rc) { + ap_dev->private = NULL; + zcrypt_device_free(zdev); + } return rc; } diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index cdaa401f20cd..d5f770a71d6c 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -43,10 +43,13 @@ #define PCIXCC_MIN_MOD_SIZE 16 /* 128 bits */ #define PCIXCC_MIN_MOD_SIZE_OLD 64 /* 512 bits */ #define PCIXCC_MAX_MOD_SIZE 256 /* 2048 bits */ +#define CEX3C_MIN_MOD_SIZE PCIXCC_MIN_MOD_SIZE +#define CEX3C_MAX_MOD_SIZE PCIXCC_MAX_MOD_SIZE #define PCIXCC_MCL2_SPEED_RATING 7870 /* FIXME: needs finetuning */ #define PCIXCC_MCL3_SPEED_RATING 7870 #define CEX2C_SPEED_RATING 8540 +#define CEX3C_SPEED_RATING 10000 /* FIXME: needs finetuning */ #define PCIXCC_MAX_ICA_MESSAGE_SIZE 0x77c /* max size type6 v2 crt message */ #define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply */ @@ -1026,14 +1029,15 @@ out_free: static int zcrypt_pcixcc_probe(struct ap_device *ap_dev) { struct zcrypt_device *zdev; - int rc; + int rc = 0; zdev = zcrypt_device_alloc(PCIXCC_MAX_RESPONSE_SIZE); if (!zdev) return -ENOMEM; zdev->ap_dev = ap_dev; zdev->online = 1; - if (ap_dev->device_type == AP_DEVICE_TYPE_PCIXCC) { + switch (ap_dev->device_type) { + case AP_DEVICE_TYPE_PCIXCC: rc = zcrypt_pcixcc_mcl(ap_dev); if (rc < 0) { zcrypt_device_free(zdev); @@ -1051,13 +1055,25 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev) zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE; zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE; } - } else { + break; + case AP_DEVICE_TYPE_CEX2C: zdev->user_space_type = ZCRYPT_CEX2C; zdev->type_string = "CEX2C"; zdev->speed_rating = CEX2C_SPEED_RATING; zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE; zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE; + break; + case AP_DEVICE_TYPE_CEX3C: + zdev->user_space_type = ZCRYPT_CEX3C; + zdev->type_string = "CEX3C"; + zdev->speed_rating = CEX3C_SPEED_RATING; + zdev->min_mod_size = CEX3C_MIN_MOD_SIZE; + zdev->max_mod_size = CEX3C_MAX_MOD_SIZE; + break; + default: + goto out_free; } + rc = zcrypt_pcixcc_rng_supported(ap_dev); if (rc < 0) { zcrypt_device_free(zdev); -- cgit v1.2.3 From 18278dffc18d3b22dedd6285b2bb491329d888f5 Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Mon, 7 Dec 2009 12:51:58 +0100 Subject: [S390] zcrypt: adjust speed rating between cex2 and pcixcc Cards with lower speed rating are prefered. Thus change adjust the speed rating of cex2c adapters to be prefered before pcixcc, although they should not appear in the same machine anyway. Signed-off-by: Felix Beck Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/zcrypt_pcixcc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index d5f770a71d6c..65cf17b7c5d0 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -46,9 +46,9 @@ #define CEX3C_MIN_MOD_SIZE PCIXCC_MIN_MOD_SIZE #define CEX3C_MAX_MOD_SIZE PCIXCC_MAX_MOD_SIZE -#define PCIXCC_MCL2_SPEED_RATING 7870 /* FIXME: needs finetuning */ +#define PCIXCC_MCL2_SPEED_RATING 7870 #define PCIXCC_MCL3_SPEED_RATING 7870 -#define CEX2C_SPEED_RATING 8540 +#define CEX2C_SPEED_RATING 7000 #define CEX3C_SPEED_RATING 10000 /* FIXME: needs finetuning */ #define PCIXCC_MAX_ICA_MESSAGE_SIZE 0x77c /* max size type6 v2 crt message */ -- cgit v1.2.3 From 7a6f5cd07ee5648f5768637ff082133384af415c Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Mon, 7 Dec 2009 12:51:59 +0100 Subject: [S390] zcrypt: adjust speed rating of cex3 adapters Cex3 needs a lower speed rating. Otherwise cex2 adapters will be prefered. Signed-off-by: Felix Beck Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/zcrypt_cex2a.c | 2 +- drivers/s390/crypto/zcrypt_pcixcc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c index a90b76079fa9..c6fb0aa89507 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -43,7 +43,7 @@ #define CEX3A_MAX_MOD_SIZE CEX2A_MAX_MOD_SIZE #define CEX2A_SPEED_RATING 970 -#define CEX3A_SPEED_RATING 1100 +#define CEX3A_SPEED_RATING 900 /* Fixme: Needs finetuning */ #define CEX2A_MAX_MESSAGE_SIZE 0x390 /* sizeof(struct type50_crb2_msg) */ #define CEX2A_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */ diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 65cf17b7c5d0..79c120578e61 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -49,7 +49,7 @@ #define PCIXCC_MCL2_SPEED_RATING 7870 #define PCIXCC_MCL3_SPEED_RATING 7870 #define CEX2C_SPEED_RATING 7000 -#define CEX3C_SPEED_RATING 10000 /* FIXME: needs finetuning */ +#define CEX3C_SPEED_RATING 6500 /* FIXME: needs finetuning */ #define PCIXCC_MAX_ICA_MESSAGE_SIZE 0x77c /* max size type6 v2 crt message */ #define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply */ -- cgit v1.2.3 From 93521314cb3132f40a4bf6d76b4bbcdbc9e59dfb Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Mon, 7 Dec 2009 12:52:00 +0100 Subject: [S390] zcrypt: Do not simultaneously schedule hrtimer Protect the hrtimer ap_poll_timer from being scheduled at the same time from several processes. Signed-off-by: Felix Beck Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 21077f4b8c50..20836eff88c5 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -102,6 +102,7 @@ static atomic_t ap_poll_requests = ATOMIC_INIT(0); static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait); static struct task_struct *ap_poll_kthread = NULL; static DEFINE_MUTEX(ap_poll_thread_mutex); +static DEFINE_SPINLOCK(ap_poll_timer_lock); static void *ap_interrupt_indicator; static struct hrtimer ap_poll_timer; /* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds. @@ -1170,16 +1171,19 @@ ap_config_timeout(unsigned long ptr) static inline void ap_schedule_poll_timer(void) { ktime_t hr_time; + + spin_lock_bh(&ap_poll_timer_lock); if (ap_using_interrupts() || ap_suspend_flag) - return; + goto out; if (hrtimer_is_queued(&ap_poll_timer)) - return; + goto out; if (ktime_to_ns(hrtimer_expires_remaining(&ap_poll_timer)) <= 0) { hr_time = ktime_set(0, poll_timeout); hrtimer_forward_now(&ap_poll_timer, hr_time); hrtimer_restart(&ap_poll_timer); } - return; +out: + spin_unlock_bh(&ap_poll_timer_lock); } /** @@ -1668,6 +1672,7 @@ int __init ap_module_init(void) */ if (MACHINE_IS_VM) poll_timeout = 1500000; + spin_lock_init(&ap_poll_timer_lock); hrtimer_init(&ap_poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ap_poll_timer.function = ap_poll_timeout; -- cgit v1.2.3 From c1b283d55d1806680e3ad1cfbef581afecbea5cb Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 7 Dec 2009 12:52:01 +0100 Subject: [S390] tape: remove f_pos changes from read / write function. The change to f_pos in the read / write method has no effect as the value is controlled by the VFS layer, e.g. for vfs_read: loff_t pos = file_pos_read(file); ret = vfs_read(file, buf, count, &pos); file_pos_write(file, pos); Signed-off-by: Martin Schwidefsky --- drivers/s390/char/tape_char.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c index 31566c55adfe..b47e20d3d1da 100644 --- a/drivers/s390/char/tape_char.c +++ b/drivers/s390/char/tape_char.c @@ -170,7 +170,6 @@ tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos) if (rc == 0) { rc = block_size - request->rescnt; DBF_EVENT(6, "TCHAR:rbytes: %x\n", rc); - filp->f_pos += rc; /* Copy data from idal buffer to user space. */ if (idal_buffer_to_user(device->char_data.idal_buf, data, rc) != 0) @@ -238,7 +237,6 @@ tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t break; DBF_EVENT(6, "TCHAR:wbytes: %lx\n", block_size - request->rescnt); - filp->f_pos += block_size - request->rescnt; written += block_size - request->rescnt; if (request->rescnt != 0) break; -- cgit v1.2.3 From 1b52fff059d660d4bf83d97c389dd80f1e6aad9a Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 7 Dec 2009 12:52:02 +0100 Subject: [S390] tape: fix tape remove function Fix a bug introduced with git commit dff59b64af94dc58: - if (cdev->dev.driver_data != NULL) { + if (!dev_get_drvdata(&cdev->dev)) { These two are not equivalent. Signed-off-by: Martin Schwidefsky --- drivers/s390/char/tape_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 27503a778fcb..6311018e6543 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -665,7 +665,7 @@ tape_generic_remove(struct ccw_device *cdev) tape_cleanup_device(device); } - if (!dev_get_drvdata(&cdev->dev)) { + if (dev_get_drvdata(&cdev->dev)) { sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group); dev_set_drvdata(&cdev->dev, tape_put_device(dev_get_drvdata(&cdev->dev))); } -- cgit v1.2.3 From 8fd138c366a8a302d9da8a428c6e927c8bff7d35 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 7 Dec 2009 12:52:03 +0100 Subject: [S390] tape: cleanup reference counting Rename tape_get_device to tape_find_device and tape_get_device_reference to tape_get_device. The old names didn't make too much sense. Follow the get_device()/put_device() semantic and convert tape_put_device to a void function. Signed-off-by: Martin Schwidefsky --- drivers/s390/char/tape.h | 6 ++--- drivers/s390/char/tape_34xx.c | 8 +++--- drivers/s390/char/tape_3590.c | 2 +- drivers/s390/char/tape_block.c | 15 ++++++----- drivers/s390/char/tape_char.c | 7 ++--- drivers/s390/char/tape_core.c | 61 ++++++++++++++++++++---------------------- drivers/s390/char/tape_proc.c | 2 +- 7 files changed, 50 insertions(+), 51 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index a26333774701..fbe41e0377b8 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -292,9 +292,9 @@ extern int tape_generic_pm_suspend(struct ccw_device *); extern int tape_generic_probe(struct ccw_device *); extern void tape_generic_remove(struct ccw_device *); -extern struct tape_device *tape_get_device(int devindex); -extern struct tape_device *tape_get_device_reference(struct tape_device *); -extern struct tape_device *tape_put_device(struct tape_device *); +extern struct tape_device *tape_find_device(int devindex); +extern struct tape_device *tape_get_device(struct tape_device *); +extern void tape_put_device(struct tape_device *); /* Externals from tape_char.c */ extern int tapechar_init(void); diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index 2fe45ff77b75..3657fe103c27 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -113,16 +113,16 @@ tape_34xx_work_handler(struct work_struct *work) { struct tape_34xx_work *p = container_of(work, struct tape_34xx_work, work); + struct tape_device *device = p->device; switch(p->op) { case TO_MSEN: - tape_34xx_medium_sense(p->device); + tape_34xx_medium_sense(device); break; default: DBF_EVENT(3, "T34XX: internal error: unknown work\n"); } - - p->device = tape_put_device(p->device); + tape_put_device(device); kfree(p); } @@ -136,7 +136,7 @@ tape_34xx_schedule_work(struct tape_device *device, enum tape_op op) INIT_WORK(&p->work, tape_34xx_work_handler); - p->device = tape_get_device_reference(device); + p->device = tape_get_device(device); p->op = op; schedule_work(&p->work); diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index e4cc3aae9162..0c72aadb8391 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -608,7 +608,7 @@ tape_3590_schedule_work(struct tape_device *device, enum tape_op op) INIT_WORK(&p->work, tape_3590_work_handler); - p->device = tape_get_device_reference(device); + p->device = tape_get_device(device); p->op = op; schedule_work(&p->work); diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index 0c0705b91c28..c97c6aac8ed5 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -239,7 +239,7 @@ tapeblock_setup_device(struct tape_device * device) disk->major = tapeblock_major; disk->first_minor = device->first_minor; disk->fops = &tapeblock_fops; - disk->private_data = tape_get_device_reference(device); + disk->private_data = tape_get_device(device); disk->queue = blkdat->request_queue; set_capacity(disk, 0); sprintf(disk->disk_name, "btibm%d", @@ -247,11 +247,11 @@ tapeblock_setup_device(struct tape_device * device) blkdat->disk = disk; blkdat->medium_changed = 1; - blkdat->request_queue->queuedata = tape_get_device_reference(device); + blkdat->request_queue->queuedata = tape_get_device(device); add_disk(disk); - tape_get_device_reference(device); + tape_get_device(device); INIT_WORK(&blkdat->requeue_task, tapeblock_requeue); return 0; @@ -274,13 +274,14 @@ tapeblock_cleanup_device(struct tape_device *device) } del_gendisk(device->blk_data.disk); - device->blk_data.disk->private_data = - tape_put_device(device->blk_data.disk->private_data); + device->blk_data.disk->private_data = NULL; + tape_put_device(device); put_disk(device->blk_data.disk); device->blk_data.disk = NULL; cleanup_queue: - device->blk_data.request_queue->queuedata = tape_put_device(device); + device->blk_data.request_queue->queuedata = NULL; + tape_put_device(device); blk_cleanup_queue(device->blk_data.request_queue); device->blk_data.request_queue = NULL; @@ -363,7 +364,7 @@ tapeblock_open(struct block_device *bdev, fmode_t mode) struct tape_device * device; int rc; - device = tape_get_device_reference(disk->private_data); + device = tape_get_device(disk->private_data); if (device->required_tapemarks) { DBF_EVENT(2, "TBLOCK: missing tapemarks\n"); diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c index b47e20d3d1da..d44e6981eeac 100644 --- a/drivers/s390/char/tape_char.c +++ b/drivers/s390/char/tape_char.c @@ -286,9 +286,9 @@ tapechar_open (struct inode *inode, struct file *filp) lock_kernel(); minor = iminor(filp->f_path.dentry->d_inode); - device = tape_get_device(minor / TAPE_MINORS_PER_DEV); + device = tape_find_device(minor / TAPE_MINORS_PER_DEV); if (IS_ERR(device)) { - DBF_EVENT(3, "TCHAR:open: tape_get_device() failed\n"); + DBF_EVENT(3, "TCHAR:open: tape_find_device() failed\n"); rc = PTR_ERR(device); goto out; } @@ -340,7 +340,8 @@ tapechar_release(struct inode *inode, struct file *filp) device->char_data.idal_buf = NULL; } tape_release(device); - filp->private_data = tape_put_device(device); + filp->private_data = NULL; + tape_put_device(device); return 0; } diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 6311018e6543..a7b7e72c691b 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -511,11 +511,12 @@ tape_alloc_device(void) * increment the reference count. */ struct tape_device * -tape_get_device_reference(struct tape_device *device) +tape_get_device(struct tape_device *device) { - DBF_EVENT(4, "tape_get_device_reference(%p) = %i\n", device, - atomic_inc_return(&device->ref_count)); + int count; + count = atomic_inc_return(&device->ref_count); + DBF_EVENT(4, "tape_get_device(%p) = %i\n", device, count); return device; } @@ -525,32 +526,25 @@ tape_get_device_reference(struct tape_device *device) * The function returns a NULL pointer to be used by the caller * for clearing reference pointers. */ -struct tape_device * +void tape_put_device(struct tape_device *device) { - int remain; + int count; - remain = atomic_dec_return(&device->ref_count); - if (remain > 0) { - DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, remain); - } else { - if (remain < 0) { - DBF_EVENT(4, "put device without reference\n"); - } else { - DBF_EVENT(4, "tape_free_device(%p)\n", device); - kfree(device->modeset_byte); - kfree(device); - } + count = atomic_dec_return(&device->ref_count); + DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, count); + BUG_ON(count < 0); + if (count == 0) { + kfree(device->modeset_byte); + kfree(device); } - - return NULL; } /* * Find tape device by a device index. */ struct tape_device * -tape_get_device(int devindex) +tape_find_device(int devindex) { struct tape_device *device, *tmp; @@ -558,7 +552,7 @@ tape_get_device(int devindex) read_lock(&tape_device_lock); list_for_each_entry(tmp, &tape_device_list, node) { if (tmp->first_minor / TAPE_MINORS_PER_DEV == devindex) { - device = tape_get_device_reference(tmp); + device = tape_get_device(tmp); break; } } @@ -607,7 +601,8 @@ __tape_discard_requests(struct tape_device *device) list_del(&request->list); /* Decrease ref_count for removed request. */ - request->device = tape_put_device(device); + request->device = NULL; + tape_put_device(device); request->rc = -EIO; if (request->callback != NULL) request->callback(request, request->callback_data); @@ -665,9 +660,11 @@ tape_generic_remove(struct ccw_device *cdev) tape_cleanup_device(device); } - if (dev_get_drvdata(&cdev->dev)) { + device = dev_get_drvdata(&cdev->dev); + if (device) { sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group); - dev_set_drvdata(&cdev->dev, tape_put_device(dev_get_drvdata(&cdev->dev))); + dev_set_drvdata(&cdev->dev, NULL); + tape_put_device(device); } } @@ -722,9 +719,8 @@ tape_free_request (struct tape_request * request) { DBF_LH(6, "Free request %p\n", request); - if (request->device != NULL) { - request->device = tape_put_device(request->device); - } + if (request->device) + tape_put_device(request->device); kfree(request->cpdata); kfree(request->cpaddr); kfree(request); @@ -839,7 +835,8 @@ static void tape_long_busy_timeout(unsigned long data) BUG_ON(request->status != TAPE_REQUEST_LONG_BUSY); DBF_LH(6, "%08x: Long busy timeout.\n", device->cdev_id); __tape_start_next_request(device); - device->lb_timeout.data = (unsigned long) tape_put_device(device); + device->lb_timeout.data = 0UL; + tape_put_device(device); spin_unlock_irq(get_ccwdev_lock(device->cdev)); } @@ -919,7 +916,7 @@ __tape_start_request(struct tape_device *device, struct tape_request *request) } /* Increase use count of device for the added request. */ - request->device = tape_get_device_reference(device); + request->device = tape_get_device(device); if (list_empty(&device->req_queue)) { /* No other requests are on the queue. Start this one. */ @@ -1118,8 +1115,8 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) if (req->status == TAPE_REQUEST_LONG_BUSY) { DBF_EVENT(3, "(%08x): del timer\n", device->cdev_id); if (del_timer(&device->lb_timeout)) { - device->lb_timeout.data = (unsigned long) - tape_put_device(device); + device->lb_timeout.data = 0UL; + tape_put_device(device); __tape_start_next_request(device); } return; @@ -1174,7 +1171,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) break; case TAPE_IO_LONG_BUSY: device->lb_timeout.data = - (unsigned long)tape_get_device_reference(device); + (unsigned long) tape_get_device(device); device->lb_timeout.expires = jiffies + LONG_BUSY_TIMEOUT * HZ; DBF_EVENT(3, "(%08x): add timer\n", device->cdev_id); @@ -1327,7 +1324,7 @@ EXPORT_SYMBOL(tape_generic_online); EXPORT_SYMBOL(tape_generic_offline); EXPORT_SYMBOL(tape_generic_pm_suspend); EXPORT_SYMBOL(tape_put_device); -EXPORT_SYMBOL(tape_get_device_reference); +EXPORT_SYMBOL(tape_get_device); EXPORT_SYMBOL(tape_state_verbose); EXPORT_SYMBOL(tape_op_verbose); EXPORT_SYMBOL(tape_state_set); diff --git a/drivers/s390/char/tape_proc.c b/drivers/s390/char/tape_proc.c index 202f42132939..ebd820ccfb24 100644 --- a/drivers/s390/char/tape_proc.c +++ b/drivers/s390/char/tape_proc.c @@ -45,7 +45,7 @@ static int tape_proc_show(struct seq_file *m, void *v) seq_printf(m, "TapeNo\tBusID CuType/Model\t" "DevType/Model\tBlkSize\tState\tOp\tMedState\n"); } - device = tape_get_device(n); + device = tape_find_device(n); if (IS_ERR(device)) return 0; spin_lock_irq(get_ccwdev_lock(device->cdev)); -- cgit v1.2.3 From 369a46325d07061e0f66e16a1f59ef4f526a6464 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 7 Dec 2009 12:52:04 +0100 Subject: [S390] tape: remove BKL from tape driver Replace BLK with a per device mutex. Signed-off-by: Martin Schwidefsky --- drivers/s390/char/tape.h | 3 +++ drivers/s390/char/tape_block.c | 2 +- drivers/s390/char/tape_char.c | 45 ++++++++++++++++++++++-------------------- drivers/s390/char/tape_core.c | 1 + 4 files changed, 29 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index fbe41e0377b8..7a242f073632 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -212,6 +212,9 @@ struct tape_device { struct tape_class_device * nt; struct tape_class_device * rt; + /* Device mutex to serialize tape commands. */ + struct mutex mutex; + /* Device discipline information. */ struct tape_discipline * discipline; void * discdata; diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index c97c6aac8ed5..4799cc2f73c3 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -54,7 +54,7 @@ static const struct block_device_operations tapeblock_fops = { .owner = THIS_MODULE, .open = tapeblock_open, .release = tapeblock_release, - .locked_ioctl = tapeblock_ioctl, + .ioctl = tapeblock_ioctl, .media_changed = tapeblock_medium_changed, .revalidate_disk = tapeblock_revalidate_disk, }; diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c index d44e6981eeac..23d773a0d113 100644 --- a/drivers/s390/char/tape_char.c +++ b/drivers/s390/char/tape_char.c @@ -33,8 +33,7 @@ static ssize_t tapechar_read(struct file *, char __user *, size_t, loff_t *); static ssize_t tapechar_write(struct file *, const char __user *, size_t, loff_t *); static int tapechar_open(struct inode *,struct file *); static int tapechar_release(struct inode *,struct file *); -static int tapechar_ioctl(struct inode *, struct file *, unsigned int, - unsigned long); +static long tapechar_ioctl(struct file *, unsigned int, unsigned long); static long tapechar_compat_ioctl(struct file *, unsigned int, unsigned long); @@ -43,7 +42,7 @@ static const struct file_operations tape_fops = .owner = THIS_MODULE, .read = tapechar_read, .write = tapechar_write, - .ioctl = tapechar_ioctl, + .unlocked_ioctl = tapechar_ioctl, .compat_ioctl = tapechar_compat_ioctl, .open = tapechar_open, .release = tapechar_release, @@ -284,26 +283,20 @@ tapechar_open (struct inode *inode, struct file *filp) if (imajor(filp->f_path.dentry->d_inode) != tapechar_major) return -ENODEV; - lock_kernel(); minor = iminor(filp->f_path.dentry->d_inode); device = tape_find_device(minor / TAPE_MINORS_PER_DEV); if (IS_ERR(device)) { DBF_EVENT(3, "TCHAR:open: tape_find_device() failed\n"); - rc = PTR_ERR(device); - goto out; + return PTR_ERR(device); } - rc = tape_open(device); if (rc == 0) { filp->private_data = device; - rc = nonseekable_open(inode, filp); - } - else + nonseekable_open(inode, filp); + } else tape_put_device(device); -out: - unlock_kernel(); return rc; } @@ -350,16 +343,11 @@ tapechar_release(struct inode *inode, struct file *filp) * Tape device io controls. */ static int -tapechar_ioctl(struct inode *inp, struct file *filp, - unsigned int no, unsigned long data) +__tapechar_ioctl(struct tape_device *device, + unsigned int no, unsigned long data) { - struct tape_device *device; int rc; - DBF_EVENT(6, "TCHAR:ioct\n"); - - device = (struct tape_device *) filp->private_data; - if (no == MTIOCTOP) { struct mtop op; @@ -451,6 +439,21 @@ tapechar_ioctl(struct inode *inp, struct file *filp, return device->discipline->ioctl_fn(device, no, data); } +static long +tapechar_ioctl(struct file *filp, unsigned int no, unsigned long data) +{ + struct tape_device *device; + long rc; + + DBF_EVENT(6, "TCHAR:ioct\n"); + + device = (struct tape_device *) filp->private_data; + mutex_lock(&device->mutex); + rc = __tapechar_ioctl(device, no, data); + mutex_unlock(&device->mutex); + return rc; +} + static long tapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data) { @@ -458,9 +461,9 @@ tapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data) int rval = -ENOIOCTLCMD; if (device->discipline->ioctl_fn) { - lock_kernel(); + mutex_lock(&device->mutex); rval = device->discipline->ioctl_fn(device, no, data); - unlock_kernel(); + mutex_unlock(&device->mutex); if (rval == -EINVAL) rval = -ENOIOCTLCMD; } diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index a7b7e72c691b..f5d6802dc5da 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -492,6 +492,7 @@ tape_alloc_device(void) kfree(device); return ERR_PTR(-ENOMEM); } + mutex_init(&device->mutex); INIT_LIST_HEAD(&device->req_queue); INIT_LIST_HEAD(&device->node); init_waitqueue_head(&device->state_change_wq); -- cgit v1.2.3 From cb9d71604a6e3d6aa93bb663747a62fc520da483 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 7 Dec 2009 12:52:12 +0100 Subject: [S390] sclp: improve servicability setting Set dump indicator on read-scp-info command to get meaningful dumps. Signed-off-by: Martin Schwidefsky --- drivers/s390/char/sclp_cmd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 5cc11c636d38..28b5afc129c3 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -84,6 +84,7 @@ static void __init sclp_read_info_early(void) do { memset(sccb, 0, sizeof(*sccb)); sccb->header.length = sizeof(*sccb); + sccb->header.function_code = 0x80; sccb->header.control_mask[2] = 0x80; rc = sclp_cmd_sync_early(commands[i], sccb); } while (rc == -EBUSY); -- cgit v1.2.3 From 9d4bfd418503e90789bbc6685ddde994e613214d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 7 Dec 2009 12:52:13 +0100 Subject: [S390] tty3270: move keyboard compat ioctls All keyboard ioctls are compatible, so we can simply move the compat handling into the vt and tty3270 drivers. Signed-off-by: Arnd Bergmann Signed-off-by: Martin Schwidefsky --- drivers/s390/char/tty3270.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 38385677c653..911822db614d 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -1731,6 +1732,22 @@ tty3270_ioctl(struct tty_struct *tty, struct file *file, return kbd_ioctl(tp->kbd, file, cmd, arg); } +#ifdef CONFIG_COMPAT +static long +tty3270_compat_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct tty3270 *tp; + + tp = tty->driver_data; + if (!tp) + return -ENODEV; + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + return kbd_ioctl(tp->kbd, file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + static const struct tty_operations tty3270_ops = { .open = tty3270_open, .close = tty3270_close, @@ -1745,6 +1762,9 @@ static const struct tty_operations tty3270_ops = { .hangup = tty3270_hangup, .wait_until_sent = tty3270_wait_until_sent, .ioctl = tty3270_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = tty3270_compat_ioctl, +#endif .set_termios = tty3270_set_termios }; -- cgit v1.2.3 From 1963403aae0fc5a4e20cf2e51e4f0148fdf938b4 Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Mon, 7 Dec 2009 12:52:17 +0100 Subject: [S390] monreader: remove lock_kernel() from open() function The lock_kernel() calls in the open() function are unnecessary because misc_register() is called last in module_init() and concurrent open() calls are handled by other means. Signed-off-by: Gerald Schaefer Signed-off-by: Martin Schwidefsky --- drivers/s390/char/monreader.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 66e21dd23154..60473f86e1f9 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -283,7 +282,6 @@ static int mon_open(struct inode *inode, struct file *filp) /* * only one user allowed */ - lock_kernel(); rc = -EBUSY; if (test_and_set_bit(MON_IN_USE, &mon_in_use)) goto out; @@ -321,7 +319,6 @@ static int mon_open(struct inode *inode, struct file *filp) } filp->private_data = monpriv; dev_set_drvdata(monreader_device, monpriv); - unlock_kernel(); return nonseekable_open(inode, filp); out_path: @@ -331,7 +328,6 @@ out_priv: out_use: clear_bit(MON_IN_USE, &mon_in_use); out: - unlock_kernel(); return rc; } @@ -607,6 +603,10 @@ static int __init mon_init(void) } dcss_mkname(mon_dcss_name, &user_data_connect[8]); + /* + * misc_register() has to be the last action in module_init(), because + * file operations will be available right after this. + */ rc = misc_register(&mon_dev); if (rc < 0 ) goto out; -- cgit v1.2.3 From 801f97b7da9dff4aace7111bfe0d073caf5febd2 Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Mon, 7 Dec 2009 12:52:18 +0100 Subject: [S390] monwriter: remove lock_kernel() from open() function The lock_kernel() calls in the open() function are unnecessary because misc_register() is called last in module_init() and concurrent open() calls are handled by other means. Signed-off-by: Gerald Schaefer Signed-off-by: Martin Schwidefsky --- drivers/s390/char/monwriter.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index 66fb8eba93f4..6532ed8b4afa 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -185,13 +184,11 @@ static int monwrite_open(struct inode *inode, struct file *filp) monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL); if (!monpriv) return -ENOMEM; - lock_kernel(); INIT_LIST_HEAD(&monpriv->list); monpriv->hdr_to_read = sizeof(monpriv->hdr); mutex_init(&monpriv->thread_mutex); filp->private_data = monpriv; list_add_tail(&monpriv->priv_list, &mon_priv_list); - unlock_kernel(); return nonseekable_open(inode, filp); } @@ -364,6 +361,10 @@ static int __init mon_init(void) goto out_driver; } + /* + * misc_register() has to be the last action in module_init(), because + * file operations will be available right after this. + */ rc = misc_register(&mon_dev); if (rc) goto out_device; -- cgit v1.2.3 From feb5c5a450a5e56f3be09a18b541996c86bd94bc Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Mon, 7 Dec 2009 12:52:19 +0100 Subject: [S390] vmwatchdog: remove lock_kernel() from open() function The lock_kernel() calls in the open() function are unnecessary because misc_register() is called last in module_init() and concurrent open() calls are handled by other means. Signed-off-by: Gerald Schaefer Signed-off-by: Martin Schwidefsky --- drivers/s390/char/vmwatchdog.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index f2bc287b69e4..c974058e48d2 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -49,6 +48,8 @@ static unsigned int vmwdt_interval = 60; static unsigned long vmwdt_is_open; static int vmwdt_expect_close; +static DEFINE_MUTEX(vmwdt_mutex); + #define VMWDT_OPEN 0 /* devnode is open or suspend in progress */ #define VMWDT_RUNNING 1 /* The watchdog is armed */ @@ -133,15 +134,11 @@ static int __init vmwdt_probe(void) static int vmwdt_open(struct inode *i, struct file *f) { int ret; - lock_kernel(); - if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) { - unlock_kernel(); + if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) return -EBUSY; - } ret = vmwdt_keepalive(); if (ret) clear_bit(VMWDT_OPEN, &vmwdt_is_open); - unlock_kernel(); return ret ? ret : nonseekable_open(i, f); } @@ -160,8 +157,7 @@ static struct watchdog_info vmwdt_info = { .identity = "z/VM Watchdog Timer", }; -static int vmwdt_ioctl(struct inode *i, struct file *f, - unsigned int cmd, unsigned long arg) +static int __vmwdt_ioctl(unsigned int cmd, unsigned long arg) { switch (cmd) { case WDIOC_GETSUPPORT: @@ -205,10 +201,19 @@ static int vmwdt_ioctl(struct inode *i, struct file *f, case WDIOC_KEEPALIVE: return vmwdt_keepalive(); } - return -EINVAL; } +static long vmwdt_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + int rc; + + mutex_lock(&vmwdt_mutex); + rc = __vmwdt_ioctl(cmd, arg); + mutex_unlock(&vmwdt_mutex); + return (long) rc; +} + static ssize_t vmwdt_write(struct file *f, const char __user *buf, size_t count, loff_t *ppos) { @@ -288,7 +293,7 @@ static struct notifier_block vmwdt_power_notifier = { static const struct file_operations vmwdt_fops = { .open = &vmwdt_open, .release = &vmwdt_close, - .ioctl = &vmwdt_ioctl, + .unlocked_ioctl = &vmwdt_ioctl, .write = &vmwdt_write, .owner = THIS_MODULE, }; @@ -309,6 +314,10 @@ static int __init vmwdt_init(void) ret = register_pm_notifier(&vmwdt_power_notifier); if (ret) return ret; + /* + * misc_register() has to be the last action in module_init(), because + * file operations will be available right after this. + */ ret = misc_register(&vmwdt_dev); if (ret) { unregister_pm_notifier(&vmwdt_power_notifier); -- cgit v1.2.3 From 5cbb5f579e5b6f2e686880ffafa64e49323320c1 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 7 Dec 2009 12:52:20 +0100 Subject: [S390] 3270: remove BKL Replace BLK use in fs3270 with mutex. Signed-off-by: Martin Schwidefsky --- drivers/s390/char/fs3270.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 097d3846a828..d449063c30fe 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -38,6 +38,8 @@ struct fs3270 { size_t rdbuf_size; /* size of data returned by RDBUF */ }; +static DEFINE_MUTEX(fs3270_mutex); + static void fs3270_wake_up(struct raw3270_request *rq, void *data) { @@ -328,7 +330,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (!fp) return -ENODEV; rc = 0; - lock_kernel(); + mutex_lock(&fs3270_mutex); switch (cmd) { case TUBICMD: fp->read_command = arg; @@ -354,7 +356,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) rc = -EFAULT; break; } - unlock_kernel(); + mutex_unlock(&fs3270_mutex); return rc; } @@ -437,7 +439,7 @@ fs3270_open(struct inode *inode, struct file *filp) minor = tty->index + RAW3270_FIRSTMINOR; tty_kref_put(tty); } - lock_kernel(); + mutex_lock(&fs3270_mutex); /* Check if some other program is already using fullscreen mode. */ fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor); if (!IS_ERR(fp)) { @@ -478,7 +480,7 @@ fs3270_open(struct inode *inode, struct file *filp) } filp->private_data = fp; out: - unlock_kernel(); + mutex_unlock(&fs3270_mutex); return rc; } -- cgit v1.2.3 From b8ccc51f6fc6cde3644b4a2159fb57fe6c38d465 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 7 Dec 2009 12:52:21 +0100 Subject: [S390] zcrypt: remove BKL Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/zcrypt_api.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index fe214770f2e7..0d4d18bdd45c 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -299,9 +299,7 @@ static ssize_t zcrypt_write(struct file *filp, const char __user *buf, */ static int zcrypt_open(struct inode *inode, struct file *filp) { - lock_kernel(); atomic_inc(&zcrypt_open_count); - unlock_kernel(); return 0; } -- cgit v1.2.3 From 3c492d2033f4c67e967e85ff46b8ebac0c5c4036 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 7 Dec 2009 12:52:22 +0100 Subject: [S390] vmur: remove BKL Signed-off-by: Martin Schwidefsky --- drivers/s390/char/vmur.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 77571b68539a..cc56fc708bae 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -695,7 +695,6 @@ static int ur_open(struct inode *inode, struct file *file) if (accmode == O_RDWR) return -EACCES; - lock_kernel(); /* * We treat the minor number as the devno of the ur device * to find in the driver tree. @@ -749,7 +748,6 @@ static int ur_open(struct inode *inode, struct file *file) goto fail_urfile_free; urf->file_reclen = rc; file->private_data = urf; - unlock_kernel(); return 0; fail_urfile_free: @@ -761,7 +759,6 @@ fail_unlock: fail_put: urdev_put(urd); out: - unlock_kernel(); return rc; } -- cgit v1.2.3 From 3b47f9d5ec646bc50148c664ce5895ff9837699f Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 7 Dec 2009 12:52:23 +0100 Subject: [S390] vmlogrdr: remove BKL Signed-off-by: Martin Schwidefsky --- drivers/s390/char/vmlogrdr.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index d1a142fa3eb4..899aa795bf38 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -312,11 +312,9 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp) return -ENOSYS; /* Besure this device hasn't already been opened */ - lock_kernel(); spin_lock_bh(&logptr->priv_lock); if (logptr->dev_in_use) { spin_unlock_bh(&logptr->priv_lock); - unlock_kernel(); return -EBUSY; } logptr->dev_in_use = 1; @@ -360,9 +358,8 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp) || (logptr->iucv_path_severed)); if (logptr->iucv_path_severed) goto out_record; - ret = nonseekable_open(inode, filp); - unlock_kernel(); - return ret; + nonseekable_open(inode, filp); + return 0; out_record: if (logptr->autorecording) @@ -372,7 +369,6 @@ out_path: logptr->path = NULL; out_dev: logptr->dev_in_use = 0; - unlock_kernel(); return -EIO; } -- cgit v1.2.3 From 5e24eb08e8f4df1f6ef913b6ff94c16b9c51d85a Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 7 Dec 2009 12:52:24 +0100 Subject: [S390] dasd: remove BKL from extended error reporting code Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eer.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index d96039eae59b..1f3e967aaba8 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -536,7 +536,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp) eerb = kzalloc(sizeof(struct eerbuffer), GFP_KERNEL); if (!eerb) return -ENOMEM; - lock_kernel(); eerb->buffer_page_count = eer_pages; if (eerb->buffer_page_count < 1 || eerb->buffer_page_count > INT_MAX / PAGE_SIZE) { @@ -544,7 +543,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp) DBF_EVENT(DBF_WARNING, "can't open device since module " "parameter eer_pages is smaller than 1 or" " bigger than %d", (int)(INT_MAX / PAGE_SIZE)); - unlock_kernel(); return -EINVAL; } eerb->buffersize = eerb->buffer_page_count * PAGE_SIZE; @@ -552,14 +550,12 @@ static int dasd_eer_open(struct inode *inp, struct file *filp) GFP_KERNEL); if (!eerb->buffer) { kfree(eerb); - unlock_kernel(); return -ENOMEM; } if (dasd_eer_allocate_buffer_pages(eerb->buffer, eerb->buffer_page_count)) { kfree(eerb->buffer); kfree(eerb); - unlock_kernel(); return -ENOMEM; } filp->private_data = eerb; @@ -567,7 +563,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp) list_add(&eerb->list, &bufferlist); spin_unlock_irqrestore(&bufferlock, flags); - unlock_kernel(); return nonseekable_open(inp,filp); } -- cgit v1.2.3 From 42d61b9b415686d81eaa022b846737548876e51d Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 7 Dec 2009 12:52:25 +0100 Subject: [S390] 3215/3270 console: remove wrong comment When converting these two drivers from bootmem to slab allocations I forgot to remove two comments which state that this wouldn't be possible. So remove them now since this caused confusion already. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/char/con3215.c | 1 - drivers/s390/char/con3270.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 21639d6c996f..9d61683b5633 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -857,7 +857,6 @@ static struct console con3215 = { /* * 3215 console initialization code called from console_init(). - * NOTE: This is called before kmalloc is available. */ static int __init con3215_init(void) { diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index bb838bdf829d..6bca81aea396 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -572,7 +572,6 @@ static struct console con3270 = { /* * 3270 console initialization code called from console_init(). - * NOTE: This is called before kmalloc is available. */ static int __init con3270_init(void) -- cgit v1.2.3