diff options
| author | haad <haad@NetBSD.org> | 2008-12-19 15:24:03 +0000 |
|---|---|---|
| committer | haad <haad@NetBSD.org> | 2008-12-19 15:24:03 +0000 |
| commit | c9d0c62a0c2516f692248741b9902592dfd4f690 (patch) | |
| tree | df3160366e6de1d5dc788216cbed8f3cad3e04fe /sys/dev | |
| parent | 992269c5c4ca1327588284bd09118ceb10888690 (diff) | |
Merge the haad-dm branch to -current. This branch adds LVM functionality to
the base NetBSD system. It uses Linux LVM2 tools and our BSD licensed
device-mapper driver.
The device-mapper driver can be used to create virtual block devices which
maps virtual blocks to real with target mapping called target. Currently
these targets are available a linear, zero, error and a snapshot (this is
work in progress and doesn't work yet).
The lvm2tools adds lvm and dmsetup binary to based system, where the lvm
tool is used to manage and administer whole LVM and the dmestup is used to
communicate iwith device-mapper kernel driver. With these tools also
a libdevmapper library is instaled to the base system.
Building of tools and driver is currently disable and can be enabled with
MKLVM=yes in mk.conf. I will add sets lists and rc.d script soon.
Oked by agc@ and cube@.
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/dm/TODO | 18 | ||||
| -rw-r--r-- | sys/dev/dm/device-mapper.c | 540 | ||||
| -rw-r--r-- | sys/dev/dm/dm.h | 341 | ||||
| -rw-r--r-- | sys/dev/dm/dm_dev.c | 384 | ||||
| -rw-r--r-- | sys/dev/dm/dm_ioctl.c | 952 | ||||
| -rw-r--r-- | sys/dev/dm/dm_pdev.c | 246 | ||||
| -rw-r--r-- | sys/dev/dm/dm_table.c | 273 | ||||
| -rw-r--r-- | sys/dev/dm/dm_target.c | 290 | ||||
| -rw-r--r-- | sys/dev/dm/dm_target_error.c | 99 | ||||
| -rw-r--r-- | sys/dev/dm/dm_target_linear.c | 237 | ||||
| -rw-r--r-- | sys/dev/dm/dm_target_snapshot.c | 427 | ||||
| -rw-r--r-- | sys/dev/dm/dm_target_zero.c | 108 | ||||
| -rw-r--r-- | sys/dev/dm/doc/design.txt | 475 | ||||
| -rw-r--r-- | sys/dev/dm/doc/proposal-dm.txt | 90 | ||||
| -rw-r--r-- | sys/dev/dm/files.dm | 11 | ||||
| -rw-r--r-- | sys/dev/dm/netbsd-dm.h | 283 |
16 files changed, 4774 insertions, 0 deletions
diff --git a/sys/dev/dm/TODO b/sys/dev/dm/TODO new file mode 100644 index 00000000000..d14858b0217 --- /dev/null +++ b/sys/dev/dm/TODO @@ -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. + diff --git a/sys/dev/dm/device-mapper.c b/sys/dev/dm/device-mapper.c new file mode 100644 index 00000000000..1691e45c806 --- /dev/null +++ b/sys/dev/dm/device-mapper.c @@ -0,0 +1,540 @@ +/* $NetBSD: device-mapper.c,v 1.2 2008/12/19 15:24:03 haad Exp $ */ + +/* + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Hamsik. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * I want to say thank you to all people who helped me with this project. + */ + +#include <sys/types.h> +#include <sys/param.h> + +#include <sys/buf.h> +#include <sys/conf.h> +#include <sys/dkio.h> +#include <sys/disk.h> +#include <sys/disklabel.h> +#include <sys/ioctl.h> +#include <sys/ioccom.h> +#include <sys/kmem.h> +#include <sys/module.h> + +#include "netbsd-dm.h" +#include "dm.h" + +static dev_type_open(dmopen); +static dev_type_close(dmclose); +static dev_type_read(dmread); +static dev_type_write(dmwrite); +static dev_type_ioctl(dmioctl); +static dev_type_strategy(dmstrategy); +static dev_type_dump(dmdump); +static dev_type_size(dmsize); + +/* attach and detach routines */ +int dmattach(void); +int dmdestroy(void); + +static int dm_cmd_to_fun(prop_dictionary_t); +static int disk_ioctl_switch(dev_t, u_long, void *); +static int dm_ioctl_switch(u_long); +static void dmminphys(struct buf *); + +/* ***Variable-definitions*** */ +const struct bdevsw dm_bdevsw = { + dmopen, dmclose, dmstrategy, dmioctl, dmdump, dmsize, D_DISK | D_MPSAFE +}; + +const struct cdevsw dm_cdevsw = { + dmopen, dmclose, dmread, dmwrite, dmioctl, + nostop, notty, nopoll, nommap, nokqfilter, D_DISK | D_MPSAFE +}; + +int unload; + +/* + * This array is used to translate cmd to function pointer. + * + * Interface between libdevmapper and lvm2tools uses different + * names for one IOCTL call because libdevmapper do another thing + * then. When I run "info" or "mknodes" libdevmapper will send same + * ioctl to kernel but will do another things in userspace. + * + */ +struct cmd_function cmd_fn[] = { + {"version", dm_get_version_ioctl}, + {"targets", dm_list_versions_ioctl}, + {"create", dm_dev_create_ioctl}, + {"info", dm_dev_status_ioctl}, + {"mknodes", dm_dev_status_ioctl}, + {"names", dm_dev_list_ioctl}, + {"suspend", dm_dev_suspend_ioctl}, + {"remove", dm_dev_remove_ioctl}, + {"rename", dm_dev_rename_ioctl}, + {"resume", dm_dev_resume_ioctl}, + {"clear", dm_table_clear_ioctl}, + {"deps", dm_table_deps_ioctl}, + {"reload", dm_table_load_ioctl}, + {"status", dm_table_status_ioctl}, + {"table", dm_table_status_ioctl}, + {NULL, NULL} +}; + + +MODULE(MODULE_CLASS_MISC, dm, NULL); + +/* New module handle routine */ +static int +dm_modcmd(modcmd_t cmd, void *arg) +{ +#ifdef _MODULE + int bmajor = -1, cmajor = -1; + + switch (cmd) { + case MODULE_CMD_INIT: + dmattach(); + return devsw_attach("dm", &dm_bdevsw, &bmajor, + &dm_cdevsw, &cmajor); + break; + + case MODULE_CMD_FINI: + dmdestroy(); + return devsw_detach(&dm_bdevsw, &dm_cdevsw); + break; + + case MODULE_CMD_STAT: + return ENOTTY; + + default: + return ENOTTY; + } + + return 0; +#else + + if (cmd == MODULE_CMD_INIT) + return 0; + return ENOTTY; + +#endif /* _MODULE */ +} + + +/* attach routine */ +int +dmattach(void) +{ + dm_target_init(); + dm_dev_init(); + dm_pdev_init(); + + return 0; +} + +/* Destroy routine */ +int +dmdestroy(void) +{ + atomic_inc_32(&unload); + + dm_dev_destroy(); + dm_pdev_destroy(); + dm_target_destroy(); + + return 0; +} + +static int +dmopen(dev_t dev, int flags, int mode, struct lwp *l) +{ + aprint_debug("open routine called %d\n", minor(dev)); + + if (unload == 1) + return EBUSY; + + return 0; +} + +static int +dmclose(dev_t dev, int flags, int mode, struct lwp *l) +{ + aprint_debug("CLOSE routine called\n"); + + return 0; +} + + +static int +dmioctl(dev_t dev, const u_long cmd, void *data, int flag, struct lwp *l) +{ + int r; + prop_dictionary_t dm_dict_in; + + r = 0; + + aprint_debug("dmioctl called\n"); + + KASSERT(data != NULL); + + if (disk_ioctl_switch(dev, cmd, data) != 0) { + struct plistref *pref = (struct plistref *) data; + + if((r = prop_dictionary_copyin_ioctl(pref, cmd, &dm_dict_in)) != 0) + return r; + + dm_check_version(dm_dict_in); + + /* call cmd selected function */ + if ((r = dm_ioctl_switch(cmd)) != 0) { + prop_object_release(dm_dict_in); + return r; + } + + /* run ioctl routine */ + if ((r = dm_cmd_to_fun(dm_dict_in)) != 0) { + prop_object_release(dm_dict_in); + return r; + } + + r = prop_dictionary_copyout_ioctl(pref, cmd, dm_dict_in); + + prop_object_release(dm_dict_in); + } + + return r; +} + +/* + * Translate command sent from libdevmapper to func. + */ +static int +dm_cmd_to_fun(prop_dictionary_t dm_dict){ + int i, r; + prop_string_t command; + + r = 0; + + if ((command = prop_dictionary_get(dm_dict, DM_IOCTL_COMMAND)) == NULL) + return EINVAL; + + for(i = 0; cmd_fn[i].cmd != NULL; i++) + if (prop_string_equals_cstring(command, cmd_fn[i].cmd)) + break; + + if (cmd_fn[i].cmd == NULL) + return EINVAL; + + aprint_debug("ioctl %s called\n", cmd_fn[i].cmd); + r = cmd_fn[i].fn(dm_dict); + + return r; +} + +/* Call apropriate ioctl handler function. */ +static int +dm_ioctl_switch(u_long cmd) +{ + int r; + + r = 0; + + switch(cmd) { + + case NETBSD_DM_IOCTL: + aprint_debug("NetBSD_DM_IOCTL called\n"); + break; + + default: + aprint_debug("unknown ioctl called\n"); + return ENOTTY; + break; /* NOT REACHED */ + } + + return r; +} + + /* + * Check for disk specific ioctls. + */ + +static int +disk_ioctl_switch(dev_t dev, u_long cmd, void *data) +{ + dm_dev_t *dmv; + + switch(cmd) { + case DIOCGWEDGEINFO: + { + struct dkwedge_info *dkw = (void *) data; + + if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) + return ENOENT; + + aprint_normal("DIOCGWEDGEINFO ioctl called\n"); + + strlcpy(dkw->dkw_devname, dmv->name, 16); + strlcpy(dkw->dkw_wname, dmv->name, DM_NAME_LEN); + strlcpy(dkw->dkw_parent, dmv->name, 16); + + dkw->dkw_offset = 0; + dkw->dkw_size = dm_table_size(&dmv->table_head); + strcpy(dkw->dkw_ptype, DKW_PTYPE_FFS); + + dm_dev_unbusy(dmv); + break; + } + + case DIOCGDINFO: + { + + if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) + return ENOENT; + + aprint_debug("DIOCGDINFO %d\n", dmv->dk_label->d_secsize); + + *(struct disklabel *)data = *(dmv->dk_label); + + dm_dev_unbusy(dmv); + break; + } + + case DIOCGPART: + { + if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) + return ENOENT; + + ((struct partinfo *)data)->disklab = dmv->dk_label; + ((struct partinfo *)data)->part = &dmv->dk_label->d_partitions[0]; + + dm_dev_unbusy(dmv); + break; + } + case DIOCWDINFO: + case DIOCSDINFO: + case DIOCKLABEL: + case DIOCWLABEL: + case DIOCGDEFLABEL: + + default: + aprint_debug("unknown disk_ioctl called\n"); + return 1; + break; /* NOT REACHED */ + } + + return 0; +} + +/* + * Do all IO operations on dm logical devices. + */ +static void +dmstrategy(struct buf *bp) +{ + dm_dev_t *dmv; + dm_table_t *tbl; + dm_table_entry_t *table_en; + struct buf *nestbuf; + + uint32_t dev_type; + + uint64_t buf_start, buf_len, issued_len; + uint64_t table_start, table_end; + uint64_t start, end; + + buf_start = bp->b_blkno * DEV_BSIZE; + buf_len = bp->b_bcount; + + tbl = NULL; + + table_end = 0; + dev_type = 0; + issued_len = 0; + + if ((dmv = dm_dev_lookup(NULL, NULL, minor(bp->b_dev))) == NULL) { + bp->b_error = EIO; + bp->b_resid = bp->b_bcount; + biodone(bp); + return; + } + + /* Select active table */ + tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); + + /* Nested buffers count down to zero therefore I have + to set bp->b_resid to maximal value. */ + bp->b_resid = bp->b_bcount; + + /* + * Find out what tables I want to select. + */ + SLIST_FOREACH(table_en, tbl, next) + { + /* I need need number of bytes not blocks. */ + table_start = table_en->start * DEV_BSIZE; + /* + * I have to sub 1 from table_en->length to prevent + * off by one error + */ + table_end = table_start + (table_en->length)* DEV_BSIZE; + + start = MAX(table_start, buf_start); + + end = MIN(table_end, buf_start + buf_len); + + aprint_debug("----------------------------------------\n"); + aprint_debug("table_start %010" PRIu64", table_end %010" + PRIu64 "\n", table_start, table_end); + aprint_debug("buf_start %010" PRIu64", buf_len %010" + PRIu64"\n", buf_start, buf_len); + aprint_debug("start-buf_start %010"PRIu64", end %010" + PRIu64"\n", start - buf_start, end); + aprint_debug("start %010" PRIu64" , end %010" + PRIu64"\n", start, end); + aprint_debug("\n----------------------------------------\n"); + + if (start < end) { + /* create nested buffer */ + nestbuf = getiobuf(NULL, true); + + nestiobuf_setup(bp, nestbuf, start - buf_start, + (end - start)); + + issued_len += end - start; + + /* I need number of blocks. */ + nestbuf->b_blkno = (start - table_start) / DEV_BSIZE; + + table_en->target->strategy(table_en, nestbuf); + } + } + + if (issued_len < buf_len) + nestiobuf_done(bp, buf_len - issued_len, EINVAL); + + dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); + dm_dev_unbusy(dmv); + + return; +} + + +static int +dmread(dev_t dev, struct uio *uio, int flag) +{ + return (physio(dmstrategy, NULL, dev, B_READ, dmminphys, uio)); +} + +static int +dmwrite(dev_t dev, struct uio *uio, int flag) +{ + return (physio(dmstrategy, NULL, dev, B_WRITE, dmminphys, uio)); +} + +static int +dmdump(dev_t dev, daddr_t blkno, void *va, size_t size) +{ + return ENODEV; +} + +static int +dmsize(dev_t dev) +{ + dm_dev_t *dmv; + uint64_t size; + + size = 0; + + if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) + return -ENOENT; + + size = dm_table_size(&dmv->table_head); + dm_dev_unbusy(dmv); + + return size; +} + +static void +dmminphys(struct buf *bp) +{ + bp->b_bcount = MIN(bp->b_bcount, MAXPHYS); +} + + /* + * Load the label information on the named device + * Actually fabricate a disklabel. + * + * EVENTUALLY take information about different + * data tracks from the TOC and put it in the disklabel + * + * Copied from vnd code. + */ +void +dmgetdisklabel(struct disklabel *lp, dm_table_head_t *head) +{ + struct partition *pp; + int dmp_size; + + dmp_size = dm_table_size(head); + + /* + * Size must be at least 2048 DEV_BSIZE blocks + * (1M) in order to use this geometry. + */ + + lp->d_secperunit = dmp_size; + lp->d_secsize = DEV_BSIZE; + lp->d_nsectors = 32; + lp->d_ntracks = 64; + lp->d_ncylinders = dmp_size / (lp->d_nsectors * lp->d_ntracks); + lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; + + strncpy(lp->d_typename, "lvm", sizeof(lp->d_typename)); + lp->d_type = DTYPE_DM; + strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); + lp->d_rpm = 3600; + lp->d_interleave = 1; + lp->d_flags = 0; + + pp = &lp->d_partitions[0]; + /* + * This is logical offset and therefore it can be 0 + * I will consider table offsets later in dmstrategy. + */ + pp->p_offset = 0; + pp->p_size = dmp_size * DEV_BSIZE; + pp->p_fstype = FS_BSDFFS; /* default value */ + lp->d_npartitions = 1; + + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_checksum = dkcksum(lp); + + return; +} diff --git a/sys/dev/dm/dm.h b/sys/dev/dm/dm.h new file mode 100644 index 00000000000..7ca325c8aa3 --- /dev/null +++ b/sys/dev/dm/dm.h @@ -0,0 +1,341 @@ +/* $NetBSD: dm.h,v 1.2 2008/12/19 15:24:03 haad Exp $ */ + +/* + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Hamsik. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DM_DEV_H_ +#define _DM_DEV_H_ + + +#ifdef _KERNEL + +#include <sys/errno.h> + +#include <sys/atomic.h> +#include <sys/condvar.h> +#include <sys/mutex.h> +#include <sys/rwlock.h> +#include <sys/queue.h> + +#define DM_MAX_TYPE_NAME 16 +#define DM_NAME_LEN 128 +#define DM_UUID_LEN 129 + +#define DM_VERSION_MAJOR 4 +#define DM_VERSION_MINOR 13 +#define DM_VERSION_PATCHLEVEL 0 + +/*** Internal device-mapper structures ***/ + +/* + * A table entry describes a physical range of the logical volume. + */ +#define MAX_TARGET_STRING_LEN 32 + +/* + * A device mapper table is a list of physical ranges plus the mapping target + * applied to them. + */ + +typedef struct dm_table_entry { + struct dm_dev *dm_dev; /* backlink */ + uint64_t start; + uint64_t length; + + struct dm_target *target; /* Link to table target. */ + void *target_config; /* Target specific data. */ + SLIST_ENTRY(dm_table_entry) next; +} dm_table_entry_t; + +SLIST_HEAD(dm_table, dm_table_entry); + +typedef struct dm_table dm_table_t; + +typedef struct dm_table_head { + /* Current active table is selected with this. */ + int cur_active_table; + struct dm_table tables[2]; + + kmutex_t table_mtx; + kcondvar_t table_cv; /*IO waiting cv */ + + uint32_t io_cnt; +} dm_table_head_t; + +#define MAX_DEV_NAME 32 + +/* + * This structure is used to store opened vnodes for disk with name. + * I need this because devices can be opened only once, but I can + * have more then one device on one partition. + */ + +typedef struct dm_pdev { + char name[MAX_DEV_NAME]; + + struct vnode *pdev_vnode; + int ref_cnt; /* reference counter for users ofthis pdev */ + + SLIST_ENTRY(dm_pdev) next_pdev; +} dm_pdev_t; + +/* + * This structure is called for every device-mapper device. + * It points to SLIST of device tables and mirrored, snapshoted etc. devices. + */ +TAILQ_HEAD(dm_dev_head, dm_dev) dm_devs; + +typedef struct dm_dev { + char name[DM_NAME_LEN]; + char uuid[DM_UUID_LEN]; + + int minor; + uint32_t flags; /* store communication protocol flags */ + + kmutex_t dev_mtx; /* mutex for generall device lock */ + kcondvar_t dev_cv; /* cv for between ioctl synchronisation */ + + uint32_t event_nr; + uint32_t ref_cnt; + + uint32_t dev_type; + + dm_table_head_t table_head; + + struct dm_dev_head upcalls; + + struct disklabel *dk_label; /* Disklabel for this table. */ + + TAILQ_ENTRY(dm_dev) next_upcall; /* LIST of mirrored, snapshoted devices. */ + + TAILQ_ENTRY(dm_dev) next_devlist; /* Major device list. */ +} dm_dev_t; + +/* Device types used for upcalls */ +#define DM_ZERO_DEV (1 << 0) +#define DM_ERROR_DEV (1 << 1) +#define DM_LINEAR_DEV (1 << 2) +#define DM_MIRROR_DEV (1 << 3) +#define DM_STRIPE_DEV (1 << 4) +#define DM_SNAPSHOT_DEV (1 << 5) +#define DM_SNAPSHOT_ORIG_DEV (1 << 6) +#define DM_SPARE_DEV (1 << 7) +/* Set this device type only during dev remove ioctl. */ +#define DM_DELETING_DEV (1 << 8) + + +/* for zero, error : dm_target->target_config == NULL */ + +/* + * Target config is initiated with target_init function. + */ + +/* for linear : */ +typedef struct target_linear_config { + dm_pdev_t *pdev; + uint64_t offset; +} dm_target_linear_config_t; + + +/* for mirror : */ +typedef struct target_mirror_config { +#define MAX_MIRROR_COPIES 4 + dm_pdev_t *orig; + dm_pdev_t *copies[MAX_MIRROR_COPIES]; + + /* copied blocks bitmaps administration etc*/ + dm_pdev_t *log_pdev; /* for administration */ + uint64_t log_regionsize; /* blocksize of mirror */ + + /* list of parts that still need copied etc.; run length encoded? */ +} dm_target_mirror_config_t; + + +/* for snapshot : */ +typedef struct target_snapshot_config { + dm_pdev_t *tsc_snap_dev; + /* cow dev is set only for persistent snapshot devices */ + dm_pdev_t *tsc_cow_dev; + + uint64_t tsc_chunk_size; + uint32_t tsc_persistent_dev; +} dm_target_snapshot_config_t; + +/* for snapshot-origin devices */ +typedef struct target_snapshot_origin_config { + dm_pdev_t *tsoc_real_dev; + /* list of snapshots ? */ +} dm_target_snapshot_origin_config_t; + +/* constant dm_target structures for error, zero, linear, stripes etc. */ +typedef struct dm_target { + char name[DM_MAX_TYPE_NAME]; + /* Initialize target_config area */ + int (*init)(dm_dev_t *, void **, char *); + + /* Destroy target_config area */ + int (*destroy)(dm_table_entry_t *); + + int (*deps) (dm_table_entry_t *, prop_array_t); + /* + * Status routine is called to get params string, which is target + * specific. When dm_table_status_ioctl is called with flag + * DM_STATUS_TABLE_FLAG I have to sent params string back. + */ + char * (*status)(void *); + int (*strategy)(dm_table_entry_t *, struct buf *); + int (*upcall)(dm_table_entry_t *, struct buf *); + + uint32_t version[3]; + + TAILQ_ENTRY(dm_target) dm_target_next; +} dm_target_t; + +/* Interface structures */ + +/* + * This structure is used to translate command sent to kernel driver in + * <key>command</key> + * <value></value> + * to function which I can call. + */ +struct cmd_function { + const char *cmd; + int (*fn)(prop_dictionary_t); +}; + +/* device-mapper */ +void dmgetdisklabel(struct disklabel *, dm_table_head_t *); + +/* dm_ioctl.c */ +int dm_dev_create_ioctl(prop_dictionary_t); +int dm_dev_list_ioctl(prop_dictionary_t); +int dm_dev_remove_ioctl(prop_dictionary_t); +int dm_dev_rename_ioctl(prop_dictionary_t); +int dm_dev_resume_ioctl(prop_dictionary_t); +int dm_dev_status_ioctl(prop_dictionary_t); +int dm_dev_suspend_ioctl(prop_dictionary_t); + +int dm_check_version(prop_dictionary_t); +int dm_get_version_ioctl(prop_dictionary_t); +int dm_list_versions_ioctl(prop_dictionary_t); + +int dm_table_clear_ioctl(prop_dictionary_t); +int dm_table_deps_ioctl(prop_dictionary_t); +int dm_table_load_ioctl(prop_dictionary_t); +int dm_table_status_ioctl(prop_dictionary_t); + +/* dm_target.c */ +int dm_target_destroy(void); +int dm_target_insert(dm_target_t *); +prop_array_t dm_target_prop_list(void); +dm_target_t* dm_target_lookup_name(const char *); +int dm_target_rem(char *); + +/* XXX temporally add */ +int dm_target_init(void); + +/* dm_target_zero.c */ +int dm_target_zero_init(dm_dev_t *, void**, char *); +char * dm_target_zero_status(void *); +int dm_target_zero_strategy(dm_table_entry_t *, struct buf *); +int dm_target_zero_destroy(dm_table_entry_t *); +int dm_target_zero_deps(dm_table_entry_t *, prop_array_t); +int dm_target_zero_upcall(dm_table_entry_t *, struct buf *); + +/* dm_target_error.c */ +int dm_target_error_init(dm_dev_t *, void**, char *); +char * dm_target_error_status(void *); +int dm_target_error_strategy(dm_table_entry_t *, struct buf *); +int dm_target_error_deps(dm_table_entry_t *, prop_array_t); +int dm_target_error_destroy(dm_table_entry_t *); +int dm_target_error_upcall(dm_table_entry_t *, struct buf *); + +/* dm_target_linear.c */ +int dm_target_linear_init(dm_dev_t *, void**, char *); +char * dm_target_linear_status(void *); +int dm_target_linear_strategy(dm_table_entry_t *, struct buf *); +int dm_target_linear_deps(dm_table_entry_t *, prop_array_t); +int dm_target_linear_destroy(dm_table_entry_t *); +int dm_target_linear_upcall(dm_table_entry_t *, struct buf *); + +/* Generic function used to convert char to string */ +uint64_t atoi(const char *); + +/* dm_target_snapshot.c */ +int dm_target_snapshot_init(dm_dev_t *, void**, char *); +char * dm_target_snapshot_status(void *); +int dm_target_snapshot_strategy(dm_table_entry_t *, struct buf *); +int dm_target_snapshot_deps(dm_table_entry_t *, prop_array_t); +int dm_target_snapshot_destroy(dm_table_entry_t *); +int dm_target_snapshot_upcall(dm_table_entry_t *, struct buf *); + +/* dm snapshot origin driver */ +int dm_target_snapshot_orig_init(dm_dev_t *, void**, char *); +char * dm_target_snapshot_orig_status(void *); +int dm_target_snapshot_orig_strategy(dm_table_entry_t *, struct buf *); +int dm_target_snapshot_orig_deps(dm_table_entry_t *, prop_array_t); +int dm_target_snapshot_orig_destroy(dm_table_entry_t *); +int dm_target_snapshot_orig_upcall(dm_table_entry_t *, struct buf *); + +/* dm_table.c */ +#define DM_TABLE_ACTIVE 0 +#define DM_TABLE_INACTIVE 1 + +int dm_table_destroy(dm_table_head_t *, uint8_t); +uint64_t dm_table_size(dm_table_head_t *); +dm_table_t * dm_table_get_entry(dm_table_head_t *, uint8_t); +int dm_table_get_target_count(dm_table_head_t *, uint8_t); +void dm_table_release(dm_table_head_t *, uint8_t s); +void dm_table_switch_tables(dm_table_head_t *); +void dm_table_head_init(dm_table_head_t *); +void dm_table_head_destroy(dm_table_head_t *); + +/* dm_dev.c */ +dm_dev_t* dm_dev_alloc(void); +void dm_dev_busy(dm_dev_t *); +int dm_dev_destroy(void); +int dm_dev_free(dm_dev_t *); +int dm_dev_init(void); +int dm_dev_insert(dm_dev_t *); +dm_dev_t* dm_dev_lookup(const char *, const char *, int); +prop_array_t dm_dev_prop_list(void); +dm_dev_t* dm_dev_rem(const char *, const char *, int); +/*int dm_dev_test_minor(int);*/ +void dm_dev_unbusy(dm_dev_t *); + +/* dm_pdev.c */ +int dm_pdev_decr(dm_pdev_t *); +int dm_pdev_destroy(void); +int dm_pdev_init(void); +dm_pdev_t* dm_pdev_insert(const char *); + +#endif /*_KERNEL*/ + +#endif /*_DM_DEV_H_*/ diff --git a/sys/dev/dm/dm_dev.c b/sys/dev/dm/dm_dev.c new file mode 100644 index 00000000000..fa5ce67636b --- /dev/null +++ b/sys/dev/dm/dm_dev.c @@ -0,0 +1,384 @@ +/* $NetBSD: dm_dev.c,v 1.2 2008/12/19 15:24:03 haad Exp $ */ + +/* + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Hamsik. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/param.h> + +#include <sys/disklabel.h> +#include <sys/ioctl.h> +#include <sys/ioccom.h> +#include <sys/kmem.h> + +#include "netbsd-dm.h" +#include "dm.h" + +static dm_dev_t* dm_dev_lookup_name(const char *); +static dm_dev_t* dm_dev_lookup_uuid(const char *); +static dm_dev_t* dm_dev_lookup_minor(int); + +static struct dm_dev_head dm_dev_list = +TAILQ_HEAD_INITIALIZER(dm_dev_list); + +kmutex_t dm_dev_mutex; + +__inline static void +disable_dev(dm_dev_t *dmv) +{ + TAILQ_REMOVE(&dm_dev_list, dmv, next_devlist); + mutex_enter(&dmv->dev_mtx); + mutex_exit(&dm_dev_mutex); + while(dmv->ref_cnt != 0) + cv_wait(&dmv->dev_cv, &dmv->dev_mtx); + mutex_exit(&dmv->dev_mtx); +} + +/* + * Generic function used to lookup dm_dev_t. Calling with dm_dev_name + * and dm_dev_uuid NULL is allowed. + */ +dm_dev_t* +dm_dev_lookup(const char *dm_dev_name, const char *dm_dev_uuid, + int dm_dev_minor) +{ + dm_dev_t *dmv; + + dmv = NULL; + mutex_enter(&dm_dev_mutex); + + /* KASSERT(dm_dev_name != NULL && dm_dev_uuid != NULL && dm_dev_minor > 0); */ + if (dm_dev_minor > 0) + if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL){ + dm_dev_busy(dmv); + mutex_exit(&dm_dev_mutex); + return dmv; + } + + if (dm_dev_name != NULL) + if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL){ + dm_dev_busy(dmv); + mutex_exit(&dm_dev_mutex); + return dmv; + } + + if (dm_dev_uuid != NULL) + if ((dmv = dm_dev_lookup_name(dm_dev_uuid)) != NULL){ + dm_dev_busy(dmv); + mutex_exit(&dm_dev_mutex); + return dmv; + } + mutex_exit(&dm_dev_mutex); + return NULL; +} + + +/* + * Lookup device with its minor number. + */ +static dm_dev_t* +dm_dev_lookup_minor(int dm_dev_minor) +{ + dm_dev_t *dmv; + + TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist){ + if (dm_dev_minor == dmv->minor) + return dmv; + } + + return NULL; +} + +/* + * Lookup device with it's device name. + */ +static dm_dev_t* +dm_dev_lookup_name(const char *dm_dev_name) +{ + dm_dev_t *dmv; + int dlen; int slen; + + slen = strlen(dm_dev_name); + + if (slen == 0) + return NULL; + + TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist){ + + dlen = strlen(dmv->name); + + if(slen != dlen) + continue; + + if (strncmp(dm_dev_name, dmv->name, slen) == 0) + return dmv; + } + + return NULL; +} + +/* + * Lookup device with it's device uuid. Used mostly by LVM2tools. + */ +static dm_dev_t* +dm_dev_lookup_uuid(const char *dm_dev_uuid) +{ + dm_dev_t *dmv; + size_t len; + + len = 0; + len = strlen(dm_dev_uuid); + + if (len == 0) + return NULL; + + TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist){ + + if (strlen(dmv->uuid) != len) + continue; + + if (strncmp(dm_dev_uuid, dmv->uuid, strlen(dmv->uuid)) == 0) + return dmv; + } + + return NULL; +} + +/* + * Insert new device to the global list of devices. + */ +int +dm_dev_insert(dm_dev_t *dev) +{ + dm_dev_t *dmv; + int r; + + dmv = NULL; + r = 0; + + KASSERT(dev != NULL); + mutex_enter(&dm_dev_mutex); + if (((dmv = dm_dev_lookup_uuid(dev->uuid)) == NULL) && + ((dmv = dm_dev_lookup_name(dev->name)) == NULL) && + ((dmv = dm_dev_lookup_minor(dev->minor)) == NULL)){ + + TAILQ_INSERT_TAIL(&dm_dev_list, dev, next_devlist); + + } else + r = EEXIST; + + mutex_exit(&dm_dev_mutex); + return r; +} + +#ifdef notyet +/* + * Lookup device with its minor number. + */ +int +dm_dev_test_minor(int dm_dev_minor) +{ + dm_dev_t *dmv; + + mutex_enter(&dm_dev_mutex); + TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist){ + if (dm_dev_minor == dmv->minor){ + mutex_exit(&dm_dev_mutex); + return 1; + } + } + mutex_exit(&dm_dev_mutex); + + return 0; +} +#endif + +/* + * Remove device selected with dm_dev from global list of devices. + */ +dm_dev_t* +dm_dev_rem(const char *dm_dev_name, const char *dm_dev_uuid, + int dm_dev_minor) +{ + dm_dev_t *dmv; + dmv = NULL; + + mutex_enter(&dm_dev_mutex); + + if (dm_dev_minor > 0) + if ((dmv = dm_dev_lookup_minor(dm_dev_minor)) != NULL){ + disable_dev(dmv); + return dmv; + } + + if (dm_dev_name != NULL) + if ((dmv = dm_dev_lookup_name(dm_dev_name)) != NULL){ + disable_dev(dmv); + return dmv; + } + + if (dm_dev_uuid != NULL) + if ((dmv = dm_dev_lookup_name(dm_dev_uuid)) != NULL){ + disable_dev(dmv); + return dmv; + } + mutex_exit(&dm_dev_mutex); + + return NULL; +} + +/* + * Destroy all devices created in device-mapper. Remove all tables + * free all allocated memmory. + */ +int +dm_dev_destroy(void) +{ + dm_dev_t *dmv; + mutex_enter(&dm_dev_mutex); + + while (TAILQ_FIRST(&dm_dev_list) != NULL){ + + dmv = TAILQ_FIRST(&dm_dev_list); + + TAILQ_REMOVE(&dm_dev_list, TAILQ_FIRST(&dm_dev_list), + next_devlist); + + mutex_enter(&dmv->dev_mtx); + + while (dmv->ref_cnt != 0) + cv_wait(&dmv->dev_cv, &dmv->dev_mtx); + + /* Destroy active table first. */ + dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE); + + /* Destroy inactive table if exits, too. */ + dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); + + dm_table_head_destroy(&dmv->table_head); + + mutex_exit(&dmv->dev_mtx); + mutex_destroy(&dmv->dev_mtx); + cv_destroy(&dmv->dev_cv); + + (void)kmem_free(dmv, sizeof(dm_dev_t)); + } + mutex_exit(&dm_dev_mutex); + + mutex_destroy(&dm_dev_mutex); + return 0; +} + +/* + * Allocate new device entry. + */ +dm_dev_t* +dm_dev_alloc() +{ + dm_dev_t *dmv; + + dmv = kmem_zalloc(sizeof(dm_dev_t), KM_NOSLEEP); + dmv->dk_label = kmem_zalloc(sizeof(struct disklabel), KM_NOSLEEP); + + return dmv; +} + +/* + * Freed device entry. + */ +int +dm_dev_free(dm_dev_t *dmv) +{ + KASSERT(dmv != NULL); + + if (dmv->dk_label != NULL) + (void)kmem_free(dmv->dk_label, sizeof(struct disklabel)); + + (void)kmem_free(dmv, sizeof(dm_dev_t)); + + return 0; +} + +void +dm_dev_busy(dm_dev_t *dmv) +{ + mutex_enter(&dmv->dev_mtx); + dmv->ref_cnt++; + mutex_exit(&dmv->dev_mtx); +} + +void +dm_dev_unbusy(dm_dev_t *dmv) +{ + KASSERT(dmv->ref_cnt != 0); + + mutex_enter(&dmv->dev_mtx); + if (--dmv->ref_cnt == 0) + cv_broadcast(&dmv->dev_cv); + mutex_exit(&dmv->dev_mtx); +} + +/* + * Return prop_array of dm_targer_list dictionaries. + */ +prop_array_t +dm_dev_prop_list(void) +{ + dm_dev_t *dmv; + prop_array_t dev_array; + prop_dictionary_t dev_dict; + + dev_array = prop_array_create(); + + mutex_enter(&dm_dev_mutex); + + TAILQ_FOREACH(dmv, &dm_dev_list, next_devlist) { + dev_dict = prop_dictionary_create(); + + prop_dictionary_set_cstring(dev_dict, DM_DEV_NAME, dmv->name); + prop_dictionary_set_uint32(dev_dict, DM_DEV_DEV, dmv->minor); + + prop_array_add(dev_array, dev_dict); + prop_object_release(dev_dict); + } + + mutex_exit(&dm_dev_mutex); + return dev_array; +} + +/* + * Initialize global device mutex. + */ +int +dm_dev_init() +{ + TAILQ_INIT(&dm_dev_list); /* initialize global dev list */ + mutex_init(&dm_dev_mutex, MUTEX_DEFAULT, IPL_NONE); + return 0; +} diff --git a/sys/dev/dm/dm_ioctl.c b/sys/dev/dm/dm_ioctl.c new file mode 100644 index 00000000000..e6c28f423df --- /dev/null +++ b/sys/dev/dm/dm_ioctl.c @@ -0,0 +1,952 @@ +/* $NetBSD: dm_ioctl.c,v 1.2 2008/12/19 15:24:03 haad Exp $ */ + +/* + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Hamsik. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Locking is used to synchronise between ioctl calls and between dm_table's + * users. + * + * ioctl locking: + * Simple reference counting, to count users of device will be used routines + * dm_dev_busy/dm_dev_unbusy are used for that. + * dm_dev_lookup/dm_dev_rem call dm_dev_busy before return(caller is therefore + * holder of reference_counter last). + * + * ioctl routines which change/remove dm_dev parameters must wait on + * dm_dev::dev_cv and when last user will call dm_dev_unbusy they will wake + * up them. + * + * table_head locking: + * To access table entries dm_table_* routines must be used. + * + * dm_table_get_entry will increment table users reference + * counter. It will return active or inactive table depedns + * on uint8_t argument. + * + * dm_table_release must be called for every table_entry from + * dm_table_get_entry. Between these to calls tables can'tbe switched + * or destroyed. + * + * dm_table_head_init initialize talbe_entries SLISTS and io_cv. + * + * dm_table_head_destroy destroy cv. + * + * There are two types of users for dm_table_head first type will + * only read list and try to do anything with it e.g. dmstrategy, + * dm_table_size etc. There is another user for table_head which wants + * to change table lists e.g. dm_dev_resume_ioctl, dm_dev_remove_ioctl, + * dm_table_clear_ioctl. + * + * NOTE: It is not allowed to call dm_table_destroy, dm_table_switch_tables + * with hold table reference counter. Table reference counter is hold + * after calling dm_table_get_entry routine. After calling this + * function user must call dm_table_release before any writer table + * operation. + * + * Example: dm_table_get_entry + * dm_table_destroy/dm_table_switch_tables + * This exaple will lead to deadlock situation because after dm_table_get_entry + * table reference counter is != 0 and dm_table_destroy have to wait on cv until + * reference counter is 0. + * + */ + +#include <sys/types.h> +#include <sys/param.h> + +#include <sys/disklabel.h> +#include <sys/kmem.h> +#include <sys/malloc.h> +#include <sys/vnode.h> + +#include <machine/int_fmtio.h> + +#include "netbsd-dm.h" +#include "dm.h" + +static uint32_t sc_minor_num; + +#define DM_REMOVE_FLAG(flag, name) do { \ + prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \ + flag &= ~name; \ + prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \ + } while (/*CONSTCOND*/0) + +#define DM_ADD_FLAG(flag, name) do { \ + prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \ + flag |= name; \ + prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \ + } while (/*CONSTCOND*/0) + +static int dm_dbg_print_flags(int); + +/* + * Print flags sent to the kernel from libevmapper. + */ +static int +dm_dbg_print_flags(int flags) +{ + aprint_debug("dbg_print --- %d\n",flags); + + if (flags & DM_READONLY_FLAG) + aprint_debug("dbg_flags: DM_READONLY_FLAG set In/Out\n"); + + if (flags & DM_SUSPEND_FLAG) + aprint_debug("dbg_flags: DM_SUSPEND_FLAG set In/Out \n"); + + if (flags & DM_PERSISTENT_DEV_FLAG) + aprint_debug("db_flags: DM_PERSISTENT_DEV_FLAG set In\n"); + + if (flags & DM_STATUS_TABLE_FLAG) + aprint_debug("dbg_flags: DM_STATUS_TABLE_FLAG set In\n"); + + if (flags & DM_ACTIVE_PRESENT_FLAG) + aprint_debug("dbg_flags: DM_ACTIVE_PRESENT_FLAG set Out\n"); + + if (flags & DM_INACTIVE_PRESENT_FLAG) + aprint_debug("dbg_flags: DM_INACTIVE_PRESENT_FLAG set Out\n"); + + if (flags & DM_BUFFER_FULL_FLAG) + aprint_debug("dbg_flags: DM_BUFFER_FULL_FLAG set Out\n"); + + if (flags & DM_SKIP_BDGET_FLAG) + aprint_debug("dbg_flags: DM_SKIP_BDGET_FLAG set In\n"); + + if (flags & DM_SKIP_LOCKFS_FLAG) + aprint_debug("dbg_flags: DM_SKIP_LOCKFS_FLAG set In\n"); + + if (flags & DM_NOFLUSH_FLAG) + aprint_debug("dbg_flags: DM_NOFLUSH_FLAG set In\n"); + + return 0; +} + +/* + * Get version ioctl call I do it as default therefore this + * function is unused now. + */ +int +dm_get_version_ioctl(prop_dictionary_t dm_dict) +{ + return 0; +} + +/* + * Get list of all available targets from global + * target list and sent them back to libdevmapper. + */ +int +dm_list_versions_ioctl(prop_dictionary_t dm_dict) +{ + prop_array_t target_list; + uint32_t flags; + + flags = 0; + + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); + + dm_dbg_print_flags(flags); + + target_list = dm_target_prop_list(); + + prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, target_list); + + prop_object_release(target_list); + + return 0; +} + +/* + * Create in-kernel entry for device. Device attributes such as name, uuid are + * taken from proplib dictionary. + * + */ +int +dm_dev_create_ioctl(prop_dictionary_t dm_dict) +{ + dm_dev_t *dmv; + const char *name, *uuid; + int r, flags; + + r = 0; + flags = 0; + name = NULL; + uuid = NULL; + + /* Get needed values from dictionary. */ + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); + + dm_dbg_print_flags(flags); + + /* Lookup name and uuid if device already exist quit. */ + if ((dmv = dm_dev_lookup(name, uuid, -1)) != NULL) { + DM_ADD_FLAG(flags, DM_EXISTS_FLAG); /* Device already exists */ + dm_dev_unbusy(dmv); + return EEXIST; + } + + if ((dmv = dm_dev_alloc()) == NULL) + return ENOMEM; + + if (uuid) + strncpy(dmv->uuid, uuid, DM_UUID_LEN); + else + dmv->uuid[0] = '\0'; + + if (name) + strlcpy(dmv->name, name, DM_NAME_LEN); + + dmv->minor = atomic_inc_32_nv(&sc_minor_num); + + dmv->flags = 0; /* device flags are set when needed */ + dmv->ref_cnt = 0; + dmv->event_nr = 0; + dmv->dev_type = 0; + + mutex_init(&dmv->dev_mtx, MUTEX_DEFAULT, IPL_NONE); + cv_init(&dmv->dev_cv, "dm_dev"); + + dm_table_head_init(&dmv->table_head); + + if (flags & DM_READONLY_FLAG) + dmv->flags |= DM_READONLY_FLAG; + + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); + + if ((r = dm_dev_insert(dmv)) != 0){ + dm_dev_free(dmv); + } + + DM_ADD_FLAG(flags, DM_EXISTS_FLAG); + DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); + + return r; +} + +/* + * Get list of created device-mapper devices fromglobal list and + * send it to kernel. + * + * Output dictionary: + * + * <key>cmd_data</key> + * <array> + * <dict> + * <key>name<key> + * <string>...</string> + * + * <key>dev</key> + * <integer>...</integer> + * </dict> + * </array> + * + */ +int +dm_dev_list_ioctl(prop_dictionary_t dm_dict) +{ + prop_array_t dev_list; + + uint32_t flags; + + flags = 0; + + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); + + dm_dbg_print_flags(flags); + + dev_list = dm_dev_prop_list(); + + prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, dev_list); + prop_object_release(dev_list); + + return 0; +} + +/* + * Rename selected devices old name is in struct dm_ioctl. + * newname is taken from dictionary + * + * <key>cmd_data</key> + * <array> + * <string>...</string> + * </array> + */ +int +dm_dev_rename_ioctl(prop_dictionary_t dm_dict) +{ + prop_array_t cmd_array; + dm_dev_t *dmv; + + const char *name, *uuid, *n_name; + uint32_t flags, minor; + + name = NULL; + uuid = NULL; + minor = 0; + + /* Get needed values from dictionary. */ + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); + + dm_dbg_print_flags(flags); + + cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA); + + prop_array_get_cstring_nocopy(cmd_array, 0, &n_name); + + if (strlen(n_name) + 1 > DM_NAME_LEN) + return EINVAL; + + if ((dmv = dm_dev_rem(name, uuid, minor)) == NULL) { + DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); + return ENOENT; + } + + /* change device name */ + /* XXX How to deal with this change, name only used in + * dm_dev_routines, should I add dm_dev_change_name which will run under the + * dm_dev_list mutex ? + */ + strlcpy(dmv->name, n_name, DM_NAME_LEN); + + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); + prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); + + dm_dev_insert(dmv); + + return 0; +} + +/* + * Remove device from global list I have to remove active + * and inactive tables first. + */ +int +dm_dev_remove_ioctl(prop_dictionary_t dm_dict) +{ + dm_dev_t *dmv; + const char *name, *uuid; + uint32_t flags, minor; + + flags = 0; + name = NULL; + uuid = NULL; + + /* Get needed values from dictionary. */ + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); + + dm_dbg_print_flags(flags); + + /* Remove device from global device list */ + if ((dmv = dm_dev_rem(name, uuid, minor)) == NULL){ + DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); + return ENOENT; + } + + /* Destroy active table first. */ + dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE); + + /* Destroy inactive table if exits, too. */ + dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); + + dm_table_head_destroy(&dmv->table_head); + + mutex_destroy(&dmv->dev_mtx); + cv_destroy(&dmv->dev_cv); + + /* Destroy device */ + (void)dm_dev_free(dmv); + + return 0; +} + +/* + * Return actual state of device to libdevmapper. + */ +int +dm_dev_status_ioctl(prop_dictionary_t dm_dict) +{ + dm_dev_t *dmv; + const char *name, *uuid; + uint32_t flags, j, minor; + + name = NULL; + uuid = NULL; + flags = 0; + j = 0; + + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); + + if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { + DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); + return ENOENT; + } + + dm_dbg_print_flags(dmv->flags); + + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); + prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); + + if (dmv->flags & DM_SUSPEND_FLAG) + DM_ADD_FLAG(flags, DM_SUSPEND_FLAG); + + /* Add status flags for tables I have to check both + active and inactive tables. */ + if ((j = dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE))) { + DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); + } else + DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); + + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_TARGET_COUNT, j); + + if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE)) + DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); + else + DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); + + dm_dev_unbusy(dmv); + + return 0; +} + +/* + * Set only flag to suggest that device is suspended. This call is + * not supported in NetBSD. + * + */ +int +dm_dev_suspend_ioctl(prop_dictionary_t dm_dict) +{ + dm_dev_t *dmv; + const char *name, *uuid; + uint32_t flags, minor; + + name = NULL; + uuid = NULL; + flags = 0; + + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); + + if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL){ + DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); + return ENOENT; + } + + atomic_or_32(&dmv->flags, DM_SUSPEND_FLAG); + + dm_dbg_print_flags(dmv->flags); + + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, dmv->flags); + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); + + dm_dev_unbusy(dmv); + + /* Add flags to dictionary flag after dmv -> dict copy */ + DM_ADD_FLAG(flags, DM_EXISTS_FLAG); + + return 0; +} + +/* + * Simulate Linux behaviour better and switch tables here and not in + * dm_table_load_ioctl. + */ +int +dm_dev_resume_ioctl(prop_dictionary_t dm_dict) +{ + dm_dev_t *dmv; + const char *name, *uuid; + uint32_t flags, minor; + + name = NULL; + uuid = NULL; + flags = 0; + +/* char *xml; + xml = prop_dictionary_externalize(dm_dict); + printf("%s\n",xml);*/ + + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); + + /* Remove device from global device list */ + if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL){ + DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); + return ENOENT; + } + + atomic_and_32(&dmv->flags, ~(DM_SUSPEND_FLAG | DM_INACTIVE_PRESENT_FLAG)); + atomic_or_32(&dmv->flags, DM_ACTIVE_PRESENT_FLAG); + + dm_table_switch_tables(&dmv->table_head); + + DM_ADD_FLAG(flags, DM_EXISTS_FLAG); + + dmgetdisklabel(dmv->dk_label, &dmv->table_head); + + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, dmv->flags); + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); + + dm_dev_unbusy(dmv); + + /* Destroy inactive table after resume. */ + dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); + + return 0; +} + +/* + * Table management routines + * lvm2tools doens't send name/uuid to kernel with table + * for lookup I have to use minor number. + */ + +/* + * Remove inactive table from device. Routines which work's with inactive tables + * doesn't need to synchronise with dmstrategy. They can synchronise themselves with mutex?. + * + */ +int +dm_table_clear_ioctl(prop_dictionary_t dm_dict) +{ + dm_dev_t *dmv; + const char *name, *uuid; + uint32_t flags, minor; + + dmv = NULL; + name = NULL; + uuid = NULL; + flags = 0; + minor = 0; + + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); + + aprint_debug("Clearing inactive table from device: %s--%s\n", + name, uuid); + + if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL){ + DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); + return ENOENT; + } + + /* Select unused table */ + dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); + + atomic_and_32(&dmv->flags, ~DM_INACTIVE_PRESENT_FLAG); + + dm_dev_unbusy(dmv); + + return 0; +} + +/* + * Get list of physical devices for active table. + * Get dev_t from pdev vnode and insert it into cmd_array. + * + * XXX. This function is called from lvm2tools to get information + * about physical devices, too e.g. during vgcreate. + */ +int +dm_table_deps_ioctl(prop_dictionary_t dm_dict) +{ + dm_dev_t *dmv; + dm_table_t *tbl; + dm_table_entry_t *table_en; + + prop_array_t cmd_array; + const char *name, *uuid; + uint32_t flags, minor; + + size_t i; + + name = NULL; + uuid = NULL; + dmv = NULL; + flags = 0; + + i = 0; + + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); + + /* create array for dev_t's */ + cmd_array = prop_array_create(); + + if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL){ + DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); + return ENOENT; + } + + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); + prop_dictionary_set_cstring(dm_dict, DM_IOCTL_NAME, dmv->name); + prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); + + aprint_debug("Getting table deps for device: %s\n", dmv->name); + + tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); + + SLIST_FOREACH(table_en, tbl, next) + table_en->target->deps(table_en, cmd_array); + + dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); + dm_dev_unbusy(dmv); + + prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array); + prop_object_release(cmd_array); + + return 0; +} + +/* + * Load new table/tables to device. + * Call apropriate target init routine open all physical pdev's and + * link them to device. For other targets mirror, strip, snapshot + * etc. also add dependency devices to upcalls list. + * + * Load table to inactive slot table are switched in dm_device_resume_ioctl. + * This simulates Linux behaviour better there should not be any difference. + * + */ +int +dm_table_load_ioctl(prop_dictionary_t dm_dict) +{ + dm_dev_t *dmv; + dm_table_entry_t *table_en, *last_table; + dm_table_t *tbl; + dm_target_t *target; + + prop_object_iterator_t iter; + prop_array_t cmd_array; + prop_dictionary_t target_dict; + + const char *name, *uuid, *type; + + uint32_t flags, ret, minor; + + char *str; + + ret = 0; + flags = 0; + name = NULL; + uuid = NULL; + dmv = NULL; + last_table = NULL; + str = NULL; + +/* char *xml; + xml = prop_dictionary_externalize(dm_dict); + printf("%s\n",xml);*/ + + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); + + cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA); + iter = prop_array_iterator(cmd_array); + dm_dbg_print_flags(flags); + + if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL){ + DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); + return ENOENT; + } + + aprint_debug("Loading table to device: %s--%d\n",name,dmv->table_head.cur_active_table); + + /* + * I have to check if this table slot is not used by another table list. + * if it is used I should free them. + */ + if (dmv->flags & DM_INACTIVE_PRESENT_FLAG) + dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); + + dm_dbg_print_flags(dmv->flags); + tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_INACTIVE); + + aprint_debug("dmv->name = %s\n", dmv->name); + + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); + + while((target_dict = prop_object_iterator_next(iter)) != NULL){ + + prop_dictionary_get_cstring_nocopy(target_dict, + DM_TABLE_TYPE, &type); + + /* + * If we want to deny table with 2 or more different + * target we should do it here + */ + if ((target = dm_target_lookup_name(type)) == NULL) + return ENOENT; + + if ((table_en=kmem_alloc(sizeof(dm_table_entry_t), + KM_NOSLEEP)) == NULL) + return ENOMEM; + + prop_dictionary_get_uint64(target_dict, DM_TABLE_START, + &table_en->start); + prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH, + &table_en->length); + + table_en->target = target; + table_en->dm_dev = dmv; + table_en->target_config = NULL; + + /* + * There is a parameter string after dm_target_spec + * structure which points to /dev/wd0a 284 part of + * table. String str points to this text. This can be + * null and therefore it should be checked before we try to + * use it. + */ + prop_dictionary_get_cstring(target_dict, + DM_TABLE_PARAMS, (char**)&str); + + if (SLIST_EMPTY(tbl)) + /* insert this table to head */ + SLIST_INSERT_HEAD(tbl, table_en, next); + else + SLIST_INSERT_AFTER(last_table, table_en, next); + + /* + * Params string is different for every target, + * therfore I have to pass it to target init + * routine and parse parameters there. + */ + + if ((ret = target->init(dmv, &table_en->target_config, + str)) != 0) { + + dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); + free(str, M_TEMP); + + return ret; + } + + last_table = table_en; + + free(str, M_TEMP); + } + prop_object_iterator_release(iter); + + DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); + atomic_or_32(&dmv->flags, DM_INACTIVE_PRESENT_FLAG); + + dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); + dm_dev_unbusy(dmv); + return 0; +} + +/* + * Get description of all tables loaded to device from kernel + * and send it to libdevmapper. + * + * Output dictionary for every table: + * + * <key>cmd_data</key> + * <array> + * <dict> + * <key>type<key> + * <string>...</string> + * + * <key>start</key> + * <integer>...</integer> + * + * <key>length</key> + * <integer>...</integer> + * + * <key>params</key> + * <string>...</string> + * </dict> + * </array> + * + */ +int +dm_table_status_ioctl(prop_dictionary_t dm_dict) +{ + dm_dev_t *dmv; + dm_table_t *tbl; + dm_table_entry_t *table_en; + + prop_array_t cmd_array; + prop_dictionary_t target_dict; + + uint32_t rec_size, minor; + + const char *name, *uuid; + char *params; + int flags; + + dmv = NULL; + uuid = NULL; + name = NULL; + params = NULL; + flags = 0; + rec_size = 0; + + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); + prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); + prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); + + cmd_array = prop_array_create(); + + if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL){ + DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); + return ENOENT; + } + + if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE)) + DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); + else { + DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); + + if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE)) + DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); + else { + DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); + } + } + + if (dmv->flags & DM_SUSPEND_FLAG) + DM_ADD_FLAG(flags, DM_SUSPEND_FLAG); + + prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); + + /* I should use mutex here and not rwlock there can be IO operation + during this ioctl on device. */ + + aprint_debug("Status of device tables: %s--%d\n", + name, dmv->table_head.cur_active_table); + + tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); + + SLIST_FOREACH(table_en, tbl, next) + { + target_dict = prop_dictionary_create(); + aprint_debug("%016" PRIu64 ", length %016" PRIu64 + ", target %s\n", table_en->start, table_en->length, + table_en->target->name); + + prop_dictionary_set_uint64(target_dict, DM_TABLE_START, + table_en->start); + prop_dictionary_set_uint64(target_dict, DM_TABLE_LENGTH, + table_en->length); + + prop_dictionary_set_cstring(target_dict, DM_TABLE_TYPE, + table_en->target->name); + + /* dm_table_get_cur_actv.table ?? */ + prop_dictionary_set_int32(target_dict, DM_TABLE_STAT, + dmv->table_head.cur_active_table); + + if (flags |= DM_STATUS_TABLE_FLAG) { + params = table_en->target->status + (table_en->target_config); + + if(params != NULL){ + prop_dictionary_set_cstring(target_dict, + DM_TABLE_PARAMS, params); + + kmem_free(params, strlen(params) + 1); + } + } + + prop_array_add(cmd_array, target_dict); + prop_object_release(target_dict); + } + + dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); + dm_dev_unbusy(dmv); + + prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array); + prop_object_release(cmd_array); + + return 0; +} + + +/* + * For every call I have to set kernel driver version. + * Because I can have commands supported only in other + * newer/later version. This routine is called for every + * ioctl command. + */ +int +dm_check_version(prop_dictionary_t dm_dict) +{ + size_t i; + int dm_version[3]; + prop_array_t ver; + + ver = prop_dictionary_get(dm_dict, DM_IOCTL_VERSION); + + for(i=0; i < 3; i++) + prop_array_get_uint32(ver, i, &dm_version[i]); + + if (DM_VERSION_MAJOR != dm_version[0] || DM_VERSION_MINOR < dm_version[1]){ + aprint_debug("libdevmapper/kernel version mismatch " + "kernel: %d.%d.%d libdevmapper: %d.%d.%d\n", + DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, + dm_version[0], dm_version[1], dm_version[2]); + + return EIO; + } + + prop_array_set_uint32(ver, 0, DM_VERSION_MAJOR); + prop_array_set_uint32(ver, 1, DM_VERSION_MINOR); + prop_array_set_uint32(ver, 2, DM_VERSION_PATCHLEVEL); + + return 0; +} diff --git a/sys/dev/dm/dm_pdev.c b/sys/dev/dm/dm_pdev.c new file mode 100644 index 00000000000..b04d2cd7b14 --- /dev/null +++ b/sys/dev/dm/dm_pdev.c @@ -0,0 +1,246 @@ +/* $NetBSD: dm_pdev.c,v 1.2 2008/12/19 15:24:03 haad Exp $ */ + +/* + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Hamsik. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/param.h> + +#include <sys/disk.h> +#include <sys/fcntl.h> +#include <sys/kmem.h> +#include <sys/namei.h> +#include <sys/vnode.h> + +#include <dev/dkvar.h> + +#include "dm.h" + +SLIST_HEAD(dm_pdevs, dm_pdev) dm_pdev_list; + +kmutex_t dm_pdev_mutex; + +static dm_pdev_t *dm_pdev_alloc(const char *); +static int dm_pdev_rem(dm_pdev_t *); +static dm_pdev_t* dm_pdev_lookup_name(const char *); + +/* + * Find used pdev with name == dm_pdev_name. + */ +dm_pdev_t* +dm_pdev_lookup_name(const char *dm_pdev_name) +{ + dm_pdev_t *dm_pdev; + int dlen; int slen; + + KASSERT(dm_pdev_name != NULL); + + slen = strlen(dm_pdev_name); + + SLIST_FOREACH(dm_pdev, &dm_pdev_list, next_pdev) { + dlen = strlen(dm_pdev->name); + + if (slen != dlen) + continue; + + if (strncmp(dm_pdev_name, dm_pdev->name, slen) == 0) + return dm_pdev; + } + + return NULL; +} + +/* + * Create entry for device with name dev_name and open vnode for it. + * If entry already exists in global SLIST I will only increment + * reference counter. + */ +dm_pdev_t* +dm_pdev_insert(const char *dev_name) +{ + dm_pdev_t *dmp; + int error; + + KASSERT(dev_name != NULL); + + mutex_enter(&dm_pdev_mutex); + dmp = dm_pdev_lookup_name(dev_name); + + if (dmp != NULL) { + dmp->ref_cnt++; + aprint_verbose("dmp_pdev_insert pdev %s already in tree\n",dev_name); + mutex_exit(&dm_pdev_mutex); + return dmp; + } + mutex_exit(&dm_pdev_mutex); + + if ((dmp = dm_pdev_alloc(dev_name)) == NULL) + return NULL; + + error = dk_lookup(dev_name, curlwp, &dmp->pdev_vnode, UIO_SYSSPACE); + if (error) { + aprint_verbose("dk_lookup on device: %s failed with error %d!\n", + dev_name, error); + kmem_free(dmp, sizeof(dm_pdev_t)); + return NULL; + } + + dmp->ref_cnt = 1; + + mutex_enter(&dm_pdev_mutex); + SLIST_INSERT_HEAD(&dm_pdev_list, dmp, next_pdev); + mutex_exit(&dm_pdev_mutex); + + return dmp; +} + +/* + * Initialize pdev subsystem. + */ +int +dm_pdev_init(void) +{ + SLIST_INIT(&dm_pdev_list); /* initialize global pdev list */ + mutex_init(&dm_pdev_mutex, MUTEX_DEFAULT, IPL_NONE); + + return 0; +} + +/* + * Allocat new pdev structure if is not already present and + * set name. + */ +static dm_pdev_t* +dm_pdev_alloc(const char *name) +{ + dm_pdev_t *dmp; + + if ((dmp = kmem_zalloc(sizeof(dm_pdev_t), KM_NOSLEEP)) == NULL) + return NULL; + + strlcpy(dmp->name, name, MAX_DEV_NAME); + + dmp->ref_cnt = 0; + dmp->pdev_vnode = NULL; + + return dmp; +} + +/* + * Destroy allocated dm_pdev. + */ +static int +dm_pdev_rem(dm_pdev_t *dmp) +{ + int err; + + KASSERT(dmp != NULL); + + if (dmp->pdev_vnode != NULL) { + err = vn_close(dmp->pdev_vnode, FREAD | FWRITE, FSCRED); + if (err != 0) + return err; + } + + kmem_free(dmp, sizeof(*dmp)); + dmp = NULL; + + return 0; +} + +/* + * Destroy all existing pdev's in device-mapper. + */ +int +dm_pdev_destroy(void) +{ + dm_pdev_t *dm_pdev; + + mutex_enter(&dm_pdev_mutex); + while (!SLIST_EMPTY(&dm_pdev_list)) { /* List Deletion. */ + + dm_pdev = SLIST_FIRST(&dm_pdev_list); + + SLIST_REMOVE_HEAD(&dm_pdev_list, next_pdev); + + dm_pdev_rem(dm_pdev); + } + mutex_exit(&dm_pdev_mutex); + + mutex_destroy(&dm_pdev_mutex); + return 0; +} + +/* + * This funcion is called from dm_dev_remove_ioctl. + * When I'm removing device from list, I have to decrement + * reference counter. If reference counter is 0 I will remove + * dmp from global list and from device list to. And I will CLOSE + * dmp vnode too. + */ + +/* + * Decrement pdev reference counter if 0 remove it. + */ +int +dm_pdev_decr(dm_pdev_t *dmp) +{ + KASSERT(dmp != NULL); + /* + * If this was last reference remove dmp from + * global list also. + */ + mutex_enter(&dm_pdev_mutex); + + if (--dmp->ref_cnt == 0) { + SLIST_REMOVE(&dm_pdev_list, dmp, dm_pdev, next_pdev); + mutex_exit(&dm_pdev_mutex); + dm_pdev_rem(dmp); + return 0; + } + + mutex_exit(&dm_pdev_mutex); + return 0; +} + +/*static int + dm_pdev_dump_list() + { + dm_pdev_t *dmp; + + aprint_verbose("Dumping dm_pdev_list \n"); + + SLIST_FOREACH(dmp, &dm_pdev_list, next_pdev) { + aprint_verbose("dm_pdev_name %s ref_cnt %d list_rf_cnt %d\n", + dmp->name, dmp->ref_cnt, dmp->list_ref_cnt); + } + + return 0; + + }*/ diff --git a/sys/dev/dm/dm_table.c b/sys/dev/dm/dm_table.c new file mode 100644 index 00000000000..a20897a367c --- /dev/null +++ b/sys/dev/dm/dm_table.c @@ -0,0 +1,273 @@ +/* $NetBSD: dm_table.c,v 1.2 2008/12/19 15:24:03 haad Exp $ */ + +/* + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Hamsik. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/param.h> + +#include <sys/kmem.h> + +#include "dm.h" + +/* + * There are two types of users of this interface: + * + * a) Readers such as + * dmstrategy, dmgetdisklabel, dmsize, dm_dev_status_ioctl, + * dm_table_deps_ioctl, dm_table_status_ioctl, dm_table_reload_ioctl + * + * b) Writers such as + * dm_dev_remove_ioctl, dm_dev_resume_ioctl, dm_table_clear_ioctl + * + * Writers can work with table_head only when there are no readers. I + * use reference counting on io_cnt. + * + */ + +static int dm_table_busy(dm_table_head_t *, uint8_t ); +static void dm_table_unbusy(dm_table_head_t *); + +/* + * Function to increment table user reference counter. Return id + * of table_id table. + * DM_TABLE_ACTIVE will return active table id. + * DM_TABLE_INACTIVE will return inactive table id. + */ +static int +dm_table_busy(dm_table_head_t *head, uint8_t table_id) +{ + uint8_t id; + + id = 0; + + mutex_enter(&head->table_mtx); + + if (table_id == DM_TABLE_ACTIVE) + id = head->cur_active_table; + else + id = 1 - head->cur_active_table; + + head->io_cnt++; + + mutex_exit(&head->table_mtx); + return id; +} + +/* + * Function release table lock and eventually wakeup all waiters. + */ +static void +dm_table_unbusy(dm_table_head_t *head) +{ + KASSERT(head->io_cnt != 0); + + mutex_enter(&head->table_mtx); + + if (--head->io_cnt == 0) + cv_broadcast(&head->table_cv); + + mutex_exit(&head->table_mtx); +} + +/* + * Return current active table to caller, increment io_cnt reference counter. + */ +dm_table_t * +dm_table_get_entry(dm_table_head_t *head, uint8_t table_id) +{ + uint8_t id; + + id = dm_table_busy(head, table_id); + + return &head->tables[id]; +} + +/* + * Decrement io reference counter and wake up all callers, with table_head cv. + */ +void +dm_table_release(dm_table_head_t *head, uint8_t table_id) +{ + dm_table_unbusy(head); +} + +/* + * Switch table from inactive to active mode. Have to wait until io_cnt is 0. + */ +void +dm_table_switch_tables(dm_table_head_t *head) +{ + mutex_enter(&head->table_mtx); + + while (head->io_cnt != 0) + cv_wait(&head->table_cv, &head->table_mtx); + + head->cur_active_table = 1 - head->cur_active_table; + + mutex_exit(&head->table_mtx); +} + +/* + * Destroy all table data. This function can run when there are no + * readers on table lists. + * + * XXX Is it ok to call kmem_free and potentialy VOP_CLOSE with held mutex ?xs + */ +int +dm_table_destroy(dm_table_head_t *head, uint8_t table_id) +{ + dm_table_t *tbl; + dm_table_entry_t *table_en; + uint8_t id; + + mutex_enter(&head->table_mtx); + + printf("dm_Table_destroy called with %d--%d\n", table_id, head->io_cnt); + + while (head->io_cnt != 0) + cv_wait(&head->table_cv, &head->table_mtx); + + if (table_id == DM_TABLE_ACTIVE) + id = head->cur_active_table; + else + id = 1 - head->cur_active_table; + + tbl = &head->tables[id]; + + while (!SLIST_EMPTY(tbl)) { /* List Deletion. */ + table_en = SLIST_FIRST(tbl); + /* + * Remove target specific config data. After successfull + * call table_en->target_config must be set to NULL. + */ + table_en->target->destroy(table_en); + + SLIST_REMOVE_HEAD(tbl, next); + + kmem_free(table_en, sizeof(*table_en)); + } + + mutex_exit(&head->table_mtx); + + return 0; +} + +/* + * Return length of active table in device. + */ +uint64_t +dm_table_size(dm_table_head_t *head) +{ + dm_table_t *tbl; + dm_table_entry_t *table_en; + uint64_t length; + uint8_t id; + + length = 0; + + id = dm_table_busy(head, DM_TABLE_ACTIVE); + + /* Select active table */ + tbl = &head->tables[id]; + + /* + * Find out what tables I want to select. + * if length => rawblkno then we should used that table. + */ + SLIST_FOREACH(table_en, tbl, next) + length += table_en->length; + + dm_table_unbusy(head); + + return length / DEV_BSIZE; +} + +/* + * Return > 0 if table is at least one table entry (returns number of entries) + * and return 0 if there is not. Target count returned from this function + * doesn't need to be true when userspace user receive it (after return + * there can be dm_dev_resume_ioctl), therfore this isonly informative. + */ +int +dm_table_get_target_count(dm_table_head_t *head, uint8_t table_id) +{ + dm_table_entry_t *table_en; + dm_table_t *tbl; + uint32_t target_count; + uint8_t id; + + target_count = 0; + + id = dm_table_busy(head, table_id); + + tbl = &head->tables[id]; + + SLIST_FOREACH(table_en, tbl, next) + target_count++; + + dm_table_unbusy(head); + + return target_count; +} + + +/* + * Initialize table_head structures, I'm trying to keep this structure as + * opaque as possible. + */ +void +dm_table_head_init(dm_table_head_t *head) +{ + head->cur_active_table = 0; + head->io_cnt = 0; + + /* Initialize tables. */ + SLIST_INIT(&head->tables[0]); + SLIST_INIT(&head->tables[1]); + + mutex_init(&head->table_mtx, MUTEX_DEFAULT, IPL_NONE); + cv_init(&head->table_cv, "dm_io"); +} + +/* + * Destroy all variables in table_head + */ +void +dm_table_head_destroy(dm_table_head_t *head) +{ + KASSERT(!mutex_owned(&head->table_mtx)); + KASSERT(!cv_has_waiters(&head->table_cv)); + /* tables doens't exists when I call this routine, therefore + it doesn't make sense to have io_cnt != 0 */ + KASSERT(head->io_cnt == 0); + + cv_destroy(&head->table_cv); + mutex_destroy(&head->table_mtx); +} diff --git a/sys/dev/dm/dm_target.c b/sys/dev/dm/dm_target.c new file mode 100644 index 00000000000..d1ab3f9e259 --- /dev/null +++ b/sys/dev/dm/dm_target.c @@ -0,0 +1,290 @@ +/* $NetBSD: dm_target.c,v 1.2 2008/12/19 15:24:03 haad Exp $ */ + +/* + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Hamsik. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code Must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/param.h> + +#include <sys/kmem.h> + +#include "netbsd-dm.h" +#include "dm.h" + +static dm_target_t* dm_target_alloc(const char *); + +TAILQ_HEAD(dm_target_head, dm_target); + +static struct dm_target_head dm_target_list = +TAILQ_HEAD_INITIALIZER(dm_target_list); + +/* + * Search for name in TAIL and return apropriate pointer. + */ +dm_target_t* +dm_target_lookup_name(const char *dm_target_name) +{ + dm_target_t *dm_target; + int dlen; int slen; + + slen = strlen(dm_target_name)+1; + + TAILQ_FOREACH (dm_target, &dm_target_list, dm_target_next) { + dlen = strlen(dm_target->name)+1; + + if (dlen != slen) + continue; + + if (strncmp(dm_target_name, dm_target->name, slen) == 0) + return dm_target; + } + + return NULL; +} + +/* + * Insert new target struct into the TAIL. + * dm_target + * contains name, version, function pointer to specifif target functions. + */ +int +dm_target_insert(dm_target_t *dm_target) +{ + dm_target_t *dmt; + + dmt = dm_target_lookup_name(dm_target->name); + + if (dmt != NULL) + return EEXIST; + + TAILQ_INSERT_TAIL(&dm_target_list, dm_target, dm_target_next); + + return 0; +} + + +/* + * Remove target from TAIL, target is selected with it's name. + */ +int +dm_target_rem(char *dm_target_name) +{ + dm_target_t *dm_target; + + KASSERT(dm_target_name != NULL); + + dm_target = dm_target_lookup_name(dm_target_name); + if (dm_target == NULL) + return ENOENT; + + TAILQ_REMOVE(&dm_target_list, + dm_target, dm_target_next); + + (void)kmem_free(dm_target, sizeof(dm_target_t)); + + return 0; +} + +/* + * Destroy all targets and remove them from queue. + * This routine is called from dm_detach, before module + * is unloaded. + */ + +int +dm_target_destroy(void) +{ + dm_target_t *dm_target; + + while (TAILQ_FIRST(&dm_target_list) != NULL){ + + dm_target = TAILQ_FIRST(&dm_target_list); + + TAILQ_REMOVE(&dm_target_list, TAILQ_FIRST(&dm_target_list), + dm_target_next); + + (void)kmem_free(dm_target, sizeof(dm_target_t)); + } + + return 0; +} + +/* + * Allocate new target entry. + */ +dm_target_t* +dm_target_alloc(const char *name) +{ + return kmem_zalloc(sizeof(dm_target_t), KM_NOSLEEP); +} + +/* + * Return prop_array of dm_target dictionaries. + */ +prop_array_t +dm_target_prop_list(void) +{ + prop_array_t target_array,ver; + prop_dictionary_t target_dict; + dm_target_t *dm_target; + + size_t i; + + target_array = prop_array_create(); + + TAILQ_FOREACH (dm_target, &dm_target_list, dm_target_next){ + + target_dict = prop_dictionary_create(); + ver = prop_array_create(); + prop_dictionary_set_cstring(target_dict, DM_TARGETS_NAME, + dm_target->name); + + for (i = 0; i < 3; i++) + prop_array_add_uint32(ver, dm_target->version[i]); + + prop_dictionary_set(target_dict, DM_TARGETS_VERSION, ver); + prop_array_add(target_array, target_dict); + + prop_object_release(ver); + prop_object_release(target_dict); + } + + return target_array; +} + +/* Initialize dm_target subsystem. */ +int +dm_target_init(void) +{ + dm_target_t *dmt,*dmt1,*dmt2,*dmt3,*dmt4,*dmt5,*dmt6; + int r; + + r = 0; + + dmt = dm_target_alloc("linear"); + dmt1 = dm_target_alloc("zero"); + dmt2 = dm_target_alloc("error"); + dmt3 = dm_target_alloc("striped"); + dmt4 = dm_target_alloc("mirror"); + dmt5 = dm_target_alloc("snapshot"); + dmt6 = dm_target_alloc("snapshot-origin"); + + dmt->version[0] = 1; + dmt->version[1] = 0; + dmt->version[2] = 2; + strlcpy(dmt->name, "linear", DM_MAX_TYPE_NAME); + dmt->init = &dm_target_linear_init; + dmt->status = &dm_target_linear_status; + dmt->strategy = &dm_target_linear_strategy; + dmt->deps = &dm_target_linear_deps; + dmt->destroy = &dm_target_linear_destroy; + dmt->upcall = &dm_target_linear_upcall; + + r = dm_target_insert(dmt); + + dmt1->version[0] = 1; + dmt1->version[1] = 0; + dmt1->version[2] = 0; + strlcpy(dmt1->name, "zero", DM_MAX_TYPE_NAME); + dmt1->init = &dm_target_zero_init; + dmt1->status = &dm_target_zero_status; + dmt1->strategy = &dm_target_zero_strategy; + dmt1->deps = &dm_target_zero_deps; + dmt1->destroy = &dm_target_zero_destroy; + dmt1->upcall = &dm_target_zero_upcall; + + r = dm_target_insert(dmt1); + + dmt2->version[0] = 1; + dmt2->version[1] = 0; + dmt2->version[2] = 0; + strlcpy(dmt2->name, "error", DM_MAX_TYPE_NAME); + dmt2->init = &dm_target_error_init; + dmt2->status = &dm_target_error_status; + dmt2->strategy = &dm_target_error_strategy; + dmt2->deps = &dm_target_error_deps; + dmt2->destroy = &dm_target_error_destroy; + dmt2->upcall = &dm_target_error_upcall; + + r = dm_target_insert(dmt2); + + dmt3->version[0] = 1; + dmt3->version[1] = 0; + dmt3->version[2] = 3; + strlcpy(dmt3->name, "striped", DM_MAX_TYPE_NAME); + dmt3->init = &dm_target_linear_init; + dmt3->status = &dm_target_linear_status; + dmt3->strategy = &dm_target_linear_strategy; + dmt3->deps = &dm_target_linear_deps; + dmt3->destroy = &dm_target_linear_destroy; + dmt3->upcall = NULL; + + r = dm_target_insert(dmt3); + + dmt4->version[0] = 1; + dmt4->version[1] = 0; + dmt4->version[2] = 3; + strlcpy(dmt4->name, "mirror", DM_MAX_TYPE_NAME); + dmt4->init = NULL; + dmt4->status = NULL; + dmt4->strategy = NULL; + dmt4->deps = NULL; + dmt4->destroy = NULL; + dmt4->upcall = NULL; + + r = dm_target_insert(dmt4); + + dmt5->version[0] = 1; + dmt5->version[1] = 0; + dmt5->version[2] = 5; + strlcpy(dmt5->name, "snapshot", DM_MAX_TYPE_NAME); + dmt5->init = &dm_target_snapshot_init; + dmt5->status = &dm_target_snapshot_status; + dmt5->strategy = &dm_target_snapshot_strategy; + dmt5->deps = &dm_target_snapshot_deps; + dmt5->destroy = &dm_target_snapshot_destroy; + dmt5->upcall = &dm_target_snapshot_upcall; + + r = dm_target_insert(dmt5); + + dmt6->version[0] = 1; + dmt6->version[1] = 0; + dmt6->version[2] = 5; + strlcpy(dmt6->name, "snapshot-origin", DM_MAX_TYPE_NAME); + dmt6->init = &dm_target_snapshot_orig_init; + dmt6->status = &dm_target_snapshot_orig_status; + dmt6->strategy = &dm_target_snapshot_orig_strategy; + dmt6->deps = &dm_target_snapshot_orig_deps; + dmt6->destroy = &dm_target_snapshot_orig_destroy; + dmt6->upcall = &dm_target_snapshot_orig_upcall; + + r = dm_target_insert(dmt6); + + return r; +} diff --git a/sys/dev/dm/dm_target_error.c b/sys/dev/dm/dm_target_error.c new file mode 100644 index 00000000000..09efdf703ea --- /dev/null +++ b/sys/dev/dm/dm_target_error.c @@ -0,0 +1,99 @@ +/* $NetBSD: dm_target_error.c,v 1.2 2008/12/19 15:24:03 haad Exp $ */ + +/* + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Hamsik. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file implements initial version of device-mapper error target. + */ +#include <sys/types.h> +#include <sys/param.h> + +#include <sys/buf.h> + +#include "dm.h" + +/* Init function called from dm_table_load_ioctl. */ +int +dm_target_error_init(dm_dev_t *dmv, void **target_config, char *argv) +{ + + printf("Error target init function called!!\n"); + + *target_config = NULL; + + dmv->dev_type = DM_ERROR_DEV; + + return 0; +} + +/* Status routine called to get params string. */ +char * +dm_target_error_status(void *target_config) +{ + return NULL; +} + +/* Strategy routine called from dm_strategy. */ +int +dm_target_error_strategy(dm_table_entry_t *table_en, struct buf *bp) +{ + + printf("Error target read function called!!\n"); + + bp->b_error = EIO; + bp->b_resid = 0; + + biodone(bp); + + return 0; +} + +/* Doesn't do anything here. */ +int +dm_target_error_destroy(dm_table_entry_t *table_en) +{ + table_en->target_config = NULL; + + return 0; +} + +/* Doesn't not need to do anything here. */ +int +dm_target_error_deps(dm_table_entry_t *table_en, prop_array_t prop_array) +{ + return 0; +} + +/* Unsupported for this target. */ +int +dm_target_error_upcall(dm_table_entry_t *table_en, struct buf *bp) +{ + return 0; +} diff --git a/sys/dev/dm/dm_target_linear.c b/sys/dev/dm/dm_target_linear.c new file mode 100644 index 00000000000..c65e21fc3a3 --- /dev/null +++ b/sys/dev/dm/dm_target_linear.c @@ -0,0 +1,237 @@ +/* $NetBSD: dm_target_linear.c,v 1.2 2008/12/19 15:24:03 haad Exp $ */ + +/* + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Hamsik. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +/* + * This file implements initial version of device-mapper dklinear target. + */ + +#include <sys/types.h> +#include <sys/param.h> + +#include <sys/buf.h> +#include <sys/kmem.h> +#include <sys/vnode.h> + +#include <machine/int_fmtio.h> + +#include "dm.h" + +/* + * Allocate target specific config data, and link them to table. + * This function is called only when, flags is not READONLY and + * therefore we can add things to pdev list. This should not a + * problem because this routine is called onyl from dm_table_load_ioctl. + * @argv[0] is name, + * @argv[1] is physical data offset. + */ +int +dm_target_linear_init(dm_dev_t *dmv, void **target_config, char *params) +{ + dm_target_linear_config_t *tlc; + dm_pdev_t *dmp; + + char **ap, *argv[3]; + + if(params == NULL) + return EINVAL; + + /* + * Parse a string, containing tokens delimited by white space, + * into an argument vector + */ + for (ap = argv; ap < &argv[9] && + (*ap = strsep(¶ms, " \t")) != NULL;) { + if (**ap != '\0') + ap++; + } + + /* Insert dmp to global pdev list */ + if ((dmp = dm_pdev_insert(argv[0])) == NULL) + return ENOENT; + + aprint_debug("Linear target init function called %s--%s!!\n", + argv[0], argv[1]); + + if ((tlc = kmem_alloc(sizeof(dm_target_linear_config_t), KM_NOSLEEP)) + == NULL) + return 1; + + tlc->pdev = dmp; + tlc->offset = 0; /* default settings */ + + /* Check user input if it is not leave offset as 0. */ + tlc->offset = atoi(argv[1]); + + *target_config = tlc; + + dmv->dev_type = DM_LINEAR_DEV; + + return 0; +} + +/* + * Status routine is called to get params string, which is target + * specific. When dm_table_status_ioctl is called with flag + * DM_STATUS_TABLE_FLAG I have to sent params string back. + */ +char * +dm_target_linear_status(void *target_config) +{ + dm_target_linear_config_t *tlc; + char *params; + uint32_t i; + uint32_t count; + size_t prm_len; + + tlc = target_config; + prm_len = 0; + count = 0; + + /* count number of chars in offset */ + for(i = tlc->offset; i != 0; i /= 10) + count++; + + aprint_debug("Linear target status function called\n"); + + /* length of name + count of chars + one space and null char */ + prm_len = strlen(tlc->pdev->name) + count + 2; + + if ((params = kmem_alloc(prm_len, KM_NOSLEEP)) == NULL) + return NULL; + + aprint_debug("%s %"PRIu64, tlc->pdev->name, tlc->offset); + snprintf(params, prm_len,"%s %"PRIu64, tlc->pdev->name, tlc->offset); + + return params; +} + +/* + * Do IO operation, called from dmstrategy routine. + */ +int +dm_target_linear_strategy(dm_table_entry_t *table_en, struct buf *bp) +{ + dm_target_linear_config_t *tlc; + + tlc = table_en->target_config; + +/* printf("Linear target read function called %" PRIu64 "!!\n", + tlc->offset);*/ + + bp->b_blkno += tlc->offset; + + VOP_STRATEGY(tlc->pdev->pdev_vnode, bp); + + return 0; + +} + +/* + * Destroy target specific data. Decrement table pdevs. + */ +int +dm_target_linear_destroy(dm_table_entry_t *table_en) +{ + dm_target_linear_config_t *tlc; + + /* + * Destroy function is called for every target even if it + * doesn't have target_config. + */ + + if (table_en->target_config == NULL) + return 0; + + tlc = table_en->target_config; + + /* Decrement pdev ref counter if 0 remove it */ + dm_pdev_decr(tlc->pdev); + + kmem_free(table_en->target_config, sizeof(dm_target_linear_config_t)); + + table_en->target_config = NULL; + + return 0; +} + +/* Add this target pdev dependiences to prop_array_t */ +int +dm_target_linear_deps(dm_table_entry_t *table_en, prop_array_t prop_array) +{ + dm_target_linear_config_t *tlc; + struct vattr va; + + int error; + + if (table_en->target_config == NULL) + return ENOENT; + + tlc = table_en->target_config; + + if ((error = VOP_GETATTR(tlc->pdev->pdev_vnode, &va, curlwp->l_cred)) != 0) + return error; + + prop_array_add_uint64(prop_array, (uint64_t)va.va_rdev); + + return 0; +} + +/* + * Register upcall device. + * Linear target doesn't need any upcall devices but other targets like + * mirror, snapshot, multipath, stripe will use this functionality. + */ +int +dm_target_linear_upcall(dm_table_entry_t *table_en, struct buf *bp) +{ + return 0; +} + +/* + * Transform char s to uint64_t offset number. + */ +uint64_t +atoi(const char *s) +{ + uint64_t n; + n = 0; + + while (*s != '\0') { + if (!isdigit(*s)) + break; + + n = (10 * n) + (*s - '0'); + s++; + } + + return n; +} diff --git a/sys/dev/dm/dm_target_snapshot.c b/sys/dev/dm/dm_target_snapshot.c new file mode 100644 index 00000000000..3482fa04491 --- /dev/null +++ b/sys/dev/dm/dm_target_snapshot.c @@ -0,0 +1,427 @@ +/* $NetBSD: dm_target_snapshot.c,v 1.2 2008/12/19 15:24:03 haad Exp $ */ + +/* + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Hamsik. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * 1. Suspend my_data to temporarily stop any I/O while the snapshot is being + * activated. + * dmsetup suspend my_data + * + * 2. Create the snapshot-origin device with no table. + * dmsetup create my_data_org + * + * 3. Read the table from my_data and load it into my_data_org. + * dmsetup table my_data | dmsetup load my_data_org + * + * 4. Resume this new table. + * dmsetup resume my_data_org + * + * 5. Create the snapshot device with no table. + * dmsetup create my_data_snap + * + * 6. Load the table into my_data_snap. This uses /dev/hdd1 as the COW device and + * uses a 32kB chunk-size. + * echo "0 `blockdev --getsize /dev/mapper/my_data` snapshot \ + * /dev/mapper/my_data_org /dev/hdd1 p 64" | dmsetup load my_data_snap + * + * 7. Reload my_data as a snapshot-origin device that points to my_data_org. + * echo "0 `blockdev --getsize /dev/mapper/my_data` snapshot-origin \ + * /dev/mapper/my_data_org" | dmsetup load my_data + * + * 8. Resume the snapshot and origin devices. + * dmsetup resume my_data_snap + * dmsetup resume my_data + */ + +/* + * This file implements initial version of device-mapper snapshot target. + */ +#include <sys/types.h> +#include <sys/param.h> + +#include <sys/buf.h> +#include <sys/kmem.h> +#include <sys/vnode.h> + +#include "dm.h" + +/* + * Init function called from dm_table_load_ioctl. + * argv: /dev/mapper/my_data_org /dev/mapper/tsc_cow_dev p 64 + * snapshot_origin device, cow device, persistent flag, chunk size + */ +int +dm_target_snapshot_init(dm_dev_t *dmv, void **target_config, char *params) +{ + dm_target_snapshot_config_t *tsc; + dm_pdev_t *dmp_snap, *dmp_cow; + char **ap, *argv[5]; + + dmp_cow = NULL; + + if (params == NULL) + return EINVAL; + /* + * Parse a string, containing tokens delimited by white space, + * into an argument vector + */ + for (ap = argv; ap < &argv[4] && + (*ap = strsep(¶ms, " \t")) != NULL;) { + if (**ap != '\0') + ap++; + } + + printf("Snapshot target init function called!!\n"); + printf("Snapshotted device: %s, cow device %s,\n\t persistent flag: %s, " + "chunk size: %s\n", argv[0], argv[1], argv[2], argv[3]); + + /* Insert snap device to global pdev list */ + if ((dmp_snap = dm_pdev_insert(argv[0])) == NULL) + return ENOENT; + + if ((tsc = kmem_alloc(sizeof(dm_target_snapshot_config_t), KM_NOSLEEP)) + == NULL) + return 1; + + tsc->tsc_persistent_dev = 0; + + /* There is now cow device for nonpersistent snapshot devices */ + if (strcmp(argv[2], "p") == 0) { + tsc->tsc_persistent_dev = 1; + + /* Insert cow device to global pdev list */ + if ((dmp_cow = dm_pdev_insert(argv[1])) == NULL) + return ENOENT; + } + + tsc->tsc_chunk_size = atoi(argv[3]); + + tsc->tsc_snap_dev = dmp_snap; + tsc->tsc_cow_dev = dmp_cow; + + *target_config = tsc; + + dmv->dev_type = DM_SNAPSHOT_DEV; + + return 0; +} + +/* + * Status routine is called to get params string, which is target + * specific. When dm_table_status_ioctl is called with flag + * DM_STATUS_TABLE_FLAG I have to sent params string back. + */ +char * +dm_target_snapshot_status(void *target_config) +{ + dm_target_snapshot_config_t *tsc; + + uint32_t i; + uint32_t count; + size_t prm_len, cow_len; + char *params, *cow_name; + + tsc = target_config; + + prm_len = 0; + cow_len = 0; + count = 0; + cow_name = NULL; + + printf("Snapshot target status function called\n"); + + /* count number of chars in offset */ + for(i = tsc->tsc_chunk_size; i != 0; i /= 10) + count++; + + if(tsc->tsc_persistent_dev) + cow_len = strlen(tsc->tsc_cow_dev->name); + + /* length of names + count of chars + spaces and null char */ + prm_len = strlen(tsc->tsc_snap_dev->name) + cow_len + count + 5; + + if ((params = kmem_alloc(prm_len, KM_NOSLEEP)) == NULL) + return NULL; + + printf("%s %s %s %"PRIu64"\n", tsc->tsc_snap_dev->name, + tsc->tsc_cow_dev->name, tsc->tsc_persistent_dev ? "p":"n", + tsc->tsc_chunk_size); + + snprintf(params, prm_len, "%s %s %s %"PRIu64, tsc->tsc_snap_dev->name, + tsc->tsc_persistent_dev ? tsc->tsc_cow_dev->name : "", + tsc->tsc_persistent_dev ? "p":"n", + tsc->tsc_chunk_size); + + return params; +} + +/* Strategy routine called from dm_strategy. */ +int +dm_target_snapshot_strategy(dm_table_entry_t *table_en, struct buf *bp) +{ + + printf("Snapshot target read function called!!\n"); + + bp->b_error = EIO; + bp->b_resid = 0; + + biodone(bp); + + return 0; +} + +/* Doesn't do anything here. */ +int +dm_target_snapshot_destroy(dm_table_entry_t *table_en) +{ + dm_target_snapshot_config_t *tsc; + + /* + * Destroy function is called for every target even if it + * doesn't have target_config. + */ + + if (table_en->target_config == NULL) + return 0; + + printf("Snapshot target destroy function called\n"); + + tsc = table_en->target_config; + + /* Decrement pdev ref counter if 0 remove it */ + dm_pdev_decr(tsc->tsc_snap_dev); + + if (tsc->tsc_persistent_dev) + dm_pdev_decr(tsc->tsc_cow_dev); + + kmem_free(table_en->target_config, sizeof(dm_target_snapshot_config_t)); + + table_en->target_config = NULL; + + return 0; +} + +/* Add this target dependiences to prop_array_t */ +int +dm_target_snapshot_deps(dm_table_entry_t *table_en, + prop_array_t prop_array) +{ + dm_target_snapshot_config_t *tsc; + struct vattr va; + + int error; + + if (table_en->target_config == NULL) + return 0; + + tsc = table_en->target_config; + + if ((error = VOP_GETATTR(tsc->tsc_snap_dev->pdev_vnode, &va, curlwp->l_cred)) != 0) + return error; + + prop_array_add_uint64(prop_array, (uint64_t)va.va_rdev); + + if (tsc->tsc_persistent_dev) { + + if ((error = VOP_GETATTR(tsc->tsc_cow_dev->pdev_vnode, &va, + curlwp->l_cred)) != 0) + return error; + + prop_array_add_uint64(prop_array, (uint64_t)va.va_rdev); + + } + + return 0; +} + +/* Upcall is used to inform other depended devices about IO. */ +int +dm_target_snapshot_upcall(dm_table_entry_t *table_en, struct buf *bp) +{ + printf("dm_target_snapshot_upcall called\n"); + + printf("upcall buf flags %s %s\n", + (bp->b_flags & B_WRITE) ? "B_WRITE":"", + (bp->b_flags & B_READ) ? "B_READ":""); + + return 0; +} + +/* + * dm target snapshot origin routines. + * + * Keep for compatibility with linux lvm2tools. They use two targets + * to implement snapshots. Snapshot target will implement exception + * store and snapshot origin will implement device which calls every + * snapshot device when write is done on master device. + */ + +/* + * Init function called from dm_table_load_ioctl. + * + * argv: /dev/mapper/my_data_real + */ +int +dm_target_snapshot_orig_init(dm_dev_t *dmv, void **target_config, + char *params) +{ + dm_target_snapshot_origin_config_t *tsoc; + dm_pdev_t *dmp_real; + + if (params == NULL) + return EINVAL; + + printf("Snapshot origin target init function called!!\n"); + printf("Parent device: %s\n", params); + + /* Insert snap device to global pdev list */ + if ((dmp_real = dm_pdev_insert(params)) == NULL) + return ENOENT; + + if ((tsoc = kmem_alloc(sizeof(dm_target_snapshot_origin_config_t), KM_NOSLEEP)) + == NULL) + return 1; + + tsoc->tsoc_real_dev = dmp_real; + + dmv->dev_type = DM_SNAPSHOT_ORIG_DEV; + + *target_config = tsoc; + + return 0; +} + +/* + * Status routine is called to get params string, which is target + * specific. When dm_table_status_ioctl is called with flag + * DM_STATUS_TABLE_FLAG I have to sent params string back. + */ +char * +dm_target_snapshot_orig_status(void *target_config) +{ + dm_target_snapshot_origin_config_t *tsoc; + + size_t prm_len; + char *params; + + tsoc = target_config; + + prm_len = 0; + + printf("Snapshot origin target status function called\n"); + + /* length of names + count of chars + spaces and null char */ + prm_len = strlen(tsoc->tsoc_real_dev->name) + 1; + + printf("real_dev name %s\n",tsoc->tsoc_real_dev->name); + + if ((params = kmem_alloc(prm_len, KM_NOSLEEP)) == NULL) + return NULL; + + printf("%s\n", tsoc->tsoc_real_dev->name); + + snprintf(params, prm_len, "%s", tsoc->tsoc_real_dev->name); + + return params; +} + +/* Strategy routine called from dm_strategy. */ +int +dm_target_snapshot_orig_strategy(dm_table_entry_t *table_en, struct buf *bp) +{ + + printf("Snapshot_Orig target read function called!!\n"); + + bp->b_error = EIO; + bp->b_resid = 0; + + biodone(bp); + + return 0; +} + +/* Decrement pdev and free allocated space. */ +int +dm_target_snapshot_orig_destroy(dm_table_entry_t *table_en) +{ + dm_target_snapshot_origin_config_t *tsoc; + + /* + * Destroy function is called for every target even if it + * doesn't have target_config. + */ + + if (table_en->target_config == NULL) + return 0; + + tsoc = table_en->target_config; + + /* Decrement pdev ref counter if 0 remove it */ + dm_pdev_decr(tsoc->tsoc_real_dev); + + kmem_free(table_en->target_config, sizeof(dm_target_snapshot_origin_config_t)); + + table_en->target_config = NULL; + + return 0; +} + +/* + * Get target deps and add them to prop_array_t. + */ +int +dm_target_snapshot_orig_deps(dm_table_entry_t *table_en, + prop_array_t prop_array) +{ + dm_target_snapshot_origin_config_t *tsoc; + struct vattr va; + + int error; + + if (table_en->target_config == NULL) + return 0; + + tsoc = table_en->target_config; + + if ((error = VOP_GETATTR(tsoc->tsoc_real_dev->pdev_vnode, &va, + curlwp->l_cred)) != 0) + return error; + + prop_array_add_uint64(prop_array, (uint64_t)va.va_rdev); + + return 0; +} + +/* Unsupported for this target. */ +int +dm_target_snapshot_orig_upcall(dm_table_entry_t *table_en, struct buf *bp) +{ + return 0; +} diff --git a/sys/dev/dm/dm_target_zero.c b/sys/dev/dm/dm_target_zero.c new file mode 100644 index 00000000000..b2defff836c --- /dev/null +++ b/sys/dev/dm/dm_target_zero.c @@ -0,0 +1,108 @@ +/* $NetBSD: dm_target_zero.c,v 1.2 2008/12/19 15:24:03 haad Exp $ */ + +/* + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Hamsik. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +/* + * This file implements initial version of device-mapper zero target. + */ +#include <sys/types.h> +#include <sys/param.h> + +#include <sys/buf.h> + +#include "dm.h" + +/* + * Zero target init function. This target doesn't need + * target specific config area. + */ +int +dm_target_zero_init(dm_dev_t *dmv, void **target_config, char *argv) +{ + + printf("Zero target init function called!!\n"); + + dmv->dev_type = DM_ZERO_DEV; + + *target_config = NULL; + + return 0; +} + +/* Status routine called to get params string. */ +char * +dm_target_zero_status(void *target_config) +{ + return NULL; +} + + +/* + * This routine does IO operations. + */ +int +dm_target_zero_strategy(dm_table_entry_t *table_en, struct buf *bp) +{ + + /* printf("Zero target read function called %d!!\n", bp->b_bcount); */ + + memset(bp->b_data, 0, bp->b_bcount); + bp->b_resid = 0; /* nestiobuf_done wants b_resid = 0 to be sure + that there is no other io to done */ + + biodone(bp); + + return 0; +} + +/* Doesn't not need to do anything here. */ +int +dm_target_zero_destroy(dm_table_entry_t *table_en) +{ + table_en->target_config = NULL; + + return 0; +} + +/* Doesn't not need to do anything here. */ +int +dm_target_zero_deps(dm_table_entry_t *table_en, prop_array_t prop_array) +{ + return 0; +} + +/* Unsuported for this target. */ +int +dm_target_zero_upcall(dm_table_entry_t *table_en, struct buf *bp) +{ + return 0; +} + diff --git a/sys/dev/dm/doc/design.txt b/sys/dev/dm/doc/design.txt new file mode 100644 index 00000000000..7922be5ec72 --- /dev/null +++ b/sys/dev/dm/doc/design.txt @@ -0,0 +1,475 @@ + Device-mapper to libdevmapper protocol + + + + 1) Device mapper device in a POV of LVM it is an Logical Volume. + Logical Volume is virtual block device is made from logical blocks. + These blocks are mapped to real device blocks with algorithm called + target. + + Functions available to dm device: + create, remove, list, status of device. + + 2) device mapper target is function which defines how are Logical blocks + mapped to physical. There are many targets linear, stripe, mirror etc. + + Functions available to dm device: + list available targets. They can be added with module in linux. + + 3) dm table. + Every device-mapper device consits from one or more tables. Table specify + Start, length of logical blocks and target which is used to map them to + physical blocks. + + {start} {length} {target} | {device} {target parameters} + + after | are target specific parameters listed. + + Functions available to dm device: + load, unload, table_status. + + + List of available ioct calls + + DM_VERSION + DM_REMOVE_ALL + DM_LIST_DEVICES + DM_DEV_CREATE + DM_DEV_REMOVE + DM_DEV_RENAME + DM_DEV_SUSPEND + DM_DEV_STATUS + DM_DEV_WAIT + DM_TABLE_LOAD + DM_TABLE_CLEAR + DM_TABLE_DEPS + DM_TABLE_STATUS + DM_LIST_VERSIONS + DM_TARGET_MSG + DM_DEV_SET_GEOMETRY + + 1) DM_VERSION + + in: struct dm-ioctl + + out: struct dm-ioctl + + Fuction: + sends libdevmapper ioctl protocol version to kernel and ask for kernel version. + If major and minor numbers are good we can continue. + + 2) DM_REMOVE_ALL + + in: none + + out: none + + Function: + This ioctl will remove all DM devices/tables from DM driver. + + 3) DM_LIST_DEVICES + + in: none + + out: List of structures describing all devices created in driver. + + Function: + List all devices created in driver. (linux use struct dm_name_list) + + Implementation: + Kernel driver will place list of struct dm_name_list behind + struct dm_ioctl in userspace. Kernel driver will list through + the all devices and copyout info about them. + + 4) DM_DEV_CREATE + + in: struct dm-ioctl(name/uuid) + + out: none + + Function: + Create device in dm driver, with specified name/uuid(uuid is prefered). + (linux use struct dm_name_list) + + 5) DM_DEV_REMOVE + + in: struct dm-ioctl(name/uuid) + + out: none + + Function: + Remove device from dm driver list, also remove device tables. + + 6) DM_DEV_RENAME + + in: struct dm-ioctl(name/uuid) and string found after dm-ioctl struct in buffer + + out: none + + Function: + Rename device from name to string. + + Implementation: + Kernel driver will find device with name from struct dm_ioctl-name/uuid. + Change name of selected device to string foun behind struc dm_ioctl header + in userspace buffer. + + 7) DM_DEV_SUSPEND + + in: dm-ioctl(name/uuid) + + out: none + + Function: + Suspend all io's on device, after this ioctl. Already started io's will be done. + Newer can't be started. + + 8) DM_DEV_STATUS + + in: dm-ioctl(name/uuid) + + out: dm-ioctl (minor,open_count,target_count) + + Function: + Return status info about selected device + + Implementation: + Kernel driver will find device with name from struct dm_ioctl-name/uuid. + Change values minor,open_count,target_count in dm_ioctl struct for + selected device. + + 9) DM_DEV_WAIT + + in: dm-ioctl(name/uuid) + + out: none + + Function: + Wait for device event to happen. + + 10) DM_TABLE_LOAD + + in: dm-ioctl(name/uuid),table specification + + out: none + + Function: + Load table to selected device. Table is loaded to unused slot and than switched. + (linux use struct dm_target_spec) + + Implementation: + Kernel driver will find device with name from struct dm_ioctl-name/uuid. + Table is added to the inactive slot. Every device can have more than one + table loaded. Tables are stored in SLIST. This ioctl also open physical + device spedcified in table and add it to dm_device specific pdev list. + + 11) DM_TABLE_CLEAR + + in: dm-ioctl(name/uuid) + + out: none + + Function: + Remove table from unused slot. + + 12) DM_TABLE_DEPS + + in: dm-ioctl(name/uuid) + + out: list of dependiences devices + + Function: + Return set of device dependiences e.g. mirror device for mirror target etc.. + + 13) DM_TABLE_STATUS + + in: dm-ioctl(name/uuid) + + out: list of used tables from selected devices (linux use struct dm_target_spec) + + Function: + List all tables in active slot in device with name name/uuid. + + Implementation: + Kernel driver will find device with name from struct dm_ioctl-name/uuid. + DM driver will copyout dm_target_spec structures behidn struct dm_ioctl. + + 14) DM_LIST_VERSIONS + + in: none + + out: list of all targets in device-mapper driver (linux use struct dm_target_versions) + + Function: + List all available targets to libdevmapper. + + Implementation: + Kernel driver will copy out known target versions. + + 15) DM_TARGET_MSG + + in: message to driver (linux use struct dm_target_msg) + + out: none + + Function: + Send message to kernel driver target. + + + 16) DM_DEV_SET_GEOMETRY + + Function: + Set geometry of device-mapper driver. + + + NetBSD device-mapper driver implementation + + device-mapper devices -> devs dm_dev.c + + This entity is created with DM_DEV_CREATE ioctl, and stores info + about every device in device mapper driver. It has two slots for + active and inactive table, list of active physical devices added + to this device and list of upcalled devices (for targets which use + more than one physical device e.g. mirror, snapshot etc..). + + device-mapper physical devices -> pdevs dm_pdev.c + + This structure contains opened device VNODES. Because I physical + device can be found in more than one table loaded to different + dm devices. When device is destroyed I decrement all reference + counters for all added pdevs (I remove pdevs with ref_cnt == 0). + + device-mapper tables -> table dm_table.c, dm_ioctl.c + + Table describe how is dm device made. What blocks are mapped with + what target. In our implementation every table contains pointer to + target specific config data. These config_data are allocated in + DM_TABLE_LOAD function with target_init routine. Every table + contains pointer to used target. + + device-mapper targets -> target dm_target.c + + Target describes mapping of logical blocks to physical. It has + function pointers to function which does init, strategy, destroy, + upcall functions. + + P.S I want to thank reinod@ for great help and guidance :). + + + + Desing of new device-mapper ioctl interface + + Basic architecture of device-mapper -> libdevmapper ioctl interface is this. + Libdevmapper allocate buffer with size of data_size. At the start of this buffer + dm-ioctl structure is placed. any aditional information from/to kernel are placed + behind end (start of data part is pointed with data_start var.) of dm-ioctl struct. + + Kernel driver then after ioctl call have to copyin data from userspace to kernel. + When kernel driver want to send data back to user space library it must copyout + data from kernel. + +1) In Linux device-mapper ioctl interface implementation there are these ioctls. + + DM_VERSION * + DM_REMOVE_ALL + DM_LIST_DEVICES * + DM_DEV_CREATE * + DM_DEV_REMOVE * + DM_DEV_RENAME * + DM_DEV_SUSPEND + DM_DEV_STATUS * + DM_DEV_WAIT + DM_TABLE_LOAD * + DM_TABLE_CLEAR * + DM_TABLE_DEPS + DM_TABLE_STATUS * + DM_LIST_VERSIONS * + DM_TARGET_MSG + DM_DEV_SET_GEOMETRY + +* means implemented in current version of NetBSD device-mapper. + + 1a) struct dm_ioctl based ioctl calls + These ioctl calls communicate only with basic dm_ioctl structure. + + DM_VERSION + DM_DEV_STATUS + DM_DEV_CREATE + + Protocol structure: + + struct dm_ioctl { + uint32_t version[3]; /* device-mapper kernel/userspace version */ + uint32_t data_size; /* total size of data passed in + * including this struct */ + + uint32_t data_start; /* offset to start of data + * relative to start of this struct */ + + uint32_t target_count; /* in/out */ /* This should be set when DM_TABLE_STATUS is called */ + int32_t open_count; /* device open count */ + uint32_t flags; /* information flags */ + uint32_t event_nr; /* event counters not implemented */ + uint32_t padding; + + uint64_t dev; /* dev_t */ + + char name[DM_NAME_LEN]; /* device name */ + char uuid[DM_UUID_LEN]; /* unique identifier for + * the block device */ + + void *user_space_addr; /*this is needed for netbsd + because they differently + implement ioctl syscall*/ + }; + + As SOC task I want to replace this structure with proplib dict. Proplib dict + basic structure should be: + + Note: I don't need data_star, data_size and use_space_addr. They are needed + for current implementation. + + <dict> + <key>version</key> + <string>...</string> + + <key>target_count</key> + <integer></integer> + + <key>open_count</key> + <integer></integer> + + <key>flags</key> + <integer></integer> + + <key>event_nr</key> + <integer></integer> + + <key>dev</key> + <integer></integer> + + <key>name</key> + <string>...</string> + + <key>uuid</key> + <string>...</string> + + + <dict> + <!-- ioctl specific data --> + </dict> + </dict> + + 1b) DM_LIST_VERSIONS ioctl + + This ioctl is used to get list of supported targets from kernel. Target + define mapping of Logical blocks to physical blocks on real device. + There are linear, zero, error, mirror, snapshot, multipath etc... targets. + + For every target kernel driver should copyout this structure to userspace. + + Protocol structure: + + struct dm_target_versions { + uint32_t next; + uint32_t version[3]; + + char name[0]; + }; + + Because I need more then on dm_target_version I will need one major proplib + dictionary to store children dictionaries with target data. + + <dict> + <dict ID="id"> + <key>version</key> + <string>...</string> + + <key>name</key> + <string>...</string> + </dict> + </dict> + + 2a) DM_LIST_DEVICES + + This ioctl is used to list all devices defined in kernel driver. + + Protocol structure: + + struct dm_name_list { + uint64_t dev; + uint32_t next; /* offset to the next record from + the _start_ of this */ + char name[0]; + }; + + Again because I can have more than one device in kernel driver I need one parent + dictionary and more children dictionaries. + + <dict> + <dict ID="id"> + <key>dev</key> + <integer>...</integer> + + <key>name</key> + <string>...</string> + </dict> + </dict> + + 2b) DM_DEV_RENAME + This ioctl is called when libdevmapper want to rename device-mapper device. + Libdevmapper library appends null terminated string to dm_ioctl struct in + userspace.. + + <dict> + <key>name</key> + <string>...</string> + </dict> + + 2c) DM_DEV_CREATE, DM_DEV_REMOVE, DM_DEV_STATUS + Modify only dm_ioctl structure so I don't need to specify new structures. + + + 3a) DM_TABLE_LOAD, DM_TABLE_STATUS + DM_TABLE_LOAD ioctl loads table to device. DM_TABLE_STATUS send info about + every table for selected device to userspace. Table is different for every + target basic table structure is this + + {start} {length} {target} {additional information} + + e.g. + 0 100 zero + + 0 100 linear /dev/wdba 384 + + Protocol structure: + + struct dm_target_spec { + uint64_t sector_start; + uint64_t length; + int32_t status; /* used when reading from kernel only */ + + uint32_t next; + + char target_type[DM_MAX_TYPE_NAME]; + + /* + * Parameter string starts immediately after this object. + * Be careful to add padding after string to ensure correct + * alignment of subsequent dm_target_spec. + */ + }; + + <dict> + <key>sector_start</key> + <integer>...</integer> + + <key>length</key> + <integer>...</integer> + + <key>target_type</key> + <string>...</string> + + <key>aditional info</key> + <string>...</string> + </dict> diff --git a/sys/dev/dm/doc/proposal-dm.txt b/sys/dev/dm/doc/proposal-dm.txt new file mode 100644 index 00000000000..2caa8013991 --- /dev/null +++ b/sys/dev/dm/doc/proposal-dm.txt @@ -0,0 +1,90 @@ + + +/* constant dm_target structures for error, zero, linear, stripes etc. */ +struct dm_target { + int (*init)(struct dm_table_entry *, int argc, char **argv); + int (*destroy)(struct dm_table_entry *); + int (*strategy)(struct dm_table_entry *, struct buf *); + int (*upcall)(struct dm_table_entry *, struct buf *); + + SLIST_ENTRY(dm_target) next; +}; + + +struct dm_table_entry { + struct dm_dev *dm_dev; /* backlink */ + uint64_t start; + uint64_t length; + + struct dm_target *target; + void *target_config; + SLIST_ENTRY(dm_table_entry) next; +}; +SLIST(dm_table, dm_table_entry); + + +struct dm_pdev { + struct vnode *pdev_vnode; + int refcnt; + SLIST_ENTRY(dm_pdev) next_pdev; +}; +SLIST(dm_pdevs, pm_pdev); + + +struct dm_dev { + char name[DM_NAME_LEN]; + char uuid[DM_UUID_LEN]; + + int minor; + uint32_t flags; + + kmutex_t dev_mtx; + uint32_t event_nr; + uint32_t ref_cnt; + + struct dm_pdev pdevs; + + int cur_active_table; + struct dm_table tables[2]; + + struct dm_dev_list upcalls; + SLIST_NEXT(dm_dev) next_upcall; + + SLIST_NEXT(dm_dev) next_devlist; +}; +SLIST(dm_dev_list, dm_dev) dm_devs; + + +/* for zero,error : dm_target->target_config == NULL */ +/* for linear : */ +struct target_linear_config { + struct dm_pdev *pdev; + uint64_t offset; +}; + + +/* for mirror : */ +struct target_mirror_config { + struct dm_pdev *orig; + struct dm_pdev *copies[MAX_MIRROR_COPIES]; + + /* copied blocks bitmaps administration etc*/ + struct dm_pdev *log_pdev; /* for administration */ + uint64_t log_regionsize; /* blocksize of mirror */ + + /* list of parts that still need copied etc.; run length encoded? */ + .... +}; + + +/* for snapshot : */ +struct target_snapshot_config { + struct dm_dev *orig; + + /* modified blocks bitmaps administration etc*/ + struct dm_pdev *log_pdev; + uint64_t log_regionsize; + /* list of sector renames to the log device */ + ... +}; + diff --git a/sys/dev/dm/files.dm b/sys/dev/dm/files.dm new file mode 100644 index 00000000000..039725b5495 --- /dev/null +++ b/sys/dev/dm/files.dm @@ -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 diff --git a/sys/dev/dm/netbsd-dm.h b/sys/dev/dm/netbsd-dm.h new file mode 100644 index 00000000000..df6da9f7a9c --- /dev/null +++ b/sys/dev/dm/netbsd-dm.h @@ -0,0 +1,283 @@ +/* $NetBSD: netbsd-dm.h,v 1.2 2008/12/19 15:24:03 haad Exp $ */ + +/* + * Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Hamsik. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __NETBSD_DM_H__ +#define __NETBSD_DM_H__ + +#include <prop/proplib.h> + +#define DM_CMD_LEN 16 + +#define DM_IOCTL 0xfd + +#define DM_IOCTL_CMD 0 + +#define NETBSD_DM_IOCTL _IOWR(DM_IOCTL, DM_IOCTL_CMD, struct plistref) + + +/* + * DM-ioctl dictionary. + * + * This contains general information about dm device. + * + * <dict> + * <key>command</key> + * <string>...</string> + * + * <key>event_nr</key> + * <integer>...</integer> + * + * <key>name</key> + * <string>...</string> + * + * <key>uuid</key> + * <string>...</string> + * + * <key>dev</key> + * <integer></integer> + * + * <key>flags</key> + * <integer></integer> + * + * <key>version</key> + * <array> + * <integer>...</integer> + * <integer>...</integer> + * <integer>...</integer> + * </array> + * + * <key>cmd_data</key> + * <array> + * <!-- See below for command + * specific dictionaries --> + * </array> + * </dict> + * + * Available commands from _cmd_data_v4. + * + * create, reload, remove, remove_all, suspend, + * resume, info, deps, rename, version, status, + * table, waitevent, names, clear, mknodes, + * targets, message, setgeometry + * + */ + +/* + * DM_LIST_VERSIONS == "targets" command dictionary entry. + * Lists all available targets with their version. + * + * <array> + * <dict> + * <key>name<key> + * <string>...</string> + * + * <key>version</key> + * <array> + * <integer>...</integer> + * <integer>...</integer> + * <integer>...</integer> + * </array> + * </dict> + * </array> + * + */ + +/* + * DM_DEV_LIST == "names" + * Request list of device-mapper created devices from kernel. + * + * <array> + * <dict> + * <key>name<key> + * <string>...</string> + * + * <key>dev</key> + * <integer>...</integer> + * </dict> + * </array> + * + * dev is uint64_t + * + */ + + /* + * DM_DEV_RENAME == "rename" + * Rename device to string. + * + * <array> + * <string>...</string> + * </array> + * + */ + + /* + * DM_DEV_STATUS == "info, mknodes" + * Will change fields DM_IOCTL_OPEN, DM_IOCTL_DEV in received dictionary, + * with dm device values with name or uuid from list. + * + */ + + /* + * DM_TABLE_STATUS == "status,table" + * Request list of device-mapper created devices from kernel. + * + * <array> + * <dict> + * <key>type<key> + * <string>...</string> + * + * <key>start</key> + * <integer>...</integer> + * + * <key>length</key> + * <integer>...</integer> + * + * <key>params</key> + * <string>...</string> + * </dict> + * </array> + * + * params is string which contains {device} {parameters} + * + */ + + /* + * DM_TABLE_DEPS == "deps" + * Request list active table device dependiences. + * + * This command is also run to get dm-device + * dependiences for existing real block device. + * + * eg. vgcreate calls DM_TABLE_DEPS + * + * <array> + * <integer>...</integer> + * </array> + * + */ + +#define DM_IOCTL_COMMAND "command" +#define DM_IOCTL_VERSION "version" +#define DM_IOCTL_OPEN "open_count" +#define DM_IOCTL_MINOR "minor" +#define DM_IOCTL_NAME "name" +#define DM_IOCTL_UUID "uuid" +#define DM_IOCTL_TARGET_COUNT "target_count" +#define DM_IOCTL_EVENT "event_nr" +#define DM_IOCTL_FLAGS "flags" +#define DM_IOCTL_CMD_DATA "cmd_data" + +#define DM_TARGETS_NAME "name" +#define DM_TARGETS_VERSION "ver" + +#define DM_DEV_NEWNAME "newname" +#define DM_DEV_NAME "name" +#define DM_DEV_DEV "dev" + +#define DM_TABLE_TYPE "type" +#define DM_TABLE_START "start" +#define DM_TABLE_STAT "status" +#define DM_TABLE_LENGTH "length" +#define DM_TABLE_PARAMS "params" +#define DM_TABLE_DEPS "deps" + + +/* Status bits */ +/* IO mode of device */ +#define DM_READONLY_FLAG (1 << 0) /* In/Out *//* to kernel/from kernel */ +#define DM_SUSPEND_FLAG (1 << 1) /* In/Out */ +/* XXX. This flag is undocumented. */ +#define DM_EXISTS_FLAG (1 << 2) /* In/Out */ +/* Minor number is persistent */ +#define DM_PERSISTENT_DEV_FLAG (1 << 3) /* In */ + +/* + * Flag passed into ioctl STATUS command to get table information + * rather than current status. + */ +#define DM_STATUS_TABLE_FLAG (1 << 4) /* In */ + +/* + * Flags that indicate whether a table is present in either of + * the two table slots that a device has. + */ +#define DM_ACTIVE_PRESENT_FLAG (1 << 5) /* Out */ +#define DM_INACTIVE_PRESENT_FLAG (1 << 6) /* Out */ + +/* + * Indicates that the buffer passed in wasn't big enough for the + * results. + */ +#define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */ + +/* + * This flag is now ignored. + */ +#define DM_SKIP_BDGET_FLAG (1 << 9) /* In */ + +/* + * Set this to avoid attempting to freeze any filesystem when suspending. + */ +#define DM_SKIP_LOCKFS_FLAG (1 << 10) /* In */ + +/* + * Set this to suspend without flushing queued ios. + */ +#define DM_NOFLUSH_FLAG (1 << 11) /* In */ + + +#ifdef __LIB_DEVMAPPER__ + +# define MAJOR(x) major((x)) +# define MINOR(x) minor((x)) +# define MKDEV(x,y) makedev((x),(y)) + +/* Name of device-mapper driver in kernel */ +#define DM_NAME "dm" + +/* Types for nbsd_get_dm_major */ +#define DM_CHAR_MAJOR 1 +#define DM_BLOCK_MAJOR 2 + +/* libdm_netbsd.c */ +/* Get dm device major/minor numbers */ +int nbsd_get_dm_major(uint32_t *, uint32_t *, int); + +int nbsd_dmi_add_cmd(const char *, prop_dictionary_t); +int nbsd_dmi_add_version(const int [3], prop_dictionary_t); +int nbsd_dm_add_uint(const char *, uint64_t, prop_dictionary_t); +int nbsd_dm_add_str(const char *, char *, prop_dictionary_t ); + +struct dm_ioctl* nbsd_dm_dict_to_dmi(prop_dictionary_t, const int); + +#endif /* __LIB_DEVMAPPER__ */ + +#endif /* __NETBSD_DM_H__ */ |
