Commit 6aa2677a authored by Sebastian Ott's avatar Sebastian Ott Committed by Martin Schwidefsky

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: default avatarPeter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 69db3b5e
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
*/ */
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/completion.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/device.h> #include <linux/device.h>
...@@ -159,6 +160,9 @@ static void eadm_subchannel_irq(struct subchannel *sch) ...@@ -159,6 +160,9 @@ static void eadm_subchannel_irq(struct subchannel *sch)
} }
scm_irq_handler((struct aob *)(unsigned long)scsw->aob, error); scm_irq_handler((struct aob *)(unsigned long)scsw->aob, error);
private->state = EADM_IDLE; private->state = EADM_IDLE;
if (private->completion)
complete(private->completion);
} }
static struct subchannel *eadm_get_idle_sch(void) static struct subchannel *eadm_get_idle_sch(void)
...@@ -255,13 +259,32 @@ static int eadm_subchannel_probe(struct subchannel *sch) ...@@ -255,13 +259,32 @@ static int eadm_subchannel_probe(struct subchannel *sch)
static void eadm_quiesce(struct subchannel *sch) static void eadm_quiesce(struct subchannel *sch)
{ {
struct eadm_private *private = get_eadm_private(sch);
DECLARE_COMPLETION_ONSTACK(completion);
int ret; int ret;
do {
spin_lock_irq(sch->lock); spin_lock_irq(sch->lock);
ret = cio_disable_subchannel(sch); if (private->state != EADM_BUSY)
goto disable;
if (eadm_subchannel_clear(sch))
goto disable;
private->completion = &completion;
spin_unlock_irq(sch->lock); 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 {
ret = cio_disable_subchannel(sch);
} while (ret == -EBUSY); } while (ret == -EBUSY);
spin_unlock_irq(sch->lock);
} }
static int eadm_subchannel_remove(struct subchannel *sch) static int eadm_subchannel_remove(struct subchannel *sch)
......
#ifndef EADM_SCH_H #ifndef EADM_SCH_H
#define EADM_SCH_H #define EADM_SCH_H
#include <linux/completion.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/list.h> #include <linux/list.h>
...@@ -9,9 +10,10 @@ ...@@ -9,9 +10,10 @@
struct eadm_private { struct eadm_private {
union orb orb; union orb orb;
enum {EADM_IDLE, EADM_BUSY, EADM_NOT_OPER} state; enum {EADM_IDLE, EADM_BUSY, EADM_NOT_OPER} state;
struct completion *completion;
struct subchannel *sch;
struct timer_list timer; struct timer_list timer;
struct list_head head; struct list_head head;
struct subchannel *sch;
} __aligned(8); } __aligned(8);
#define get_eadm_private(n) ((struct eadm_private *)dev_get_drvdata(&n->dev)) #define get_eadm_private(n) ((struct eadm_private *)dev_get_drvdata(&n->dev))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment