diff options
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 24 |
1 files changed, 18 insertions, 6 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index df14c51f6532..8e04c00cf0ad 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -541,15 +541,24 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, | |||
541 | int force, ret; | 541 | int force, ret; |
542 | unsigned long i; | 542 | unsigned long i; |
543 | 543 | ||
544 | if (!dev_fsm_final_state(cdev) && | 544 | /* Prevent conflict between multiple on-/offline processing requests. */ |
545 | cdev->private->state != DEV_STATE_DISCONNECTED) | ||
546 | return -EAGAIN; | ||
547 | if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) | 545 | if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) |
548 | return -EAGAIN; | 546 | return -EAGAIN; |
547 | /* Prevent conflict between internal I/Os and on-/offline processing. */ | ||
548 | if (!dev_fsm_final_state(cdev) && | ||
549 | cdev->private->state != DEV_STATE_DISCONNECTED) { | ||
550 | ret = -EAGAIN; | ||
551 | goto out_onoff; | ||
552 | } | ||
553 | /* Prevent conflict between pending work and on-/offline processing.*/ | ||
554 | if (work_pending(&cdev->private->todo_work)) { | ||
555 | ret = -EAGAIN; | ||
556 | goto out_onoff; | ||
557 | } | ||
549 | 558 | ||
550 | if (cdev->drv && !try_module_get(cdev->drv->driver.owner)) { | 559 | if (cdev->drv && !try_module_get(cdev->drv->driver.owner)) { |
551 | atomic_set(&cdev->private->onoff, 0); | 560 | ret = -EINVAL; |
552 | return -EINVAL; | 561 | goto out_onoff; |
553 | } | 562 | } |
554 | if (!strncmp(buf, "force\n", count)) { | 563 | if (!strncmp(buf, "force\n", count)) { |
555 | force = 1; | 564 | force = 1; |
@@ -574,6 +583,7 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, | |||
574 | out: | 583 | out: |
575 | if (cdev->drv) | 584 | if (cdev->drv) |
576 | module_put(cdev->drv->driver.owner); | 585 | module_put(cdev->drv->driver.owner); |
586 | out_onoff: | ||
577 | atomic_set(&cdev->private->onoff, 0); | 587 | atomic_set(&cdev->private->onoff, 0); |
578 | return (ret < 0) ? ret : count; | 588 | return (ret < 0) ? ret : count; |
579 | } | 589 | } |
@@ -1311,10 +1321,12 @@ static int purge_fn(struct device *dev, void *data) | |||
1311 | 1321 | ||
1312 | spin_lock_irq(cdev->ccwlock); | 1322 | spin_lock_irq(cdev->ccwlock); |
1313 | if (is_blacklisted(id->ssid, id->devno) && | 1323 | if (is_blacklisted(id->ssid, id->devno) && |
1314 | (cdev->private->state == DEV_STATE_OFFLINE)) { | 1324 | (cdev->private->state == DEV_STATE_OFFLINE) && |
1325 | (atomic_cmpxchg(&cdev->private->onoff, 0, 1) == 0)) { | ||
1315 | CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", id->ssid, | 1326 | CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", id->ssid, |
1316 | id->devno); | 1327 | id->devno); |
1317 | ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); | 1328 | ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); |
1329 | atomic_set(&cdev->private->onoff, 0); | ||
1318 | } | 1330 | } |
1319 | spin_unlock_irq(cdev->ccwlock); | 1331 | spin_unlock_irq(cdev->ccwlock); |
1320 | /* Abort loop in case of pending signal. */ | 1332 | /* Abort loop in case of pending signal. */ |