Hello FS developers,
for those of you working on filesystem development (especially journalling
filesystems), I'm making a kernel patch + tool available.  Basically, what
it allows you to do is create arbitrary disk "failures" either at critical
points in the code (via a kernel function call), or via a user-space IOCTL.

This allows you, for example, to set up a test situation in user-space, let
it get written to the journal, and then cause the device to "fail" before
the data makes it into the filesystem so that you can verify the journal
recovery works properly.

I used this a lot when developing the ext3 "orphan" code by opening some
files with "more", deleting them from the directory, letting this make it
to the journal, and then causing a loop device failure before quitting
"more" and having the files deleted from the filesystem.  I can then
unmount the filesystem and remount it to test journal recovery without
having to wait for a whole reboot.

Note that I only tested it with write failures (basically all writes are
discarded), and not with read failures (although that is also possible -
which return undefined data in the buffer rather than actually causing an
I/O error).


To use the user-space tool, simply:

loop_discard /dev/loop0 1  # disables writes to /dev/loop0
loop_discard /dev/loop0 0  # enables reads/writes to /dev/loop0

Normally, I don't re-enable writes to a device after I disable them, as this
would cause a very confused filesystem, rather than simply simulating a
crash or total disk/adapter/cable failure.

Info is printed to the kernel log about discarded I/Os.

Cheers, Andreas
--- cut here ---
--- linux/include/linux/loop.h.orig     Wed Jun 14 12:35:12 2000
+++ linux/include/linux/loop.h  Wed Jun 14 12:36:03 2000
@@ -52,6 +52,8 @@
  */
 #define LO_FLAGS_DO_BMAP       0x00000001
 #define LO_FLAGS_READ_ONLY     0x00000002
+#define LO_FLAGS_DISCARD_WRITE 0x00000004
+#define LO_FLAGS_DISCARD_READ  0x00000008
 
 /* 
  * Note that this structure gets the wrong offsets when directly used
@@ -126,5 +128,6 @@
 #define LOOP_CLR_FD    0x4C01
 #define LOOP_SET_STATUS        0x4C02
 #define LOOP_GET_STATUS        0x4C03
+#define LOOP_SET_DISCARD_IO    0x4C04
 
 #endif
--- linux/drivers/block/loop.c.loopdiscard.orig Tue Jan  4 11:12:14 2000
+++ linux/drivers/block/loop.c  Wed Jun 14 12:27:57 2000
@@ -175,6 +175,17 @@
        if (!lo->lo_dentry || !lo->transfer)
                goto error_out;
 
+       /* For testing purposes, we can throw away the I/O request if desired */
+       if ((lo->lo_flags & LO_FLAGS_DISCARD_WRITE &&
+            current_request->cmd == WRITE) ||
+           (lo->lo_flags & LO_FLAGS_DISCARD_READ &&
+            current_request->cmd == READ)) {
+               printk(KERN_DEBUG "discarding %s I/O on %s\n",
+                      current_request->cmd == WRITE ? "write" : "read",
+                      kdevname(current_request->rq_dev));
+               goto error_discard;
+       }
+
        blksize = BLOCK_SIZE;
        if (blksize_size[MAJOR(lo->lo_device)]) {
            blksize = blksize_size[MAJOR(lo->lo_device)][MINOR(lo->lo_device)];
@@ -274,6 +285,7 @@
                block++;
        }
        spin_lock_irq(&io_request_lock);
+error_discard:
        current_request->next=CURRENT;
        CURRENT=current_request;
        end_request(1);
@@ -564,6 +576,38 @@
        return copy_to_user(arg, &info, sizeof(info)) ? -EFAULT : 0;
 }
 
