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