diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2013-09-25 06:29:05 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-10-24 11:16:58 -0400 |
commit | 6aa2677a57fdd8964ccd44766cdc06cdd9c5db5b (patch) | |
tree | 6da34df3a028e8ce177a4b41f09ad3f6158e4d50 /drivers/s390 | |
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')
-rw-r--r-- | drivers/s390/cio/eadm_sch.c | 27 | ||||
-rw-r--r-- | drivers/s390/cio/eadm_sch.h | 4 |
2 files changed, 28 insertions, 3 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 @@ | |||
6 | */ | 6 | */ |
7 | 7 | ||
8 | #include <linux/kernel_stat.h> | 8 | #include <linux/kernel_stat.h> |
9 | #include <linux/completion.h> | ||
9 | #include <linux/workqueue.h> | 10 | #include <linux/workqueue.h> |
10 | #include <linux/spinlock.h> | 11 | #include <linux/spinlock.h> |
11 | #include <linux/device.h> | 12 | #include <linux/device.h> |
@@ -159,6 +160,9 @@ static void eadm_subchannel_irq(struct subchannel *sch) | |||
159 | } | 160 | } |
160 | scm_irq_handler((struct aob *)(unsigned long)scsw->aob, error); | 161 | scm_irq_handler((struct aob *)(unsigned long)scsw->aob, error); |
161 | private->state = EADM_IDLE; | 162 | private->state = EADM_IDLE; |
163 | |||
164 | if (private->completion) | ||
165 | complete(private->completion); | ||
162 | } | 166 | } |
163 | 167 | ||
164 | static struct subchannel *eadm_get_idle_sch(void) | 168 | static struct subchannel *eadm_get_idle_sch(void) |
@@ -255,13 +259,32 @@ out: | |||
255 | 259 | ||
256 | static void eadm_quiesce(struct subchannel *sch) | 260 | static void eadm_quiesce(struct subchannel *sch) |
257 | { | 261 | { |
262 | struct eadm_private *private = get_eadm_private(sch); | ||
263 | DECLARE_COMPLETION_ONSTACK(completion); | ||
258 | int ret; | 264 | int ret; |
259 | 265 | ||
266 | spin_lock_irq(sch->lock); | ||
267 | if (private->state != EADM_BUSY) | ||
268 | goto disable; | ||
269 | |||
270 | if (eadm_subchannel_clear(sch)) | ||
271 | goto disable; | ||
272 | |||
273 | private->completion = &completion; | ||
274 | spin_unlock_irq(sch->lock); | ||
275 | |||
276 | wait_for_completion_io(&completion); | ||
277 | |||
278 | spin_lock_irq(sch->lock); | ||
279 | private->completion = NULL; | ||
280 | |||
281 | disable: | ||
282 | eadm_subchannel_set_timeout(sch, 0); | ||
260 | do { | 283 | do { |
261 | spin_lock_irq(sch->lock); | ||
262 | ret = cio_disable_subchannel(sch); | 284 | ret = cio_disable_subchannel(sch); |
263 | spin_unlock_irq(sch->lock); | ||
264 | } while (ret == -EBUSY); | 285 | } while (ret == -EBUSY); |
286 | |||
287 | spin_unlock_irq(sch->lock); | ||
265 | } | 288 | } |
266 | 289 | ||
267 | static int eadm_subchannel_remove(struct subchannel *sch) | 290 | static int eadm_subchannel_remove(struct subchannel *sch) |
diff --git a/drivers/s390/cio/eadm_sch.h b/drivers/s390/cio/eadm_sch.h index 2779be093982..9664e4653f98 100644 --- a/drivers/s390/cio/eadm_sch.h +++ b/drivers/s390/cio/eadm_sch.h | |||
@@ -1,6 +1,7 @@ | |||
1 | #ifndef EADM_SCH_H | 1 | #ifndef EADM_SCH_H |
2 | #define EADM_SCH_H | 2 | #define EADM_SCH_H |
3 | 3 | ||
4 | #include <linux/completion.h> | ||
4 | #include <linux/device.h> | 5 | #include <linux/device.h> |
5 | #include <linux/timer.h> | 6 | #include <linux/timer.h> |
6 | #include <linux/list.h> | 7 | #include <linux/list.h> |
@@ -9,9 +10,10 @@ | |||
9 | struct eadm_private { | 10 | struct eadm_private { |
10 | union orb orb; | 11 | union orb orb; |
11 | enum {EADM_IDLE, EADM_BUSY, EADM_NOT_OPER} state; | 12 | enum {EADM_IDLE, EADM_BUSY, EADM_NOT_OPER} state; |
13 | struct completion *completion; | ||
14 | struct subchannel *sch; | ||
12 | struct timer_list timer; | 15 | struct timer_list timer; |
13 | struct list_head head; | 16 | struct list_head head; |
14 | struct subchannel *sch; | ||
15 | } __aligned(8); | 17 | } __aligned(8); |
16 | 18 | ||
17 | #define get_eadm_private(n) ((struct eadm_private *)dev_get_drvdata(&n->dev)) | 19 | #define get_eadm_private(n) ((struct eadm_private *)dev_get_drvdata(&n->dev)) |