aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/s390/cio/ccwgroup.c18
1 files changed, 13 insertions, 5 deletions
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 17fa009d9959..918e6fce2573 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -91,15 +91,23 @@ ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const
91 91
92 gdev = to_ccwgroupdev(dev); 92 gdev = to_ccwgroupdev(dev);
93 93
94 if (gdev->state != CCWGROUP_OFFLINE) 94 /* Prevent concurrent online/offline processing and ungrouping. */
95 return -EINVAL; 95 if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
96 96 return -EAGAIN;
97 if (gdev->state != CCWGROUP_OFFLINE) {
98 rc = -EINVAL;
99 goto out;
100 }
97 /* Note that we cannot unregister the device from one of its 101 /* Note that we cannot unregister the device from one of its
98 * attribute methods, so we have to use this roundabout approach. 102 * attribute methods, so we have to use this roundabout approach.
99 */ 103 */
100 rc = device_schedule_callback(dev, ccwgroup_ungroup_callback); 104 rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
101 if (rc) 105out:
102 count = rc; 106 if (rc) {
107 /* Release onoff "lock" when ungrouping failed. */
108 atomic_set(&gdev->onoff, 0);
109 return rc;
110 }
103 return count; 111 return count;
104} 112}
105 113