CRC loop device

From: Pavel Machek (pavel@suse.cz)
Date: Thu Apr 27 2000 - 02:34:12 EST


Hi!

Here's my attempt at crc-ing loop device. There's slight problem with
it: it locks up io subsystem under heavy load. Did I do something very
stupid with respect to memory managment? Or do I need to set limit for
bufferheads lower because I do 2 io requests for every io request on
loop?

                                                                Pavel
--- clean/drivers/block/loop.c Tue Mar 21 20:30:08 2000
+++ linux/drivers/block/loop.c Wed Apr 19 22:39:59 2000
@@ -61,6 +61,7 @@
 #include <linux/devfs_fs_kernel.h>
 
 #include <asm/uaccess.h>
+#include <asm/checksum.h>
 
 #include <linux/loop.h>
 
@@ -118,6 +119,101 @@
         return 0;
 }
 
+#define ID printk(KERN_ERR "crc: info about (%s, %d, %d) ", kdevname(lo->lo_device), real_block, blksize);
+
+
+static int transfer_crc(struct loop_device *lo, int cmd, char *raw_buf,
+ char *loop_buf, int size, int real_block)
+{
+ struct buffer_head *bh;
+ int blksize = 1024, nsect; /* Size of block on auxilary media */
+ int cksum;
+ u32 *data;
+ nsect = blksize / 4;
+
+ if (!lo->second_device) {
+ ID; printk( "reading from not-yet-setup crc device can result in armagedon. Dont try again.\n" );
+ return -1;
+ }
+ bh = getblk(lo->second_device, 1+real_block/nsect, blksize);
+ if (!bh) {
+ ID; printk( "getblk returned NULL.\n" );
+ return -1;
+ }
+ if (!buffer_uptodate(bh)) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
+ ID; printk( "could not read block with CRC\n" );
+ goto error;
+ }
+ }
+
+ data = bh->b_data;
+ if (cmd == READ)
+ cksum = csum_partial_copy_nocheck(raw_buf, loop_buf, size, 0);
+ else
+ cksum = csum_partial_copy_nocheck(loop_buf, raw_buf, size, 0);
+
+ if (cmd == READ) {
+ if (le32_to_cpu(data[real_block%nsect]) != cksum) {
+ if (lo->lo_encrypt_key_size == 0) { /* Normal mode */
+ ID; printk( "wrong checksum reading, is %x, should be %x\n", cksum, 0x1234 );
+ goto error;
+ } else {
+ ID; printk( "wrong checksum repairing, setting to %x\n", cksum );
+ goto repair;
+ }
+ }
+ } else {
+ repair:
+ data[real_block%nsect] = cpu_to_le32(cksum);
+ mark_buffer_uptodate(bh, 1);
+ mark_buffer_dirty(bh, 1);
+ }
+
+ brelse(bh);
+ return 0;
+error:
+ brelse(bh);
+ return -1;
+
+}
+
+static int ioctl_crc(struct loop_device *lo, int cmd, unsigned long arg)
+{
+ struct file *file;
+ struct inode *inode;
+ int error;
+
+ printk( "Entering ioctl_crc\n" );
+ if (cmd != LOOP_CRC_SET_FD)
+ return -EINVAL;
+
+ error = -EBADF;
+ file = fget(arg);
+ if (!file)
+ return -EINVAL;
+
+ error = -EINVAL;
+ inode = file->f_dentry->d_inode;
+ if (!inode) {
+ printk(KERN_ERR "ioctl_crc: NULL inode?!?\n");
+ goto out;
+ }
+
+ if (S_ISBLK(inode->i_mode)) {
+ error = blkdev_open(inode, file);
+ lo->second_device = inode->i_rdev;
+ printk( "loop_crc: Registered device %x\n", lo->second_device );
+ return error;
+ } else {
+ out:
+ fput(file);
+ return -EINVAL;
+ }
+}
+
 static int none_status(struct loop_device *lo, struct loop_info *info)
 {
         return 0;
@@ -142,10 +238,19 @@
         init: xor_status
 };
 
+struct loop_func_table crc_funcs = {
+ number: LO_CRYPT_CRC,
+ transfer: transfer_crc,
+ init: none_status,
+ ioctl: ioctl_crc
+};
+
 /* xfer_funcs[0] is special - its release function is never called */
 struct loop_func_table *xfer_funcs[MAX_LO_CRYPT] = {
         &none_funcs,
- &xor_funcs
+ &xor_funcs,
+ NULL, NULL, NULL, NULL, NULL,
+ &crc_funcs,
 };
 
 #define MAX_DISK_SIZE 1024*1024*1024
@@ -539,6 +644,7 @@
         lo->transfer = NULL;
         lo->ioctl = NULL;
         lo->lo_device = 0;
+ lo->second_device = 0;
         lo->lo_encrypt_type = 0;
         lo->lo_offset = 0;
         lo->lo_encrypt_key_size = 0;
--- clean/include/linux/loop.h Mon Nov 23 06:29:54 1998
+++ linux/include/linux/loop.h Sat Apr 15 13:22:57 2000
@@ -22,6 +22,7 @@
         struct dentry *lo_dentry;
         int lo_refcnt;
         kdev_t lo_device;
+ kdev_t second_device;
         int lo_offset;
         int lo_encrypt_type;
         int lo_encrypt_key_size;
@@ -94,6 +95,7 @@
 #define LO_CRYPT_BLOW 4
 #define LO_CRYPT_CAST128 5
 #define LO_CRYPT_IDEA 6
