diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2009-12-07 06:51:41 -0500 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2009-12-07 06:51:33 -0500 |
commit | d40f7b75a23d1e59b6ec9d6701231fd4c6992ac6 (patch) | |
tree | 74dc2947aea7aadc5a2ec81e70c5e41111f5d3ae /drivers/s390/cio/device.c | |
parent | de1b04388f63cbddf91d9f6c50c29be7232881ca (diff) |
[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 <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 29 |
1 files changed, 20 insertions, 9 deletions
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) | |||
314 | } | 314 | } |
315 | } | 315 | } |
316 | 316 | ||
317 | static void io_subchannel_quiesce(struct subchannel *); | ||
318 | |||
317 | /** | 319 | /** |
318 | * ccw_device_set_offline() - disable a ccw device for I/O | 320 | * ccw_device_set_offline() - disable a ccw device for I/O |
319 | * @cdev: target ccw device | 321 | * @cdev: target ccw device |
@@ -327,7 +329,8 @@ static void ccw_device_unregister(struct ccw_device *cdev) | |||
327 | */ | 329 | */ |
328 | int ccw_device_set_offline(struct ccw_device *cdev) | 330 | int ccw_device_set_offline(struct ccw_device *cdev) |
329 | { | 331 | { |
330 | int ret; | 332 | struct subchannel *sch; |
333 | int ret, state; | ||
331 | 334 | ||
332 | if (!cdev) | 335 | if (!cdev) |
333 | return -ENODEV; | 336 | return -ENODEV; |
@@ -341,6 +344,7 @@ int ccw_device_set_offline(struct ccw_device *cdev) | |||
341 | } | 344 | } |
342 | cdev->online = 0; | 345 | cdev->online = 0; |
343 | spin_lock_irq(cdev->ccwlock); | 346 | spin_lock_irq(cdev->ccwlock); |
347 | sch = to_subchannel(cdev->dev.parent); | ||
344 | /* Wait until a final state or DISCONNECTED is reached */ | 348 | /* Wait until a final state or DISCONNECTED is reached */ |
345 | while (!dev_fsm_final_state(cdev) && | 349 | while (!dev_fsm_final_state(cdev) && |
346 | cdev->private->state != DEV_STATE_DISCONNECTED) { | 350 | cdev->private->state != DEV_STATE_DISCONNECTED) { |
@@ -349,9 +353,21 @@ int ccw_device_set_offline(struct ccw_device *cdev) | |||
349 | cdev->private->state == DEV_STATE_DISCONNECTED)); | 353 | cdev->private->state == DEV_STATE_DISCONNECTED)); |
350 | spin_lock_irq(cdev->ccwlock); | 354 | spin_lock_irq(cdev->ccwlock); |
351 | } | 355 | } |
352 | ret = ccw_device_offline(cdev); | 356 | do { |
353 | if (ret) | 357 | ret = ccw_device_offline(cdev); |
354 | goto error; | 358 | if (!ret) |
359 | break; | ||
360 | CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device " | ||
361 | "0.%x.%04x\n", ret, cdev->private->dev_id.ssid, | ||
362 | cdev->private->dev_id.devno); | ||
363 | if (ret != -EBUSY) | ||
364 | goto error; | ||
365 | state = cdev->private->state; | ||
366 | spin_unlock_irq(cdev->ccwlock); | ||
367 | io_subchannel_quiesce(sch); | ||
368 | spin_lock_irq(cdev->ccwlock); | ||
369 | cdev->private->state = state; | ||
370 | } while (ret == -EBUSY); | ||
355 | spin_unlock_irq(cdev->ccwlock); | 371 | spin_unlock_irq(cdev->ccwlock); |
356 | wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) || | 372 | wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) || |
357 | cdev->private->state == DEV_STATE_DISCONNECTED)); | 373 | cdev->private->state == DEV_STATE_DISCONNECTED)); |
@@ -368,9 +384,6 @@ int ccw_device_set_offline(struct ccw_device *cdev) | |||
368 | return 0; | 384 | return 0; |
369 | 385 | ||
370 | error: | 386 | error: |
371 | CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device 0.%x.%04x\n", | ||
372 | ret, cdev->private->dev_id.ssid, | ||
373 | cdev->private->dev_id.devno); | ||
374 | cdev->private->state = DEV_STATE_OFFLINE; | 387 | cdev->private->state = DEV_STATE_OFFLINE; |
375 | dev_fsm_event(cdev, DEV_EVENT_NOTOPER); | 388 | dev_fsm_event(cdev, DEV_EVENT_NOTOPER); |
376 | spin_unlock_irq(cdev->ccwlock); | 389 | spin_unlock_irq(cdev->ccwlock); |
@@ -1059,8 +1072,6 @@ out_schedule: | |||
1059 | return 0; | 1072 | return 0; |
1060 | } | 1073 | } |
1061 | 1074 | ||
1062 | static void io_subchannel_quiesce(struct subchannel *); | ||
1063 | |||
1064 | static int | 1075 | static int |
1065 | io_subchannel_remove (struct subchannel *sch) | 1076 | io_subchannel_remove (struct subchannel *sch) |
1066 | { | 1077 | { |