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(¶ms, " \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(¶ms, " \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 */
+ ...
+};
+