From: Jeff Mahoney <jeffm@suse.com>
Subject: block: add dev_check_rdonly and friends for Lustre testing
References: FATE#314679 bnc#802764
Patch-mainline: Never

Lustre testing wants to be able to logically turn off disks so that
writes just stop.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---

 block/blk-core.c   |   76 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/block_dev.c     |    3 ++
 include/linux/fs.h |    7 ++++
 3 files changed, 86 insertions(+)

--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1491,6 +1491,18 @@ static inline void __generic_make_reques
 		if (unlikely(blk_queue_dead(q)))
 			goto end_io;
 
+#ifndef CONFIG_FAIL_MAKE_REQUEST
+                /*
+		 * this is lustre's dev_rdonly check
+		 * without CONFIG_FAIL_MAKE_REQUEST set, this will only
+		 * be set by dev_set_rdonly
+		 */
+                if (bio_rw(bio) == WRITE &&
+		    test_bit(QUEUE_FLAG_FAIL_IO, &q->queue_flags)) {
+			err = 0;
+			goto end_io;
+                }
+#endif
 		if (should_fail_request(bio))
 			goto end_io;
 
@@ -2796,6 +2808,76 @@ void blk_finish_plug(struct blk_plug *pl
 }
 EXPORT_SYMBOL(blk_finish_plug);
 
+/*
+ * This feature depends on CONFIG_FAIL_MAKE_REQUEST being disabled.
+ * It is in all non-debug flavors of the SLES kernel.
+ *
+ * It could be extended to work with FAIL_MAKE_REQUEST but we don't
+ * need it to work. Future versions of Lustre don't use it.
+ */
+#ifndef CONFIG_FAIL_MAKE_REQUEST
+
+/*
+* Debug code for turning block devices "read-only" (will discard writes
+* silently).  This is for filesystem crash/recovery testing.
+*/
+
+int dev_check_rdonly(struct block_device *bdev)
+{
+	struct request_queue *q = bdev_get_queue(bdev);
+
+	/*
+	 * We can't key this off of LUSTRE_SUPER_MAGIC or
+	 * LUSTRE_CLIENT_SUPER_MAGIC because Lustre's ldiskfs uses
+	 * it and it shares the ext2/3/4 sb->s_super magic
+	 */
+
+	if (q)
+		return test_bit(QUEUE_FLAG_FAIL_IO, &q->queue_flags);
+	return 0;
+}
+
+void dev_set_rdonly(struct block_device *bdev)
+{
+	struct request_queue *q = bdev_get_queue(bdev);
+
+	if (q) {
+		spin_lock_irq(q->queue_lock);
+		queue_flag_set(QUEUE_FLAG_FAIL_IO, q);
+		spin_unlock_irq(q->queue_lock);
+
+		pr_warn("Turning device %s (%#x) read-only\n",
+		       bdev->bd_disk ? bdev->bd_disk->disk_name : "",
+		       bdev->bd_dev);
+	} else {
+		pr_warn("Couldn't turn device %s (%#x) read-only -- NULL q\n",
+		       bdev->bd_disk ? bdev->bd_disk->disk_name : "",
+		       bdev->bd_dev);
+	}
+}
+
+void dev_clear_rdonly(struct block_device *bdev)
+{
+	struct request_queue *q = bdev_get_queue(bdev);
+	if (q) {
+		unsigned long flags;
+		int was_set;
+		spin_lock_irqsave(q->queue_lock, flags);
+		was_set = queue_flag_test_and_clear(QUEUE_FLAG_FAIL_IO, q);
+		spin_unlock_irqrestore(q->queue_lock, flags);
+
+		if (was_set)
+			pr_warn("Clearing device %s (%#x) read-only\n",
+			       bdev->bd_disk ? bdev->bd_disk->disk_name : "",
+			       bdev->bd_dev);
+	}
+}
+
+EXPORT_SYMBOL(dev_set_rdonly);
+EXPORT_SYMBOL(dev_clear_rdonly);
+EXPORT_SYMBOL(dev_check_rdonly);
+#endif
+
 int __init blk_dev_init(void)
 {
 	BUILD_BUG_ON(__REQ_NR_BITS > 8 *
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1453,6 +1453,9 @@ static int __blkdev_put(struct block_dev
 
 		disk_put_part(bdev->bd_part);
 		bdev->bd_part = NULL;
+#ifndef CONFIG_FAIL_MAKE_REQUEST
+		dev_clear_rdonly(bdev);
+#endif
 		bdev->bd_disk = NULL;
 		if (bdev != bdev->bd_contains)
 			victim = bdev->bd_contains;
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2325,6 +2325,12 @@ extern void inode_sb_list_add(struct ino
 extern void submit_bio(int, struct bio *);
 extern int bdev_read_only(struct block_device *);
 #endif
+#ifndef CONFIG_FAIL_MAKE_REQUEST
+#define HAVE_CLEAR_RDONLY_ON_PUT
+extern void dev_set_rdonly(struct block_device *bdev);
+extern int dev_check_rdonly(struct block_device *bdev);
+extern void dev_clear_rdonly(struct block_device *bdev);
+#endif
 extern int set_blocksize(struct block_device *, int);
 extern int sb_set_blocksize(struct super_block *, int);
 extern int sb_min_blocksize(struct super_block *, int);
