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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +#include +#include +#include +#include +#include + +#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 + * command + * + * 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 +#include + +#include +#include +#include +#include + +#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 +#include + +#include +#include +#include +#include + +#include + +#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: + * + * cmd_data + * + * + * name + * ... + * + * dev + * ... + * + * + * + */ +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 + * + * cmd_data + * + * ... + * + */ +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: + * + * cmd_data + * + * + * type + * ... + * + * start + * ... + * + * length + * ... + * + * params + * ... + * + * + * + */ +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 +#include + +#include +#include +#include +#include +#include + +#include + +#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 +#include + +#include + +#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 +#include + +#include + +#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 +#include + +#include + +#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 +#include + +#include +#include +#include + +#include + +#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 +#include + +#include +#include +#include + +#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 +#include + +#include + +#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 + +#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. + * + * + * command + * ... + * + * event_nr + * ... + * + * name + * ... + * + * uuid + * ... + * + * dev + * + * + * flags + * + * + * version + * + * ... + * ... + * ... + * + * + * cmd_data + * + * + * + * + * + * 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. + * + * + * + * name + * ... + * + * version + * + * ... + * ... + * ... + * + * + * + * + */ + +/* + * DM_DEV_LIST == "names" + * Request list of device-mapper created devices from kernel. + * + * + * + * name + * ... + * + * dev + * ... + * + * + * + * dev is uint64_t + * + */ + + /* + * DM_DEV_RENAME == "rename" + * Rename device to string. + * + * + * ... + * + * + */ + + /* + * 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. + * + * + * + * type + * ... + * + * start + * ... + * + * length + * ... + * + * params + * ... + * + * + * + * 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 + * + * + * ... + * + * + */ + +#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. + + + version + ... + + target_count + + + open_count + + + flags + + + event_nr + + + dev + + + name + ... + + uuid + ... + + + + + + + + 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. + + + + version + ... + + name + ... + + + + 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. + + + + dev + ... + + name + ... + + + + 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.. + + + name + ... + + + 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. + */ + }; + + + sector_start + ... + + length + ... + + target_type + ... + + aditional info + ... + 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 */ + ... +}; +