diff options
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 43 |
1 files changed, 27 insertions, 16 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index c4d2f667a2f6..35441fa16be1 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -310,8 +310,6 @@ static void ccw_device_remove_orphan_cb(struct work_struct *work) | |||
310 | put_device(&cdev->dev); | 310 | put_device(&cdev->dev); |
311 | } | 311 | } |
312 | 312 | ||
313 | static void ccw_device_call_sch_unregister(struct work_struct *work); | ||
314 | |||
315 | static void | 313 | static void |
316 | ccw_device_remove_disconnected(struct ccw_device *cdev) | 314 | ccw_device_remove_disconnected(struct ccw_device *cdev) |
317 | { | 315 | { |
@@ -335,11 +333,10 @@ ccw_device_remove_disconnected(struct ccw_device *cdev) | |||
335 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 333 | spin_unlock_irqrestore(cdev->ccwlock, flags); |
336 | PREPARE_WORK(&cdev->private->kick_work, | 334 | PREPARE_WORK(&cdev->private->kick_work, |
337 | ccw_device_remove_orphan_cb); | 335 | ccw_device_remove_orphan_cb); |
336 | queue_work(slow_path_wq, &cdev->private->kick_work); | ||
338 | } else | 337 | } else |
339 | /* Deregister subchannel, which will kill the ccw device. */ | 338 | /* Deregister subchannel, which will kill the ccw device. */ |
340 | PREPARE_WORK(&cdev->private->kick_work, | 339 | ccw_device_schedule_sch_unregister(cdev); |
341 | ccw_device_call_sch_unregister); | ||
342 | queue_work(slow_path_wq, &cdev->private->kick_work); | ||
343 | } | 340 | } |
344 | 341 | ||
345 | /** | 342 | /** |
@@ -471,7 +468,7 @@ static int online_store_recog_and_online(struct ccw_device *cdev) | |||
471 | int ret; | 468 | int ret; |
472 | 469 | ||
473 | /* Do device recognition, if needed. */ | 470 | /* Do device recognition, if needed. */ |
474 | if (cdev->id.cu_type == 0) { | 471 | if (cdev->private->state == DEV_STATE_BOXED) { |
475 | ret = ccw_device_recognition(cdev); | 472 | ret = ccw_device_recognition(cdev); |
476 | if (ret) { | 473 | if (ret) { |
477 | CIO_MSG_EVENT(0, "Couldn't start recognition " | 474 | CIO_MSG_EVENT(0, "Couldn't start recognition " |
@@ -482,17 +479,21 @@ static int online_store_recog_and_online(struct ccw_device *cdev) | |||
482 | } | 479 | } |
483 | wait_event(cdev->private->wait_q, | 480 | wait_event(cdev->private->wait_q, |
484 | cdev->private->flags.recog_done); | 481 | cdev->private->flags.recog_done); |
482 | if (cdev->private->state != DEV_STATE_OFFLINE) | ||
483 | /* recognition failed */ | ||
484 | return -EAGAIN; | ||
485 | } | 485 | } |
486 | if (cdev->drv && cdev->drv->set_online) | 486 | if (cdev->drv && cdev->drv->set_online) |
487 | ccw_device_set_online(cdev); | 487 | ccw_device_set_online(cdev); |
488 | return 0; | 488 | return 0; |
489 | } | 489 | } |
490 | |||
490 | static int online_store_handle_online(struct ccw_device *cdev, int force) | 491 | static int online_store_handle_online(struct ccw_device *cdev, int force) |
491 | { | 492 | { |
492 | int ret; | 493 | int ret; |
493 | 494 | ||
494 | ret = online_store_recog_and_online(cdev); | 495 | ret = online_store_recog_and_online(cdev); |
495 | if (ret) | 496 | if (ret && !force) |
496 | return ret; | 497 | return ret; |
497 | if (force && cdev->private->state == DEV_STATE_BOXED) { | 498 | if (force && cdev->private->state == DEV_STATE_BOXED) { |
498 | ret = ccw_device_stlck(cdev); | 499 | ret = ccw_device_stlck(cdev); |
@@ -500,7 +501,9 @@ static int online_store_handle_online(struct ccw_device *cdev, int force) | |||
500 | return ret; | 501 | return ret; |
501 | if (cdev->id.cu_type == 0) | 502 | if (cdev->id.cu_type == 0) |
502 | cdev->private->state = DEV_STATE_NOT_OPER; | 503 | cdev->private->state = DEV_STATE_NOT_OPER; |
503 | online_store_recog_and_online(cdev); | 504 | ret = online_store_recog_and_online(cdev); |
505 | if (ret) | ||
506 | return ret; | ||
504 | } | 507 | } |
505 | return 0; | 508 | return 0; |
506 | } | 509 | } |
@@ -512,7 +515,11 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, | |||
512 | int force, ret; | 515 | int force, ret; |
513 | unsigned long i; | 516 | unsigned long i; |
514 | 517 | ||
515 | if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) | 518 | if ((cdev->private->state != DEV_STATE_OFFLINE && |
519 | cdev->private->state != DEV_STATE_ONLINE && | ||
520 | cdev->private->state != DEV_STATE_BOXED && | ||
521 | cdev->private->state != DEV_STATE_DISCONNECTED) || | ||
522 | atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) | ||
516 | return -EAGAIN; | 523 | return -EAGAIN; |
517 | 524 | ||
518 | if (cdev->drv && !try_module_get(cdev->drv->owner)) { | 525 | if (cdev->drv && !try_module_get(cdev->drv->owner)) { |
@@ -1014,6 +1021,13 @@ static void ccw_device_call_sch_unregister(struct work_struct *work) | |||
1014 | put_device(&sch->dev); | 1021 | put_device(&sch->dev); |
1015 | } | 1022 | } |
1016 | 1023 | ||
1024 | void ccw_device_schedule_sch_unregister(struct ccw_device *cdev) | ||
1025 | { | ||
1026 | PREPARE_WORK(&cdev->private->kick_work, | ||
1027 | ccw_device_call_sch_unregister); | ||
1028 | queue_work(slow_path_wq, &cdev->private->kick_work); | ||
1029 | } | ||
1030 | |||
1017 | /* | 1031 | /* |
1018 | * subchannel recognition done. Called from the state machine. | 1032 | * subchannel recognition done. Called from the state machine. |
1019 | */ | 1033 | */ |
@@ -1025,19 +1039,17 @@ io_subchannel_recog_done(struct ccw_device *cdev) | |||
1025 | return; | 1039 | return; |
1026 | } | 1040 | } |
1027 | switch (cdev->private->state) { | 1041 | switch (cdev->private->state) { |
1042 | case DEV_STATE_BOXED: | ||
1043 | /* Device did not respond in time. */ | ||
1028 | case DEV_STATE_NOT_OPER: | 1044 | case DEV_STATE_NOT_OPER: |
1029 | cdev->private->flags.recog_done = 1; | 1045 | cdev->private->flags.recog_done = 1; |
1030 | /* Remove device found not operational. */ | 1046 | /* Remove device found not operational. */ |
1031 | if (!get_device(&cdev->dev)) | 1047 | if (!get_device(&cdev->dev)) |
1032 | break; | 1048 | break; |
1033 | PREPARE_WORK(&cdev->private->kick_work, | 1049 | ccw_device_schedule_sch_unregister(cdev); |
1034 | ccw_device_call_sch_unregister); | ||
1035 | queue_work(slow_path_wq, &cdev->private->kick_work); | ||
1036 | if (atomic_dec_and_test(&ccw_device_init_count)) | 1050 | if (atomic_dec_and_test(&ccw_device_init_count)) |
1037 | wake_up(&ccw_device_init_wq); | 1051 | wake_up(&ccw_device_init_wq); |
1038 | break; | 1052 | break; |
1039 | case DEV_STATE_BOXED: | ||
1040 | /* Device did not respond in time. */ | ||
1041 | case DEV_STATE_OFFLINE: | 1053 | case DEV_STATE_OFFLINE: |
1042 | /* | 1054 | /* |
1043 | * We can't register the device in interrupt context so | 1055 | * We can't register the device in interrupt context so |
@@ -1551,8 +1563,7 @@ static int purge_fn(struct device *dev, void *data) | |||
1551 | goto out; | 1563 | goto out; |
1552 | CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid, | 1564 | CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid, |
1553 | priv->dev_id.devno); | 1565 | priv->dev_id.devno); |
1554 | PREPARE_WORK(&cdev->private->kick_work, ccw_device_call_sch_unregister); | 1566 | ccw_device_schedule_sch_unregister(cdev); |
1555 | queue_work(slow_path_wq, &cdev->private->kick_work); | ||
1556 | 1567 | ||
1557 | out: | 1568 | out: |
1558 | /* Abort loop in case of pending signal. */ | 1569 | /* Abort loop in case of pending signal. */ |