Tell userspace if a device has ever been opened read-write while open continuously and prevent it being changed to read-only while something might have it open read-write. --- drivers/md/dm-ioctl.c | 30 ++++++++++++++++++++++++++++-- drivers/md/dm.c | 15 +++++++++++++++ drivers/md/dm.h | 1 + include/uapi/linux/dm-ioctl.h | 5 +++++ 4 files changed, 49 insertions(+), 2 deletions(-) Index: linux/drivers/md/dm-ioctl.c =================================================================== --- linux.orig/drivers/md/dm-ioctl.c +++ linux/drivers/md/dm-ioctl.c @@ -659,11 +659,14 @@ static void __dev_status(struct mapped_d struct dm_table *table; param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | - DM_ACTIVE_PRESENT_FLAG); + DM_ACTIVE_PRESENT_FLAG | DM_OPENED_WRITEABLE_FLAG); if (dm_suspended_md(md)) param->flags |= DM_SUSPEND_FLAG; + if (dm_opened_writeable(md)) + param->flags |= DM_OPENED_WRITEABLE_FLAG; + param->dev = huge_encode_dev(disk_devt(disk)); /* @@ -951,7 +954,7 @@ out: static int do_resume(struct dm_ioctl *param) { - int r = 0; + int r = 0, was_opened_writeable = 0; unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG; struct hash_cell *hc; struct mapped_device *md; @@ -969,11 +972,34 @@ static int do_resume(struct dm_ioctl *pa md = hc->md; new_map = hc->new_map; + + /* + * Are we switching from read-write to read-only? + */ + if (new_map && !(dm_table_get_mode(new_map) & FMODE_WRITE) && + !get_disk_ro(dm_disk(md))) { + set_disk_ro(dm_disk(md), 1); // Race if we enforce ro on bios + if (dm_opened_writeable(md)) { + was_opened_writeable = 1; + set_disk_ro(dm_disk(md), 0); + } + } + hc->new_map = NULL; param->flags &= ~DM_INACTIVE_PRESENT_FLAG; up_write(&_hash_lock); + /* + * A device that has been open continuously since it was opened + * read-write may not be made read-only. + */ + if (was_opened_writeable) { + DMWARN("..."); // Suitable message? + dm_put(md); + return -ENXIO; // better error? + } + /* Do we need to load a new map ? */ if (new_map) { /* Suspend if it isn't already suspended */ Index: linux/drivers/md/dm.c =================================================================== --- linux.orig/drivers/md/dm.c +++ linux/drivers/md/dm.c @@ -125,6 +125,7 @@ struct mapped_device { rwlock_t map_lock; atomic_t holders; atomic_t open_count; + atomic_t opened_writeable; unsigned long flags; @@ -348,6 +349,8 @@ static int dm_blk_open(struct block_devi dm_get(md); atomic_inc(&md->open_count); + if (mode & FMODE_WRITE) + atomic_set(&md->opened_writeable, 1); out: spin_unlock(&_minor_lock); @@ -362,6 +365,9 @@ static int dm_blk_close(struct gendisk * spin_lock(&_minor_lock); atomic_dec(&md->open_count); + if (!atomic_read(&md->open_count)) + atomic_set(&md->opened_writeable, 0); + dm_put(md); spin_unlock(&_minor_lock); @@ -369,6 +375,14 @@ static int dm_blk_close(struct gendisk * return 0; } +/* + * Returns 1 if the device is open read-write. + */ +int dm_opened_writeable(struct mapped_device *md) +{ + return atomic_read(&md->opened_writeable); +} + int dm_open_count(struct mapped_device *md) { return atomic_read(&md->open_count); @@ -1919,6 +1933,7 @@ static struct mapped_device *alloc_dev(i rwlock_init(&md->map_lock); atomic_set(&md->holders, 1); atomic_set(&md->open_count, 0); + atomic_set(&md->opened_writeable, 0); atomic_set(&md->event_nr, 0); atomic_set(&md->uevent_seq, 0); INIT_LIST_HEAD(&md->uevent_list); Index: linux/drivers/md/dm.h =================================================================== --- linux.orig/drivers/md/dm.h +++ linux/drivers/md/dm.h @@ -144,6 +144,7 @@ void dm_stripe_exit(void); */ void dm_destroy(struct mapped_device *md); void dm_destroy_immediate(struct mapped_device *md); +int dm_opened_writeable(struct mapped_device *md); int dm_open_count(struct mapped_device *md); int dm_lock_for_deletion(struct mapped_device *md); Index: linux/include/uapi/linux/dm-ioctl.h =================================================================== --- linux.orig/include/uapi/linux/dm-ioctl.h +++ linux/include/uapi/linux/dm-ioctl.h @@ -336,4 +336,9 @@ enum { */ #define DM_SECURE_DATA_FLAG (1 << 15) /* In */ +/* + * Set if the device is currently open read-write. + */ +#define DM_OPENED_WRITEABLE_FLAG (1 << 16) /* Out */ + #endif /* _LINUX_DM_IOCTL_H */