Commit 9f9d53e5 authored by Jan Höppner's avatar Jan Höppner Committed by Martin Schwidefsky

s390/dasd: Fix locking issue when changing RO attribute

The function dasd_ro_store() calls set_disk_ro() to set the device in
question read-only. Since set_disk_ro() might sleep, we can't call it
while holding a lock. However, we also can't simply check if the device,
block, and gdp references are valid before we call set_disk_ro() because
an offline processing might have been started in the meanwhile which
will destroy those references.

In order to reliably call set_disk_ro() we have to ensure several
things:

- Still check validity of the mentioned references but additionally
  check if offline processing is running and bail out accordingly. Also,
  do this while holding the device lock.
- To ensure that the block device is still safe after the lock, increase
  the open_count while still holding the device lock.
Reviewed-by: default avatarStefan Haberland <sth@linux.vnet.ibm.com>
Signed-off-by: default avatarJan Höppner <hoeppner@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 9de67725
...@@ -761,6 +761,7 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr, ...@@ -761,6 +761,7 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
{ {
struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device *cdev = to_ccwdev(dev);
struct dasd_device *device; struct dasd_device *device;
unsigned long flags;
unsigned int val; unsigned int val;
int rc; int rc;
...@@ -775,10 +776,22 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr, ...@@ -775,10 +776,22 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
if (IS_ERR(device)) if (IS_ERR(device))
return PTR_ERR(device); return PTR_ERR(device);
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
val = val || test_bit(DASD_FLAG_DEVICE_RO, &device->flags); val = val || test_bit(DASD_FLAG_DEVICE_RO, &device->flags);
if (device->block && device->block->gdp)
set_disk_ro(device->block->gdp, val);
if (!device->block || !device->block->gdp ||
test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
goto out;
}
/* Increase open_count to avoid losing the block device */
atomic_inc(&device->block->open_count);
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
set_disk_ro(device->block->gdp, val);
atomic_dec(&device->block->open_count);
out:
dasd_put_device(device); dasd_put_device(device);
return count; return count;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment