]> Pileus Git - ~andy/linux/blobdiff - drivers/s390/cio/device.c
[S390] cio: prevent purging of CCW devices in the online state
[~andy/linux] / drivers / s390 / cio / device.c
index e50b12163afeafe74fabe266b51dfffd9b2c5fe8..8e04c00cf0ad10d522128b97e33cefcf3b870adf 100644 (file)
@@ -127,7 +127,7 @@ static int ccw_uevent(struct device *dev, struct kobj_uevent_env *env)
        return ret;
 }
 
-struct bus_type ccw_bus_type;
+static struct bus_type ccw_bus_type;
 
 static void io_subchannel_irq(struct subchannel *);
 static int io_subchannel_probe(struct subchannel *);
@@ -541,15 +541,24 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr,
        int force, ret;
        unsigned long i;
 
-       if (!dev_fsm_final_state(cdev) &&
-           cdev->private->state != DEV_STATE_DISCONNECTED)
-               return -EAGAIN;
+       /* Prevent conflict between multiple on-/offline processing requests. */
        if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
                return -EAGAIN;
+       /* Prevent conflict between internal I/Os and on-/offline processing. */
+       if (!dev_fsm_final_state(cdev) &&
+           cdev->private->state != DEV_STATE_DISCONNECTED) {
+               ret = -EAGAIN;
+               goto out_onoff;
+       }
+       /* Prevent conflict between pending work and on-/offline processing.*/
+       if (work_pending(&cdev->private->todo_work)) {
+               ret = -EAGAIN;
+               goto out_onoff;
+       }
 
-       if (cdev->drv && !try_module_get(cdev->drv->owner)) {
-               atomic_set(&cdev->private->onoff, 0);
-               return -EINVAL;
+       if (cdev->drv && !try_module_get(cdev->drv->driver.owner)) {
+               ret = -EINVAL;
+               goto out_onoff;
        }
        if (!strncmp(buf, "force\n", count)) {
                force = 1;
@@ -573,7 +582,8 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr,
        }
 out:
        if (cdev->drv)
-               module_put(cdev->drv->owner);
+               module_put(cdev->drv->driver.owner);
+out_onoff:
        atomic_set(&cdev->private->onoff, 0);
        return (ret < 0) ? ret : count;
 }
@@ -1311,10 +1321,12 @@ static int purge_fn(struct device *dev, void *data)
 
        spin_lock_irq(cdev->ccwlock);
        if (is_blacklisted(id->ssid, id->devno) &&
-           (cdev->private->state == DEV_STATE_OFFLINE)) {
+           (cdev->private->state == DEV_STATE_OFFLINE) &&
+           (atomic_cmpxchg(&cdev->private->onoff, 0, 1) == 0)) {
                CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", id->ssid,
                              id->devno);
                ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
+               atomic_set(&cdev->private->onoff, 0);
        }
        spin_unlock_irq(cdev->ccwlock);
        /* Abort loop in case of pending signal. */
@@ -1970,7 +1982,7 @@ static const struct dev_pm_ops ccw_pm_ops = {
        .restore = ccw_device_pm_restore,
 };
 
-struct bus_type ccw_bus_type = {
+static struct bus_type ccw_bus_type = {
        .name   = "ccw",
        .match  = ccw_bus_match,
        .uevent = ccw_uevent,
@@ -1993,8 +2005,6 @@ int ccw_driver_register(struct ccw_driver *cdriver)
        struct device_driver *drv = &cdriver->driver;
 
        drv->bus = &ccw_bus_type;
-       drv->name = cdriver->name;
-       drv->owner = cdriver->owner;
 
        return driver_register(drv);
 }
@@ -2112,5 +2122,4 @@ EXPORT_SYMBOL(ccw_device_set_offline);
 EXPORT_SYMBOL(ccw_driver_register);
 EXPORT_SYMBOL(ccw_driver_unregister);
 EXPORT_SYMBOL(get_ccwdev_by_busid);
-EXPORT_SYMBOL(ccw_bus_type);
 EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id);