summaryrefslogtreecommitdiffstats
path: root/drivers/s390/block
diff options
context:
space:
mode:
authorJan Höppner <hoeppner@linux.vnet.ibm.com>2016-10-13 13:31:39 +0200
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2016-10-28 10:09:04 +0200
commit9f9d53e5bdb4362a73b801176cae49e77bea41fb (patch)
tree6905495f3538632f731934c83f920eae671892ac /drivers/s390/block
parent9de67725c80cb8a7b34b1932e196d2ed61a08879 (diff)
downloadlinux-9f9d53e5bdb4362a73b801176cae49e77bea41fb.tar.bz2
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: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/block')
-rw-r--r--drivers/s390/block/dasd_devmap.c17
1 files changed, 15 insertions, 2 deletions
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index c1979ecce821..cbae6ab448b8 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -761,6 +761,7 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
{
struct ccw_device *cdev = to_ccwdev(dev);
struct dasd_device *device;
+ unsigned long flags;
unsigned int val;
int rc;
@@ -775,10 +776,22 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
if (IS_ERR(device))
return PTR_ERR(device);
+ spin_lock_irqsave(get_ccwdev_lock(cdev), 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);
return count;