aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/device.c
diff options
context:
space:
mode:
authorMichael Ernst <mernst@de.ibm.com>2009-09-11 04:28:21 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2009-09-11 04:29:38 -0400
commit217ee6c64a9589bc5ad4d1c88136fc359d17930b (patch)
tree5a01eaa37365d6efbef12aa1f2c5e04db0e42244 /drivers/s390/cio/device.c
parentbe7a2ddce66991c05a1c6ad19790289591e53547 (diff)
[S390] cio: failing set online/offline processing.
When unit checks trigger sensing the device state is set to W4SENSE until sense completion; then the device state is set back to ONLINE. If a unit check occurs while set online or set offline requests are processed then it might happen that the device's temporary W4SENSE state causes these functions to terminate, leaving the device in an inconsistent state when the state is set back to ONLINE later on so that the device cannot be set online or offline any longer. To solve this, set online/offline and related rollback or error routines are processed only if the device is in a final or DISCONNECTED state. Signed-off-by: Michael Ernst <mernst@de.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.c100
1 files changed, 65 insertions, 35 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index a7a340b1d71..a50cfa51aa3 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -380,30 +380,34 @@ int ccw_device_set_offline(struct ccw_device *cdev)
380 } 380 }
381 cdev->online = 0; 381 cdev->online = 0;
382 spin_lock_irq(cdev->ccwlock); 382 spin_lock_irq(cdev->ccwlock);
383 ret = ccw_device_offline(cdev); 383 /* Wait until a final state or DISCONNECTED is reached */
384 if (ret == -ENODEV) { 384 while (!dev_fsm_final_state(cdev) &&
385 if (cdev->private->state != DEV_STATE_NOT_OPER) { 385 cdev->private->state != DEV_STATE_DISCONNECTED) {
386 cdev->private->state = DEV_STATE_OFFLINE;
387 dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
388 }
389 spin_unlock_irq(cdev->ccwlock); 386 spin_unlock_irq(cdev->ccwlock);
390 /* Give up reference from ccw_device_set_online(). */ 387 wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
391 put_device(&cdev->dev); 388 cdev->private->state == DEV_STATE_DISCONNECTED));
392 return ret; 389 spin_lock_irq(cdev->ccwlock);
393 } 390 }
391 ret = ccw_device_offline(cdev);
392 if (ret)
393 goto error;
394 spin_unlock_irq(cdev->ccwlock); 394 spin_unlock_irq(cdev->ccwlock);
395 if (ret == 0) { 395 wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
396 wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); 396 cdev->private->state == DEV_STATE_DISCONNECTED));
397 /* Give up reference from ccw_device_set_online(). */ 397 /* Give up reference from ccw_device_set_online(). */
398 put_device(&cdev->dev); 398 put_device(&cdev->dev);
399 } else { 399 return 0;
400 CIO_MSG_EVENT(0, "ccw_device_offline returned %d, " 400
401 "device 0.%x.%04x\n", 401error:
402 ret, cdev->private->dev_id.ssid, 402 CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device 0.%x.%04x\n",
403 cdev->private->dev_id.devno); 403 ret, cdev->private->dev_id.ssid,
404 cdev->online = 1; 404 cdev->private->dev_id.devno);
405 } 405 cdev->private->state = DEV_STATE_OFFLINE;
406 return ret; 406 dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
407 spin_unlock_irq(cdev->ccwlock);
408 /* Give up reference from ccw_device_set_online(). */
409 put_device(&cdev->dev);
410 return -ENODEV;
407} 411}
408 412
409/** 413/**
@@ -421,6 +425,7 @@ int ccw_device_set_offline(struct ccw_device *cdev)
421int ccw_device_set_online(struct ccw_device *cdev) 425int ccw_device_set_online(struct ccw_device *cdev)
422{ 426{
423 int ret; 427 int ret;
428 int ret2;
424 429
425 if (!cdev) 430 if (!cdev)
426 return -ENODEV; 431 return -ENODEV;
@@ -444,28 +449,53 @@ int ccw_device_set_online(struct ccw_device *cdev)
444 put_device(&cdev->dev); 449 put_device(&cdev->dev);
445 return ret; 450 return ret;
446 } 451 }
447 if (cdev->private->state != DEV_STATE_ONLINE) { 452 spin_lock_irq(cdev->ccwlock);
453 /* Check if online processing was successful */
454 if ((cdev->private->state != DEV_STATE_ONLINE) &&
455 (cdev->private->state != DEV_STATE_W4SENSE)) {
456 spin_unlock_irq(cdev->ccwlock);
448 /* Give up online reference since onlining failed. */ 457 /* Give up online reference since onlining failed. */
449 put_device(&cdev->dev); 458 put_device(&cdev->dev);
450 return -ENODEV; 459 return -ENODEV;
451 } 460 }
452 if (!cdev->drv->set_online || cdev->drv->set_online(cdev) == 0) { 461 spin_unlock_irq(cdev->ccwlock);
453 cdev->online = 1; 462 if (cdev->drv->set_online)
454 return 0; 463 ret = cdev->drv->set_online(cdev);
455 } 464 if (ret)
465 goto rollback;
466 cdev->online = 1;
467 return 0;
468
469rollback:
456 spin_lock_irq(cdev->ccwlock); 470 spin_lock_irq(cdev->ccwlock);
457 ret = ccw_device_offline(cdev); 471 /* Wait until a final state or DISCONNECTED is reached */
472 while (!dev_fsm_final_state(cdev) &&
473 cdev->private->state != DEV_STATE_DISCONNECTED) {
474 spin_unlock_irq(cdev->ccwlock);
475 wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
476 cdev->private->state == DEV_STATE_DISCONNECTED));
477 spin_lock_irq(cdev->ccwlock);
478 }
479 ret2 = ccw_device_offline(cdev);
480 if (ret2)
481 goto error;
458 spin_unlock_irq(cdev->ccwlock); 482 spin_unlock_irq(cdev->ccwlock);
459 if (ret == 0) 483 wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
460 wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); 484 cdev->private->state == DEV_STATE_DISCONNECTED));
461 else
462 CIO_MSG_EVENT(0, "ccw_device_offline returned %d, "
463 "device 0.%x.%04x\n",
464 ret, cdev->private->dev_id.ssid,
465 cdev->private->dev_id.devno);
466 /* Give up online reference since onlining failed. */ 485 /* Give up online reference since onlining failed. */
467 put_device(&cdev->dev); 486 put_device(&cdev->dev);
468 return (ret == 0) ? -ENODEV : ret; 487 return ret;
488
489error:
490 CIO_MSG_EVENT(0, "rollback ccw_device_offline returned %d, "
491 "device 0.%x.%04x\n",
492 ret2, cdev->private->dev_id.ssid,
493 cdev->private->dev_id.devno);
494 cdev->private->state = DEV_STATE_OFFLINE;
495 spin_unlock_irq(cdev->ccwlock);
496 /* Give up online reference since onlining failed. */
497 put_device(&cdev->dev);
498 return ret;
469} 499}
470 500
471static int online_store_handle_offline(struct ccw_device *cdev) 501static int online_store_handle_offline(struct ccw_device *cdev)