This functionality is mainly used during testing, in order to simulate a server crash for ldiskfs by discarding all of the writes to the filesystem. For recovery testing we could simulate this by using a special loopback or DM device that also discards writes to the device. This functionality is also used by target "failback" in order to speed up service shutdown and takeover by the other node during controlled operation. However, it would also be possible to do this by simply allowing all of the in-flight requests to complete and then waiting for the service to stop. This will also be needed by the DMU-OSD, because discarding of writes on a DMU-based target is not safe as it could trigger a storage failure if the data is ever read from disk again and the checksum does not match that expected by the block pointer. Initial efforts to remove this patch are under way in bug 20776. Once this work comes to fruition this patch can be dropped. Index: linux-2.6.32-71.18.1.el6-master/block/blk-core.c =================================================================== --- linux-2.6.32-71.18.1.el6-master.orig/block/blk-core.c 2011-03-05 11:35:40.404043293 +0800 +++ linux-2.6.32-71.18.1.el6-master/block/blk-core.c 2011-03-11 20:21:10.492302510 +0800 @@ -1405,6 +1405,8 @@ #endif /* CONFIG_FAIL_MAKE_REQUEST */ +int dev_check_rdonly(struct block_device *bdev); + /* * Check whether this bio extends beyond the end of the device. */ @@ -1506,6 +1508,23 @@ if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) goto end_io; + /* this is cfs's dev_rdonly check */ + if (bio_rw(bio) == WRITE && dev_check_rdonly(bio->bi_bdev)) { + struct block_device *bdev = bio->bi_bdev; + + printk(KERN_WARNING "Write to readonly device %s (%#x) " + "bi_flags: %lx, bi_vcnt: %d, bi_idx: %d, " + "bi->size: %d, bi_cnt: %d, bi_private: %p\n", + bdev->bd_disk ? bdev->bd_disk->disk_name : "", + bdev->bd_dev, bio->bi_flags, bio->bi_vcnt, + bio->bi_idx, bio->bi_size, + atomic_read(&bio->bi_cnt), bio->bi_private); + set_bit(BIO_RDONLY, &bio->bi_flags); + bio_endio(bio, 0); + clear_bit(BIO_RDONLY, &bio->bi_flags); + break; + } + if (should_fail_request(bio)) goto end_io; @@ -2578,6 +2586,99 @@ } EXPORT_SYMBOL(kblockd_schedule_work); + /* + * Debug code for turning block devices "read-only" (will discard writes + * silently). This is for filesystem crash/recovery testing. + */ +struct deventry { + dev_t dev; + struct deventry *next; +}; + +static struct deventry *devlist = NULL; +static spinlock_t devlock = SPIN_LOCK_UNLOCKED; + +int dev_check_rdonly(struct block_device *bdev) +{ + struct deventry *cur; + + if (!bdev) + return 0; + + spin_lock(&devlock); + cur = devlist; + while(cur) { + if (bdev->bd_dev == cur->dev) { + spin_unlock(&devlock); + return 1; + } + cur = cur->next; + } + spin_unlock(&devlock); + return 0; +} + +void dev_set_rdonly(struct block_device *bdev) +{ + struct deventry *newdev, *cur; + + if (!bdev) + return; + + newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL); + if (!newdev) + return; + + spin_lock(&devlock); + cur = devlist; + while(cur) { + if (bdev->bd_dev == cur->dev) { + spin_unlock(&devlock); + kfree(newdev); + return; + } + cur = cur->next; + } + newdev->dev = bdev->bd_dev; + newdev->next = devlist; + devlist = newdev; + spin_unlock(&devlock); + printk(KERN_WARNING "Turning device %s (%#x) read-only\n", + bdev->bd_disk ? bdev->bd_disk->disk_name : "", bdev->bd_dev); +} + +void dev_clear_rdonly(struct block_device *bdev) +{ + struct deventry *cur, *last = NULL; + + if (!bdev) + return; + + spin_lock(&devlock); + cur = devlist; + while(cur) { + if (bdev->bd_dev == cur->dev) { + if (last) + last->next = cur->next; + else + devlist = cur->next; + spin_unlock(&devlock); + kfree(cur); + printk(KERN_WARNING "Removing read-only on %s (%#x)\n", + bdev->bd_disk ? bdev->bd_disk->disk_name : + "unknown block", + bdev->bd_dev); + return; + } + last = cur; + cur = cur->next; + } + spin_unlock(&devlock); +} + +EXPORT_SYMBOL(dev_set_rdonly); +EXPORT_SYMBOL(dev_clear_rdonly); +EXPORT_SYMBOL(dev_check_rdonly); int __init blk_dev_init(void) { BUILD_BUG_ON(__REQ_NR_BITS > 8 * Index: linux-2.6.32-71.18.1.el6-master/fs/block_dev.c =================================================================== --- linux-2.6.32-71.18.1.el6-master.orig/fs/block_dev.c 2011-03-05 11:35:40.486042782 +0800 +++ linux-2.6.32-71.18.1.el6-master/fs/block_dev.c 2011-03-05 11:37:35.624324775 +0800 @@ -1389,6 +1389,7 @@ if (bdev != bdev->bd_contains) victim = bdev->bd_contains; bdev->bd_contains = NULL; + dev_clear_rdonly(bdev); } unlock_kernel(); mutex_unlock(&bdev->bd_mutex); Index: linux-2.6.32-71.18.1.el6-master/include/linux/fs.h =================================================================== --- linux-2.6.32-71.18.1.el6-master.orig/include/linux/fs.h 2011-03-05 11:35:40.445043037 +0800 +++ linux-2.6.32-71.18.1.el6-master/include/linux/fs.h 2011-03-05 11:37:35.726324137 +0800 @@ -2204,6 +2204,10 @@ extern void submit_bio(int, struct bio *); extern int bdev_read_only(struct block_device *); #endif +#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); 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); Index: linux+rh+chaos/include/linux/bio.h =================================================================== --- linux+rh+chaos.orig/include/linux/bio.h +++ linux+rh+chaos/include/linux/bio.h @@ -126,6 +126,7 @@ struct bio { #define BIO_NULL_MAPPED 9 /* contains invalid user pages */ #define BIO_FS_INTEGRITY 10 /* fs owns integrity data, not block layer */ #define BIO_QUIET 11 /* Make BIO Quiet */ +#define BIO_RDONLY 31 /* device is readonly */ #define bio_flagged(bio, flag) ((bio)->bi_flags & (1 << (flag))) /*