Index: ./conf/files
===================================================================
RCS file: /cvsroot/src/sys/conf/files,v
retrieving revision 1.930
diff -u -p -r1.930 files
--- ./conf/files        11 Dec 2008 05:42:18 -0000      1.930
+++ ./conf/files        18 Dec 2008 23:44:23 -0000
@@ -1,5 +1,4 @@
#      $NetBSD: files,v 1.930 2008/12/11 05:42:18 alc Exp $
-
#      @(#)files.newconf       7.5 (Berkeley) 5/10/93

version        20080610
@@ -1280,6 +1279,9 @@ defpseudo pud
file   dev/pud/pud.c                   pud
file   dev/pud/pud_dev.c               pud

+# device-mapper driver for LVM
+include "dev/dm/files.dm"
+
#
# File systems
#
Index: ./conf/majors
===================================================================
RCS file: /cvsroot/src/sys/conf/majors,v
retrieving revision 1.43
diff -u -p -r1.43 majors
--- ./conf/majors       5 Dec 2008 13:06:50 -0000       1.43
+++ ./conf/majors       18 Dec 2008 23:44:23 -0000
@@ -38,3 +38,4 @@ device-major  zfs             char 190 block 190      zfs
device-major   tprof           char 191                tprof
device-major   isv             char 192                isv
device-major   video           char 193                video
+device-major    dm              char 194 block 169      dm
Index: ./dev/dm/TODO
===================================================================
RCS file: ./dev/dm/TODO
diff -N ./dev/dm/TODO
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/TODO       18 Dec 2008 23:44:23 -0000
@@ -0,0 +1,18 @@
+Important unimplemented features in current device-mapper implementation
+
+* implement dm_dev_event_ioctl and dm_target_msg_ioctl functions
+
+* write more targets mirror, stripe.
+
+* snapshot target, there is some code in repository already
+
+* enable dynamically loadable targets
+
+                       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.
+
Index: ./dev/dm/device-mapper.c
===================================================================
RCS file: ./dev/dm/device-mapper.c
diff -N ./dev/dm/device-mapper.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/device-mapper.c    18 Dec 2008 23:44:24 -0000
@@ -0,0 +1,540 @@
+/*        $NetBSD: device-mapper.c,v 1.1.2.17 2008/12/03 00:10: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.
+ */
+
+/*
+ * 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/dkio.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/ioccom.h>
+#include <sys/kmem.h>
+#include <sys/module.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_dump(dmdump);
+static dev_type_size(dmsize);
+
+/* attach and detach routines */
+int dmattach(void);
+int dmdestroy(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 *);
+
+/* ***Variable-definitions*** */
+const struct bdevsw dm_bdevsw = {
+       dmopen, dmclose, dmstrategy, dmioctl, dmdump, dmsize, D_DISK | D_MPSAFE
+};
+
+const struct cdevsw dm_cdevsw = {
+       dmopen, dmclose, dmread, dmwrite, dmioctl,
+       nostop, notty, nopoll, nommap, nokqfilter, D_DISK | D_MPSAFE
+};
+
+int unload;
+
+/*
+ * 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[] = {
+               {"version", dm_get_version_ioctl},
+               {"targets", dm_list_versions_ioctl},
+               {"create",  dm_dev_create_ioctl},
+               {"info",    dm_dev_status_ioctl},
+               {"mknodes", dm_dev_status_ioctl},
+               {"names",   dm_dev_list_ioctl},
+               {"suspend", dm_dev_suspend_ioctl},
+               {"remove",  dm_dev_remove_ioctl},
+               {"rename",  dm_dev_rename_ioctl},
+               {"resume",  dm_dev_resume_ioctl},
+               {"clear",   dm_table_clear_ioctl},
+               {"deps",    dm_table_deps_ioctl},
+               {"reload",  dm_table_load_ioctl},
+               {"status",  dm_table_status_ioctl},
+               {"table",   dm_table_status_ioctl},
+               {NULL, NULL}
+};
+
+
+MODULE(MODULE_CLASS_MISC, dm, NULL);
+
+/* New module handle routine */
+static int
+dm_modcmd(modcmd_t cmd, void *arg)
+{
+#ifdef _MODULE
+       int bmajor = -1, cmajor = -1;
+
+       switch (cmd) {
+       case MODULE_CMD_INIT:
+               dmattach();
+               return devsw_attach("dm", &dm_bdevsw, &bmajor,
+                   &dm_cdevsw, &cmajor);
+               break;
+
+       case MODULE_CMD_FINI:
+               dmdestroy();
+               return devsw_detach(&dm_bdevsw, &dm_cdevsw);
+               break;
+
+       case MODULE_CMD_STAT:
+               return ENOTTY;
+
+       default:
+               return ENOTTY;
+       }
+
+       return 0;
+#else
+
+       if (cmd == MODULE_CMD_INIT)
+               return 0;
+       return ENOTTY;
+
+#endif /* _MODULE */
+}
+
+
+/* attach routine */
+int
+dmattach(void)
+{
+       dm_target_init();
+       dm_dev_init();
+       dm_pdev_init();
+
+       return 0;
+}
+
+/* Destroy routine */
+int
+dmdestroy(void)
+{
+       atomic_inc_32(&unload);
+
+       dm_dev_destroy();
+       dm_pdev_destroy();
+       dm_target_destroy();
+
+       return 0;
+}
+
+static int
+dmopen(dev_t dev, int flags, int mode, struct lwp *l)
+{
+       aprint_debug("open routine called %d\n", minor(dev));
+
+       if (unload == 1)
+               return EBUSY;
+
+       return 0;
+}
+
+static int
+dmclose(dev_t dev, int flags, int mode, struct lwp *l)
+{
+       aprint_debug("CLOSE routine called\n");
+
+       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 (disk_ioctl_switch(dev, cmd, data) != 0) {
+               struct plistref *pref = (struct plistref *) data;
+
+               if((r = prop_dictionary_copyin_ioctl(pref, cmd, &dm_dict_in)) != 0)
+                       return r;
+
+               dm_check_version(dm_dict_in);
+
+               /* call cmd selected function */
+               if ((r = dm_ioctl_switch(cmd)) != 0) {
+                       prop_object_release(dm_dict_in);
+                       return r;
+               }
+
+               /* run ioctl routine */
+               if ((r = dm_cmd_to_fun(dm_dict_in)) != 0) {
+                       prop_object_release(dm_dict_in);
+                       return r;
+               }
+
+               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)
+{
+       int r;
+
+       r = 0;
+
+       switch(cmd) {
+
+       case NETBSD_DM_IOCTL:
+               aprint_debug("NetBSD_DM_IOCTL called\n");
+               break;
+
+       default:
+                aprint_debug("unknown ioctl called\n");
+                return ENOTTY;
+                break; /* NOT REACHED */
+       }
+
+        return r;
+}
+
+ /*
+  * Check for disk specific ioctls.
+  */
+
+static int
+disk_ioctl_switch(dev_t dev, u_long cmd, void *data)
+{
+       dm_dev_t *dmv;
+
+       switch(cmd) {
+       case DIOCGWEDGEINFO:
+       {
+               struct dkwedge_info *dkw = (void *) data;
+
+               if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL)
+                       return ENOENT;
+
+               aprint_normal("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 DIOCGDINFO:
+       {
+
+               if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL)
+                       return ENOENT;
+
+               aprint_debug("DIOCGDINFO %d\n", dmv->dk_label->d_secsize);
+
+               *(struct disklabel *)data = *(dmv->dk_label);
+
+               dm_dev_unbusy(dmv);
+               break;
+       }
+
+       case DIOCGPART:
+       {
+               if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL)
+                       return ENOENT;
+
+               ((struct partinfo *)data)->disklab = dmv->dk_label;
+               ((struct partinfo *)data)->part = &dmv->dk_label->d_partitions[0];
+
+               dm_dev_unbusy(dmv);
+               break;
+       }
+       case DIOCWDINFO:
+       case DIOCSDINFO:
+       case DIOCKLABEL:
+       case DIOCWLABEL:
+       case DIOCGDEFLABEL:
+
+       default:
+               aprint_debug("unknown disk_ioctl called\n");
+               return 1;
+               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;
+       }
+
+       /* 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);
+
+       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
+dmdump(dev_t dev, daddr_t blkno, void *va, size_t size)
+{
+       return ENODEV;
+}
+
+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);
+}
+
+ /*
+  * Load the label information on the named device
+  * Actually fabricate a disklabel.
+  *
+  * EVENTUALLY take information about different
+  * data tracks from the TOC and put it in the disklabel
+  *
+  * Copied from vnd code.
+  */
+void
+dmgetdisklabel(struct disklabel *lp, dm_table_head_t *head)
+{
+       struct partition *pp;
+       int dmp_size;
+
+       dmp_size = dm_table_size(head);
+
+       /*
+        * Size must be at least 2048 DEV_BSIZE blocks
+        * (1M) in order to use this geometry.
+        */
+
+       lp->d_secperunit = dmp_size;
+       lp->d_secsize = DEV_BSIZE;
+       lp->d_nsectors = 32;
+       lp->d_ntracks = 64;
+       lp->d_ncylinders = dmp_size / (lp->d_nsectors * lp->d_ntracks);
+       lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
+
+       strncpy(lp->d_typename, "lvm", sizeof(lp->d_typename));
+       lp->d_type = DTYPE_DM;
+       strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
+       lp->d_rpm = 3600;
+       lp->d_interleave = 1;
+       lp->d_flags = 0;
+
+       pp = &lp->d_partitions[0];
+       /*
+        * This is logical offset and therefore it can be 0
+        * I will consider table offsets later in dmstrategy.
+        */
+       pp->p_offset = 0;
+       pp->p_size = dmp_size * DEV_BSIZE;
+       pp->p_fstype = FS_BSDFFS;  /* default value */
+       lp->d_npartitions = 1;
+
+       lp->d_magic = DISKMAGIC;
+       lp->d_magic2 = DISKMAGIC;
+       lp->d_checksum = dkcksum(lp);
+
+       return;
+}
Index: ./dev/dm/dm.h
===================================================================
RCS file: ./dev/dm/dm.h
diff -N ./dev/dm/dm.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/dm.h       18 Dec 2008 23:44:24 -0000
@@ -0,0 +1,341 @@
+/*        $NetBSD: dm.h,v 1.1.2.20 2008/11/05 13:45:02 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>
+
+#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       13
+#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];
+
+       int 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 disklabel *dk_label;    /* Disklabel for this table. */
+
+       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 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];
+
+       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 dmgetdisklabel(struct disklabel *, 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 */
+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_name(const char *);
+int dm_target_rem(char *);
+
+/* XXX temporally add */
+int dm_target_init(void);
+
+/* 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_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);
+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_*/
Index: ./dev/dm/dm_dev.c
===================================================================
RCS file: ./dev/dm/dm_dev.c
diff -N ./dev/dm/dm_dev.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/dm_dev.c   18 Dec 2008 23:44:24 -0000
@@ -0,0 +1,384 @@
+/*        $NetBSD: dm_dev.c,v 1.1.2.15 2008/12/03 00:10: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/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;
+
+__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_name(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
+
+/*
+ * 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()
+{
+       dm_dev_t *dmv;
+
+       dmv = kmem_zalloc(sizeof(dm_dev_t), KM_NOSLEEP);
+       dmv->dk_label = kmem_zalloc(sizeof(struct disklabel), KM_NOSLEEP);
+
+       return dmv;
+}
+
+/*
+ * Freed device entry.
+ */
+int
+dm_dev_free(dm_dev_t *dmv)
+{
+       KASSERT(dmv != NULL);
+
+       if (dmv->dk_label != NULL)
+               (void)kmem_free(dmv->dk_label, sizeof(struct disklabel));
+
+       (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()
+{
+       TAILQ_INIT(&dm_dev_list); /* initialize global dev list */
+       mutex_init(&dm_dev_mutex, MUTEX_DEFAULT, IPL_NONE);
+       return 0;
+}
Index: ./dev/dm/dm_ioctl.c
===================================================================
RCS file: ./dev/dm/dm_ioctl.c
diff -N ./dev/dm/dm_ioctl.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/dm_ioctl.c 18 Dec 2008 23:44:25 -0000
@@ -0,0 +1,952 @@
+/*        $NetBSD: dm_ioctl.c,v 1.1.2.23 2008/12/03 00:10: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.
+ */
+
+/*
+ * 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/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 uint32_t      sc_minor_num;
+
+#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;
+
+       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 ((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_32_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;
+
+       mutex_init(&dmv->dev_mtx, MUTEX_DEFAULT, IPL_NONE);
+       cv_init(&dmv->dev_cv, "dm_dev");
+
+       dm_table_head_init(&dmv->table_head);
+
+       if (flags & DM_READONLY_FLAG)
+               dmv->flags |= DM_READONLY_FLAG;
+
+       prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor);
+
+       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);
+
+       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;
+
+       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);
+
+       /* Remove device from global device list */
+       if ((dmv = dm_dev_rem(name, uuid, minor)) == NULL){
+               DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG);
+               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);
+
+       mutex_destroy(&dmv->dev_mtx);
+       cv_destroy(&dmv->dev_cv);
+
+       /* Destroy device */
+       (void)dm_dev_free(dmv);
+
+       return 0;
+}
+
+/*
+ * 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);
+
+       dmgetdisklabel(dmv->dk_label, &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, dmv->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;
+
+       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);
+
+       tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE);
+
+       SLIST_FOREACH(table_en, tbl, next)
+                       table_en->target->deps(table_en, cmd_array);
+
+       dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE);
+       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_name(type)) == NULL)
+                       return ENOENT;
+
+               if ((table_en=kmem_alloc(sizeof(dm_table_entry_t),
+                           KM_NOSLEEP)) == NULL)
+                       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_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
+                       free(str, M_TEMP);
+
+                       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;
+
+       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_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);
+
+       /* I should use mutex here and not rwlock there can be IO operation
+          during this ioctl on device. */
+
+       aprint_debug("Status of device tables: %s--%d\n",
+           name, dmv->table_head.cur_active_table);
+
+       tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE);
+
+       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, strlen(params) + 1);
+                       }
+               }
+
+               prop_array_add(cmd_array, target_dict);
+               prop_object_release(target_dict);
+       }
+
+       dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE);
+       dm_dev_unbusy(dmv);
+
+       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);
+
+       return 0;
+}
Index: ./dev/dm/dm_pdev.c
===================================================================
RCS file: ./dev/dm/dm_pdev.c
diff -N ./dev/dm/dm_pdev.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/dm_pdev.c  18 Dec 2008 23:44:25 -0000
@@ -0,0 +1,246 @@
+/*        $NetBSD: dm_pdev.c,v 1.1.2.13 2008/11/05 13:45:02 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_verbose("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_verbose("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_NOSLEEP)) == 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()
+  {
+  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;
+
+  }*/
Index: ./dev/dm/dm_table.c
===================================================================
RCS file: ./dev/dm/dm_table.c
diff -N ./dev/dm/dm_table.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/dm_table.c 18 Dec 2008 23:44:25 -0000
@@ -0,0 +1,273 @@
+/*        $NetBSD: dm_table.c,v 1.1.2.12 2008/11/05 13:45:02 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);
+
+       printf("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 / DEV_BSIZE;
+}
+
+/*
+ * 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);
+}
Index: ./dev/dm/dm_target.c
===================================================================
RCS file: ./dev/dm/dm_target.c
diff -N ./dev/dm/dm_target.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/dm_target.c        18 Dec 2008 23:44:25 -0000
@@ -0,0 +1,290 @@
+/*        $NetBSD: dm_target.c,v 1.1.2.17 2008/12/03 00:10: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 "netbsd-dm.h"
+#include "dm.h"
+
+static dm_target_t* dm_target_alloc(const char *);
+
+TAILQ_HEAD(dm_target_head, dm_target);
+
+static struct dm_target_head dm_target_list =
+TAILQ_HEAD_INITIALIZER(dm_target_list);
+
+/*
+ * Search for name in TAIL and return apropriate pointer.
+ */
+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;
+
+       dmt = dm_target_lookup_name(dm_target->name);
+
+       if (dmt != NULL)
+               return EEXIST;
+
+       TAILQ_INSERT_TAIL(&dm_target_list, dm_target, dm_target_next);
+
+       return 0;
+}
+
+
+/*
+ * Remove target from TAIL, target is selected with it's name.
+ */
+int
+dm_target_rem(char *dm_target_name)
+{
+       dm_target_t *dm_target;
+
+       KASSERT(dm_target_name != NULL);
+
+       dm_target = dm_target_lookup_name(dm_target_name);
+       if (dm_target == NULL)
+               return ENOENT;
+
+       TAILQ_REMOVE(&dm_target_list,
+           dm_target, dm_target_next);
+
+       (void)kmem_free(dm_target, 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;
+
+       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));
+       }
+
+       return 0;
+}
+
+/*
+ * Allocate new target entry.
+ */
+dm_target_t*
+dm_target_alloc(const char *name)
+{
+       return kmem_zalloc(sizeof(dm_target_t), KM_NOSLEEP);
+}
+
+/*
+ * 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();
+
+       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);
+       }
+
+       return target_array;
+}
+
+/* Initialize dm_target subsystem. */
+int
+dm_target_init(void)
+{
+       dm_target_t *dmt,*dmt1,*dmt2,*dmt3,*dmt4,*dmt5,*dmt6;
+       int r;
+
+       r = 0;
+
+       dmt = dm_target_alloc("linear");
+       dmt1 = dm_target_alloc("zero");
+       dmt2 = dm_target_alloc("error");
+       dmt3 = dm_target_alloc("striped");
+       dmt4 = dm_target_alloc("mirror");
+       dmt5 = dm_target_alloc("snapshot");
+       dmt6 = dm_target_alloc("snapshot-origin");
+
+       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);
+
+       dmt1->version[0] = 1;
+       dmt1->version[1] = 0;
+       dmt1->version[2] = 0;
+       strlcpy(dmt1->name, "zero", DM_MAX_TYPE_NAME);
+       dmt1->init = &dm_target_zero_init;
+       dmt1->status = &dm_target_zero_status;
+       dmt1->strategy = &dm_target_zero_strategy;
+       dmt1->deps = &dm_target_zero_deps;
+       dmt1->destroy = &dm_target_zero_destroy;
+       dmt1->upcall = &dm_target_zero_upcall;
+
+       r = dm_target_insert(dmt1);
+
+       dmt2->version[0] = 1;
+       dmt2->version[1] = 0;
+       dmt2->version[2] = 0;
+       strlcpy(dmt2->name, "error", DM_MAX_TYPE_NAME);
+       dmt2->init = &dm_target_error_init;
+       dmt2->status = &dm_target_error_status;
+       dmt2->strategy = &dm_target_error_strategy;
+       dmt2->deps = &dm_target_error_deps;
+       dmt2->destroy = &dm_target_error_destroy;
+       dmt2->upcall = &dm_target_error_upcall;
+
+       r = dm_target_insert(dmt2);
+
+       dmt3->version[0] = 1;
+       dmt3->version[1] = 0;
+       dmt3->version[2] = 3;
+       strlcpy(dmt3->name, "striped", DM_MAX_TYPE_NAME);
+       dmt3->init = &dm_target_linear_init;
+       dmt3->status = &dm_target_linear_status;
+       dmt3->strategy = &dm_target_linear_strategy;
+       dmt3->deps = &dm_target_linear_deps;
+       dmt3->destroy = &dm_target_linear_destroy;
+       dmt3->upcall = NULL;
+
+       r = dm_target_insert(dmt3);
+
+       dmt4->version[0] = 1;
+       dmt4->version[1] = 0;
+       dmt4->version[2] = 3;
+       strlcpy(dmt4->name, "mirror", DM_MAX_TYPE_NAME);
+       dmt4->init = NULL;
+       dmt4->status = NULL;
+       dmt4->strategy = NULL;
+       dmt4->deps = NULL;
+       dmt4->destroy = NULL;
+       dmt4->upcall = NULL;
+
+       r = dm_target_insert(dmt4);
+
+       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);
+
+       return r;
+}
Index: ./dev/dm/dm_target_error.c
===================================================================
RCS file: ./dev/dm/dm_target_error.c
diff -N ./dev/dm/dm_target_error.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/dm_target_error.c  18 Dec 2008 23:44:25 -0000
@@ -0,0 +1,99 @@
+/*        $NetBSD: dm_target_error.c,v 1.1.2.9 2008/11/05 13:45:02 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"
+
+/* 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;
+
+       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;
+}
Index: ./dev/dm/dm_target_linear.c
===================================================================
RCS file: ./dev/dm/dm_target_linear.c
diff -N ./dev/dm/dm_target_linear.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/dm_target_linear.c 18 Dec 2008 23:44:25 -0000
@@ -0,0 +1,237 @@
+/*        $NetBSD: dm_target_linear.c,v 1.1.2.20 2008/12/03 00:10: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 onyl 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[9] &&
+                (*ap = strsep(&params, " \t")) != NULL;) {
+               if (**ap != '\0')
+                       ap++;
+       }
+
+       /* Insert dmp to global pdev list */
+       if ((dmp = dm_pdev_insert(argv[0])) == NULL)
+               return ENOENT;
+
+       aprint_debug("Linear target init function called %s--%s!!\n",
+           argv[0], argv[1]);
+
+       if ((tlc = kmem_alloc(sizeof(dm_target_linear_config_t), KM_NOSLEEP))
+           == NULL)
+               return 1;
+
+       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;
+       uint32_t i;
+       uint32_t count;
+       size_t prm_len;
+
+       tlc = target_config;
+       prm_len = 0;
+       count = 0;
+
+       /* count number of chars in offset */
+       for(i = tlc->offset; i != 0; i /= 10)
+               count++;
+
+       aprint_debug("Linear target status function called\n");
+
+       /* length of name + count of chars + one space and null char */
+       prm_len = strlen(tlc->pdev->name) + count + 2;
+
+       if ((params = kmem_alloc(prm_len, KM_NOSLEEP)) == NULL)
+               return NULL;
+
+       aprint_debug("%s %"PRIu64, tlc->pdev->name, tlc->offset);
+       snprintf(params, prm_len,"%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);
+
+       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;
+}
Index: ./dev/dm/dm_target_snapshot.c
===================================================================
RCS file: ./dev/dm/dm_target_snapshot.c
diff -N ./dev/dm/dm_target_snapshot.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/dm_target_snapshot.c       18 Dec 2008 23:44:25 -0000
@@ -0,0 +1,427 @@
+/*        $NetBSD: dm_target_snapshot.c,v 1.1.2.13 2008/11/05 13:45:02 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
+ */
+
+/*
+ * 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"
+
+/*
+ * 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);
+
+       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,
+       char *params)
+{
+       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);
+
+       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;
+}
Index: ./dev/dm/dm_target_zero.c
===================================================================
RCS file: ./dev/dm/dm_target_zero.c
diff -N ./dev/dm/dm_target_zero.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/dm_target_zero.c   18 Dec 2008 23:44:25 -0000
@@ -0,0 +1,108 @@
+/*        $NetBSD: dm_target_zero.c,v 1.1.2.11 2008/11/05 13:45:02 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"
+
+/*
+ * 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;
+
+       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;
+}
+
Index: ./dev/dm/files.dm
===================================================================
RCS file: ./dev/dm/files.dm
diff -N ./dev/dm/files.dm
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/files.dm   18 Dec 2008 23:44:25 -0000
@@ -0,0 +1,11 @@
+defpseudo   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_error.c    dm
+file        dev/dm/dm_target_linear.c   dm
+file        dev/dm/dm_target_zero.c     dm
+file       dev/dm/dm_target_snapshot.c dm
Index: ./dev/dm/netbsd-dm.h
===================================================================
RCS file: ./dev/dm/netbsd-dm.h
diff -N ./dev/dm/netbsd-dm.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/netbsd-dm.h        18 Dec 2008 23:44:26 -0000
@@ -0,0 +1,283 @@
+/*        $NetBSD: netbsd-dm.h,v 1.1.2.6 2008/11/05 13:45:02 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"
+#define DM_TABLE_DEPS         "deps"
+
+
+/* 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 */
+
+
+#ifdef __LIB_DEVMAPPER__
+
+#  define MAJOR(x) major((x))
+#  define MINOR(x) minor((x))
+#  define MKDEV(x,y) makedev((x),(y))
+
+/* Name of device-mapper driver in kernel */
+#define DM_NAME "dm"
+
+/* Types for nbsd_get_dm_major */
+#define DM_CHAR_MAJOR 1
+#define DM_BLOCK_MAJOR 2
+
+/* libdm_netbsd.c */
+/* Get dm device major/minor numbers */
+int nbsd_get_dm_major(uint32_t *, uint32_t *, int);
+
+int nbsd_dmi_add_cmd(const char *, prop_dictionary_t);
+int nbsd_dmi_add_version(const int [3], prop_dictionary_t);
+int nbsd_dm_add_uint(const char *, uint64_t, prop_dictionary_t);
+int nbsd_dm_add_str(const char *, char *, prop_dictionary_t );
+
+struct dm_ioctl* nbsd_dm_dict_to_dmi(prop_dictionary_t, const int);
+
+#endif /* __LIB_DEVMAPPER__ */
+
+#endif /* __NETBSD_DM_H__ */
Index: ./dev/dm/doc/design.txt
===================================================================
RCS file: ./dev/dm/doc/design.txt
diff -N ./dev/dm/doc/design.txt
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/doc/design.txt     18 Dec 2008 23:44:26 -0000
@@ -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>
Index: ./dev/dm/doc/proposal-dm.txt
===================================================================
RCS file: ./dev/dm/doc/proposal-dm.txt
diff -N ./dev/dm/doc/proposal-dm.txt
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ./dev/dm/doc/proposal-dm.txt        18 Dec 2008 23:44:26 -0000
@@ -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 */
+       ...
+};
+