diff options
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 100 |
1 files changed, 65 insertions, 35 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index a7a340b1d713..a50cfa51aa3c 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", | 401 | error: |
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) | |||
421 | int ccw_device_set_online(struct ccw_device *cdev) | 425 | int 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 | |||
469 | rollback: | ||
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 | |||
489 | error: | ||
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 | ||
471 | static int online_store_handle_offline(struct ccw_device *cdev) | 501 | static int online_store_handle_offline(struct ccw_device *cdev) |