dm - Import verbatim from NetBSD
authorAlex Hornung <ahornung@gmail.com>
Tue, 27 Apr 2010 19:13:09 +0000 (19:13 +0000)
committerAlex Hornung <ahornung@gmail.com>
Sun, 11 Jul 2010 21:05:46 +0000 (22:05 +0100)
19 files changed:
sys/dev/disk/dm/Makefile [new file with mode: 0644]
sys/dev/disk/dm/TODO [new file with mode: 0644]
sys/dev/disk/dm/device-mapper.c [new file with mode: 0644]
sys/dev/disk/dm/dm.h [new file with mode: 0644]
sys/dev/disk/dm/dm_dev.c [new file with mode: 0644]
sys/dev/disk/dm/dm_ioctl.c [new file with mode: 0644]
sys/dev/disk/dm/dm_pdev.c [new file with mode: 0644]
sys/dev/disk/dm/dm_table.c [new file with mode: 0644]
sys/dev/disk/dm/dm_target.c [new file with mode: 0644]
sys/dev/disk/dm/dm_target_error.c [new file with mode: 0644]
sys/dev/disk/dm/dm_target_linear.c [new file with mode: 0644]
sys/dev/disk/dm/dm_target_mirror.c [new file with mode: 0644]
sys/dev/disk/dm/dm_target_snapshot.c [new file with mode: 0644]
sys/dev/disk/dm/dm_target_stripe.c [new file with mode: 0644]
sys/dev/disk/dm/dm_target_zero.c [new file with mode: 0644]
sys/dev/disk/dm/doc/design.txt [new file with mode: 0644]
sys/dev/disk/dm/doc/proposal-dm.txt [new file with mode: 0644]
sys/dev/disk/dm/files.dm [new file with mode: 0644]
sys/dev/disk/dm/netbsd-dm.h [new file with mode: 0644]

