Browse Source

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>
Sebastian Ott 12 years ago
parent
commit
6aa2677a57
2 changed files with 28 additions and 3 deletions
  1. 25 2
      drivers/s390/cio/eadm_sch.c
  2. 3 1
      drivers/s390/cio/eadm_sch.h

+ 25 - 2
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)

+ 3 - 1
drivers/s390/cio/eadm_sch.h

@@ -1,6 +1,7 @@
 #ifndef EADM_SCH_H
 #define EADM_SCH_H
 
+#include <linux/completion.h>
 #include <linux/device.h>
 #include <linux/timer.h>
 #include <linux/list.h>
@@ -9,9 +10,10 @@
 struct eadm_private {
 	union orb orb;
 	enum {EADM_IDLE, EADM_BUSY, EADM_NOT_OPER} state;
+	struct completion *completion;
+	struct subchannel *sch;
 	struct timer_list timer;
 	struct list_head head;
-	struct subchannel *sch;
 } __aligned(8);
 
 #define get_eadm_private(n) ((struct eadm_private *)dev_get_drvdata(&n->dev))