From: Jan Höppner Date: Thu, 13 Oct 2016 11:31:39 +0000 (+0200) Subject: s390/dasd: Fix locking issue when changing RO attribute X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=9f9d53e5bdb4362a73b801176cae49e77bea41fb;p=linux-beck.git 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 Signed-off-by: Jan Höppner Signed-off-by: Martin Schwidefsky --- 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;