diff --git a/sys/dev/disk/dm/Makefile b/sys/dev/disk/dm/Makefile
new file mode 100644 (file)
index 0000000..be6c844
--- /dev/null
@@ -0,0 +1,8 @@
+#       $NetBSD: Makefile,v 1.1 2009/12/05 11:34:37 haad Exp $
+
+INCSDIR= /usr/include/dev/dm
+
+# Only install includes which are used by userland
+INCS=   netbsd-dm.h
+
+.include <bsd.kinc.mk>
diff --git a/sys/dev/disk/dm/TODO b/sys/dev/disk/dm/TODO
new file mode 100644 (file)
index 0000000..719ec6c
--- /dev/null
@@ -0,0 +1,23 @@
+Important unimplemented features in current device-mapper implementation
+
+* Implement dm_dev_event_ioctl and dm_target_msg_ioctl functions
+
+* Write more targets mirror.
+
+* Snapshot target, there is some code in repository already
+
+                       General LVM tasks
+
+* Cleanup resize_ffs to properly resize filesystem on LV
+
+* Integrate LVM with the sysinst, so we can configure LVM during installation
+
+                       Pain in the sky tasks
+
+* Implement multipath target for network attached storage devices.
+
+* Cluster lvm extension, NetBSD needs Distributed Lock Manager for this.
+
+* Write GPL free libdevmapper and lvm2tools.
+  See http://mail-index.netbsd.org/tech-kern/2008/09/24/msg002794.html
+       
diff --git a/sys/dev/disk/dm/device-mapper.c b/sys/dev/disk/dm/device-mapper.c
new file mode 100644 (file)
index 0000000..21a106d
--- /dev/null
@@ -0,0 +1,660 @@
+/*        $NetBSD: device-mapper.c,v 1.22 2010/03/26 15:46:04 jakllsch Exp $ */
+
+/*
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * I want to say thank you to all people who helped me with this project.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/dkio.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/ioccom.h>
+#include <sys/kmem.h>
+
+#include "netbsd-dm.h"
+#include "dm.h"
+
+static dev_type_open(dmopen);
+static dev_type_close(dmclose);
+static dev_type_read(dmread);
+static dev_type_write(dmwrite);
+static dev_type_ioctl(dmioctl);
+static dev_type_strategy(dmstrategy);
+static dev_type_size(dmsize);
+
+/* attach and detach routines */
+void dmattach(int);
+#ifdef _MODULE
+static int dmdestroy(void);
+#endif
+
+static void dm_doinit(void);
+
+static int dm_cmd_to_fun(prop_dictionary_t);
+static int disk_ioctl_switch(dev_t, u_long, void *);
+static int dm_ioctl_switch(u_long);
+static void dmminphys(struct buf *);
+
+/* CF attach/detach functions used for power management */
+static int dm_detach(device_t, int);
+static void dm_attach(device_t, device_t, void *);
+static int dm_match(device_t, cfdata_t, void *);
+
+/* ***Variable-definitions*** */
+const struct bdevsw dm_bdevsw = {
+       .d_open = dmopen,
+       .d_close = dmclose,
+       .d_strategy = dmstrategy,
+       .d_ioctl = dmioctl,
+       .d_dump = nodump,
+       .d_psize = dmsize,
+       .d_flag = D_DISK | D_MPSAFE
+};
+
+const struct cdevsw dm_cdevsw = {
+       .d_open = dmopen,
+       .d_close = dmclose,
+       .d_read = dmread,
+       .d_write = dmwrite,
+       .d_ioctl = dmioctl,
+       .d_stop = nostop,
+       .d_tty = notty,
+       .d_poll = nopoll,
+       .d_mmap = nommap,
+       .d_kqfilter = nokqfilter,
+       .d_flag = D_DISK | D_MPSAFE
+};
+
+const struct dkdriver dmdkdriver = {
+       .d_strategy = dmstrategy
+};
+
+CFATTACH_DECL3_NEW(dm, 0,
+     dm_match, dm_attach, dm_detach, NULL, NULL, NULL,
+     DVF_DETACH_SHUTDOWN);
+
+extern struct cfdriver dm_cd;
+
+extern uint64_t dm_dev_counter;
+
+/*
+ * This array is used to translate cmd to function pointer.
+ *
+ * Interface between libdevmapper and lvm2tools uses different 
+ * names for one IOCTL call because libdevmapper do another thing
+ * then. When I run "info" or "mknodes" libdevmapper will send same
+ * ioctl to kernel but will do another things in userspace.
+ *
+ */
+struct cmd_function cmd_fn[] = {
+               { .cmd = "version", .fn = dm_get_version_ioctl},
+               { .cmd = "targets", .fn = dm_list_versions_ioctl},
+               { .cmd = "create",  .fn = dm_dev_create_ioctl},
+               { .cmd = "info",    .fn = dm_dev_status_ioctl},
+               { .cmd = "mknodes", .fn = dm_dev_status_ioctl},         
+               { .cmd = "names",   .fn = dm_dev_list_ioctl},
+               { .cmd = "suspend", .fn = dm_dev_suspend_ioctl},
+               { .cmd = "remove",  .fn = dm_dev_remove_ioctl}, 
+               { .cmd = "rename",  .fn = dm_dev_rename_ioctl},
+               { .cmd = "resume",  .fn = dm_dev_resume_ioctl},
+               { .cmd = "clear",   .fn = dm_table_clear_ioctl},
+               { .cmd = "deps",    .fn = dm_table_deps_ioctl},
+               { .cmd = "reload",  .fn = dm_table_load_ioctl},
+               { .cmd = "status",  .fn = dm_table_status_ioctl},
+               { .cmd = "table",   .fn = dm_table_status_ioctl},
+               {NULL, NULL}    
+};
+
+#ifdef _MODULE
+#include <sys/module.h>
+
+/* Autoconf defines */
+CFDRIVER_DECL(dm, DV_DISK, NULL);
+
+MODULE(MODULE_CLASS_DRIVER, dm, NULL);
+
+/* New module handle routine */
+static int
+dm_modcmd(modcmd_t cmd, void *arg)
+{
+       int error, bmajor, cmajor;
+
+       error = 0;
+       bmajor = -1;
+       cmajor = -1;
+
+       switch (cmd) {
+       case MODULE_CMD_INIT:
+               error = config_cfdriver_attach(&dm_cd);
+               if (error)
+                       break;
+
+               error = config_cfattach_attach(dm_cd.cd_name, &dm_ca);
+               if (error) {
+                       aprint_error("%s: unable to register cfattach\n",
+                           dm_cd.cd_name);
+                       return error;
+               }
+
+               error = devsw_attach(dm_cd.cd_name, &dm_bdevsw, &bmajor,
+                   &dm_cdevsw, &cmajor);
+               if (error) {
+                       config_cfattach_detach(dm_cd.cd_name, &dm_ca);
+                       config_cfdriver_detach(&dm_cd);
+                       break;
+               }
+
+               dm_doinit();
+
+               break;
+
+       case MODULE_CMD_FINI:
+               /*
+                * Disable unloading of dm module if there are any devices
+                * defined in driver. This is probably too strong we need
+                * to disable auto-unload only if there is mounted dm device
+                * present.
+                */ 
+               if (dm_dev_counter > 0)
+                       return EBUSY;
+
+               error = dmdestroy();
+               if (error)
+                       break;
+
+               config_cfdriver_detach(&dm_cd);
+
+               devsw_detach(&dm_bdevsw, &dm_cdevsw);
+               break;
+       case MODULE_CMD_STAT:
+               return ENOTTY;
+
+       default:
+               return ENOTTY;
+       }
+
+       return error;
+}
+#endif /* _MODULE */
+
+/*
+ * dm_match:
+ *
+ *     Autoconfiguration match function for pseudo-device glue.
+ */
+static int
+dm_match(device_t parent, cfdata_t match,
+    void *aux)
+{
+
+       /* Pseudo-device; always present. */
+       return (1);
+}
+
+/*
+ * dm_attach:
+ *
+ *     Autoconfiguration attach function for pseudo-device glue.
+ */
+static void
+dm_attach(device_t parent, device_t self,
+    void *aux)
+{
+       return;
+}
+
+
+/*
+ * dm_detach:
+ *
+ *     Autoconfiguration detach function for pseudo-device glue.
+ * This routine is called by dm_ioctl::dm_dev_remove_ioctl and by autoconf to
+ * remove devices created in device-mapper. 
+ */
+static int
+dm_detach(device_t self, int flags)
+{
+       dm_dev_t *dmv;
+
+       /* Detach device from global device list */
+       if ((dmv = dm_dev_detach(self)) == NULL)
+               return ENOENT;
+
+       /* Destroy active table first.  */
+       dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE);
+
+       /* Destroy inactive table if exits, too. */
+       dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
+       
+       dm_table_head_destroy(&dmv->table_head);
+
+       /* Destroy disk device structure */
+       disk_detach(dmv->diskp);
+       disk_destroy(dmv->diskp);
+
+       /* Destroy device */
+       (void)dm_dev_free(dmv);
+
+       /* Decrement device counter After removing device */
+       atomic_dec_64(&dm_dev_counter);
+
+       return 0;
+}
+
+static void
+dm_doinit(void)
+{
+       dm_target_init();
+       dm_dev_init();
+       dm_pdev_init();
+}
+
+/* attach routine */
+void
+dmattach(int n)
+{
+       int error;
+
+       error = config_cfattach_attach(dm_cd.cd_name, &dm_ca);
+       if (error) {
+               aprint_error("%s: unable to register cfattach\n",
+                   dm_cd.cd_name);
+       } else {
+               dm_doinit();
+       }
+}
+
+#ifdef _MODULE
+/* Destroy routine */
+static int
+dmdestroy(void)
+{
+       int error;
+
+       error = config_cfattach_detach(dm_cd.cd_name, &dm_ca);
+       if (error)
+               return error;
+       
+       dm_dev_destroy();
+       dm_pdev_destroy();
+       dm_target_destroy();
+
+       return 0;
+}
+#endif /* _MODULE */
+
+static int
+dmopen(dev_t dev, int flags, int mode, struct lwp *l)
+{
+
+       aprint_debug("dm open routine called %" PRIu32 "\n", minor(dev));
+       return 0;
+}
+
+static int
+dmclose(dev_t dev, int flags, int mode, struct lwp *l)
+{
+
+       aprint_debug("dm close routine called %" PRIu32 "\n", minor(dev));
+       return 0;
+}
+
+
+static int
+dmioctl(dev_t dev, const u_long cmd, void *data, int flag, struct lwp *l)
+{
+       int r;
+       prop_dictionary_t dm_dict_in;
+
+       r = 0;
+
+       aprint_debug("dmioctl called\n");
+       
+       KASSERT(data != NULL);
+       
+       if (( r = disk_ioctl_switch(dev, cmd, data)) == ENOTTY) {
+               struct plistref *pref = (struct plistref *) data;
+
+               /* Check if we were called with NETBSD_DM_IOCTL ioctl
+                  otherwise quit. */
+               if ((r = dm_ioctl_switch(cmd)) != 0)
+                       return r;
+
+               if((r = prop_dictionary_copyin_ioctl(pref, cmd, &dm_dict_in)) != 0)
+                       return r;
+
+               if ((r = dm_check_version(dm_dict_in)) != 0)
+                       goto cleanup_exit;
+
+               /* run ioctl routine */
+               if ((r = dm_cmd_to_fun(dm_dict_in)) != 0)
+                       goto cleanup_exit;
+
+cleanup_exit:
+               r = prop_dictionary_copyout_ioctl(pref, cmd, dm_dict_in);
+               prop_object_release(dm_dict_in);
+       }
+
+       return r;
+}
+
+/*
+ * Translate command sent from libdevmapper to func.
+ */
+static int
+dm_cmd_to_fun(prop_dictionary_t dm_dict){
+       int i, r;
+       prop_string_t command;
+       
+       r = 0;
+
+       if ((command = prop_dictionary_get(dm_dict, DM_IOCTL_COMMAND)) == NULL)
+               return EINVAL;
+
+       for(i = 0; cmd_fn[i].cmd != NULL; i++)
+               if (prop_string_equals_cstring(command, cmd_fn[i].cmd))
+                       break;
+
+       if (cmd_fn[i].cmd == NULL)
+               return EINVAL;
+
+       aprint_debug("ioctl %s called\n", cmd_fn[i].cmd);
+       r = cmd_fn[i].fn(dm_dict);
+
+       return r;
+}
+
+/* Call apropriate ioctl handler function. */
+static int
+dm_ioctl_switch(u_long cmd)
+{
+
+       switch(cmd) {
+
+       case NETBSD_DM_IOCTL:
+               aprint_debug("dm NetBSD_DM_IOCTL called\n");
+               break;
+       default:
+                aprint_debug("dm unknown ioctl called\n");
+                return ENOTTY;
+                break; /* NOT REACHED */
+       }
+
+        return 0;
+}
+
+ /*
+  * Check for disk specific ioctls.
+  */
+
+static int
+disk_ioctl_switch(dev_t dev, u_long cmd, void *data)
+{
+       dm_dev_t *dmv;
+
+       /* disk ioctls make sense only on block devices */
+       if (minor(dev) == 0)
+               return ENOTTY;
+       
+       switch(cmd) {
+       case DIOCGWEDGEINFO:
+       {
+               struct dkwedge_info *dkw = (void *) data;
+
+               if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL)
+                       return ENODEV;
+
+               aprint_debug("DIOCGWEDGEINFO ioctl called\n");
+
+               strlcpy(dkw->dkw_devname, dmv->name, 16);
+               strlcpy(dkw->dkw_wname, dmv->name, DM_NAME_LEN);
+               strlcpy(dkw->dkw_parent, dmv->name, 16);
+
+               dkw->dkw_offset = 0;
+               dkw->dkw_size = dm_table_size(&dmv->table_head);
+               strcpy(dkw->dkw_ptype, DKW_PTYPE_FFS);
+
+               dm_dev_unbusy(dmv);
+               break;
+       }
+
+       case DIOCGDISKINFO:
+       {
+               struct plistref *pref = (struct plistref *) data;
+
+               if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL)
+                       return ENODEV;
+
+               if (dmv->diskp->dk_info == NULL) {
+                       dm_dev_unbusy(dmv);
+                       return ENOTSUP;
+               } else
+                       prop_dictionary_copyout_ioctl(pref, cmd,
+                           dmv->diskp->dk_info);
+
+               dm_dev_unbusy(dmv);
+               break;
+       }
+       
+       default:
+               aprint_debug("unknown disk_ioctl called\n");
+               return ENOTTY;
+               break; /* NOT REACHED */
+       }
+
+       return 0;
+}
+
+/*
+ * Do all IO operations on dm logical devices.
+ */
+static void
+dmstrategy(struct buf *bp)
+{
+       dm_dev_t *dmv;
+       dm_table_t  *tbl;
+       dm_table_entry_t *table_en;
+       struct buf *nestbuf;
+
+       uint32_t dev_type;
+
+       uint64_t buf_start, buf_len, issued_len;
+       uint64_t table_start, table_end;
+       uint64_t start, end;
+
+       buf_start = bp->b_blkno * DEV_BSIZE;
+       buf_len = bp->b_bcount;
+
+       tbl = NULL; 
+
+       table_end = 0;
+       dev_type = 0;
+       issued_len = 0;
+
+       if ((dmv = dm_dev_lookup(NULL, NULL, minor(bp->b_dev))) == NULL) {
+               bp->b_error = EIO;
+               bp->b_resid = bp->b_bcount;
+               biodone(bp);
+               return;
+       } 
+
+       if (bounds_check_with_mediasize(bp, DEV_BSIZE,
+           dm_table_size(&dmv->table_head)) <= 0) {
+               dm_dev_unbusy(dmv);
+               bp->b_resid = bp->b_bcount;
+               biodone(bp);
+               return;
+       }
+
+       /*
+        * disk(9) is part of device structure and it can't be used without
+        * mutual exclusion, use diskp_mtx until it will be fixed.
+        */
+       mutex_enter(&dmv->diskp_mtx);
+       disk_busy(dmv->diskp);
+       mutex_exit(&dmv->diskp_mtx);
+
+       /* Select active table */
+       tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE);
+
+        /* Nested buffers count down to zero therefore I have
+           to set bp->b_resid to maximal value. */
+       bp->b_resid = bp->b_bcount;
+
+       /*
+        * Find out what tables I want to select.
+        */
+       SLIST_FOREACH(table_en, tbl, next)
+       {
+               /* I need need number of bytes not blocks. */
+               table_start = table_en->start * DEV_BSIZE;
+               /*
+                * I have to sub 1 from table_en->length to prevent
+                * off by one error
+                */
+               table_end = table_start + (table_en->length)* DEV_BSIZE;
+
+               start = MAX(table_start, buf_start);
+
+               end = MIN(table_end, buf_start + buf_len);
+
+               aprint_debug("----------------------------------------\n");
+               aprint_debug("table_start %010" PRIu64", table_end %010"
+                   PRIu64 "\n", table_start, table_end);
+               aprint_debug("buf_start %010" PRIu64", buf_len %010"
+                   PRIu64"\n", buf_start, buf_len);
+               aprint_debug("start-buf_start %010"PRIu64", end %010"
+                   PRIu64"\n", start - buf_start, end);
+               aprint_debug("start %010" PRIu64" , end %010"
+                    PRIu64"\n", start, end);
+               aprint_debug("\n----------------------------------------\n");
+
+               if (start < end) {
+                       /* create nested buffer  */
+                       nestbuf = getiobuf(NULL, true);
+
+                       nestiobuf_setup(bp, nestbuf, start - buf_start,
+                           (end - start));
+
+                       issued_len += end - start;
+
+                       /* I need number of blocks. */
+                       nestbuf->b_blkno = (start - table_start) / DEV_BSIZE;
+
+                       table_en->target->strategy(table_en, nestbuf);
+               }
+       }
+
+       if (issued_len < buf_len)
+               nestiobuf_done(bp, buf_len - issued_len, EINVAL);
+
+       mutex_enter(&dmv->diskp_mtx);
+       disk_unbusy(dmv->diskp, buf_len, bp != NULL ? bp->b_flags & B_READ : 0);
+       mutex_exit(&dmv->diskp_mtx);
+
+       dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE);
+       dm_dev_unbusy(dmv);
+
+       return;
+}
+
+
+static int
+dmread(dev_t dev, struct uio *uio, int flag)
+{
+
+       return (physio(dmstrategy, NULL, dev, B_READ, dmminphys, uio));
+}
+
+static int
+dmwrite(dev_t dev, struct uio *uio, int flag)
+{
+
+       return (physio(dmstrategy, NULL, dev, B_WRITE, dmminphys, uio));
+}
+
+static int
+dmsize(dev_t dev)
+{
+       dm_dev_t *dmv;
+       uint64_t size;
+
+       size = 0;
+
+       if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL)
+                       return -ENOENT;
+
+       size = dm_table_size(&dmv->table_head);
+       dm_dev_unbusy(dmv);
+       
+       return size;
+}
+
+static void
+dmminphys(struct buf *bp)
+{
+
+       bp->b_bcount = MIN(bp->b_bcount, MAXPHYS);
+}
+
+void
+dmgetproperties(struct disk *disk, dm_table_head_t *head)
+{
+       prop_dictionary_t disk_info, odisk_info, geom;
+       int dmp_size;
+
+       dmp_size = dm_table_size(head);
+       disk_info = prop_dictionary_create();
+       geom = prop_dictionary_create();
+
+       prop_dictionary_set_cstring_nocopy(disk_info, "type", "ESDI");
+       prop_dictionary_set_uint64(geom, "sectors-per-unit", dmp_size);
+       prop_dictionary_set_uint32(geom, "sector-size",
+           DEV_BSIZE /* XXX 512? */);
+       prop_dictionary_set_uint32(geom, "sectors-per-track", 32);
+       prop_dictionary_set_uint32(geom, "tracks-per-cylinder", 64);
+       prop_dictionary_set_uint32(geom, "cylinders-per-unit", dmp_size / 2048);
+       prop_dictionary_set(disk_info, "geometry", geom);
+       prop_object_release(geom);
+
+       odisk_info = disk->dk_info;
+       disk->dk_info = disk_info;
+
+       if (odisk_info != NULL)
+               prop_object_release(odisk_info);
+}      
diff --git a/sys/dev/disk/dm/dm.h b/sys/dev/disk/dm/dm.h
new file mode 100644 (file)
index 0000000..1cb6b9c
--- /dev/null
@@ -0,0 +1,381 @@
+/*        $NetBSD: dm.h,v 1.17 2009/12/29 23:37:48 haad Exp $      */
+
+/*
+ * Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _DM_DEV_H_
+#define _DM_DEV_H_
+
+
+#ifdef _KERNEL
+
+#include <sys/errno.h>
+
+#include <sys/atomic.h>
+#include <sys/condvar.h>
+#include <sys/mutex.h>
+#include <sys/rwlock.h>
+#include <sys/queue.h>
+
+#include <sys/device.h>
+#include <sys/disklabel.h>
+
+#include <prop/proplib.h>
+
+#define DM_MAX_TYPE_NAME 16
+#define DM_NAME_LEN 128
+#define DM_UUID_LEN 129
+
+#define DM_VERSION_MAJOR       4
+#define DM_VERSION_MINOR       16
+
+#define DM_VERSION_PATCHLEVEL  0
+
+/*** Internal device-mapper structures ***/
+
+/*
+ * A table entry describes a physical range of the logical volume.
+ */
+#define MAX_TARGET_STRING_LEN 32
+
+/*
+ * A device mapper table is a list of physical ranges plus the mapping target
+ * applied to them.
+ */
+
+typedef struct dm_table_entry {
+       struct dm_dev *dm_dev;          /* backlink */
+       uint64_t start;
+       uint64_t length;
+
+       struct dm_target *target;      /* Link to table target. */
+       void *target_config;           /* Target specific data. */
+       SLIST_ENTRY(dm_table_entry) next;
+} dm_table_entry_t;
+
+SLIST_HEAD(dm_table, dm_table_entry);
+
+typedef struct dm_table dm_table_t;
+
+typedef struct dm_table_head {
+       /* Current active table is selected with this. */
+       int cur_active_table; 
+       struct dm_table tables[2];
+
+       kmutex_t   table_mtx;
+       kcondvar_t table_cv; /*IO waiting cv */
+
+       uint32_t io_cnt;
+} dm_table_head_t;
+
+#define MAX_DEV_NAME 32
+
+/*
+ * This structure is used to store opened vnodes for disk with name.
+ * I need this because devices can be opened only once, but I can
+ * have more then one device on one partition.
+ */
+
+typedef struct dm_pdev {
+       char name[MAX_DEV_NAME];
+
+       struct vnode *pdev_vnode;
+       int ref_cnt; /* reference counter for users ofthis pdev */
+
+       SLIST_ENTRY(dm_pdev) next_pdev;
+} dm_pdev_t;
+
+/*
+ * This structure is called for every device-mapper device.
+ * It points to SLIST of device tables and mirrored, snapshoted etc. devices.
+ */
+TAILQ_HEAD(dm_dev_head, dm_dev) dm_devs;
+                               
+typedef struct dm_dev {
+       char name[DM_NAME_LEN];
+       char uuid[DM_UUID_LEN];
+
+       device_t devt; /* pointer to autoconf device_t structure */
+       uint64_t minor;
+       uint32_t flags; /* store communication protocol flags */
+
+       kmutex_t dev_mtx; /* mutex for generall device lock */
+       kcondvar_t dev_cv; /* cv for between ioctl synchronisation */
+       
+       uint32_t event_nr;
+       uint32_t ref_cnt;
+
+       uint32_t dev_type;
+
+       dm_table_head_t table_head;
+
+       struct dm_dev_head upcalls;
+       
+       struct disk *diskp;
+       kmutex_t diskp_mtx;
+       
+       TAILQ_ENTRY(dm_dev) next_upcall; /* LIST of mirrored, snapshoted devices. */
+
+       TAILQ_ENTRY(dm_dev) next_devlist; /* Major device list. */
+} dm_dev_t;
+
+/* Device types used for upcalls */
+#define DM_ZERO_DEV            (1 << 0)
+#define DM_ERROR_DEV           (1 << 1)        
+#define DM_LINEAR_DEV          (1 << 2)
+#define DM_MIRROR_DEV          (1 << 3)
+#define DM_STRIPE_DEV          (1 << 4)
+#define DM_SNAPSHOT_DEV        (1 << 5)
+#define DM_SNAPSHOT_ORIG_DEV   (1 << 6)
+#define DM_SPARE_DEV           (1 << 7)
+/* Set this device type only during dev remove ioctl. */
+#define DM_DELETING_DEV        (1 << 8) 
+
+
+/* for zero, error : dm_target->target_config == NULL */
+                               
+/*
+ * Target config is initiated with target_init function.
+ */
+                               
+/* for linear : */
+typedef struct target_linear_config {
+       dm_pdev_t *pdev;
+       uint64_t offset;
+} dm_target_linear_config_t;
+
+/* for stripe : */
+typedef struct target_stripe_config {
+#define MAX_STRIPES 2
+       struct target_linear_config stripe_devs[MAX_STRIPES];
+       uint8_t stripe_num;
+       uint64_t stripe_chunksize;
+       size_t params_len;
+} dm_target_stripe_config_t;
+
+/* for mirror : */
+typedef struct target_mirror_config {
+#define MAX_MIRROR_COPIES 4
+       dm_pdev_t *orig;
+       dm_pdev_t *copies[MAX_MIRROR_COPIES];
+
+       /* copied blocks bitmaps administration etc*/
+       dm_pdev_t *log_pdev;    /* for administration */
+       uint64_t log_regionsize;        /* blocksize of mirror */
+
+       /* list of parts that still need copied etc.; run length encoded? */
+} dm_target_mirror_config_t;
+
+
+/* for snapshot : */
+typedef struct target_snapshot_config {
+       dm_pdev_t *tsc_snap_dev;
+       /* cow dev is set only for persistent snapshot devices */
+       dm_pdev_t *tsc_cow_dev;
+       
+       uint64_t tsc_chunk_size;
+       uint32_t tsc_persistent_dev;
+} dm_target_snapshot_config_t;
+
+/* for snapshot-origin devices */
+typedef struct target_snapshot_origin_config {
+       dm_pdev_t *tsoc_real_dev;
+       /* list of snapshots ? */
+} dm_target_snapshot_origin_config_t;
+
+/* constant dm_target structures for error, zero, linear, stripes etc. */
+typedef struct dm_target {
+       char name[DM_MAX_TYPE_NAME];
+       /* Initialize target_config area */
+       int (*init)(dm_dev_t *, void **, char *);
+
+       /* Destroy target_config area */
+       int (*destroy)(dm_table_entry_t *);
+       
+       int (*deps) (dm_table_entry_t *, prop_array_t);
+       /*
+        * Status routine is called to get params string, which is target
+        * specific. When dm_table_status_ioctl is called with flag
+        * DM_STATUS_TABLE_FLAG I have to sent params string back.
+        */
+       char * (*status)(void *);
+       int (*strategy)(dm_table_entry_t *, struct buf *);
+       int (*upcall)(dm_table_entry_t *, struct buf *);
+       
+       uint32_t version[3];
+       int ref_cnt;
+       
+       TAILQ_ENTRY(dm_target) dm_target_next;
+} dm_target_t;
+
+/* Interface structures */
+
+/*
+ * This structure is used to translate command sent to kernel driver in
+ * <key>command</key>
+ * <value></value>
+ * to function which I can call.
+ */
+struct cmd_function {
+       const char *cmd;
+       int  (*fn)(prop_dictionary_t);
+};
+
+/* device-mapper */
+void dmgetproperties(struct disk *, dm_table_head_t *);
+
+/* dm_ioctl.c */
+int dm_dev_create_ioctl(prop_dictionary_t);
+int dm_dev_list_ioctl(prop_dictionary_t);
+int dm_dev_remove_ioctl(prop_dictionary_t);
+int dm_dev_rename_ioctl(prop_dictionary_t);
+int dm_dev_resume_ioctl(prop_dictionary_t);
+int dm_dev_status_ioctl(prop_dictionary_t);
+int dm_dev_suspend_ioctl(prop_dictionary_t);
+
+int dm_check_version(prop_dictionary_t);
+int dm_get_version_ioctl(prop_dictionary_t);
+int dm_list_versions_ioctl(prop_dictionary_t);
+
+int dm_table_clear_ioctl(prop_dictionary_t);
+int dm_table_deps_ioctl(prop_dictionary_t);
+int dm_table_load_ioctl(prop_dictionary_t);
+int dm_table_status_ioctl(prop_dictionary_t);
+
+/* dm_target.c */
+dm_target_t* dm_target_alloc(const char *);
+dm_target_t* dm_target_autoload(const char *);
+int dm_target_destroy(void);
+int dm_target_insert(dm_target_t *);
+prop_array_t dm_target_prop_list(void);
+dm_target_t* dm_target_lookup(const char *);
+int dm_target_rem(char *);
+void dm_target_unbusy(dm_target_t *);
+void dm_target_busy(dm_target_t *);
+
+/* XXX temporally add */
+int dm_target_init(void);
+
+#define DM_MAX_PARAMS_SIZE 1024
+
+/* dm_target_zero.c */
+int dm_target_zero_init(dm_dev_t *, void**,  char *);
+char * dm_target_zero_status(void *);
+int dm_target_zero_strategy(dm_table_entry_t *, struct buf *);
+int dm_target_zero_destroy(dm_table_entry_t *);
+int dm_target_zero_deps(dm_table_entry_t *, prop_array_t);
+int dm_target_zero_upcall(dm_table_entry_t *, struct buf *);
+
+/* dm_target_error.c */
+int dm_target_error_init(dm_dev_t *, void**, char *);
+char * dm_target_error_status(void *);
+int dm_target_error_strategy(dm_table_entry_t *, struct buf *);
+int dm_target_error_deps(dm_table_entry_t *, prop_array_t);
+int dm_target_error_destroy(dm_table_entry_t *);
+int dm_target_error_upcall(dm_table_entry_t *, struct buf *);
+
+/* dm_target_linear.c */
+int dm_target_linear_init(dm_dev_t *, void**, char *);
+char * dm_target_linear_status(void *);
+int dm_target_linear_strategy(dm_table_entry_t *, struct buf *);
+int dm_target_linear_deps(dm_table_entry_t *, prop_array_t);
+int dm_target_linear_destroy(dm_table_entry_t *);
+int dm_target_linear_upcall(dm_table_entry_t *, struct buf *);
+
+/* Generic function used to convert char to string */
+uint64_t atoi(const char *); 
+
+/* dm_target_mirror.c */
+int dm_target_mirror_init(dm_dev_t *, void**, char *);
+char * dm_target_mirror_status(void *);
+int dm_target_mirror_strategy(dm_table_entry_t *, struct buf *);
+int dm_target_mirror_deps(dm_table_entry_t *, prop_array_t);
+int dm_target_mirror_destroy(dm_table_entry_t *);
+int dm_target_mirror_upcall(dm_table_entry_t *, struct buf *);
+
+/* dm_target_stripe.c */
+int dm_target_stripe_init(dm_dev_t *, void**, char *);
+char * dm_target_stripe_status(void *);
+int dm_target_stripe_strategy(dm_table_entry_t *, struct buf *);
+int dm_target_stripe_deps(dm_table_entry_t *, prop_array_t);
+int dm_target_stripe_destroy(dm_table_entry_t *);
+int dm_target_stripe_upcall(dm_table_entry_t *, struct buf *);
+
+/* dm_target_snapshot.c */
+int dm_target_snapshot_init(dm_dev_t *, void**, char *);
+char * dm_target_snapshot_status(void *);
+int dm_target_snapshot_strategy(dm_table_entry_t *, struct buf *);
+int dm_target_snapshot_deps(dm_table_entry_t *, prop_array_t);
+int dm_target_snapshot_destroy(dm_table_entry_t *);
+int dm_target_snapshot_upcall(dm_table_entry_t *, struct buf *);
+
+/* dm snapshot origin driver */
+int dm_target_snapshot_orig_init(dm_dev_t *, void**, char *);
+char * dm_target_snapshot_orig_status(void *);
+int dm_target_snapshot_orig_strategy(dm_table_entry_t *, struct buf *);
+int dm_target_snapshot_orig_deps(dm_table_entry_t *, prop_array_t);
+int dm_target_snapshot_orig_destroy(dm_table_entry_t *);
+int dm_target_snapshot_orig_upcall(dm_table_entry_t *, struct buf *);
+
+/* dm_table.c  */
+#define DM_TABLE_ACTIVE 0
+#define DM_TABLE_INACTIVE 1
+
+int dm_table_destroy(dm_table_head_t *, uint8_t);
+uint64_t dm_table_size(dm_table_head_t *);
+dm_table_t * dm_table_get_entry(dm_table_head_t *, uint8_t);
+int dm_table_get_target_count(dm_table_head_t *, uint8_t);
+void dm_table_release(dm_table_head_t *, uint8_t s);
+void dm_table_switch_tables(dm_table_head_t *);
+void dm_table_head_init(dm_table_head_t *);
+void dm_table_head_destroy(dm_table_head_t *);
+
+/* dm_dev.c */
+dm_dev_t* dm_dev_alloc(void);
+void dm_dev_busy(dm_dev_t *);
+int dm_dev_destroy(void);
+dm_dev_t* dm_dev_detach(device_t);
+int dm_dev_free(dm_dev_t *);
+int dm_dev_init(void);
+int dm_dev_insert(dm_dev_t *);
+dm_dev_t* dm_dev_lookup(const char *, const char *, int);
+prop_array_t dm_dev_prop_list(void);
+dm_dev_t* dm_dev_rem(const char *, const char *, int);
+/*int dm_dev_test_minor(int);*/
+void dm_dev_unbusy(dm_dev_t *);
+
+/* dm_pdev.c */
+int dm_pdev_decr(dm_pdev_t *);
+int dm_pdev_destroy(void);
+int dm_pdev_init(void);
+dm_pdev_t* dm_pdev_insert(const char *);
+
+#endif /*_KERNEL*/
+
+#endif /*_DM_DEV_H_*/
diff --git a/sys/dev/disk/dm/dm_dev.c b/sys/dev/disk/dm/dm_dev.c
new file mode 100644 (file)
index 0000000..bf6f646
--- /dev/null
@@ -0,0 +1,402 @@
+/*        $NetBSD: dm_dev.c,v 1.8 2010/01/04 00:19:08 haad Exp $      */
+
+/*
+ * Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/ioccom.h>
+#include <sys/kmem.h>
+
+#include "netbsd-dm.h"
+#include "dm.h"
+
+static dm_dev_t *dm_dev_lookup_name(const char *);
+static dm_dev_t *dm_dev_lookup_uuid(const char *);
+static dm_dev_t *dm_dev_lookup_minor(int);
+
+static struct dm_dev_head dm_dev_list =
+TAILQ_HEAD_INITIALIZER(dm_dev_list);
+
+kmutex_t dm_dev_mutex;
+
+/* dm_dev_mutex must be holdby caller before using disable_dev. */
+__inline static void
+disable_dev(dm_dev_t * dmv)
+{
+       TAILQ_REMOVE(&dm_dev_list, dmv, next_devlist);
+       mutex_enter(&dmv->dev_mtx);
+       mutex_exit(&dm_dev_mutex);
+       while (dmv->ref_cnt != 0)
+               cv_wait(&dmv->dev_cv, &dmv->dev_mtx);
+       mutex_exit(&dmv->dev_mtx);
+}
+/*
+ * Generic function used to lookup dm_dev_t. Calling with dm_dev_name
+ * and dm_dev_uuid NULL is allowed.
+ */
+dm_dev_t *
+dm_dev_lookup(const char *dm_dev_name, const char *dm_dev_uuid,
+    int dm_dev_minor)
+{
+       dm_dev_t *dmv;
+
+       dmv = NULL;
+       mutex_enter(&dm_dev_mutex);
+
+       /* KASSERT(dm_dev_name != NULL && dm_dev_uuid != NULL && dm_dev_minor
+        * > 0); */
+       if (dm_dev_minor > 0)
+               if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL) {
+                       dm_dev_busy(dmv);
+                       mutex_exit(&dm_dev_mutex);
+                       return dmv;
+               }
+       if (dm_dev_name != NULL)
+               if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL) {
+                       dm_dev_busy(dmv);
+                       mutex_exit(&dm_dev_mutex);
+                       return dmv;
+               }
+       if (dm_dev_uuid != NULL)
+               if ((dmv = dm_dev_lookup_uuid(dm_dev_uuid)) != NULL) {
+                       dm_dev_busy(dmv);
+                       mutex_exit(&dm_dev_mutex);
+                       return dmv;
+               }
+       mutex_exit(&dm_dev_mutex);
+       return NULL;
+}
+
+
+/*
+ * Lookup device with its minor number.
+ */
+static dm_dev_t *
+dm_dev_lookup_minor(int dm_dev_minor)
+{
+       dm_dev_t *dmv;
+
+       TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
+               if (dm_dev_minor == dmv->minor)
+                       return dmv;
+       }
+
+       return NULL;
+}
+/*
+ * Lookup device with it's device name.
+ */
+static dm_dev_t *
+dm_dev_lookup_name(const char *dm_dev_name)
+{
+       dm_dev_t *dmv;
+       int dlen;
+       int slen;
+
+       slen = strlen(dm_dev_name);
+
+       if (slen == 0)
+               return NULL;
+
+       TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
+
+               dlen = strlen(dmv->name);
+
+               if (slen != dlen)
+                       continue;
+
+               if (strncmp(dm_dev_name, dmv->name, slen) == 0)
+                       return dmv;
+       }
+
+       return NULL;
+}
+/*
+ * Lookup device with it's device uuid. Used mostly by LVM2tools.
+ */
+static dm_dev_t *
+dm_dev_lookup_uuid(const char *dm_dev_uuid)
+{
+       dm_dev_t *dmv;
+       size_t len;
+
+       len = 0;
+       len = strlen(dm_dev_uuid);
+
+       if (len == 0)
+               return NULL;
+
+       TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
+
+               if (strlen(dmv->uuid) != len)
+                       continue;
+
+               if (strncmp(dm_dev_uuid, dmv->uuid, strlen(dmv->uuid)) == 0)
+                       return dmv;
+       }
+
+       return NULL;
+}
+/*
+ * Insert new device to the global list of devices.
+ */
+int
+dm_dev_insert(dm_dev_t * dev)
+{
+       dm_dev_t *dmv;
+       int r;
+
+       dmv = NULL;
+       r = 0;
+
+       KASSERT(dev != NULL);
+       mutex_enter(&dm_dev_mutex);
+       if (((dmv = dm_dev_lookup_uuid(dev->uuid)) == NULL) &&
+           ((dmv = dm_dev_lookup_name(dev->name)) == NULL) &&
+           ((dmv = dm_dev_lookup_minor(dev->minor)) == NULL)) {
+
+               TAILQ_INSERT_TAIL(&dm_dev_list, dev, next_devlist);
+
+       } else
+               r = EEXIST;
+
+       mutex_exit(&dm_dev_mutex);
+       return r;
+}
+#ifdef notyet
+/*
+ * Lookup device with its minor number.
+ */
+int
+dm_dev_test_minor(int dm_dev_minor)
+{
+       dm_dev_t *dmv;
+
+       mutex_enter(&dm_dev_mutex);
+       TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
+               if (dm_dev_minor == dmv->minor) {
+                       mutex_exit(&dm_dev_mutex);
+                       return 1;
+               }
+       }
+       mutex_exit(&dm_dev_mutex);
+
+       return 0;
+}
+#endif
+
+/*
+ * dm_dev_lookup_devt look for selected device_t. We keep this routine
+ * outside of dm_dev_lookup because it is a temporally solution.
+ *
+ * TODO: This is a hack autoconf should be more flexible.
+ */
+dm_dev_t *
+dm_dev_detach(device_t devt)
+{
+       dm_dev_t *dmv;
+
+       mutex_enter(&dm_dev_mutex);
+       TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
+               if (devt == dmv->devt) {
+                       disable_dev(dmv);
+                       return dmv;
+               }
+       }
+       mutex_exit(&dm_dev_mutex);
+
+       return NULL;
+}
+/*
+ * Remove device selected with dm_dev from global list of devices.
+ */
+dm_dev_t *
+dm_dev_rem(const char *dm_dev_name, const char *dm_dev_uuid,
+    int dm_dev_minor)
+{
+       dm_dev_t *dmv;
+       dmv = NULL;
+
+       mutex_enter(&dm_dev_mutex);
+
+       if (dm_dev_minor > 0)
+               if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL) {
+                       disable_dev(dmv);
+                       return dmv;
+               }
+       if (dm_dev_name != NULL)
+               if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL) {
+                       disable_dev(dmv);
+                       return dmv;
+               }
+       if (dm_dev_uuid != NULL)
+               if ((dmv = dm_dev_lookup_name(dm_dev_uuid)) != NULL) {
+                       disable_dev(dmv);
+                       return dmv;
+               }
+       mutex_exit(&dm_dev_mutex);
+
+       return NULL;
+}
+/*
+ * Destroy all devices created in device-mapper. Remove all tables
+ * free all allocated memmory.
+ */
+int
+dm_dev_destroy(void)
+{
+       dm_dev_t *dmv;
+       mutex_enter(&dm_dev_mutex);
+
+       while (TAILQ_FIRST(&dm_dev_list) != NULL) {
+
+               dmv = TAILQ_FIRST(&dm_dev_list);
+
+               TAILQ_REMOVE(&dm_dev_list, TAILQ_FIRST(&dm_dev_list),
+                   next_devlist);
+
+               mutex_enter(&dmv->dev_mtx);
+
+               while (dmv->ref_cnt != 0)
+                       cv_wait(&dmv->dev_cv, &dmv->dev_mtx);
+
+               /* Destroy active table first.  */
+               dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE);
+
+               /* Destroy inactive table if exits, too. */
+               dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
+
+               dm_table_head_destroy(&dmv->table_head);
+
+               mutex_exit(&dmv->dev_mtx);
+               mutex_destroy(&dmv->dev_mtx);
+               cv_destroy(&dmv->dev_cv);
+
+               (void) kmem_free(dmv, sizeof(dm_dev_t));
+       }
+       mutex_exit(&dm_dev_mutex);
+
+       mutex_destroy(&dm_dev_mutex);
+       return 0;
+}
+/*
+ * Allocate new device entry.
+ */
+dm_dev_t *
+dm_dev_alloc(void)
+{
+       dm_dev_t *dmv;
+
+       dmv = kmem_zalloc(sizeof(dm_dev_t), KM_SLEEP);
+
+       if (dmv != NULL)
+               dmv->diskp = kmem_zalloc(sizeof(struct disk), KM_SLEEP);
+
+       return dmv;
+}
+/*
+ * Freed device entry.
+ */
+int
+dm_dev_free(dm_dev_t * dmv)
+{
+       KASSERT(dmv != NULL);
+
+       mutex_destroy(&dmv->dev_mtx);
+       mutex_destroy(&dmv->diskp_mtx);
+       cv_destroy(&dmv->dev_cv);
+
+       if (dmv->diskp != NULL)
+               (void) kmem_free(dmv->diskp, sizeof(struct disk));
+
+       (void) kmem_free(dmv, sizeof(dm_dev_t));
+
+       return 0;
+}
+
+void
+dm_dev_busy(dm_dev_t * dmv)
+{
+       mutex_enter(&dmv->dev_mtx);
+       dmv->ref_cnt++;
+       mutex_exit(&dmv->dev_mtx);
+}
+
+void
+dm_dev_unbusy(dm_dev_t * dmv)
+{
+       KASSERT(dmv->ref_cnt != 0);
+
+       mutex_enter(&dmv->dev_mtx);
+       if (--dmv->ref_cnt == 0)
+               cv_broadcast(&dmv->dev_cv);
+       mutex_exit(&dmv->dev_mtx);
+}
+/*
+ * Return prop_array of dm_targer_list dictionaries.
+ */
+prop_array_t
+dm_dev_prop_list(void)
+{
+       dm_dev_t *dmv;
+       prop_array_t dev_array;
+       prop_dictionary_t dev_dict;
+
+       dev_array = prop_array_create();
+
+       mutex_enter(&dm_dev_mutex);
+
+       TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) {
+               dev_dict = prop_dictionary_create();
+
+               prop_dictionary_set_cstring(dev_dict, DM_DEV_NAME, dmv->name);
+               prop_dictionary_set_uint32(dev_dict, DM_DEV_DEV, dmv->minor);
+
+               prop_array_add(dev_array, dev_dict);
+               prop_object_release(dev_dict);
+       }
+
+       mutex_exit(&dm_dev_mutex);
+       return dev_array;
+}
+/*
+ * Initialize global device mutex.
+ */
+int
+dm_dev_init(void)
+{
+       TAILQ_INIT(&dm_dev_list);       /* initialize global dev list */
+       mutex_init(&dm_dev_mutex, MUTEX_DEFAULT, IPL_NONE);
+       return 0;
+}
diff --git a/sys/dev/disk/dm/dm_ioctl.c b/sys/dev/disk/dm/dm_ioctl.c
new file mode 100644 (file)
index 0000000..4e00dbf
--- /dev/null
@@ -0,0 +1,977 @@
+/* $NetBSD: dm_ioctl.c,v 1.21 2010/02/25 20:48:58 jakllsch Exp $      */
+
+/*
+ * Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Locking is used to synchronise between ioctl calls and between dm_table's
+ * users.
+ *
+ * ioctl locking:
+ * Simple reference counting, to count users of device will be used routines
+ * dm_dev_busy/dm_dev_unbusy are used for that.
+ * dm_dev_lookup/dm_dev_rem call dm_dev_busy before return(caller is therefore
+ * holder of reference_counter last).
+ *
+ * ioctl routines which change/remove dm_dev parameters must wait on
+ * dm_dev::dev_cv and when last user will call dm_dev_unbusy they will wake
+ * up them.
+ *
+ * table_head locking:
+ * To access table entries dm_table_* routines must be used.
+ *
+ * dm_table_get_entry will increment table users reference
+ * counter. It will return active or inactive table depedns
+ * on uint8_t argument.
+ *
+ * dm_table_release must be called for every table_entry from
+ * dm_table_get_entry. Between these to calls tables can'tbe switched
+ * or destroyed.
+ *
+ * dm_table_head_init initialize talbe_entries SLISTS and io_cv.
+ *
+ * dm_table_head_destroy destroy cv.
+ *
+ * There are two types of users for dm_table_head first type will
+ * only read list and try to do anything with it e.g. dmstrategy,
+ * dm_table_size etc. There is another user for table_head which wants
+ * to change table lists e.g. dm_dev_resume_ioctl, dm_dev_remove_ioctl,
+ * dm_table_clear_ioctl.
+ *
+ * NOTE: It is not allowed to call dm_table_destroy, dm_table_switch_tables
+ *       with hold table reference counter. Table reference counter is hold
+ *       after calling dm_table_get_entry routine. After calling this
+ *       function user must call dm_table_release before any writer table
+ *       operation.
+ *
+ * Example: dm_table_get_entry
+ *          dm_table_destroy/dm_table_switch_tables
+ * This exaple will lead to deadlock situation because after dm_table_get_entry
+ * table reference counter is != 0 and dm_table_destroy have to wait on cv until
+ * reference counter is 0.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/device.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/kmem.h>
+#include <sys/malloc.h>
+#include <sys/vnode.h>
+
+#include <machine/int_fmtio.h>
+
+#include "netbsd-dm.h"
+#include "dm.h"
+
+static uint64_t sc_minor_num;
+extern const struct dkdriver dmdkdriver;
+uint64_t dm_dev_counter;
+
+/* Generic cf_data for device-mapper driver */
+static struct cfdata dm_cfdata = {
+       .cf_name = "dm",
+       .cf_atname = "dm",
+       .cf_fstate = FSTATE_STAR,
+       .cf_unit = 0
+};
+#define DM_REMOVE_FLAG(flag, name) do {                                        \
+               prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \
+               flag &= ~name;                                          \
+               prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \
+} while (/*CONSTCOND*/0)
+
+#define DM_ADD_FLAG(flag, name) do {                                   \
+               prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \
+               flag |= name;                                           \
+               prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \
+} while (/*CONSTCOND*/0)
+
+static int dm_dbg_print_flags(int);
+
+/*
+ * Print flags sent to the kernel from libevmapper.
+ */
+static int
+dm_dbg_print_flags(int flags)
+{
+       aprint_debug("dbg_print --- %d\n", flags);
+
+       if (flags & DM_READONLY_FLAG)
+               aprint_debug("dbg_flags: DM_READONLY_FLAG set In/Out\n");
+
+       if (flags & DM_SUSPEND_FLAG)
+               aprint_debug("dbg_flags: DM_SUSPEND_FLAG set In/Out \n");
+
+       if (flags & DM_PERSISTENT_DEV_FLAG)
+               aprint_debug("db_flags: DM_PERSISTENT_DEV_FLAG set In\n");
+
+       if (flags & DM_STATUS_TABLE_FLAG)
+               aprint_debug("dbg_flags: DM_STATUS_TABLE_FLAG set In\n");
+
+       if (flags & DM_ACTIVE_PRESENT_FLAG)
+               aprint_debug("dbg_flags: DM_ACTIVE_PRESENT_FLAG set Out\n");
+
+       if (flags & DM_INACTIVE_PRESENT_FLAG)
+               aprint_debug("dbg_flags: DM_INACTIVE_PRESENT_FLAG set Out\n");
+
+       if (flags & DM_BUFFER_FULL_FLAG)
+               aprint_debug("dbg_flags: DM_BUFFER_FULL_FLAG set Out\n");
+
+       if (flags & DM_SKIP_BDGET_FLAG)
+               aprint_debug("dbg_flags: DM_SKIP_BDGET_FLAG set In\n");
+
+       if (flags & DM_SKIP_LOCKFS_FLAG)
+               aprint_debug("dbg_flags: DM_SKIP_LOCKFS_FLAG set In\n");
+
+       if (flags & DM_NOFLUSH_FLAG)
+               aprint_debug("dbg_flags: DM_NOFLUSH_FLAG set In\n");
+
+       return 0;
+}
+/*
+ * Get version ioctl call I do it as default therefore this
+ * function is unused now.
+ */
+int
+dm_get_version_ioctl(prop_dictionary_t dm_dict)
+{
+
+       return 0;
+}
+/*
+ * Get list of all available targets from global
+ * target list and sent them back to libdevmapper.
+ */
+int
+dm_list_versions_ioctl(prop_dictionary_t dm_dict)
+{
+       prop_array_t target_list;
+       uint32_t flags;
+
+       flags = 0;
+
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
+
+       dm_dbg_print_flags(flags);
+       target_list = dm_target_prop_list();
+
+       prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, target_list);
+       prop_object_release(target_list);
+
+       return 0;
+}
+/*
+ * Create in-kernel entry for device. Device attributes such as name, uuid are
+ * taken from proplib dictionary.
+ *
+ */
+int
+dm_dev_create_ioctl(prop_dictionary_t dm_dict)
+{
+       dm_dev_t *dmv;
+       const char *name, *uuid;
+       int r, flags;
+       device_t devt;
+
+       r = 0;
+       flags = 0;
+       name = NULL;
+       uuid = NULL;
+
+       /* Get needed values from dictionary. */
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
+
+       dm_dbg_print_flags(flags);
+
+       /* Lookup name and uuid if device already exist quit. */
+       if ((dmv = dm_dev_lookup(name, uuid, -1)) != NULL) {
+               DM_ADD_FLAG(flags, DM_EXISTS_FLAG);     /* Device already exists */
+               dm_dev_unbusy(dmv);
+               return EEXIST;
+       }
+       if ((devt = config_attach_pseudo(&dm_cfdata)) == NULL) {
+               aprint_error("Unable to attach pseudo device dm/%s\n", name);
+               return (ENOMEM);
+       }
+       if ((dmv = dm_dev_alloc()) == NULL)
+               return ENOMEM;
+
+       if (uuid)
+               strncpy(dmv->uuid, uuid, DM_UUID_LEN);
+       else
+               dmv->uuid[0] = '\0';
+
+       if (name)
+               strlcpy(dmv->name, name, DM_NAME_LEN);
+
+       dmv->minor = atomic_inc_64_nv(&sc_minor_num);
+       dmv->flags = 0;         /* device flags are set when needed */
+       dmv->ref_cnt = 0;
+       dmv->event_nr = 0;
+       dmv->dev_type = 0;
+       dmv->devt = devt;
+
+       dm_table_head_init(&dmv->table_head);
+
+       mutex_init(&dmv->dev_mtx, MUTEX_DEFAULT, IPL_NONE);
+       mutex_init(&dmv->diskp_mtx, MUTEX_DEFAULT, IPL_NONE);
+       cv_init(&dmv->dev_cv, "dm_dev");
+
+       if (flags & DM_READONLY_FLAG)
+               dmv->flags |= DM_READONLY_FLAG;
+
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
+
+       disk_init(dmv->diskp, dmv->name, &dmdkdriver);
+       disk_attach(dmv->diskp);
+
+       dmv->diskp->dk_info = NULL;
+
+       if ((r = dm_dev_insert(dmv)) != 0)
+               dm_dev_free(dmv);
+
+       DM_ADD_FLAG(flags, DM_EXISTS_FLAG);
+       DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
+
+       /* Increment device counter After creating device */
+       atomic_inc_64(&dm_dev_counter);
+
+       return r;
+}
+/*
+ * Get list of created device-mapper devices fromglobal list and
+ * send it to kernel.
+ *
+ * Output dictionary:
+ *
+ * <key>cmd_data</key>
+ *  <array>
+ *   <dict>
+ *    <key>name<key>
+ *    <string>...</string>
+ *
+ *    <key>dev</key>
+ *    <integer>...</integer>
+ *   </dict>
+ *  </array>
+ *
+ */
+int
+dm_dev_list_ioctl(prop_dictionary_t dm_dict)
+{
+       prop_array_t dev_list;
+
+       uint32_t flags;
+
+       flags = 0;
+
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
+
+       dm_dbg_print_flags(flags);
+
+       dev_list = dm_dev_prop_list();
+
+       prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, dev_list);
+       prop_object_release(dev_list);
+
+       return 0;
+}
+/*
+ * Rename selected devices old name is in struct dm_ioctl.
+ * newname is taken from dictionary
+ *
+ * <key>cmd_data</key>
+ *  <array>
+ *   <string>...</string>
+ *  </array>
+ */
+int
+dm_dev_rename_ioctl(prop_dictionary_t dm_dict)
+{
+       prop_array_t cmd_array;
+       dm_dev_t *dmv;
+
+       const char *name, *uuid, *n_name;
+       uint32_t flags, minor;
+
+       name = NULL;
+       uuid = NULL;
+       minor = 0;
+
+       /* Get needed values from dictionary. */
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
+
+       dm_dbg_print_flags(flags);
+
+       cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA);
+
+       prop_array_get_cstring_nocopy(cmd_array, 0, &n_name);
+
+       if (strlen(n_name) + 1 > DM_NAME_LEN)
+               return EINVAL;
+
+       if ((dmv = dm_dev_rem(name, uuid, minor)) == NULL) {
+               DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
+               return ENOENT;
+       }
+       /* change device name */
+       /*
+        * XXX How to deal with this change, name only used in
+        * dm_dev_routines, should I add dm_dev_change_name which will run
+        * under the dm_dev_list mutex ?
+        */
+       strlcpy(dmv->name, n_name, DM_NAME_LEN);
+
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
+       prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid);
+
+       dm_dev_insert(dmv);
+
+       return 0;
+}
+/*
+ * Remove device from global list I have to remove active
+ * and inactive tables first.
+ */
+int
+dm_dev_remove_ioctl(prop_dictionary_t dm_dict)
+{
+       dm_dev_t *dmv;
+       const char *name, *uuid;
+       uint32_t flags, minor;
+       device_t devt;
+
+       flags = 0;
+       name = NULL;
+       uuid = NULL;
+
+       /* Get needed values from dictionary. */
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
+
+       dm_dbg_print_flags(flags);
+
+       /*
+        * This seems as hack to me, probably use routine dm_dev_get_devt to
+        * atomicaly get devt from device.
+        */
+       if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
+               DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
+               return ENOENT;
+       }
+       devt = dmv->devt;
+
+       dm_dev_unbusy(dmv);
+
+       /*
+        * This will call dm_detach routine which will actually removes
+        * device.
+        */
+       return config_detach(devt, DETACH_QUIET);
+}
+/*
+ * Return actual state of device to libdevmapper.
+ */
+int
+dm_dev_status_ioctl(prop_dictionary_t dm_dict)
+{
+       dm_dev_t *dmv;
+       const char *name, *uuid;
+       uint32_t flags, j, minor;
+
+       name = NULL;
+       uuid = NULL;
+       flags = 0;
+       j = 0;
+
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
+
+       if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
+               DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
+               return ENOENT;
+       }
+       dm_dbg_print_flags(dmv->flags);
+
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
+       prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid);
+
+       if (dmv->flags & DM_SUSPEND_FLAG)
+               DM_ADD_FLAG(flags, DM_SUSPEND_FLAG);
+
+       /*
+        * Add status flags for tables I have to check both active and
+        * inactive tables.
+        */
+       if ((j = dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE))) {
+               DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
+       } else
+               DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
+
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_TARGET_COUNT, j);
+
+       if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE))
+               DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
+       else
+               DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
+
+       dm_dev_unbusy(dmv);
+
+       return 0;
+}
+/*
+ * Set only flag to suggest that device is suspended. This call is
+ * not supported in NetBSD.
+ *
+ */
+int
+dm_dev_suspend_ioctl(prop_dictionary_t dm_dict)
+{
+       dm_dev_t *dmv;
+       const char *name, *uuid;
+       uint32_t flags, minor;
+
+       name = NULL;
+       uuid = NULL;
+       flags = 0;
+
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
+
+       if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
+               DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
+               return ENOENT;
+       }
+       atomic_or_32(&dmv->flags, DM_SUSPEND_FLAG);
+
+       dm_dbg_print_flags(dmv->flags);
+
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, dmv->flags);
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
+
+       dm_dev_unbusy(dmv);
+
+       /* Add flags to dictionary flag after dmv -> dict copy */
+       DM_ADD_FLAG(flags, DM_EXISTS_FLAG);
+
+       return 0;
+}
+/*
+ * Simulate Linux behaviour better and switch tables here and not in
+ * dm_table_load_ioctl.
+ */
+int
+dm_dev_resume_ioctl(prop_dictionary_t dm_dict)
+{
+       dm_dev_t *dmv;
+       const char *name, *uuid;
+       uint32_t flags, minor;
+
+       name = NULL;
+       uuid = NULL;
+       flags = 0;
+
+       /*
+        * char *xml; xml = prop_dictionary_externalize(dm_dict);
+        * printf("%s\n",xml);
+        */
+
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
+
+       /* Remove device from global device list */
+       if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
+               DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
+               return ENOENT;
+       }
+       atomic_and_32(&dmv->flags, ~(DM_SUSPEND_FLAG | DM_INACTIVE_PRESENT_FLAG));
+       atomic_or_32(&dmv->flags, DM_ACTIVE_PRESENT_FLAG);
+
+       dm_table_switch_tables(&dmv->table_head);
+
+       DM_ADD_FLAG(flags, DM_EXISTS_FLAG);
+
+       dmgetproperties(dmv->diskp, &dmv->table_head);
+
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt);
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags);
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
+
+       dm_dev_unbusy(dmv);
+
+       /* Destroy inactive table after resume. */
+       dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
+
+       return 0;
+}
+/*
+ * Table management routines
+ * lvm2tools doens't send name/uuid to kernel with table
+ * for lookup I have to use minor number.
+ */
+
+/*
+ * Remove inactive table from device. Routines which work's with inactive tables
+ * doesn't need to synchronise with dmstrategy. They can synchronise themselves with mutex?.
+ *
+ */
+int
+dm_table_clear_ioctl(prop_dictionary_t dm_dict)
+{
+       dm_dev_t *dmv;
+       const char *name, *uuid;
+       uint32_t flags, minor;
+
+       dmv = NULL;
+       name = NULL;
+       uuid = NULL;
+       flags = 0;
+       minor = 0;
+
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
+
+       aprint_debug("Clearing inactive table from device: %s--%s\n",
+           name, uuid);
+
+       if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
+               DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
+               return ENOENT;
+       }
+       /* Select unused table */
+       dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
+
+       atomic_and_32(&dmv->flags, ~DM_INACTIVE_PRESENT_FLAG);
+
+       dm_dev_unbusy(dmv);
+
+       return 0;
+}
+/*
+ * Get list of physical devices for active table.
+ * Get dev_t from pdev vnode and insert it into cmd_array.
+ *
+ * XXX. This function is called from lvm2tools to get information
+ *      about physical devices, too e.g. during vgcreate.
+ */
+int
+dm_table_deps_ioctl(prop_dictionary_t dm_dict)
+{
+       dm_dev_t *dmv;
+       dm_table_t *tbl;
+       dm_table_entry_t *table_en;
+
+       prop_array_t cmd_array;
+       const char *name, *uuid;
+       uint32_t flags, minor;
+
+       int table_type;
+       size_t i;
+
+       name = NULL;
+       uuid = NULL;
+       dmv = NULL;
+       flags = 0;
+
+       i = 0;
+
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
+
+       /* create array for dev_t's */
+       cmd_array = prop_array_create();
+
+       if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
+               DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
+               return ENOENT;
+       }
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
+       prop_dictionary_set_cstring(dm_dict, DM_IOCTL_NAME, dmv->name);
+       prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid);
+
+       aprint_debug("Getting table deps for device: %s\n", dmv->name);
+
+       /*
+        * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query
+        * INACTIVE TABLE
+        */
+       if (flags & DM_QUERY_INACTIVE_TABLE_FLAG)
+               table_type = DM_TABLE_INACTIVE;
+       else
+               table_type = DM_TABLE_ACTIVE;
+
+       tbl = dm_table_get_entry(&dmv->table_head, table_type);
+
+       SLIST_FOREACH(table_en, tbl, next)
+           table_en->target->deps(table_en, cmd_array);
+
+       dm_table_release(&dmv->table_head, table_type);
+       dm_dev_unbusy(dmv);
+
+       prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array);
+       prop_object_release(cmd_array);
+
+       return 0;
+}
+/*
+ * Load new table/tables to device.
+ * Call apropriate target init routine open all physical pdev's and
+ * link them to device. For other targets mirror, strip, snapshot
+ * etc. also add dependency devices to upcalls list.
+ *
+ * Load table to inactive slot table are switched in dm_device_resume_ioctl.
+ * This simulates Linux behaviour better there should not be any difference.
+ *
+ */
+int
+dm_table_load_ioctl(prop_dictionary_t dm_dict)
+{
+       dm_dev_t *dmv;
+       dm_table_entry_t *table_en, *last_table;
+       dm_table_t *tbl;
+       dm_target_t *target;
+
+       prop_object_iterator_t iter;
+       prop_array_t cmd_array;
+       prop_dictionary_t target_dict;
+
+       const char *name, *uuid, *type;
+
+       uint32_t flags, ret, minor;
+
+       char *str;
+
+       ret = 0;
+       flags = 0;
+       name = NULL;
+       uuid = NULL;
+       dmv = NULL;
+       last_table = NULL;
+       str = NULL;
+
+       /*
+        * char *xml; xml = prop_dictionary_externalize(dm_dict);
+        * printf("%s\n",xml);
+        */
+
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
+
+       cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA);
+       iter = prop_array_iterator(cmd_array);
+       dm_dbg_print_flags(flags);
+
+       if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
+               DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
+               return ENOENT;
+       }
+       aprint_debug("Loading table to device: %s--%d\n", name,
+           dmv->table_head.cur_active_table);
+
+       /*
+        * I have to check if this table slot is not used by another table list.
+        * if it is used I should free them.
+        */
+       if (dmv->flags & DM_INACTIVE_PRESENT_FLAG)
+               dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
+
+       dm_dbg_print_flags(dmv->flags);
+       tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_INACTIVE);
+
+       aprint_debug("dmv->name = %s\n", dmv->name);
+
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
+
+       while ((target_dict = prop_object_iterator_next(iter)) != NULL) {
+
+               prop_dictionary_get_cstring_nocopy(target_dict,
+                   DM_TABLE_TYPE, &type);
+               /*
+                * If we want to deny table with 2 or more different
+                * target we should do it here
+                */
+               if (((target = dm_target_lookup(type)) == NULL) &&
+                   ((target = dm_target_autoload(type)) == NULL)) {
+                       dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
+                       dm_dev_unbusy(dmv);
+                       return ENOENT;
+               }
+               if ((table_en = kmem_alloc(sizeof(dm_table_entry_t),
+                           KM_SLEEP)) == NULL) {
+                       dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
+                       dm_dev_unbusy(dmv);
+                       return ENOMEM;
+               }
+               prop_dictionary_get_uint64(target_dict, DM_TABLE_START,
+                   &table_en->start);
+               prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH,
+                   &table_en->length);
+
+               table_en->target = target;
+               table_en->dm_dev = dmv;
+               table_en->target_config = NULL;
+
+               /*
+                * There is a parameter string after dm_target_spec
+                * structure which  points to /dev/wd0a 284 part of
+                * table. String str points to this text. This can be
+                * null and therefore it should be checked before we try to
+                * use it.
+                */
+               prop_dictionary_get_cstring(target_dict,
+                   DM_TABLE_PARAMS, (char **) &str);
+
+               if (SLIST_EMPTY(tbl))
+                       /* insert this table to head */
+                       SLIST_INSERT_HEAD(tbl, table_en, next);
+               else
+                       SLIST_INSERT_AFTER(last_table, table_en, next);
+
+               /*
+                * Params string is different for every target,
+                * therfore I have to pass it to target init
+                * routine and parse parameters there.
+                */
+
+               if ((ret = target->init(dmv, &table_en->target_config,
+                           str)) != 0) {
+
+                       dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
+                       dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
+                       free(str, M_TEMP);
+
+                       dm_dev_unbusy(dmv);
+                       return ret;
+               }
+               last_table = table_en;
+               free(str, M_TEMP);
+       }
+       prop_object_iterator_release(iter);
+
+       DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
+       atomic_or_32(&dmv->flags, DM_INACTIVE_PRESENT_FLAG);
+
+       dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE);
+       dm_dev_unbusy(dmv);
+       return 0;
+}
+/*
+ * Get description of all tables loaded to device from kernel
+ * and send it to libdevmapper.
+ *
+ * Output dictionary for every table:
+ *
+ * <key>cmd_data</key>
+ * <array>
+ *   <dict>
+ *    <key>type<key>
+ *    <string>...</string>
+ *
+ *    <key>start</key>
+ *    <integer>...</integer>
+ *
+ *    <key>length</key>
+ *    <integer>...</integer>
+ *
+ *    <key>params</key>
+ *    <string>...</string>
+ *   </dict>
+ * </array>
+ *
+ */
+int
+dm_table_status_ioctl(prop_dictionary_t dm_dict)
+{
+       dm_dev_t *dmv;
+       dm_table_t *tbl;
+       dm_table_entry_t *table_en;
+
+       prop_array_t cmd_array;
+       prop_dictionary_t target_dict;
+
+       uint32_t rec_size, minor;
+
+       const char *name, *uuid;
+       char *params;
+       int flags;
+       int table_type;
+
+       dmv = NULL;
+       uuid = NULL;
+       name = NULL;
+       params = NULL;
+       flags = 0;
+       rec_size = 0;
+
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name);
+       prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags);
+       prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor);
+
+       cmd_array = prop_array_create();
+
+       if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) {
+               DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
+               return ENOENT;
+       }
+       /*
+        * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query
+        * INACTIVE TABLE
+        */
+       if (flags & DM_QUERY_INACTIVE_TABLE_FLAG)
+               table_type = DM_TABLE_INACTIVE;
+       else
+               table_type = DM_TABLE_ACTIVE;
+
+       if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE))
+               DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
+       else {
+               DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG);
+
+               if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE))
+                       DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
+               else {
+                       DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG);
+               }
+       }
+
+       if (dmv->flags & DM_SUSPEND_FLAG)
+               DM_ADD_FLAG(flags, DM_SUSPEND_FLAG);
+
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
+
+       aprint_debug("Status of device tables: %s--%d\n",
+           name, dmv->table_head.cur_active_table);
+
+       tbl = dm_table_get_entry(&dmv->table_head, table_type);
+
+       SLIST_FOREACH(table_en, tbl, next) {
+               target_dict = prop_dictionary_create();
+               aprint_debug("%016" PRIu64 ", length %016" PRIu64
+                   ", target %s\n", table_en->start, table_en->length,
+                   table_en->target->name);
+
+               prop_dictionary_set_uint64(target_dict, DM_TABLE_START,
+                   table_en->start);
+               prop_dictionary_set_uint64(target_dict, DM_TABLE_LENGTH,
+                   table_en->length);
+
+               prop_dictionary_set_cstring(target_dict, DM_TABLE_TYPE,
+                   table_en->target->name);
+
+               /* dm_table_get_cur_actv.table ?? */
+               prop_dictionary_set_int32(target_dict, DM_TABLE_STAT,
+                   dmv->table_head.cur_active_table);
+
+               if (flags |= DM_STATUS_TABLE_FLAG) {
+                       params = table_en->target->status
+                           (table_en->target_config);
+
+                       if (params != NULL) {
+                               prop_dictionary_set_cstring(target_dict,
+                                   DM_TABLE_PARAMS, params);
+
+                               kmem_free(params, DM_MAX_PARAMS_SIZE);
+                       }
+               }
+               prop_array_add(cmd_array, target_dict);
+               prop_object_release(target_dict);
+       }
+
+       dm_table_release(&dmv->table_head, table_type);
+       dm_dev_unbusy(dmv);
+
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags);
+       prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array);
+       prop_object_release(cmd_array);
+
+       return 0;
+}
+
+
+/*
+ * For every call I have to set kernel driver version.
+ * Because I can have commands supported only in other
+ * newer/later version. This routine is called for every
+ * ioctl command.
+ */
+int
+dm_check_version(prop_dictionary_t dm_dict)
+{
+       size_t i;
+       int dm_version[3];
+       prop_array_t ver;
+
+       ver = prop_dictionary_get(dm_dict, DM_IOCTL_VERSION);
+
+       for (i = 0; i < 3; i++)
+               prop_array_get_uint32(ver, i, &dm_version[i]);
+
+       if (DM_VERSION_MAJOR != dm_version[0] || DM_VERSION_MINOR < dm_version[1]) {
+               aprint_debug("libdevmapper/kernel version mismatch "
+                   "kernel: %d.%d.%d libdevmapper: %d.%d.%d\n",
+                   DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL,
+                   dm_version[0], dm_version[1], dm_version[2]);
+
+               return EIO;
+       }
+       prop_array_set_uint32(ver, 0, DM_VERSION_MAJOR);
+       prop_array_set_uint32(ver, 1, DM_VERSION_MINOR);
+       prop_array_set_uint32(ver, 2, DM_VERSION_PATCHLEVEL);
+
+       prop_dictionary_set(dm_dict, DM_IOCTL_VERSION, ver);
+
+       return 0;
+}
diff --git a/sys/dev/disk/dm/dm_pdev.c b/sys/dev/disk/dm/dm_pdev.c
new file mode 100644 (file)
index 0000000..2506248
--- /dev/null
@@ -0,0 +1,237 @@
+/*        $NetBSD: dm_pdev.c,v 1.6 2010/01/04 00:19:08 haad Exp $      */
+
+/*
+ * Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/disk.h>
+#include <sys/fcntl.h>
+#include <sys/kmem.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+
+#include <dev/dkvar.h>
+
+#include "dm.h"
+
+SLIST_HEAD(dm_pdevs, dm_pdev) dm_pdev_list;
+
+       kmutex_t dm_pdev_mutex;
+
+       static dm_pdev_t *dm_pdev_alloc(const char *);
+       static int dm_pdev_rem(dm_pdev_t *);
+       static dm_pdev_t *dm_pdev_lookup_name(const char *);
+
+/*
+ * Find used pdev with name == dm_pdev_name.
+ */
+       dm_pdev_t *
+                 dm_pdev_lookup_name(const char *dm_pdev_name)
+{
+       dm_pdev_t *dm_pdev;
+       int dlen;
+       int slen;
+
+       KASSERT(dm_pdev_name != NULL);
+
+       slen = strlen(dm_pdev_name);
+
+       SLIST_FOREACH(dm_pdev, &dm_pdev_list, next_pdev) {
+               dlen = strlen(dm_pdev->name);
+
+               if (slen != dlen)
+                       continue;
+
+               if (strncmp(dm_pdev_name, dm_pdev->name, slen) == 0)
+                       return dm_pdev;
+       }
+
+       return NULL;
+}
+/*
+ * Create entry for device with name dev_name and open vnode for it.
+ * If entry already exists in global SLIST I will only increment
+ * reference counter.
+ */
+dm_pdev_t *
+dm_pdev_insert(const char *dev_name)
+{
+       dm_pdev_t *dmp;
+       int error;
+
+       KASSERT(dev_name != NULL);
+
+       mutex_enter(&dm_pdev_mutex);
+       dmp = dm_pdev_lookup_name(dev_name);
+
+       if (dmp != NULL) {
+               dmp->ref_cnt++;
+               aprint_debug("dmp_pdev_insert pdev %s already in tree\n", dev_name);
+               mutex_exit(&dm_pdev_mutex);
+               return dmp;
+       }
+       mutex_exit(&dm_pdev_mutex);
+
+       if ((dmp = dm_pdev_alloc(dev_name)) == NULL)
+               return NULL;
+
+       error = dk_lookup(dev_name, curlwp, &dmp->pdev_vnode, UIO_SYSSPACE);
+       if (error) {
+               aprint_debug("dk_lookup on device: %s failed with error %d!\n",
+                   dev_name, error);
+               kmem_free(dmp, sizeof(dm_pdev_t));
+               return NULL;
+       }
+       dmp->ref_cnt = 1;
+
+       mutex_enter(&dm_pdev_mutex);
+       SLIST_INSERT_HEAD(&dm_pdev_list, dmp, next_pdev);
+       mutex_exit(&dm_pdev_mutex);
+
+       return dmp;
+}
+/*
+ * Initialize pdev subsystem.
+ */
+int
+dm_pdev_init(void)
+{
+       SLIST_INIT(&dm_pdev_list);      /* initialize global pdev list */
+       mutex_init(&dm_pdev_mutex, MUTEX_DEFAULT, IPL_NONE);
+
+       return 0;
+}
+/*
+ * Allocat new pdev structure if is not already present and
+ * set name.
+ */
+static dm_pdev_t *
+dm_pdev_alloc(const char *name)
+{
+       dm_pdev_t *dmp;
+
+       if ((dmp = kmem_zalloc(sizeof(dm_pdev_t), KM_SLEEP)) == NULL)
+               return NULL;
+
+       strlcpy(dmp->name, name, MAX_DEV_NAME);
+
+       dmp->ref_cnt = 0;
+       dmp->pdev_vnode = NULL;
+
+       return dmp;
+}
+/*
+ * Destroy allocated dm_pdev.
+ */
+static int
+dm_pdev_rem(dm_pdev_t * dmp)
+{
+       int err;
+
+       KASSERT(dmp != NULL);
+
+       if (dmp->pdev_vnode != NULL) {
+               err = vn_close(dmp->pdev_vnode, FREAD | FWRITE, FSCRED);
+               if (err != 0)
+                       return err;
+       }
+       kmem_free(dmp, sizeof(*dmp));
+       dmp = NULL;
+
+       return 0;
+}
+/*
+ * Destroy all existing pdev's in device-mapper.
+ */
+int
+dm_pdev_destroy(void)
+{
+       dm_pdev_t *dm_pdev;
+
+       mutex_enter(&dm_pdev_mutex);
+       while (!SLIST_EMPTY(&dm_pdev_list)) {   /* List Deletion. */
+
+               dm_pdev = SLIST_FIRST(&dm_pdev_list);
+
+               SLIST_REMOVE_HEAD(&dm_pdev_list, next_pdev);
+
+               dm_pdev_rem(dm_pdev);
+       }
+       mutex_exit(&dm_pdev_mutex);
+
+       mutex_destroy(&dm_pdev_mutex);
+       return 0;
+}
+/*
+ * This funcion is called from dm_dev_remove_ioctl.
+ * When I'm removing device from list, I have to decrement
+ * reference counter. If reference counter is 0 I will remove
+ * dmp from global list and from device list to. And I will CLOSE
+ * dmp vnode too.
+ */
+
+/*
+ * Decrement pdev reference counter if 0 remove it.
+ */
+int
+dm_pdev_decr(dm_pdev_t * dmp)
+{
+       KASSERT(dmp != NULL);
+       /*
+        * If this was last reference remove dmp from
+        * global list also.
+        */
+       mutex_enter(&dm_pdev_mutex);
+
+       if (--dmp->ref_cnt == 0) {
+               SLIST_REMOVE(&dm_pdev_list, dmp, dm_pdev, next_pdev);
+               mutex_exit(&dm_pdev_mutex);
+               dm_pdev_rem(dmp);
+               return 0;
+       }
+       mutex_exit(&dm_pdev_mutex);
+       return 0;
+}
+/*static int
+  dm_pdev_dump_list(void)
+  {
+  dm_pdev_t *dmp;
+       
+  aprint_verbose("Dumping dm_pdev_list \n");
+       
+  SLIST_FOREACH(dmp, &dm_pdev_list, next_pdev) {
+  aprint_verbose("dm_pdev_name %s ref_cnt %d list_rf_cnt %d\n",
+  dmp->name, dmp->ref_cnt, dmp->list_ref_cnt);
+  }
+       
+  return 0;
+       
+  }*/
diff --git a/sys/dev/disk/dm/dm_table.c b/sys/dev/disk/dm/dm_table.c
new file mode 100644 (file)
index 0000000..1196aba
--- /dev/null
@@ -0,0 +1,265 @@
+/*        $NetBSD: dm_table.c,v 1.5 2010/01/04 00:19:08 haad Exp $      */
+
+/*
+ * Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/kmem.h>
+
+#include "dm.h"
+
+/*
+ * There are two types of users of this interface:
+ *
+ * a) Readers such as
+ *    dmstrategy, dmgetdisklabel, dmsize, dm_dev_status_ioctl,
+ *    dm_table_deps_ioctl, dm_table_status_ioctl, dm_table_reload_ioctl
+ *
+ * b) Writers such as
+ *    dm_dev_remove_ioctl, dm_dev_resume_ioctl, dm_table_clear_ioctl
+ *
+ * Writers can work with table_head only when there are no readers. I
+ * use reference counting on io_cnt.
+ *
+ */
+
+static int dm_table_busy(dm_table_head_t *, uint8_t);
+static void dm_table_unbusy(dm_table_head_t *);
+
+/*
+ * Function to increment table user reference counter. Return id
+ * of table_id table.
+ * DM_TABLE_ACTIVE will return active table id.
+ * DM_TABLE_INACTIVE will return inactive table id.
+ */
+static int
+dm_table_busy(dm_table_head_t * head, uint8_t table_id)
+{
+       uint8_t id;
+
+       id = 0;
+
+       mutex_enter(&head->table_mtx);
+
+       if (table_id == DM_TABLE_ACTIVE)
+               id = head->cur_active_table;
+       else
+               id = 1 - head->cur_active_table;
+
+       head->io_cnt++;
+
+       mutex_exit(&head->table_mtx);
+       return id;
+}
+/*
+ * Function release table lock and eventually wakeup all waiters.
+ */
+static void
+dm_table_unbusy(dm_table_head_t * head)
+{
+       KASSERT(head->io_cnt != 0);
+
+       mutex_enter(&head->table_mtx);
+
+       if (--head->io_cnt == 0)
+               cv_broadcast(&head->table_cv);
+
+       mutex_exit(&head->table_mtx);
+}
+/*
+ * Return current active table to caller, increment io_cnt reference counter.
+ */
+dm_table_t *
+dm_table_get_entry(dm_table_head_t * head, uint8_t table_id)
+{
+       uint8_t id;
+
+       id = dm_table_busy(head, table_id);
+
+       return &head->tables[id];
+}
+/*
+ * Decrement io reference counter and wake up all callers, with table_head cv.
+ */
+void
+dm_table_release(dm_table_head_t * head, uint8_t table_id)
+{
+       dm_table_unbusy(head);
+}
+/*
+ * Switch table from inactive to active mode. Have to wait until io_cnt is 0.
+ */
+void
+dm_table_switch_tables(dm_table_head_t * head)
+{
+       mutex_enter(&head->table_mtx);
+
+       while (head->io_cnt != 0)
+               cv_wait(&head->table_cv, &head->table_mtx);
+
+       head->cur_active_table = 1 - head->cur_active_table;
+
+       mutex_exit(&head->table_mtx);
+}
+/*
+ * Destroy all table data. This function can run when there are no
+ * readers on table lists.
+ *
+ * XXX Is it ok to call kmem_free and potentialy VOP_CLOSE with held mutex ?xs
+ */
+int
+dm_table_destroy(dm_table_head_t * head, uint8_t table_id)
+{
+       dm_table_t *tbl;
+       dm_table_entry_t *table_en;
+       uint8_t id;
+
+       mutex_enter(&head->table_mtx);
+
+       aprint_debug("dm_Table_destroy called with %d--%d\n", table_id, head->io_cnt);
+
+       while (head->io_cnt != 0)
+               cv_wait(&head->table_cv, &head->table_mtx);
+
+       if (table_id == DM_TABLE_ACTIVE)
+               id = head->cur_active_table;
+       else
+               id = 1 - head->cur_active_table;
+
+       tbl = &head->tables[id];
+
+       while (!SLIST_EMPTY(tbl)) {     /* List Deletion. */
+               table_en = SLIST_FIRST(tbl);
+               /*
+                * Remove target specific config data. After successfull
+                * call table_en->target_config must be set to NULL.
+                */
+               table_en->target->destroy(table_en);
+
+               SLIST_REMOVE_HEAD(tbl, next);
+
+               kmem_free(table_en, sizeof(*table_en));
+       }
+
+       mutex_exit(&head->table_mtx);
+
+       return 0;
+}
+/*
+ * Return length of active table in device.
+ */
+uint64_t
+dm_table_size(dm_table_head_t * head)
+{
+       dm_table_t *tbl;
+       dm_table_entry_t *table_en;
+       uint64_t length;
+       uint8_t id;
+
+       length = 0;
+
+       id = dm_table_busy(head, DM_TABLE_ACTIVE);
+
+       /* Select active table */
+       tbl = &head->tables[id];
+
+       /*
+        * Find out what tables I want to select.
+        * if length => rawblkno then we should used that table.
+        */
+       SLIST_FOREACH(table_en, tbl, next)
+           length += table_en->length;
+
+       dm_table_unbusy(head);
+
+       return length;
+}
+/*
+ * Return > 0 if table is at least one table entry (returns number of entries)
+ * and return 0 if there is not. Target count returned from this function
+ * doesn't need to be true when userspace user receive it (after return
+ * there can be dm_dev_resume_ioctl), therfore this isonly informative.
+ */
+int
+dm_table_get_target_count(dm_table_head_t * head, uint8_t table_id)
+{
+       dm_table_entry_t *table_en;
+       dm_table_t *tbl;
+       uint32_t target_count;
+       uint8_t id;
+
+       target_count = 0;
+
+       id = dm_table_busy(head, table_id);
+
+       tbl = &head->tables[id];
+
+       SLIST_FOREACH(table_en, tbl, next)
+           target_count++;
+
+       dm_table_unbusy(head);
+
+       return target_count;
+}
+
+
+/*
+ * Initialize table_head structures, I'm trying to keep this structure as
+ * opaque as possible.
+ */
+void
+dm_table_head_init(dm_table_head_t * head)
+{
+       head->cur_active_table = 0;
+       head->io_cnt = 0;
+
+       /* Initialize tables. */
+       SLIST_INIT(&head->tables[0]);
+       SLIST_INIT(&head->tables[1]);
+
+       mutex_init(&head->table_mtx, MUTEX_DEFAULT, IPL_NONE);
+       cv_init(&head->table_cv, "dm_io");
+}
+/*
+ * Destroy all variables in table_head
+ */
+void
+dm_table_head_destroy(dm_table_head_t * head)
+{
+       KASSERT(!mutex_owned(&head->table_mtx));
+       KASSERT(!cv_has_waiters(&head->table_cv));
+       /* tables doens't exists when I call this routine, therefore it
+        * doesn't make sense to have io_cnt != 0 */
+       KASSERT(head->io_cnt == 0);
+
+       cv_destroy(&head->table_cv);
+       mutex_destroy(&head->table_mtx);
+}
diff --git a/sys/dev/disk/dm/dm_target.c b/sys/dev/disk/dm/dm_target.c
new file mode 100644 (file)
index 0000000..a46b28c
--- /dev/null
@@ -0,0 +1,340 @@
+/*        $NetBSD: dm_target.c,v 1.12 2010/01/04 00:14:41 haad Exp $      */
+
+/*
+ * Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code Must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/kmem.h>
+#include <sys/module.h>
+
+
+#include "netbsd-dm.h"
+#include "dm.h"
+
+static dm_target_t *dm_target_lookup_name(const char *);
+
+TAILQ_HEAD(dm_target_head, dm_target);
+
+static struct dm_target_head dm_target_list =
+TAILQ_HEAD_INITIALIZER(dm_target_list);
+
+kmutex_t dm_target_mutex;
+
+/*
+ * Called indirectly from dm_table_load_ioctl to mark target as used.
+ */
+void
+dm_target_busy(dm_target_t * target)
+{
+       atomic_inc_32(&target->ref_cnt);
+}
+/*
+ * Release reference counter on target.
+ */
+void
+dm_target_unbusy(dm_target_t * target)
+{
+       KASSERT(target->ref_cnt > 0);
+       atomic_dec_32(&target->ref_cnt);
+}
+/*
+ * Try to autoload target module if it was not found in current
+ * target list.
+ */
+dm_target_t *
+dm_target_autoload(const char *dm_target_name)
+{
+       char name[30];
+       u_int gen;
+       dm_target_t *dmt;
+
+       snprintf(name, sizeof(name), "dm_target_%s", dm_target_name);
+       name[29] = '\0';
+
+       do {
+               gen = module_gen;
+
+               /* Try to autoload target module */
+               mutex_enter(&module_lock);
+               (void) module_autoload(name, MODULE_CLASS_MISC);
+               mutex_exit(&module_lock);
+       } while (gen != module_gen);
+
+       mutex_enter(&dm_target_mutex);
+       dmt = dm_target_lookup_name(dm_target_name);
+       if (dmt != NULL)
+               dm_target_busy(dmt);
+       mutex_exit(&dm_target_mutex);
+
+       return dmt;
+}
+/*
+ * Lookup for target in global target list.
+ */
+dm_target_t *
+dm_target_lookup(const char *dm_target_name)
+{
+       dm_target_t *dmt;
+
+       dmt = NULL;
+
+       if (dm_target_name == NULL)
+               return NULL;
+
+       mutex_enter(&dm_target_mutex);
+
+       dmt = dm_target_lookup_name(dm_target_name);
+       if (dmt != NULL)
+               dm_target_busy(dmt);
+
+       mutex_exit(&dm_target_mutex);
+
+       return dmt;
+}
+/*
+ * Search for name in TAIL and return apropriate pointer.
+ */
+static dm_target_t *
+dm_target_lookup_name(const char *dm_target_name)
+{
+       dm_target_t *dm_target;
+       int dlen;
+       int slen;
+
+       slen = strlen(dm_target_name) + 1;
+
+       TAILQ_FOREACH(dm_target, &dm_target_list, dm_target_next) {
+               dlen = strlen(dm_target->name) + 1;
+               if (dlen != slen)
+                       continue;
+
+               if (strncmp(dm_target_name, dm_target->name, slen) == 0)
+                       return dm_target;
+       }
+
+       return NULL;
+}
+/*
+ * Insert new target struct into the TAIL.
+ * dm_target
+ *   contains name, version, function pointer to specifif target functions.
+ */
+int
+dm_target_insert(dm_target_t * dm_target)
+{
+       dm_target_t *dmt;
+
+       mutex_enter(&dm_target_mutex);
+
+       dmt = dm_target_lookup_name(dm_target->name);
+       if (dmt != NULL) {
+               mutex_exit(&dm_target_mutex);
+               return EEXIST;
+       }
+       TAILQ_INSERT_TAIL(&dm_target_list, dm_target, dm_target_next);
+
+       mutex_exit(&dm_target_mutex);
+
+       return 0;
+}
+
+
+/*
+ * Remove target from TAIL, target is selected with it's name.
+ */
+int
+dm_target_rem(char *dm_target_name)
+{
+       dm_target_t *dmt;
+
+       KASSERT(dm_target_name != NULL);
+
+       mutex_enter(&dm_target_mutex);
+
+       dmt = dm_target_lookup_name(dm_target_name);
+       if (dmt == NULL) {
+               mutex_exit(&dm_target_mutex);
+               return ENOENT;
+       }
+       if (dmt->ref_cnt > 0) {
+               mutex_exit(&dm_target_mutex);
+               return EBUSY;
+       }
+       TAILQ_REMOVE(&dm_target_list,
+           dmt, dm_target_next);
+
+       mutex_exit(&dm_target_mutex);
+
+       (void) kmem_free(dmt, sizeof(dm_target_t));
+
+       return 0;
+}
+/*
+ * Destroy all targets and remove them from queue.
+ * This routine is called from dm_detach, before module
+ * is unloaded.
+ */
+int
+dm_target_destroy(void)
+{
+       dm_target_t *dm_target;
+
+       mutex_enter(&dm_target_mutex);
+       while (TAILQ_FIRST(&dm_target_list) != NULL) {
+
+               dm_target = TAILQ_FIRST(&dm_target_list);
+
+               TAILQ_REMOVE(&dm_target_list, TAILQ_FIRST(&dm_target_list),
+                   dm_target_next);
+
+               (void) kmem_free(dm_target, sizeof(dm_target_t));
+       }
+       mutex_exit(&dm_target_mutex);
+
+       mutex_destroy(&dm_target_mutex);
+
+       return 0;
+}
+/*
+ * Allocate new target entry.
+ */
+dm_target_t *
+dm_target_alloc(const char *name)
+{
+       return kmem_zalloc(sizeof(dm_target_t), KM_SLEEP);
+}
+/*
+ * Return prop_array of dm_target dictionaries.
+ */
+prop_array_t
+dm_target_prop_list(void)
+{
+       prop_array_t target_array, ver;
+       prop_dictionary_t target_dict;
+       dm_target_t *dm_target;
+
+       size_t i;
+
+       target_array = prop_array_create();
+
+       mutex_enter(&dm_target_mutex);
+
+       TAILQ_FOREACH(dm_target, &dm_target_list, dm_target_next) {
+
+               target_dict = prop_dictionary_create();
+               ver = prop_array_create();
+               prop_dictionary_set_cstring(target_dict, DM_TARGETS_NAME,
+                   dm_target->name);
+
+               for (i = 0; i < 3; i++)
+                       prop_array_add_uint32(ver, dm_target->version[i]);
+
+               prop_dictionary_set(target_dict, DM_TARGETS_VERSION, ver);
+               prop_array_add(target_array, target_dict);
+
+               prop_object_release(ver);
+               prop_object_release(target_dict);
+       }
+
+       mutex_exit(&dm_target_mutex);
+
+       return target_array;
+}
+/* Initialize dm_target subsystem. */
+int
+dm_target_init(void)
+{
+       dm_target_t *dmt, *dmt3;
+       int r;
+
+       r = 0;
+
+       mutex_init(&dm_target_mutex, MUTEX_DEFAULT, IPL_NONE);
+
+       dmt = dm_target_alloc("linear");
+       dmt3 = dm_target_alloc("striped");
+
+       dmt->version[0] = 1;
+       dmt->version[1] = 0;
+       dmt->version[2] = 2;
+       strlcpy(dmt->name, "linear", DM_MAX_TYPE_NAME);
+       dmt->init = &dm_target_linear_init;
+       dmt->status = &dm_target_linear_status;
+       dmt->strategy = &dm_target_linear_strategy;
+       dmt->deps = &dm_target_linear_deps;
+       dmt->destroy = &dm_target_linear_destroy;
+       dmt->upcall = &dm_target_linear_upcall;
+
+       r = dm_target_insert(dmt);
+
+       dmt3->version[0] = 1;
+       dmt3->version[1] = 0;
+       dmt3->version[2] = 3;
+       strlcpy(dmt3->name, "striped", DM_MAX_TYPE_NAME);
+       dmt3->init = &dm_target_stripe_init;
+       dmt3->status = &dm_target_stripe_status;
+       dmt3->strategy = &dm_target_stripe_strategy;
+       dmt3->deps = &dm_target_stripe_deps;
+       dmt3->destroy = &dm_target_stripe_destroy;
+       dmt3->upcall = &dm_target_stripe_upcall;
+
+       r = dm_target_insert(dmt3);
+
+#ifdef notyet
+       dmt5->version[0] = 1;
+       dmt5->version[1] = 0;
+       dmt5->version[2] = 5;
+       strlcpy(dmt5->name, "snapshot", DM_MAX_TYPE_NAME);
+       dmt5->init = &dm_target_snapshot_init;
+       dmt5->status = &dm_target_snapshot_status;
+       dmt5->strategy = &dm_target_snapshot_strategy;
+       dmt5->deps = &dm_target_snapshot_deps;
+       dmt5->destroy = &dm_target_snapshot_destroy;
+       dmt5->upcall = &dm_target_snapshot_upcall;
+
+       r = dm_target_insert(dmt5);
+
+       dmt6->version[0] = 1;
+       dmt6->version[1] = 0;
+       dmt6->version[2] = 5;
+       strlcpy(dmt6->name, "snapshot-origin", DM_MAX_TYPE_NAME);
+       dmt6->init = &dm_target_snapshot_orig_init;
+       dmt6->status = &dm_target_snapshot_orig_status;
+       dmt6->strategy = &dm_target_snapshot_orig_strategy;
+       dmt6->deps = &dm_target_snapshot_orig_deps;
+       dmt6->destroy = &dm_target_snapshot_orig_destroy;
+       dmt6->upcall = &dm_target_snapshot_orig_upcall;
+
+       r = dm_target_insert(dmt6);
+#endif
+
+       return r;
+}
diff --git a/sys/dev/disk/dm/dm_target_error.c b/sys/dev/disk/dm/dm_target_error.c
new file mode 100644 (file)
index 0000000..e93509a
--- /dev/null
@@ -0,0 +1,155 @@
+/*        $NetBSD: dm_target_error.c,v 1.10 2010/01/04 00:12:22 haad Exp $      */
+
+/*
+ * Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file implements initial version of device-mapper error target.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/buf.h>
+
+#include "dm.h"
+
+#ifdef DM_TARGET_MODULE
+/*
+ * Every target can be compiled directly to dm driver or as a
+ * separate module this part of target is used for loading targets
+ * to dm driver.
+ * Target can be unloaded from kernel only if there are no users of
+ * it e.g. there are no devices which uses that target.
+ */
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+MODULE(MODULE_CLASS_MISC, dm_target_error, "dm");
+
+static int
+dm_target_error_modcmd(modcmd_t cmd, void *arg)
+{
+       dm_target_t *dmt;
+       int r;
+       dmt = NULL;
+
+       switch (cmd) {
+       case MODULE_CMD_INIT:
+               if ((dmt = dm_target_lookup("error")) != NULL) {
+                       dm_target_unbusy(dmt);
+                       return EEXIST;
+               }
+               dmt = dm_target_alloc("error");
+
+               dmt->version[0] = 1;
+               dmt->version[1] = 0;
+               dmt->version[2] = 0;
+               strlcpy(dmt->name, "error", DM_MAX_TYPE_NAME);
+               dmt->init = &dm_target_error_init;
+               dmt->status = &dm_target_error_status;
+               dmt->strategy = &dm_target_error_strategy;
+               dmt->deps = &dm_target_error_deps;
+               dmt->destroy = &dm_target_error_destroy;
+               dmt->upcall = &dm_target_error_upcall;
+
+               r = dm_target_insert(dmt);
+
+               break;
+
+       case MODULE_CMD_FINI:
+               r = dm_target_rem("error");
+               break;
+
+       case MODULE_CMD_STAT:
+               return ENOTTY;
+
+       default:
+               return ENOTTY;
+       }
+
+       return r;
+}
+#endif
+
+/* Init function called from dm_table_load_ioctl. */
+int
+dm_target_error_init(dm_dev_t * dmv, void **target_config, char *argv)
+{
+
+       printf("Error target init function called!!\n");
+
+       *target_config = NULL;
+
+       dmv->dev_type = DM_ERROR_DEV;
+
+       return 0;
+}
+/* Status routine called to get params string. */
+char *
+dm_target_error_status(void *target_config)
+{
+       return NULL;
+}
+/* Strategy routine called from dm_strategy. */
+int
+dm_target_error_strategy(dm_table_entry_t * table_en, struct buf * bp)
+{
+
+       printf("Error target read function called!!\n");
+
+       bp->b_error = EIO;
+       bp->b_resid = 0;
+
+       biodone(bp);
+
+       return 0;
+}
+/* Doesn't do anything here. */
+int
+dm_target_error_destroy(dm_table_entry_t * table_en)
+{
+       table_en->target_config = NULL;
+
+       /* Unbusy target so we can unload it */
+       dm_target_unbusy(table_en->target);
+
+       return 0;
+}
+/* Doesn't not need to do anything here. */
+int
+dm_target_error_deps(dm_table_entry_t * table_en, prop_array_t prop_array)
+{
+       return 0;
+}
+/* Unsupported for this target. */
+int
+dm_target_error_upcall(dm_table_entry_t * table_en, struct buf * bp)
+{
+       return 0;
+}
diff --git a/sys/dev/disk/dm/dm_target_linear.c b/sys/dev/disk/dm/dm_target_linear.c
new file mode 100644 (file)
index 0000000..a6a9361
--- /dev/null
@@ -0,0 +1,222 @@
+/*        $NetBSD: dm_target_linear.c,v 1.9 2010/01/04 00:14:41 haad Exp $      */
+
+/*
+ * Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * This file implements initial version of device-mapper dklinear target.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/buf.h>
+#include <sys/kmem.h>
+#include <sys/vnode.h>
+
+#include <machine/int_fmtio.h>
+
+#include "dm.h"
+
+/*
+ * Allocate target specific config data, and link them to table.
+ * This function is called only when, flags is not READONLY and
+ * therefore we can add things to pdev list. This should not a
+ * problem because this routine is called only from dm_table_load_ioctl.
+ * @argv[0] is name,
+ * @argv[1] is physical data offset.
+ */
+int
+dm_target_linear_init(dm_dev_t * dmv, void **target_config, char *params)
+{
+       dm_target_linear_config_t *tlc;
+       dm_pdev_t *dmp;
+
+       char **ap, *argv[3];
+
+       if (params == NULL)
+               return EINVAL;
+
+       /*
+        * Parse a string, containing tokens delimited by white space,
+        * into an argument vector
+        */
+       for (ap = argv; ap < &argv[2] &&
+           (*ap = strsep(&params, " \t")) != NULL;) {
+               if (**ap != '\0')
+                       ap++;
+       }
+
+       aprint_debug("Linear target init function called %s--%s!!\n",
+           argv[0], argv[1]);
+
+       /* Insert dmp to global pdev list */
+       if ((dmp = dm_pdev_insert(argv[0])) == NULL)
+               return ENOENT;
+
+       if ((tlc = kmem_alloc(sizeof(dm_target_linear_config_t), KM_SLEEP))
+           == NULL)
+               return ENOMEM;
+
+       tlc->pdev = dmp;
+       tlc->offset = 0;        /* default settings */
+
+       /* Check user input if it is not leave offset as 0. */
+       tlc->offset = atoi(argv[1]);
+
+       *target_config = tlc;
+
+       dmv->dev_type = DM_LINEAR_DEV;
+
+       return 0;
+}
+/*
+ * Status routine is called to get params string, which is target
+ * specific. When dm_table_status_ioctl is called with flag
+ * DM_STATUS_TABLE_FLAG I have to sent params string back.
+ */
+char *
+dm_target_linear_status(void *target_config)
+{
+       dm_target_linear_config_t *tlc;
+       char *params;
+       tlc = target_config;
+
+       aprint_debug("Linear target status function called\n");
+
+       if ((params = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_NOSLEEP)) == NULL)
+               return NULL;
+
+       aprint_normal("%s %" PRIu64, tlc->pdev->name, tlc->offset);
+       snprintf(params, DM_MAX_PARAMS_SIZE, "%s %" PRIu64,
+           tlc->pdev->name, tlc->offset);
+
+       return params;
+}
+/*
+ * Do IO operation, called from dmstrategy routine.
+ */
+int
+dm_target_linear_strategy(dm_table_entry_t * table_en, struct buf * bp)
+{
+       dm_target_linear_config_t *tlc;
+
+       tlc = table_en->target_config;
+
+/*     printf("Linear target read function called %" PRIu64 "!!\n",
+       tlc->offset);*/
+
+       bp->b_blkno += tlc->offset;
+
+       VOP_STRATEGY(tlc->pdev->pdev_vnode, bp);
+
+       return 0;
+
+}
+/*
+ * Destroy target specific data. Decrement table pdevs.
+ */
+int
+dm_target_linear_destroy(dm_table_entry_t * table_en)
+{
+       dm_target_linear_config_t *tlc;
+
+       /*
+        * Destroy function is called for every target even if it
+        * doesn't have target_config.
+        */
+
+       if (table_en->target_config == NULL)
+               return 0;
+
+       tlc = table_en->target_config;
+
+       /* Decrement pdev ref counter if 0 remove it */
+       dm_pdev_decr(tlc->pdev);
+
+       /* Unbusy target so we can unload it */
+       dm_target_unbusy(table_en->target);
+
+       kmem_free(table_en->target_config, sizeof(dm_target_linear_config_t));
+
+       table_en->target_config = NULL;
+
+       return 0;
+}
+/* Add this target pdev dependiences to prop_array_t */
+int
+dm_target_linear_deps(dm_table_entry_t * table_en, prop_array_t prop_array)
+{
+       dm_target_linear_config_t *tlc;
+       struct vattr va;
+
+       int error;
+
+       if (table_en->target_config == NULL)
+               return ENOENT;
+
+       tlc = table_en->target_config;
+
+       if ((error = VOP_GETATTR(tlc->pdev->pdev_vnode, &va, curlwp->l_cred)) != 0)
+               return error;
+
+       prop_array_add_uint64(prop_array, (uint64_t) va.va_rdev);
+
+       return 0;
+}
+/*
+ * Register upcall device.
+ * Linear target doesn't need any upcall devices but other targets like
+ * mirror, snapshot, multipath, stripe will use this functionality.
+ */
+int
+dm_target_linear_upcall(dm_table_entry_t * table_en, struct buf * bp)
+{
+       return 0;
+}
+/*
+ * Transform char s to uint64_t offset number.
+ */
+uint64_t
+atoi(const char *s)
+{
+       uint64_t n;
+       n = 0;
+
+       while (*s != '\0') {
+               if (!isdigit(*s))
+                       break;
+
+               n = (10 * n) + (*s - '0');
+               s++;
+       }
+
+       return n;
+}
diff --git a/sys/dev/disk/dm/dm_target_mirror.c b/sys/dev/disk/dm/dm_target_mirror.c
new file mode 100644 (file)
index 0000000..65a7a5b
--- /dev/null
@@ -0,0 +1,159 @@
+/*$NetBSD: dm_target_mirror.c,v 1.8 2010/01/04 00:12:22 haad Exp $*/
+
+/*
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file implements initial version of device-mapper mirror target.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/buf.h>
+
+#include "dm.h"
+
+#ifdef DM_TARGET_MODULE
+/*
+ * Every target can be compiled directly to dm driver or as a
+ * separate module this part of target is used for loading targets
+ * to dm driver.
+ * Target can be unloaded from kernel only if there are no users of
+ * it e.g. there are no devices which uses that target.
+ */
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+MODULE(MODULE_CLASS_MISC, dm_target_mirror, "dm");
+
+static int
+dm_target_mirror_modcmd(modcmd_t cmd, void *arg)
+{
+       dm_target_t *dmt;
+       int r;
+       dmt = NULL;
+
+       switch (cmd) {
+       case MODULE_CMD_INIT:
+               if ((dmt = dm_target_lookup("mirror")) != NULL) {
+                       dm_target_unbusy(dmt);
+                       return EEXIST;
+               }
+               dmt = dm_target_alloc("mirror");
+
+               dmt->version[0] = 1;
+               dmt->version[1] = 0;
+               dmt->version[2] = 0;
+               strlcpy(dmt->name, "mirror", DM_MAX_TYPE_NAME);
+               dmt->init = &dm_target_mirror_init;
+               dmt->status = &dm_target_mirror_status;
+               dmt->strategy = &dm_target_mirror_strategy;
+               dmt->deps = &dm_target_mirror_deps;
+               dmt->destroy = &dm_target_mirror_destroy;
+               dmt->upcall = &dm_target_mirror_upcall;
+
+               r = dm_target_insert(dmt);
+
+               break;
+
+       case MODULE_CMD_FINI:
+               r = dm_target_rem("mirror");
+               break;
+
+       case MODULE_CMD_STAT:
+               return ENOTTY;
+
+       default:
+               return ENOTTY;
+       }
+
+       return r;
+}
+#endif
+
+/*
+ * Init function called from dm_table_load_ioctl.
+ * start length mirror log_type #logargs logarg1 ... logargN #devs device1 offset1 ... deviceN offsetN
+ * 0 52428800 mirror clustered_disk 4 253:2 1024 UUID block_on_error 3 253:3 0 253:4 0 253:5 0
+ */
+int
+dm_target_mirror_init(dm_dev_t * dmv, void **target_config, char *argv)
+{
+
+       printf("Mirror target init function called!!\n");
+
+       *target_config = NULL;
+
+       dmv->dev_type = DM_MIRROR_DEV;
+
+       return ENOSYS;
+}
+/* Status routine called to get params string. */
+char *
+dm_target_mirror_status(void *target_config)
+{
+       return NULL;
+}
+/* Strategy routine called from dm_strategy. */
+int
+dm_target_mirror_strategy(dm_table_entry_t * table_en, struct buf * bp)
+{
+
+       printf("Mirror target read function called!!\n");
+
+       bp->b_error = EIO;
+       bp->b_resid = 0;
+
+       biodone(bp);
+
+       return 0;
+}
+/* Doesn't do anything here. */
+int
+dm_target_mirror_destroy(dm_table_entry_t * table_en)
+{
+       table_en->target_config = NULL;
+
+       /* Unbusy target so we can unload it */
+       dm_target_unbusy(table_en->target);
+
+       return 0;
+}
+/* Doesn't not need to do anything here. */
+int
+dm_target_mirror_deps(dm_table_entry_t * table_en, prop_array_t prop_array)
+{
+       return 0;
+}
+/* Unsupported for this target. */
+int
+dm_target_mirror_upcall(dm_table_entry_t * table_en, struct buf * bp)
+{
+       return 0;
+}
diff --git a/sys/dev/disk/dm/dm_target_snapshot.c b/sys/dev/disk/dm/dm_target_snapshot.c
new file mode 100644 (file)
index 0000000..3a7709e
--- /dev/null
@@ -0,0 +1,517 @@
+/*        $NetBSD: dm_target_snapshot.c,v 1.12 2010/01/04 00:12:22 haad Exp $      */
+
+/*
+ * Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * 1. Suspend my_data to temporarily stop any I/O while the snapshot is being
+ * activated.
+ * dmsetup suspend my_data
+ *
+ * 2. Create the snapshot-origin device with no table.
+ * dmsetup create my_data_org
+ *
+ * 3. Read the table from my_data and load it into my_data_org.
+ * dmsetup table my_data | dmsetup load my_data_org
+ *
+ * 4. Resume this new table.
+ * dmsetup resume my_data_org
+ *
+ * 5. Create the snapshot device with no table.
+ * dmsetup create my_data_snap
+ *
+ * 6. Load the table into my_data_snap. This uses /dev/hdd1 as the COW device and
+ * uses a 32kB chunk-size.
+ * echo "0 `blockdev --getsize /dev/mapper/my_data` snapshot \
+ *  /dev/mapper/my_data_org /dev/hdd1 p 64" | dmsetup load my_data_snap
+ *
+ * 7. Reload my_data as a snapshot-origin device that points to my_data_org.
+ * echo "0 `blockdev --getsize /dev/mapper/my_data` snapshot-origin \
+ *  /dev/mapper/my_data_org" | dmsetup load my_data
+ *
+ * 8. Resume the snapshot and origin devices.
+ * dmsetup resume my_data_snap
+ * dmsetup resume my_data
+ *
+ * Before snapshot creation
+ *  dev_name; dev table
+ * | my_data; 0 1024 linear /dev/sd1a 384|
+ *
+ * After snapshot creation
+ *                               |my_data_org;0 1024 linear /dev/sd1a 384|
+ *                              /
+ * |my_data; 0 1024 snapshot-origin /dev/vg00/my_data_org|
+ *                           /
+ * |my_data_snap; 0 1024 snapshot /dev/vg00/my_data /dev/mapper/my_data_cow P 8
+ *                           \
+ *                            |my_data_cow; 0 256 linear /dev/sd1a 1408|
+ */
+
+/*
+ * This file implements initial version of device-mapper snapshot target.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/buf.h>
+#include <sys/kmem.h>
+#include <sys/vnode.h>
+
+#include "dm.h"
+
+#ifdef DM_TARGET_MODULE
+/*
+ * Every target can be compiled directly to dm driver or as a
+ * separate module this part of target is used for loading targets
+ * to dm driver.
+ * Target can be unloaded from kernel only if there are no users of
+ * it e.g. there are no devices which uses that target.
+ */
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+MODULE(MODULE_CLASS_MISC, dm_target_snapshot, "dm");
+
+static int
+dm_target_snapshot_modcmd(modcmd_t cmd, void *arg)
+{
+       dm_target_t *dmt, *dmt1;
+       int r;
+
+       dmt = NULL;
+       dmt1 = NULL;
+
+       switch (cmd) {
+       case MODULE_CMD_INIT:
+               if (((dmt = dm_target_lookup("snapshot")) != NULL)) {
+                       dm_target_unbusy(dmt);
+                       return EEXIST;
+               }
+               if (((dmt = dm_target_lookup("snapshot-origin")) != NULL)) {
+                       dm_target_unbusy(dmt);
+                       return EEXIST;
+               }
+               dmt = dm_target_alloc("snapshot");
+               dmt1 = dm_target_alloc("snapshot-origin");
+
+               dmt->version[0] = 1;
+               dmt->version[1] = 0;
+               dmt->version[2] = 5;
+               strlcpy(dmt->name, "snapshot", DM_MAX_TYPE_NAME);
+               dmt->init = &dm_target_snapshot_init;
+               dmt->status = &dm_target_snapshot_status;
+               dmt->strategy = &dm_target_snapshot_strategy;
+               dmt->deps = &dm_target_snapshot_deps;
+               dmt->destroy = &dm_target_snapshot_destroy;
+               dmt->upcall = &dm_target_snapshot_upcall;
+
+               r = dm_target_insert(dmt);
+
+               dmt1->version[0] = 1;
+               dmt1->version[1] = 0;
+               dmt1->version[2] = 5;
+               strlcpy(dmt1->name, "snapshot-origin", DM_MAX_TYPE_NAME);
+               dmt1->init = &dm_target_snapshot_orig_init;
+               dmt1->status = &dm_target_snapshot_orig_status;
+               dmt1->strategy = &dm_target_snapshot_orig_strategy;
+               dmt1->deps = &dm_target_snapshot_orig_deps;
+               dmt1->destroy = &dm_target_snapshot_orig_destroy;
+               dmt1->upcall = &dm_target_snapshot_orig_upcall;
+
+               r = dm_target_insert(dmt1);
+               break;
+
+       case MODULE_CMD_FINI:
+               /*
+                * Try to remove snapshot target if it works remove snap-origin
+                * it is not possible to remove snapshot and do not remove
+                * snap-origin because they are used together.
+                */
+               if ((r = dm_target_rem("snapshot")) == 0)
+                       r = dm_target_rem("snapshot-origin");
+
+               break;
+
+       case MODULE_CMD_STAT:
+               return ENOTTY;
+
+       default:
+               return ENOTTY;
+       }
+
+       return r;
+}
+#endif
+
+/*
+ * Init function called from dm_table_load_ioctl.
+ * argv:  /dev/mapper/my_data_org /dev/mapper/tsc_cow_dev p 64
+ *        snapshot_origin device, cow device, persistent flag, chunk size
+ */
+int
+dm_target_snapshot_init(dm_dev_t * dmv, void **target_config, char *params)
+{
+       dm_target_snapshot_config_t *tsc;
+       dm_pdev_t *dmp_snap, *dmp_cow;
+       char **ap, *argv[5];
+
+       dmp_cow = NULL;
+
+       if (params == NULL)
+               return EINVAL;
+       /*
+        * Parse a string, containing tokens delimited by white space,
+        * into an argument vector
+        */
+       for (ap = argv; ap < &argv[4] &&
+           (*ap = strsep(&params, " \t")) != NULL;) {
+               if (**ap != '\0')
+                       ap++;
+       }
+
+       printf("Snapshot target init function called!!\n");
+       printf("Snapshotted device: %s, cow device %s,\n\t persistent flag: %s, "
+           "chunk size: %s\n", argv[0], argv[1], argv[2], argv[3]);
+
+       /* Insert snap device to global pdev list */
+       if ((dmp_snap = dm_pdev_insert(argv[0])) == NULL)
+               return ENOENT;
+
+       if ((tsc = kmem_alloc(sizeof(dm_target_snapshot_config_t), KM_NOSLEEP))
+           == NULL)
+               return 1;
+
+       tsc->tsc_persistent_dev = 0;
+
+       /* There is now cow device for nonpersistent snapshot devices */
+       if (strcmp(argv[2], "p") == 0) {
+               tsc->tsc_persistent_dev = 1;
+
+               /* Insert cow device to global pdev list */
+               if ((dmp_cow = dm_pdev_insert(argv[1])) == NULL)
+                       return ENOENT;
+       }
+       tsc->tsc_chunk_size = atoi(argv[3]);
+
+       tsc->tsc_snap_dev = dmp_snap;
+       tsc->tsc_cow_dev = dmp_cow;
+
+       *target_config = tsc;
+
+       dmv->dev_type = DM_SNAPSHOT_DEV;
+
+       return 0;
+}
+/*
+ * Status routine is called to get params string, which is target
+ * specific. When dm_table_status_ioctl is called with flag
+ * DM_STATUS_TABLE_FLAG I have to sent params string back.
+ */
+char *
+dm_target_snapshot_status(void *target_config)
+{
+       dm_target_snapshot_config_t *tsc;
+
+       uint32_t i;
+       uint32_t count;
+       size_t prm_len, cow_len;
+       char *params, *cow_name;
+
+       tsc = target_config;
+
+       prm_len = 0;
+       cow_len = 0;
+       count = 0;
+       cow_name = NULL;
+
+       printf("Snapshot target status function called\n");
+
+       /* count number of chars in offset */
+       for (i = tsc->tsc_chunk_size; i != 0; i /= 10)
+               count++;
+
+       if (tsc->tsc_persistent_dev)
+               cow_len = strlen(tsc->tsc_cow_dev->name);
+
+       /* length of names + count of chars + spaces and null char */
+       prm_len = strlen(tsc->tsc_snap_dev->name) + cow_len + count + 5;
+
+       if ((params = kmem_alloc(prm_len, KM_NOSLEEP)) == NULL)
+               return NULL;
+
+       printf("%s %s %s %" PRIu64 "\n", tsc->tsc_snap_dev->name,
+           tsc->tsc_cow_dev->name, tsc->tsc_persistent_dev ? "p" : "n",
+           tsc->tsc_chunk_size);
+
+       snprintf(params, prm_len, "%s %s %s %" PRIu64, tsc->tsc_snap_dev->name,
+           tsc->tsc_persistent_dev ? tsc->tsc_cow_dev->name : "",
+           tsc->tsc_persistent_dev ? "p" : "n",
+           tsc->tsc_chunk_size);
+
+       return params;
+}
+/* Strategy routine called from dm_strategy. */
+int
+dm_target_snapshot_strategy(dm_table_entry_t * table_en, struct buf * bp)
+{
+
+       printf("Snapshot target read function called!!\n");
+
+       bp->b_error = EIO;
+       bp->b_resid = 0;
+
+       biodone(bp);
+
+       return 0;
+}
+/* Doesn't do anything here. */
+int
+dm_target_snapshot_destroy(dm_table_entry_t * table_en)
+{
+       dm_target_snapshot_config_t *tsc;
+
+       /*
+        * Destroy function is called for every target even if it
+        * doesn't have target_config.
+        */
+
+       if (table_en->target_config == NULL)
+               return 0;
+
+       printf("Snapshot target destroy function called\n");
+
+       tsc = table_en->target_config;
+
+       /* Decrement pdev ref counter if 0 remove it */
+       dm_pdev_decr(tsc->tsc_snap_dev);
+
+       if (tsc->tsc_persistent_dev)
+               dm_pdev_decr(tsc->tsc_cow_dev);
+
+       /* Unbusy target so we can unload it */
+       dm_target_unbusy(table_en->target);
+
+       kmem_free(table_en->target_config, sizeof(dm_target_snapshot_config_t));
+
+       table_en->target_config = NULL;
+
+       return 0;
+}
+/* Add this target dependiences to prop_array_t */
+int
+dm_target_snapshot_deps(dm_table_entry_t * table_en,
+    prop_array_t prop_array)
+{
+       dm_target_snapshot_config_t *tsc;
+       struct vattr va;
+
+       int error;
+
+       if (table_en->target_config == NULL)
+               return 0;
+
+       tsc = table_en->target_config;
+
+       if ((error = VOP_GETATTR(tsc->tsc_snap_dev->pdev_vnode, &va, curlwp->l_cred)) != 0)
+               return error;
+
+       prop_array_add_uint64(prop_array, (uint64_t) va.va_rdev);
+
+       if (tsc->tsc_persistent_dev) {
+
+               if ((error = VOP_GETATTR(tsc->tsc_cow_dev->pdev_vnode, &va,
+                           curlwp->l_cred)) != 0)
+                       return error;
+
+               prop_array_add_uint64(prop_array, (uint64_t) va.va_rdev);
+
+       }
+       return 0;
+}
+/* Upcall is used to inform other depended devices about IO. */
+int
+dm_target_snapshot_upcall(dm_table_entry_t * table_en, struct buf * bp)
+{
+       printf("dm_target_snapshot_upcall called\n");
+
+       printf("upcall buf flags %s %s\n",
+           (bp->b_flags & B_WRITE) ? "B_WRITE" : "",
+           (bp->b_flags & B_READ) ? "B_READ" : "");
+
+       return 0;
+}
+/*
+ * dm target snapshot origin routines.
+ *
+ * Keep for compatibility with linux lvm2tools. They use two targets
+ * to implement snapshots. Snapshot target will implement exception
+ * store and snapshot origin will implement device which calls every
+ * snapshot device when write is done on master device.
+ */
+
+/*
+ * Init function called from dm_table_load_ioctl.
+ *
+ * argv: /dev/mapper/my_data_real
+ */
+int
+dm_target_snapshot_orig_init(dm_dev_t * dmv, void **target_config,
+    prop_dictionary_t dict)
+{
+       dm_target_snapshot_origin_config_t *tsoc;
+       dm_pdev_t *dmp_real;
+
+       if (params == NULL)
+               return EINVAL;
+
+       printf("Snapshot origin target init function called!!\n");
+       printf("Parent device: %s\n", params);
+
+       /* Insert snap device to global pdev list */
+       if ((dmp_real = dm_pdev_insert(params)) == NULL)
+               return ENOENT;
+
+       if ((tsoc = kmem_alloc(sizeof(dm_target_snapshot_origin_config_t), KM_NOSLEEP))
+           == NULL)
+               return 1;
+
+       tsoc->tsoc_real_dev = dmp_real;
+
+       dmv->dev_type = DM_SNAPSHOT_ORIG_DEV;
+
+       *target_config = tsoc;
+
+       return 0;
+}
+/*
+ * Status routine is called to get params string, which is target
+ * specific. When dm_table_status_ioctl is called with flag
+ * DM_STATUS_TABLE_FLAG I have to sent params string back.
+ */
+char *
+dm_target_snapshot_orig_status(void *target_config)
+{
+       dm_target_snapshot_origin_config_t *tsoc;
+
+       size_t prm_len;
+       char *params;
+
+       tsoc = target_config;
+
+       prm_len = 0;
+
+       printf("Snapshot origin target status function called\n");
+
+       /* length of names + count of chars + spaces and null char */
+       prm_len = strlen(tsoc->tsoc_real_dev->name) + 1;
+
+       printf("real_dev name %s\n", tsoc->tsoc_real_dev->name);
+
+       if ((params = kmem_alloc(prm_len, KM_NOSLEEP)) == NULL)
+               return NULL;
+
+       printf("%s\n", tsoc->tsoc_real_dev->name);
+
+       snprintf(params, prm_len, "%s", tsoc->tsoc_real_dev->name);
+
+       return params;
+}
+/* Strategy routine called from dm_strategy. */
+int
+dm_target_snapshot_orig_strategy(dm_table_entry_t * table_en, struct buf * bp)
+{
+
+       printf("Snapshot_Orig target read function called!!\n");
+
+       bp->b_error = EIO;
+       bp->b_resid = 0;
+
+       biodone(bp);
+
+       return 0;
+}
+/* Decrement pdev and free allocated space. */
+int
+dm_target_snapshot_orig_destroy(dm_table_entry_t * table_en)
+{
+       dm_target_snapshot_origin_config_t *tsoc;
+
+       /*
+        * Destroy function is called for every target even if it
+        * doesn't have target_config.
+        */
+
+       if (table_en->target_config == NULL)
+               return 0;
+
+       tsoc = table_en->target_config;
+
+       /* Decrement pdev ref counter if 0 remove it */
+       dm_pdev_decr(tsoc->tsoc_real_dev);
+
+       /* Unbusy target so we can unload it */
+       dm_target_unbusy(table_en->target);
+
+       kmem_free(table_en->target_config, sizeof(dm_target_snapshot_origin_config_t));
+
+       table_en->target_config = NULL;
+
+       return 0;
+}
+/*
+ * Get target deps and add them to prop_array_t.
+ */
+int
+dm_target_snapshot_orig_deps(dm_table_entry_t * table_en,
+    prop_array_t prop_array)
+{
+       dm_target_snapshot_origin_config_t *tsoc;
+       struct vattr va;
+
+       int error;
+
+       if (table_en->target_config == NULL)
+               return 0;
+
+       tsoc = table_en->target_config;
+
+       if ((error = VOP_GETATTR(tsoc->tsoc_real_dev->pdev_vnode, &va,
+                   curlwp->l_cred)) != 0)
+               return error;
+
+       prop_array_add_uint64(prop_array, (uint64_t) va.va_rdev);
+
+       return 0;
+}
+/* Unsupported for this target. */
+int
+dm_target_snapshot_orig_upcall(dm_table_entry_t * table_en, struct buf * bp)
+{
+       return 0;
+}
diff --git a/sys/dev/disk/dm/dm_target_stripe.c b/sys/dev/disk/dm/dm_target_stripe.c
new file mode 100644 (file)
index 0000000..06fdf3b
--- /dev/null
@@ -0,0 +1,294 @@
+/*$NetBSD: dm_target_stripe.c,v 1.9 2010/01/04 00:14:41 haad Exp $*/
+
+/*
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file implements initial version of device-mapper stripe target.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/buf.h>
+#include <sys/kmem.h>
+#include <sys/vnode.h>
+
+#include "dm.h"
+
+#ifdef DM_TARGET_MODULE
+/*
+ * Every target can be compiled directly to dm driver or as a
+ * separate module this part of target is used for loading targets
+ * to dm driver.
+ * Target can be unloaded from kernel only if there are no users of
+ * it e.g. there are no devices which uses that target.
+ */
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+MODULE(MODULE_CLASS_MISC, dm_target_stripe, NULL);
+
+static int
+dm_target_stripe_modcmd(modcmd_t cmd, void *arg)
+{
+       dm_target_t *dmt;
+       int r;
+       dmt = NULL;
+
+       switch (cmd) {
+       case MODULE_CMD_INIT:
+               if ((dmt = dm_target_lookup("stripe")) != NULL) {
+                       dm_target_unbusy(dmt);
+                       return EEXIST;
+               }
+               dmt = dm_target_alloc("stripe");
+
+               dmt->version[0] = 1;
+               dmt->version[1] = 0;
+               dmt->version[2] = 0;
+               strlcpy(dmt->name, "stripe", DM_MAX_TYPE_NAME);
+               dmt->init = &dm_target_stripe_init;
+               dmt->status = &dm_target_stripe_status;
+               dmt->strategy = &dm_target_stripe_strategy;
+               dmt->deps = &dm_target_stripe_deps;
+               dmt->destroy = &dm_target_stripe_destroy;
+               dmt->upcall = &dm_target_stripe_upcall;
+
+               r = dm_target_insert(dmt);
+
+               break;
+
+       case MODULE_CMD_FINI:
+               r = dm_target_rem("stripe");
+               break;
+
+       case MODULE_CMD_STAT:
+               return ENOTTY;
+
+       default:
+               return ENOTTY;
+       }
+
+       return r;
+}
+#endif
+
+/*
+ * Init function called from dm_table_load_ioctl.
+ * Example line sent to dm from lvm tools when using striped target.
+ * start length striped #stripes chunk_size device1 offset1 ... deviceN offsetN
+ * 0 65536 striped 2 512 /dev/hda 0 /dev/hdb 0
+ */
+int
+dm_target_stripe_init(dm_dev_t * dmv, void **target_config, char *params)
+{
+       dm_target_stripe_config_t *tsc;
+       size_t len;
+       char **ap, *argv[10];
+
+       if (params == NULL)
+               return EINVAL;
+
+       len = strlen(params) + 1;
+
+       /*
+        * Parse a string, containing tokens delimited by white space,
+        * into an argument vector
+        */
+       for (ap = argv; ap < &argv[9] &&
+           (*ap = strsep(&params, " \t")) != NULL;) {
+               if (**ap != '\0')
+                       ap++;
+       }
+
+       printf("Stripe target init function called!!\n");
+
+       printf("Stripe target chunk size %s number of stripes %s\n", argv[1], argv[0]);
+       printf("Stripe target device name %s -- offset %s\n", argv[2], argv[3]);
+       printf("Stripe target device name %s -- offset %s\n", argv[4], argv[5]);
+
+       if (atoi(argv[0]) > MAX_STRIPES)
+               return ENOTSUP;
+
+       if ((tsc = kmem_alloc(sizeof(dm_target_stripe_config_t), KM_NOSLEEP))
+           == NULL)
+               return ENOMEM;
+
+       /* Insert dmp to global pdev list */
+       if ((tsc->stripe_devs[0].pdev = dm_pdev_insert(argv[2])) == NULL)
+               return ENOENT;
+
+       /* Insert dmp to global pdev list */
+       if ((tsc->stripe_devs[1].pdev = dm_pdev_insert(argv[4])) == NULL)
+               return ENOENT;
+
+       tsc->stripe_devs[0].offset = atoi(argv[3]);
+       tsc->stripe_devs[1].offset = atoi(argv[5]);
+
+       /* Save length of param string */
+       tsc->params_len = len;
+       tsc->stripe_chunksize = atoi(argv[1]);
+       tsc->stripe_num = (uint8_t) atoi(argv[0]);
+
+       *target_config = tsc;
+
+       dmv->dev_type = DM_STRIPE_DEV;
+
+       return 0;
+}
+/* Status routine called to get params string. */
+char *
+dm_target_stripe_status(void *target_config)
+{
+       dm_target_stripe_config_t *tsc;
+       char *params;
+
+       tsc = target_config;
+
+       if ((params = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP)) == NULL)
+               return NULL;
+
+       snprintf(params, DM_MAX_PARAMS_SIZE, "%d %" PRIu64 " %s %" PRIu64 " %s %" PRIu64,
+           tsc->stripe_num, tsc->stripe_chunksize,
+           tsc->stripe_devs[0].pdev->name, tsc->stripe_devs[0].offset,
+           tsc->stripe_devs[1].pdev->name, tsc->stripe_devs[1].offset);
+
+       return params;
+}
+/* Strategy routine called from dm_strategy. */
+int
+dm_target_stripe_strategy(dm_table_entry_t * table_en, struct buf * bp)
+{
+       dm_target_stripe_config_t *tsc;
+       struct buf *nestbuf;
+       uint64_t blkno, blkoff;
+       uint64_t stripe, stripe_blknr;
+       uint32_t stripe_off, stripe_rest, num_blks, issue_blks;
+       int stripe_devnr;
+
+       tsc = table_en->target_config;
+       if (tsc == NULL)
+               return 0;
+
+/*     printf("Stripe target read function called %" PRIu64 "!!\n",
+       tlc->offset);*/
+
+       /* calculate extent of request */
+       KASSERT(bp->b_resid % DEV_BSIZE == 0);
+
+       blkno = bp->b_blkno;
+       blkoff = 0;
+       num_blks = bp->b_resid / DEV_BSIZE;
+       for (;;) {
+               /* blockno to strip piece nr */
+               stripe = blkno / tsc->stripe_chunksize;
+               stripe_off = blkno % tsc->stripe_chunksize;
+
+               /* where we are inside the strip */
+               stripe_devnr = stripe % tsc->stripe_num;
+               stripe_blknr = stripe / tsc->stripe_num;
+
+               /* how much is left before we hit a boundary */
+               stripe_rest = tsc->stripe_chunksize - stripe_off;
+
+               /* issue this piece on stripe `stripe' */
+               issue_blks = MIN(stripe_rest, num_blks);
+               nestbuf = getiobuf(NULL, true);
+
+               nestiobuf_setup(bp, nestbuf, blkoff, issue_blks * DEV_BSIZE);
+               nestbuf->b_blkno = stripe_blknr * tsc->stripe_chunksize + stripe_off;
+               nestbuf->b_blkno += tsc->stripe_devs[stripe_devnr].offset;
+
+               VOP_STRATEGY(tsc->stripe_devs[stripe_devnr].pdev->pdev_vnode, nestbuf);
+
+               blkno += issue_blks;
+               blkoff += issue_blks * DEV_BSIZE;
+               num_blks -= issue_blks;
+
+               if (num_blks <= 0)
+                       break;
+       }
+
+       return 0;
+}
+/* Doesn't do anything here. */
+int
+dm_target_stripe_destroy(dm_table_entry_t * table_en)
+{
+       dm_target_stripe_config_t *tsc;
+
+       tsc = table_en->target_config;
+
+       if (tsc == NULL)
+               return 0;
+
+       dm_pdev_decr(tsc->stripe_devs[0].pdev);
+       dm_pdev_decr(tsc->stripe_devs[1].pdev);
+
+       /* Unbusy target so we can unload it */
+       dm_target_unbusy(table_en->target);
+
+       kmem_free(tsc, sizeof(dm_target_stripe_config_t));
+
+       table_en->target_config = NULL;
+
+       return 0;
+}
+/* Doesn't not need to do anything here. */
+int
+dm_target_stripe_deps(dm_table_entry_t * table_en, prop_array_t prop_array)
+{
+       dm_target_stripe_config_t *tsc;
+       struct vattr va;
+
+       int error;
+
+       if (table_en->target_config == NULL)
+               return ENOENT;
+
+       tsc = table_en->target_config;
+
+       if ((error = VOP_GETATTR(tsc->stripe_devs[0].pdev->pdev_vnode, &va, curlwp->l_cred)) != 0)
+               return error;
+
+       prop_array_add_uint64(prop_array, (uint64_t) va.va_rdev);
+
+       if ((error = VOP_GETATTR(tsc->stripe_devs[1].pdev->pdev_vnode, &va, curlwp->l_cred)) != 0)
+               return error;
+
+       prop_array_add_uint64(prop_array, (uint64_t) va.va_rdev);
+
+       return 0;
+}
+/* Unsupported for this target. */
+int
+dm_target_stripe_upcall(dm_table_entry_t * table_en, struct buf * bp)
+{
+       return 0;
+}
diff --git a/sys/dev/disk/dm/dm_target_zero.c b/sys/dev/disk/dm/dm_target_zero.c
new file mode 100644 (file)
index 0000000..494988f
--- /dev/null
@@ -0,0 +1,164 @@
+/*        $NetBSD: dm_target_zero.c,v 1.10 2010/01/04 00:12:22 haad Exp $      */
+
+/*
+ * Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * This file implements initial version of device-mapper zero target.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <sys/buf.h>
+
+#include "dm.h"
+
+#ifdef DM_TARGET_MODULE
+/*
+ * Every target can be compiled directly to dm driver or as a
+ * separate module this part of target is used for loading targets
+ * to dm driver.
+ * Target can be unloaded from kernel only if there are no users of
+ * it e.g. there are no devices which uses that target.
+ */
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+MODULE(MODULE_CLASS_MISC, dm_target_zero, "dm");
+
+static int
+dm_target_zero_modcmd(modcmd_t cmd, void *arg)
+{
+       dm_target_t *dmt;
+       int r;
+       dmt = NULL;
+
+       switch (cmd) {
+       case MODULE_CMD_INIT:
+               if ((dmt = dm_target_lookup("zero")) != NULL) {
+                       dm_target_unbusy(dmt);
+                       return EEXIST;
+               }
+               dmt = dm_target_alloc("zero");
+
+               dmt->version[0] = 1;
+               dmt->version[1] = 0;
+               dmt->version[2] = 0;
+               strlcpy(dmt->name, "zero", DM_MAX_TYPE_NAME);
+               dmt->init = &dm_target_zero_init;
+               dmt->status = &dm_target_zero_status;
+               dmt->strategy = &dm_target_zero_strategy;
+               dmt->deps = &dm_target_zero_deps;
+               dmt->destroy = &dm_target_zero_destroy;
+               dmt->upcall = &dm_target_zero_upcall;
+
+               r = dm_target_insert(dmt);
+               break;
+
+       case MODULE_CMD_FINI:
+               r = dm_target_rem("zero");
+
+               break;
+
+       case MODULE_CMD_STAT:
+               return ENOTTY;
+
+       default:
+               return ENOTTY;
+       }
+
+       return r;
+}
+#endif
+
+/*
+ * Zero target init function. This target doesn't need
+ * target specific config area.
+ */
+int
+dm_target_zero_init(dm_dev_t * dmv, void **target_config, char *argv)
+{
+
+       printf("Zero target init function called!!\n");
+
+       dmv->dev_type = DM_ZERO_DEV;
+
+       *target_config = NULL;
+
+       return 0;
+}
+/* Status routine called to get params string. */
+char *
+dm_target_zero_status(void *target_config)
+{
+       return NULL;
+}
+
+
+/*
+ * This routine does IO operations.
+ */
+int
+dm_target_zero_strategy(dm_table_entry_t * table_en, struct buf * bp)
+{
+
+       /* printf("Zero target read function called %d!!\n", bp->b_bcount); */
+
+       memset(bp->b_data, 0, bp->b_bcount);
+       bp->b_resid = 0;        /* nestiobuf_done wants b_resid = 0 to be sure
+                                * that there is no other io to done  */
+
+       biodone(bp);
+
+       return 0;
+}
+/* Doesn't not need to do anything here. */
+int
+dm_target_zero_destroy(dm_table_entry_t * table_en)
+{
+       table_en->target_config = NULL;
+
+       /* Unbusy target so we can unload it */
+       dm_target_unbusy(table_en->target);
+
+       return 0;
+}
+/* Doesn't not need to do anything here. */
+int
+dm_target_zero_deps(dm_table_entry_t * table_en, prop_array_t prop_array)
+{
+       return 0;
+}
+/* Unsuported for this target. */
+int
+dm_target_zero_upcall(dm_table_entry_t * table_en, struct buf * bp)
+{
+       return 0;
+}
diff --git a/sys/dev/disk/dm/doc/design.txt b/sys/dev/disk/dm/doc/design.txt
new file mode 100644 (file)
index 0000000..7922be5
--- /dev/null
@@ -0,0 +1,475 @@
+               Device-mapper to libdevmapper protocol
+               
+
+
+  1) Device mapper device in a POV of LVM it is an Logical Volume. 
+     Logical Volume is virtual block device is made from logical blocks.
+     These blocks are mapped to real device blocks with algorithm called 
+     target.
+     
+     Functions available to dm device:
+              create, remove, list, status of device.
+
+  2) device mapper target is function which  defines how are Logical blocks 
+     mapped to physical. There are many targets linear, stripe, mirror etc.
+
+     Functions available to dm device:
+              list available targets. They can be added with module in linux.                 
+
+  3) dm table.
+     Every device-mapper device consits from one or more tables. Table specify 
+     Start, length of logical blocks and target which is used to map them to 
+     physical blocks. 
+
+     {start} {length} {target} | {device} {target parameters}
+     
+     after | are target specific parameters listed. 
+
+     Functions available to dm device:
+              load, unload, table_status.
+
+
+   List of available ioct calls
+   
+   DM_VERSION
+   DM_REMOVE_ALL
+   DM_LIST_DEVICES
+   DM_DEV_CREATE
+   DM_DEV_REMOVE
+   DM_DEV_RENAME
+   DM_DEV_SUSPEND
+   DM_DEV_STATUS
+   DM_DEV_WAIT
+   DM_TABLE_LOAD
+   DM_TABLE_CLEAR
+   DM_TABLE_DEPS
+   DM_TABLE_STATUS
+   DM_LIST_VERSIONS
+   DM_TARGET_MSG   
+   DM_DEV_SET_GEOMETRY    
+
+   1) DM_VERSION 
+
+      in: struct dm-ioctl
+
+      out: struct dm-ioctl
+
+      Fuction:
+        sends libdevmapper ioctl protocol version to kernel and ask for kernel version.
+        If major and minor numbers are good we can continue.
+
+   2) DM_REMOVE_ALL
+      
+      in: none
+
+      out: none
+
+      Function:
+       This ioctl will remove all DM devices/tables from DM driver.
+
+  3) DM_LIST_DEVICES
+     
+     in: none
+       
+     out: List of structures describing all devices created in driver.
+
+     Function: 
+       List all devices created in driver. (linux use struct dm_name_list)
+
+     Implementation:
+       Kernel driver will place list of struct dm_name_list behind 
+       struct dm_ioctl in userspace. Kernel driver will list through
+       the all devices and copyout info about them.
+
+  4) DM_DEV_CREATE
+     
+     in: struct dm-ioctl(name/uuid)
+
+     out: none
+
+     Function:
+       Create device in dm driver, with specified name/uuid(uuid is prefered). 
+       (linux use struct dm_name_list)
+       
+ 5) DM_DEV_REMOVE
+     
+    in: struct dm-ioctl(name/uuid)
+
+    out: none
+
+    Function:
+      Remove device from dm driver list, also remove device tables.
+
+ 6) DM_DEV_RENAME
+    
+    in: struct dm-ioctl(name/uuid) and string found after dm-ioctl struct in buffer
+
+    out: none
+
+    Function:
+      Rename device from name to string.
+
+    Implementation:
+       Kernel driver will find device with name from struct dm_ioctl-name/uuid.
+       Change name of selected device to string foun behind struc dm_ioctl header 
+       in userspace buffer.
+
+ 7) DM_DEV_SUSPEND
+    
+    in: dm-ioctl(name/uuid)
+
+    out: none
+
+    Function: 
+      Suspend all io's  on device, after this ioctl. Already started io's will be done.
+      Newer can't be started.
+
+ 8) DM_DEV_STATUS
+
+    in: dm-ioctl(name/uuid)
+
+    out: dm-ioctl (minor,open_count,target_count)
+
+    Function: 
+      Return status info about selected device
+
+    Implementation:
+       Kernel driver will find device with name from struct dm_ioctl-name/uuid.
+       Change values minor,open_count,target_count in dm_ioctl struct for 
+       selected device.
+
+ 9) DM_DEV_WAIT
+
+    in: dm-ioctl(name/uuid)
+
+    out: none
+
+    Function: 
+      Wait for device event to happen.
+
+ 10) DM_TABLE_LOAD
+
+     in: dm-ioctl(name/uuid),table specification
+
+     out: none
+     Function: 
+       Load table to selected device. Table is loaded to unused slot and than switched.
+       (linux use struct dm_target_spec)
+       
+     Implementation:
+       Kernel driver will find device with name from struct dm_ioctl-name/uuid.
+       Table is added to the inactive slot. Every device can have more than one 
+       table loaded. Tables are stored in SLIST. This ioctl also open physical 
+       device spedcified in table and add it to dm_device specific pdev list.
+
+ 11) DM_TABLE_CLEAR
+
+     in: dm-ioctl(name/uuid)
+
+     out: none
+
+     Function: 
+       Remove table from unused slot. 
+
+ 12) DM_TABLE_DEPS
+
+     in: dm-ioctl(name/uuid)
+
+     out: list of dependiences devices
+
+     Function: 
+       Return set of device dependiences e.g. mirror device for mirror target etc..
+
+ 13) DM_TABLE_STATUS
+
+     in: dm-ioctl(name/uuid)
+
+     out: list of used tables from selected devices (linux use struct dm_target_spec)
+
+     Function: 
+       List all tables in active slot in device with name name/uuid.       
+
+     Implementation:
+       Kernel driver will find device with name from struct dm_ioctl-name/uuid.
+       DM driver will copyout dm_target_spec structures behidn struct dm_ioctl.
+
+ 14) DM_LIST_VERSIONS  
+     
+     in: none
+
+     out: list of all targets in device-mapper driver (linux use struct dm_target_versions)
+
+     Function:
+       List all available targets to libdevmapper.
+
+     Implementation:
+       Kernel driver will copy out known target versions.
+
+ 15) DM_TARGET_MSG
+     
+     in: message to driver (linux use struct dm_target_msg)
+
+     out: none
+
+     Function:
+      Send message to kernel driver target.
+     
+
+ 16) DM_DEV_SET_GEOMETRY
+
+     Function:
+       Set geometry of device-mapper driver.
+
+
+               NetBSD device-mapper driver implementation
+               
+   device-mapper devices -> devs dm_dev.c
+
+   This entity is created with DM_DEV_CREATE ioctl, and stores info 
+   about every device in device mapper driver. It has two slots for 
+   active and inactive table, list of active physical devices added 
+   to this device and list of upcalled devices (for targets which use
+   more than one physical device e.g. mirror, snapshot etc..).
+
+   device-mapper physical devices -> pdevs dm_pdev.c
+
+   This structure contains opened device VNODES. Because I physical 
+   device can be found in more than one table loaded to different 
+   dm devices. When device is destroyed I decrement all reference 
+   counters for all added pdevs (I remove pdevs with ref_cnt == 0).
+
+   device-mapper tables -> table  dm_table.c, dm_ioctl.c
+   
+   Table describe how is dm device made. What blocks are mapped with 
+   what target. In our implementation every table contains pointer to
+   target specific config data. These config_data are allocated in 
+   DM_TABLE_LOAD function with target_init routine. Every table 
+   contains pointer to used target.
+
+   device-mapper targets -> target dm_target.c
+   
+   Target describes mapping of logical blocks to physical. It has 
+   function pointers to function which does init, strategy, destroy,
+   upcall functions.
+
+   P.S I want to thank reinod@ for great help and guidance :).
+   
+       
+
+               Desing of new device-mapper ioctl interface
+
+   Basic architecture of device-mapper -> libdevmapper ioctl interface is this. 
+   Libdevmapper allocate buffer with size of data_size. At the start of this buffer 
+   dm-ioctl structure is placed. any aditional information from/to kernel are placed
+   behind end (start of data part is pointed with data_start var.) of dm-ioctl struct. 
+   
+   Kernel driver then after ioctl call have to copyin data from userspace to kernel.
+   When kernel driver want to send data back to user space library it must copyout 
+   data from kernel.
+
+1) In Linux device-mapper ioctl interface implementation there are these ioctls.
+
+   DM_VERSION                 *
+   DM_REMOVE_ALL             
+   DM_LIST_DEVICES           *
+   DM_DEV_CREATE             *
+   DM_DEV_REMOVE             *
+   DM_DEV_RENAME             *
+   DM_DEV_SUSPEND            
+   DM_DEV_STATUS             *
+   DM_DEV_WAIT               
+   DM_TABLE_LOAD             *
+   DM_TABLE_CLEAR            *
+   DM_TABLE_DEPS             
+   DM_TABLE_STATUS           *
+   DM_LIST_VERSIONS          *
+   DM_TARGET_MSG   
+   DM_DEV_SET_GEOMETRY
+
+* means implemented in current version of NetBSD device-mapper.
+
+  1a) struct dm_ioctl based ioctl calls
+        These ioctl calls communicate only with basic dm_ioctl structure. 
+       
+         DM_VERSION
+         DM_DEV_STATUS
+         DM_DEV_CREATE   
+
+  Protocol structure:
+         
+  struct dm_ioctl {
+       uint32_t version[3];    /* device-mapper kernel/userspace version */
+       uint32_t data_size;     /* total size of data passed in
+                                * including this struct */
+
+       uint32_t data_start;    /* offset to start of data
+                                * relative to start of this struct */
+
+       uint32_t target_count;  /* in/out */ /* This should be set when DM_TABLE_STATUS is called */
+       int32_t  open_count;    /* device open count */
+       uint32_t flags;         /* information flags  */
+        uint32_t event_nr;      /* event counters not implemented */
+       uint32_t padding;
+
+       uint64_t dev;           /* dev_t */
+
+       char name[DM_NAME_LEN]; /* device name */
+       char uuid[DM_UUID_LEN]; /* unique identifier for
+                                * the block device */
+
+        void *user_space_addr;  /*this is needed for netbsd 
+                                 because they differently 
+                                 implement ioctl syscall*/
+  };
+
+ As SOC task I want to replace this structure with proplib dict. Proplib dict 
+ basic structure should be:
+
+ Note: I don't need data_star, data_size and use_space_addr. They are needed 
+       for current implementation. 
+
+       <dict>
+               <key>version</key>
+               <string>...</string>
+
+               <key>target_count</key>
+              <integer></integer>
+
+               <key>open_count</key>
+              <integer></integer>
+
+               <key>flags</key>
+              <integer></integer>
+
+               <key>event_nr</key>
+              <integer></integer>
+
+              <key>dev</key>
+              <integer></integer>
+              
+              <key>name</key>
+               <string>...</string>
+
+               <key>uuid</key>
+               <string>...</string>
+
+
+              <dict>
+               <!-- ioctl specific data -->
+              </dict>
+       </dict>
+
+    1b) DM_LIST_VERSIONS ioctl
+
+    This ioctl is used to get list of supported targets from kernel. Target 
+    define mapping of Logical blocks to physical blocks on real device.
+    There are linear, zero, error, mirror, snapshot, multipath etc... targets.
+
+    For every target kernel driver should copyout this structure to userspace.
+
+    Protocol structure:
+
+    struct dm_target_versions {
+        uint32_t next;
+        uint32_t version[3];
+
+        char name[0];
+    };
+
+    Because I need more then on dm_target_version I will need one major proplib 
+    dictionary to store children dictionaries with target data.
+
+    <dict>
+        <dict ID="id">
+          <key>version</key>
+           <string>...</string>        
+
+          <key>name</key>
+          <string>...</string> 
+       </dict>
+    </dict>
+
+    2a) DM_LIST_DEVICES 
+    
+    This ioctl is used to list all devices defined in kernel driver. 
+
+    Protocol structure:
+    
+    struct dm_name_list {
+        uint64_t dev;
+        uint32_t next;          /* offset to the next record from                                    
+                                   the _start_ of this */
+        char name[0];
+    };
+
+    Again because I can have more than one device in kernel driver I need one parent 
+    dictionary and more children dictionaries.
+
+        <dict>
+          <dict ID="id">
+          <key>dev</key>
+           <integer>...</integer>      
+
+          <key>name</key>
+          <string>...</string> 
+         </dict>
+        </dict>
+       
+   2b) DM_DEV_RENAME 
+      This ioctl is called when libdevmapper want to rename device-mapper device.
+      Libdevmapper library appends null terminated string to dm_ioctl struct in 
+      userspace..
+      
+      <dict>
+            <key>name</key>
+           <string>...</string>
+      </dict>
+      
+   2c) DM_DEV_CREATE, DM_DEV_REMOVE, DM_DEV_STATUS 
+       Modify only dm_ioctl structure so I don't need to specify new structures.
+
+
+  3a) DM_TABLE_LOAD, DM_TABLE_STATUS
+      DM_TABLE_LOAD ioctl loads table to device. DM_TABLE_STATUS send info about 
+      every table for selected device to userspace. Table is different for every 
+      target basic table structure is this 
+
+      {start} {length} {target} {additional information}
+
+      e.g. 
+      0 100 zero 
+
+      0 100 linear /dev/wdba 384      
+
+      Protocol structure:
+
+      struct dm_target_spec {
+        uint64_t sector_start;
+        uint64_t length;
+        int32_t status;         /* used when reading from kernel only */
+
+        uint32_t next;
+
+        char target_type[DM_MAX_TYPE_NAME];
+        
+        /*
+         * Parameter string starts immediately after this object.
+         * Be careful to add padding after string to ensure correct
+         * alignment of subsequent dm_target_spec.
+        */
+      };
+
+      <dict>
+       <key>sector_start</key>
+       <integer>...</integer>
+
+       <key>length</key>
+       <integer>...</integer>
+
+       <key>target_type</key>
+       <string>...</string>
+       
+       <key>aditional info</key>
+       <string>...</string>
+      </dict>
diff --git a/sys/dev/disk/dm/doc/proposal-dm.txt b/sys/dev/disk/dm/doc/proposal-dm.txt
new file mode 100644 (file)
index 0000000..2caa801
--- /dev/null
@@ -0,0 +1,90 @@
+
+
+/* constant dm_target structures for error, zero, linear, stripes etc. */
+struct dm_target {
+       int (*init)(struct dm_table_entry *, int argc, char **argv);
+       int (*destroy)(struct dm_table_entry *);
+       int (*strategy)(struct dm_table_entry *, struct buf *);
+       int (*upcall)(struct dm_table_entry *, struct buf *);
+
+       SLIST_ENTRY(dm_target) next;
+};
+
+
+struct dm_table_entry {
+       struct dm_dev *dm_dev;          /* backlink */
+       uint64_t start;
+       uint64_t length;
+
+       struct dm_target *target;
+       void *target_config;
+       SLIST_ENTRY(dm_table_entry) next;
+};
+SLIST(dm_table, dm_table_entry);
+
+
+struct dm_pdev {
+       struct vnode *pdev_vnode;
+       int refcnt;
+       SLIST_ENTRY(dm_pdev) next_pdev;
+};
+SLIST(dm_pdevs, pm_pdev);
+
+
+struct dm_dev {
+       char name[DM_NAME_LEN];
+       char uuid[DM_UUID_LEN];
+
+       int minor;
+       uint32_t flags;
+
+       kmutex_t dev_mtx;
+       uint32_t event_nr;
+       uint32_t ref_cnt;
+
+       struct dm_pdev pdevs;
+
+       int cur_active_table;
+       struct dm_table tables[2];
+
+       struct dm_dev_list upcalls;
+       SLIST_NEXT(dm_dev) next_upcall;
+
+       SLIST_NEXT(dm_dev) next_devlist;
+};
+SLIST(dm_dev_list, dm_dev) dm_devs;
+
+
+/* for zero,error : dm_target->target_config == NULL */
+/* for linear : */
+struct target_linear_config {
+       struct dm_pdev *pdev;
+       uint64_t offset;
+};
+
+
+/* for mirror : */
+struct target_mirror_config {
+       struct dm_pdev *orig;
+       struct dm_pdev *copies[MAX_MIRROR_COPIES];
+
+       /* copied blocks bitmaps administration etc*/
+       struct dm_pdev *log_pdev;       /* for administration */
+       uint64_t log_regionsize;        /* blocksize of mirror */
+
+       /* list of parts that still need copied etc.; run length encoded? */
+       ....
+};
+
+
+/* for snapshot : */
+struct target_snapshot_config {
+       struct dm_dev *orig;
+
+       /* modified blocks bitmaps administration etc*/
+       struct dm_pdev *log_pdev;
+       uint64_t log_regionsize;
+       /* list of sector renames to the log device */
+       ...
+};
+
diff --git a/sys/dev/disk/dm/files.dm b/sys/dev/disk/dm/files.dm
new file mode 100644 (file)
index 0000000..078029d
--- /dev/null
@@ -0,0 +1,9 @@
+defpseudodev   dm
+file       dev/dm/device-mapper.c      dm
+file        dev/dm/dm_dev.c            dm
+file        dev/dm/dm_ioctl.c           dm
+file        dev/dm/dm_pdev.c            dm
+file        dev/dm/dm_table.c           dm
+file        dev/dm/dm_target.c          dm
+file        dev/dm/dm_target_linear.c   dm
+file       dev/dm/dm_target_stripe.c   dm
diff --git a/sys/dev/disk/dm/netbsd-dm.h b/sys/dev/disk/dm/netbsd-dm.h
new file mode 100644 (file)
index 0000000..7d628c9
--- /dev/null
@@ -0,0 +1,265 @@
+/*        $NetBSD: netbsd-dm.h,v 1.6 2009/12/05 11:30:26 haad Exp $      */
+
+/*
+ * Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Hamsik.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __NETBSD_DM_H__
+#define __NETBSD_DM_H__
+
+#include <prop/proplib.h>
+
+#define DM_CMD_LEN 16
+
+#define DM_IOCTL 0xfd
+
+#define DM_IOCTL_CMD 0
+
+#define NETBSD_DM_IOCTL       _IOWR(DM_IOCTL, DM_IOCTL_CMD, struct plistref)
+
+
+/*
+ * DM-ioctl dictionary.
+ *
+ * This contains general information about dm device.
+ *
+ * <dict>
+ *     <key>command</key>
+ *     <string>...</string>
+ *
+ *     <key>event_nr</key>
+ *     <integer>...</integer>
+ *
+ *     <key>name</key>
+ *     <string>...</string>
+ *
+ *     <key>uuid</key>
+ *     <string>...</string>
+ *
+ *     <key>dev</key>
+ *     <integer></integer> 
+ *
+ *     <key>flags</key>
+ *     <integer></integer>
+ *
+ *     <key>version</key>
+ *      <array>
+ *       <integer>...</integer>
+ *       <integer>...</integer>
+ *       <integer>...</integer>
+ *      </array>
+ *
+ *      <key>cmd_data</key>
+ *       <array>
+ *        <!-- See below for command
+ *             specific dictionaries -->
+ *       </array>
+ * </dict>
+ *
+ * Available commands from _cmd_data_v4.
+ *
+ * create, reload, remove, remove_all, suspend,
+ * resume, info, deps, rename, version, status,
+ * table, waitevent, names, clear, mknodes,
+ * targets, message, setgeometry
+ *
+ */
+
+/*
+ * DM_LIST_VERSIONS == "targets" command dictionary entry.
+ * Lists all available targets with their version.
+ *
+ * <array>
+ *   <dict>
+ *    <key>name<key>
+ *    <string>...</string>
+ *
+ *    <key>version</key>
+ *      <array>
+ *       <integer>...</integer>
+ *       <integer>...</integer>
+ *       <integer>...</integer>
+ *      </array>
+ *   </dict>
+ * </array>
+ *
+ */
+
+/*
+ * DM_DEV_LIST == "names" 
+ * Request list of device-mapper created devices from kernel.
+ *
+ * <array>
+ *   <dict>
+ *    <key>name<key>
+ *    <string>...</string>
+ *
+ *    <key>dev</key>
+ *    <integer>...</integer>
+ *   </dict>
+ * </array>
+ *
+ * dev is uint64_t
+ *
+ */
+
+ /*
+  * DM_DEV_RENAME == "rename" 
+  * Rename device to string.
+  *
+  * <array>
+  *    <string>...</string>
+  * </array>
+  *
+  */
+
+ /*
+  * DM_DEV_STATUS == "info, mknodes" 
+  * Will change fields DM_IOCTL_OPEN, DM_IOCTL_DEV in received dictionary,
+  * with dm device values with name or uuid from list.
+  *
+  */
+
+ /*
+  * DM_TABLE_STATUS == "status,table" 
+  * Request list of device-mapper created devices from kernel.
+  *
+  * <array>
+  *   <dict>
+  *    <key>type<key>
+  *    <string>...</string>
+  *
+  *    <key>start</key>
+  *    <integer>...</integer>
+  *
+  *    <key>length</key>
+  *    <integer>...</integer>
+  *
+  *    <key>params</key>
+  *    <string>...</string>
+  *   </dict>
+  * </array>
+  *
+  * params is string which contains {device} {parameters}
+  *
+  */ 
+
+ /*
+  * DM_TABLE_DEPS == "deps" 
+  * Request list active table device dependiences.
+  *
+  * This command is also run to get dm-device
+  * dependiences for existing real block device.
+  *
+  * eg. vgcreate calls DM_TABLE_DEPS
+  *
+  * <array>
+  *   <integer>...</integer>
+  * </array>
+  *
+  */ 
+
+
+#define DM_IOCTL_COMMAND      "command"
+#define DM_IOCTL_VERSION      "version"
+#define DM_IOCTL_OPEN         "open_count"
+#define DM_IOCTL_MINOR        "minor"
+#define DM_IOCTL_NAME         "name"
+#define DM_IOCTL_UUID         "uuid"
+#define DM_IOCTL_TARGET_COUNT "target_count"
+#define DM_IOCTL_EVENT        "event_nr"
+#define DM_IOCTL_FLAGS        "flags"
+#define DM_IOCTL_CMD_DATA     "cmd_data"
+
+#define DM_TARGETS_NAME       "name"
+#define DM_TARGETS_VERSION    "ver"
+
+#define DM_DEV_NEWNAME        "newname"
+#define DM_DEV_NAME           "name"
+#define DM_DEV_DEV            "dev"
+
+#define DM_TABLE_TYPE         "type"
+#define DM_TABLE_START        "start"
+#define DM_TABLE_STAT         "status"
+#define DM_TABLE_LENGTH       "length"
+#define DM_TABLE_PARAMS       "params"
+//#ifndef __LIB_DEVMAPPER__
+//#define DM_TABLE_DEPS         "deps"
+//#endif
+
+/* Status bits */
+/* IO mode of device */
+#define DM_READONLY_FLAG       (1 << 0) /* In/Out *//* to kernel/from kernel */
+#define DM_SUSPEND_FLAG                (1 << 1) /* In/Out */
+/* XXX. This flag is undocumented. */ 
+#define DM_EXISTS_FLAG          (1 << 2) /* In/Out */ 
+/* Minor number is persistent */
+#define DM_PERSISTENT_DEV_FLAG (1 << 3) /* In */
+
+/*
+ * Flag passed into ioctl STATUS command to get table information
+ * rather than current status.
+ */
+#define DM_STATUS_TABLE_FLAG   (1 << 4) /* In */
+
+/*
+ * Flags that indicate whether a table is present in either of
+ * the two table slots that a device has.
+ */
+#define DM_ACTIVE_PRESENT_FLAG   (1 << 5) /* Out */
+#define DM_INACTIVE_PRESENT_FLAG (1 << 6) /* Out */
+
+/*
+ * Indicates that the buffer passed in wasn't big enough for the
+ * results.
+ */
+#define DM_BUFFER_FULL_FLAG    (1 << 8) /* Out */
+
+/*
+ * This flag is now ignored.
+ */
+#define DM_SKIP_BDGET_FLAG     (1 << 9) /* In */
+
+/*
+ * Set this to avoid attempting to freeze any filesystem when suspending.
+ */
+#define DM_SKIP_LOCKFS_FLAG    (1 << 10) /* In */
+
+/*
+ * Set this to suspend without flushing queued ios.
+ */
+#define DM_NOFLUSH_FLAG                (1 << 11) /* In */
+
+/*
+ * If set, any table information returned will relate to the inactive
+ * table instead of the live one.  Always check DM_INACTIVE_PRESENT_FLAG
+ * is set before using the data returned.
+ */
+#define DM_QUERY_INACTIVE_TABLE_FLAG    (1 << 12) /* In */
+
+#endif /* __NETBSD_DM_H__ */