+#define LO_CRYPT_CRC 7
 #define LO_CRYPT_DUMMY 9
 #define LO_CRYPT_SKIPJACK 10
 #define MAX_LO_CRYPT 20
@@ -126,5 +128,6 @@
 #define LOOP_CLR_FD 0x4C01
 #define LOOP_SET_STATUS 0x4C02
 #define LOOP_GET_STATUS 0x4C03
+#define LOOP_CRC_SET_FD 0x4C04
 
 #endif
--- /dev/null Sun Jun 27 13:16:23 1999
+++ linux/Documentation/loop.txt Wed Apr 26 16:00:52 2000
@@ -0,0 +1,55 @@
+ CRC loop method
+ ---------------
+ pavel@suse.cz
+
+* What is it good for?
+~~~~~~~~~~~~~~~~~~~~~~
+
+ Assume you have flaky hardware. Your scsi cable is too long, your
+cat likes to chafe against it producing static electricity, and your
+system just crashes from time to time.
+
+ Linux filesystems are designed to be fsck-ed first. If they are
+not, they can do bad things (like crashing system
+completely). Incorrect filesystem is able to induce kernel corruption
+with any results possible. Having flaky io subsystem is equivalent (or
+worse that) unchecked filesystem and it resulted in kernel crashes
+before.
+
+ When you asked on linux-kernel, you were told to shoot your cat and
+make your cable shorter. That is not a option.
+
+ Raid5 is not going to help you in case error is between
+raid-controller and main system. What is worse, raid5 will not help
+you even if error is between raid controller and drives: raid5
+protects you against completely different kind of error: error where
+disk dies and you know it. Undetected io error is something else,
+RAID5 is USELESS there.
+
+ CRC loop method was designed to protect you from exactly this kind
+of situation. It expects io subsystem to produce errors which go away
+when retried (that may not be the case with writes: if you have
+bit-error when writing, subsequent read will discover it, but any
+number of retries is not going to help).
+
+* Usage
+~~~~~~~
+
+ CRC loop method needs two devices to work, main device and crc
+device. It exports loop device which has exactly same contents as main
+device, with exception that errors are generated when crc check
+fails. crc device is used for storing checksums, it has to be as big
+as sizeof(main) / 1024 + 16K.
+
+ CRC loop method has two modes of operation, normal and repair. In
+normal mode, when checksum error is detected on read, failure is
+generated (and error is logged). In repair mode, when checksum error
+is detected on read, failure is logged, but data on crc device is
+updated.
+
+ Repair mode is usefull when first installing CRC loop method, and
+when your machine crashes. [During crash, data could be updated on
+main but not on crc; leading to state with false errors]. Updating
+crcs is actually pretty easy: setup CRC loop in repair mode, then cat
+/dev/loop0 > /dev/null.
+

And here's software to set it up:

/*
 * crcsetup.c - setup and control crc's
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/fs.h>

#define dev_t unsigned short

#include "/usr/src/linux/include/linux/loop.h"

static char *progname;
static int repair = 0;

static char *device = "/dev/loop0";
static char *file = NULL;
static char *crcfile = NULL;

int set_loop(void)
{
        int offset = 1024;
        struct loop_info loopinfo;
        int fd, ffd, crcfd, mode, i;
        char buf[1024];
        
        mode = O_RDWR;
        if ((ffd = open (file, O_RDWR)) < 0) {
          perror (file);
          return 1;
        }
        if ((crcfd = open (crcfile, O_RDWR)) < 0) {
          perror (crcfile);
          return 1;
        }

        if ((fd = open (device, mode)) < 0) {
          perror (device);
          return 1;
        }

        memset(&loopinfo, 0, sizeof(loopinfo));
        strncpy(loopinfo.lo_name, file, LO_NAME_SIZE);
        loopinfo.lo_name[LO_NAME_SIZE-1] = 0;
        loopinfo.lo_encrypt_type = LO_CRYPT_CRC;
        loopinfo.lo_offset = offset;
        loopinfo.lo_encrypt_key_size = repair;
        if (ioctl(fd, LOOP_SET_FD, ffd) < 0) {
                perror("ioctl: LOOP_SET_FD");
                exit(1);
        }
        if (ioctl(fd, LOOP_SET_STATUS, &loopinfo) < 0) {
                (void) ioctl(fd, LOOP_CLR_FD, 0);
                perror("ioctl: LOOP_SET_STATUS");
                exit(1);
        }
        if (ioctl(fd, LOOP_CRC_SET_FD, crcfd) < 0) {
                perror("ioctl: LOOP_CRC_SET_FD");
                exit(1);
        }
        close(fd);
        close(ffd);
        return 0;
}

static int usage(void)
{
        fprintf(stderr, "usage:\n\
  %s -d loop_device -f main_device -c crc_device [-r]\n", progname);
        exit(1);
}

int main(int argc, char **argv)
{
        char c;

        progname = argv[0];
        while ((c = getopt(argc,argv,"d:f:c:r")) != EOF) {
                switch (c) {
                        case 'd':
                                device = optarg;
                                break;
                        case 'f':
                                file = optarg;
                                break;
                        case 'c':
                                crcfile = optarg;
                                break;
                        case 'r':
                                repair = 1;
                                break;
                        default:
                                usage();
                }
        }
        if ((!file) || (!crcfile))
                usage();
        set_loop();
        return 0;
}

-- 
I'm pavel@ucw.cz. "In my country we have almost anarchy and I don't care."
Panos Katsaloulis describing me w.r.t. patents me at discuss@linmodems.org

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Sun Apr 30 2000 - 21:00:12 EST