diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2013-09-25 12:29:05 +0200 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-10-24 17:16:58 +0200 |
commit | 6aa2677a57fdd8964ccd44766cdc06cdd9c5db5b (patch) | |
tree | 6da34df3a028e8ce177a4b41f09ad3f6158e4d50 /drivers/s390/cio/eadm_sch.c | |
parent | 69db3b5e850d0cac543d8523cbde17a4729f8990 (diff) |
s390/eadm_sch: improve quiesce handling
When quiescing an eadm subchannel make sure that outstanding IO is
cleared and potential timeout handlers are canceled.
Reviewed-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/eadm_sch.c')
-rw-r--r-- | drivers/s390/cio/eadm_sch.c | 27 |
1 files changed, 25 insertions, 2 deletions
diff --git a/drivers/s390/cio/eadm_sch.c b/drivers/s390/cio/eadm_sch.c index 6b54d8a05cd4..aca7bfc113aa 100644 --- a/drivers/s390/cio/eadm_sch.c +++ b/drivers/s390/cio/eadm_sch.c @@ -6,6 +6,7 @@ */ #include <linux/kernel_stat.h> +#include <linux/completion.h> #include <linux/workqueue.h> #include <linux/spinlock.h> #include <linux/device.h> @@ -159,6 +160,9 @@ static void eadm_subchannel_irq(struct subchannel *sch) } scm_irq_handler((struct aob *)(unsigned long)scsw->aob, error); private->state = EADM_IDLE; + + if (private->completion) + complete(private->completion); } static struct subchannel *eadm_get_idle_sch(void) @@ -255,13 +259,32 @@ out: static void eadm_quiesce(struct subchannel *sch) { + struct eadm_private *private = get_eadm_private(sch); + DECLARE_COMPLETION_ONSTACK(completion); int ret; + spin_lock_irq(sch->lock); + if (private->state != EADM_BUSY) + goto disable; + + if (eadm_subchannel_clear(sch)) + goto disable; + + private->completion = &completion; + spin_unlock_irq(sch->lock); + + wait_for_completion_io(&completion); + + spin_lock_irq(sch->lock); + private->completion = NULL; + +disable: + eadm_subchannel_set_timeout(sch, 0); do { - spin_lock_irq(sch->lock); ret = cio_disable_subchannel(sch); - spin_unlock_irq(sch->lock); } while (ret == -EBUSY); + + spin_unlock_irq(sch->lock); } static int eadm_subchannel_remove(struct subchannel *sch) |