+#ifdef CONFIG_LOOP_DISCARD
+int loop_discard_io(kdev_t dev, long arg)
+{
+       struct loop_device *lo;
+
+       if (MINOR(dev) >= MAX_LOOP)
+               return -ENODEV;
+       lo = &loop_dev[MINOR(dev)];
+
+       if (arg & 1) {
+               lo->lo_flags |= LO_FLAGS_DISCARD_WRITE;
+               printk(KERN_INFO "setting loop device %s to discard writes\n",
+                      kdevname(dev));
+       } else if (lo->lo_flags & LO_FLAGS_DISCARD_WRITE) {
+               lo->lo_flags &= ~LO_FLAGS_DISCARD_WRITE;
+               printk(KERN_INFO "setting loop device %s to allow writes\n",
+                      kdevname(dev));
+       }
+       if (arg & 2) {
+               lo->lo_flags |= LO_FLAGS_DISCARD_READ;
+               printk(KERN_INFO "setting loop device %s to discard reads\n",
+                      kdevname(dev));
+       } else if (lo->lo_flags & LO_FLAGS_DISCARD_READ) {
+               lo->lo_flags &= ~LO_FLAGS_DISCARD_READ;
+               printk(KERN_INFO "setting loop device %s to allow reads\n",
+                      kdevname(dev));
+       }
+
+       return 0;
+}
+#endif
+
 static int lo_ioctl(struct inode * inode, struct file * file,
        unsigned int cmd, unsigned long arg)
 {
@@ -589,6 +633,10 @@
                return loop_set_status(lo, (struct loop_info *) arg);
        case LOOP_GET_STATUS:
                return loop_get_status(lo, (struct loop_info *) arg);
+#ifdef CONFIG_LOOP_DISCARD
+       case LOOP_SET_DISCARD_IO:
+               return loop_discard_io(inode->i_rdev, arg);
+#endif
        case BLKGETSIZE:   /* Return device size */
                if (!lo->lo_dentry)
                        return -ENXIO;
@@ -703,6 +751,9 @@
 
 EXPORT_SYMBOL(loop_register_transfer);
 EXPORT_SYMBOL(loop_unregister_transfer);
+#ifdef CONFIG_LOOP_DISCARD
+EXPORT_SYMBOL(loop_discard_io);
+#endif
 
 int __init loop_init(void) 
 {
--- linux/drivers/block/Config.in.loopdiscard.orig      Wed Jun 14 12:33:45 2000
+++ linux/drivers/block/Config.in       Wed Jun 14 12:34:07 2000
@@ -97,6 +97,9 @@
 comment 'Additional Block Devices'
 
 tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
+if [ "$CONFIG_BLK_DEV_LOOP" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  bool '   Loop filesystem debugging support (DANGEROUS)' CONFIG_LOOP_DISCARD
+fi
 if [ "$CONFIG_NET" = "y" ]; then
   tristate 'Network block device support' CONFIG_BLK_DEV_NBD
 fi
--- linux/Documentation/Configure.help.loopdiscard.orig Wed Jun 14 12:14:49 2000
+++ linux/Documentation/Configure.help  Wed Jun 14 12:32:21 2000
@@ -264,6 +264,19 @@
 
   Most users will answer N here.
 
+Loop device filesystem debugging support
+CONFIG_LOOP_DISCARD
+  Saying Y here will enable a feature of the loopback device which allows
+  it to discard data that would otherwise be written to disk.  This is
+  normally NOT what you want to happen.  However, if you are a kernel
+  filesystem developer, it may be useful if you want to test how a
+  filesystem will react in case of disk failure.  You can simulate write
+  failures, read failures, or both.  Creating a write failure simulates
+  a system crash, since no more data will make it to the disk, but saves
+  you from having to reboot the computer to continue testing.
+
+  Almost all users will answer N here.
+
 Network Block Device support
 CONFIG_BLK_DEV_NBD
   Saying Y here will allow your computer to be a client for network
--- cut here ---
/* loop_discard.c
 *
 * Program to create "errors" on the loopback device in order to simulate
 * disk failure or crash while testing journalling support in presto/ext3.
 * It currently does this by interfacing with the (modified) loop device to
 * make it discard any write and/or read requests when desired.
 *
 * Andreas Dilger <[EMAIL PROTECTED]>, Peter Braam <[EMAIL PROTECTED]>
 */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/loop.h>

int main(int argc, char *argv[])
{
        int fd, err;
        long val;

        if (argc != 3) {
                fprintf(stderr, "usage: %s <device> <discard>\n", argv[0]);
                fprintf(stderr, "\tdiscard = 1 for writes, 2 for reads\n");
                return 1;
        }
        val = strtoul(argv[2], 0, 0);
        fd = open(argv[1], O_RDONLY);
        if (fd < 0) {
                perror("initial fd error");
                return 1;
        }
        err = ioctl(fd, LOOP_SET_DISCARD_IO, val);
        if (err) {
                perror("ioctl failed");
                return 1;
        }

        close(fd);
        return(err);
}
--- cut here ---

Reply via email to