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 */