]> sourceware.org Git - dm.git/commitdiff
First stab at the mirror target.
authorPatrick Caulfield <pcaulfie@redhat.com>
Thu, 31 Jan 2002 15:19:49 +0000 (15:19 +0000)
committerPatrick Caulfield <pcaulfie@redhat.com>
Thu, 31 Jan 2002 15:19:49 +0000 (15:19 +0000)
kernel/common/dm-mirror.c [new file with mode: 0644]

diff --git a/kernel/common/dm-mirror.c b/kernel/common/dm-mirror.c
new file mode 100644 (file)
index 0000000..621e861
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+
+/*
+ * Mirror: maps a mirror range of a device.
+ */
+struct mirror_c {
+       struct dm_dev *fromdev;
+       struct dm_dev *todev;
+
+       unsigned long endsec;
+       unsigned long from_delta;
+       unsigned long to_delta;
+
+       unsigned long frompos;
+       unsigned long topos;
+       unsigned long chunk_size;
+
+       struct rw_semaphore lock;
+       int error;
+};
+
+/* Called when a duplicating I/O has finished */
+static void mirror_end_io(struct buffer_head *bh, int uptodate)
+{
+       struct mirror_c *lc = (struct mirror_c *) bh->b_private;
+
+       /* Flag error if it failed */
+       if (!uptodate) {
+               DMERR("Mirror copy to %s failed\n", kdevname(lc->todev->dev));
+               lc->error = 1;
+               dm_notify(lc); /* TODO: interface ?? */
+       }
+       kfree(bh);
+}
+
+
+/* Called when a chunk I/O has finished - we move onto the next one */
+static void copy_callback(int status, void *context)
+{
+       struct mirror_c *lc = (struct mirror_c *) context;
+
+       if (status != 0) {
+               DMERR("Mirror block %s on %s failed\n", status==1?"read":"write",
+                     status==1?kdevname(lc->fromdev->dev):kdevname(lc->todev->dev));
+               lc->error = 1;
+               return;
+       }
+
+       down_write(&lc->lock);
+
+       if (lc->frompos < lc->endsec && !lc->error) {
+               int chunks = min(lc->chunk_size, lc->endsec - lc->frompos);
+
+               /* Move onto the next block */
+               lc->frompos += lc->chunk_size;
+               lc->topos += lc->chunk_size;
+
+               if (dm_blockcopy(lc->frompos, lc->topos, chunks, lc->fromdev->dev, lc->todev->dev, 0, copy_callback, lc)) {
+                       DMERR("Mirror block copy to %s failed\n", kdevname(lc->todev->dev));
+
+                       dm_notify(lc); /* TODO: interface ?? */
+               }
+       }
+       else {
+               /* Say we've finished */
+               dm_notify(lc); /* TODO: interface ?? */
+       }
+       up_write(&lc->lock);
+}
+
+/*
+ * Construct a mirror mapping: <dev_path1> <offset> <dev_path2> <offset> <chunk-size>
+ */
+static int mirror_ctr(struct dm_table *t, offset_t b, offset_t l,
+                     int argc, char **argv, void **context)
+{
+       struct mirror_c *lc;
+       unsigned int chunk_size;
+       unsigned long offset1, offset2;
+       char *value;
+
+       if (argc != 5) {
+               *context = "dm-mirror: Not enough arguments";
+               return -EINVAL;
+       }
+
+       lc = kmalloc(sizeof(*lc), GFP_KERNEL);
+       if (lc == NULL) {
+               *context = "dm-mirror: Cannot allocate mirror context";
+               return -ENOMEM;
+       }
+
+       if (dm_table_get_device(t, argv[0], 0, l, &lc->fromdev)) {
+               *context = "dm-mirror: Device lookup failed";
+               goto bad;
+       }
+
+       offset1 = simple_strtoul(argv[1], &value, 10);
+       if (value == NULL) {
+               *context = "Invalid offset for dev1";
+               goto bad;
+       }
+
+       if (dm_table_get_device(t, argv[2], 0, l, &lc->todev)) {
+               *context = "dm-mirror: Device lookup failed";
+               goto bad;
+       }
+
+       offset2 = simple_strtoul(argv[3], &value, 10);
+       if (value == NULL) {
+               *context = "Invalid offset for dev2";
+               goto bad;
+       }
+
+       chunk_size = simple_strtoul(argv[4], &value, 10);
+       if (chunk_size == 0 || value == NULL) {
+               *context = "Invalid chunk size";
+               goto bad;
+       }
+
+       lc->from_delta = (int) offset1 - (int) b;
+       lc->to_delta = (int) offset2 - (int) b;
+       lc->frompos = offset1;
+       lc->topos = offset2;
+       lc->endsec = l;
+       lc->error  = 0;
+       lc->chunk_size = chunk_size;
+       init_rwsem(&lc->lock);
+       *context = lc;
+
+       if (dm_blockcopy(offset1, offset2, chunk_size, lc->fromdev->dev, lc->todev->dev, 0, copy_callback, lc)) {
+               DMERR("Initial mirror block copy failed\n");
+               dm_table_put_device(t, lc->fromdev);
+               dm_table_put_device(t, lc->todev);
+               kfree(lc);
+               return -EINVAL;
+       }
+       return 0;
+
+ bad:
+       kfree(lc);
+       return -EINVAL;
+}
+
+static void mirror_dtr(struct dm_table *t, void *c)
+{
+       struct mirror_c *lc = (struct mirror_c *) c;
+
+       dm_table_put_device(t, lc->fromdev);
+       dm_table_put_device(t, lc->todev);
+       kfree(c);
+}
+
+static int mirror_map(struct buffer_head *bh, int rw, void *context)
+{
+       struct mirror_c *lc = (struct mirror_c *) context;
+
+       down_read(&lc->lock);
+
+       bh->b_rdev = lc->fromdev->dev;
+       bh->b_rsector = bh->b_rsector + lc->from_delta;
+
+       /* If we've already copied this block then duplicated it to the mirror device */
+       if (rw == WRITE && bh->b_rsector < lc->frompos+lc->chunk_size) {
+
+               /* Schedule copy of I/O to other target */
+               /* TODO: kmalloc is naff here */
+               struct buffer_head *dbh = kmalloc(sizeof(struct buffer_head), GFP_KERNEL);
+               if (dbh) {
+                       *dbh = *bh;
+                       dbh->b_rdev    = lc->todev->dev;
+                       dbh->b_rsector = bh->b_rsector - lc->from_delta + lc->to_delta;
+                       dbh->b_end_io  = mirror_end_io;
+                       dbh->b_private = lc;
+
+                       generic_make_request(WRITE, dbh);
+               }
+               else {
+                       DMERR("kmalloc failed for mirror bh\n");
+                       lc->error = 1;
+                       dm_notify(lc); /* TODO: interface ?? */
+               }
+       }
+       up_read(&lc->lock);
+       return 1;
+}
+
+static struct target_type mirror_target = {
+       name:   "mirror",
+       module: THIS_MODULE,
+       ctr:    mirror_ctr,
+       dtr:    mirror_dtr,
+       map:    mirror_map,
+};
+
+static int __init mirror_init(void)
+{
+       int r = dm_register_target(&mirror_target);
+       if (r < 0)
+               DMERR("mirror: register failed %d", r);
+
+       return r;
+}
+
+void dm_mirror_exit(void)
+{
+       int r = dm_unregister_target(&mirror_target);
+
+       if (r < 0)
+               DMERR("mirror: unregister failed %d", r);
+}
+
+
+module_init(mirror_init);
+module_exit(dm_mirror_exit);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
This page took 0.029073 seconds and 5 git commands to generate.