Home | History | Annotate | Line # | Download | only in dm
      1 /*        $NetBSD: device-mapper.c,v 1.64 2022/03/31 19:30:15 pgoyette Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2010 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Adam Hamsik.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * I want to say thank you to all people who helped me with this project.
     34  */
     35 
     36 #include <sys/types.h>
     37 #include <sys/param.h>
     38 #include <sys/buf.h>
     39 #include <sys/conf.h>
     40 #include <sys/device.h>
     41 #include <sys/dkio.h>
     42 #include <sys/disk.h>
     43 #include <sys/disklabel.h>
     44 #include <sys/ioctl.h>
     45 #include <sys/ioccom.h>
     46 #include <sys/kmem.h>
     47 #include <sys/kauth.h>
     48 
     49 #include "netbsd-dm.h"
     50 #include "dm.h"
     51 #include "ioconf.h"
     52 
     53 static dev_type_open(dmopen);
     54 static dev_type_close(dmclose);
     55 static dev_type_read(dmread);
     56 static dev_type_write(dmwrite);
     57 static dev_type_ioctl(dmioctl);
     58 static dev_type_strategy(dmstrategy);
     59 static dev_type_size(dmsize);
     60 
     61 /* attach and detach routines */
     62 #ifdef _MODULE
     63 static int dmdestroy(void);
     64 #endif
     65 
     66 static void dm_doinit(void);
     67 
     68 static int dm_cmd_to_fun(prop_dictionary_t);
     69 static int disk_ioctl_switch(dev_t, unsigned long, void *);
     70 static void dmminphys(struct buf *);
     71 
     72 /* CF attach/detach functions used for power management */
     73 static int dm_detach(device_t, int);
     74 static void dm_attach(device_t, device_t, void *);
     75 static int dm_match(device_t, cfdata_t, void *);
     76 
     77 /* ***Variable-definitions*** */
     78 const struct bdevsw dm_bdevsw = {
     79 	.d_open = dmopen,
     80 	.d_close = dmclose,
     81 	.d_strategy = dmstrategy,
     82 	.d_ioctl = dmioctl,
     83 	.d_dump = nodump,
     84 	.d_psize = dmsize,
     85 	.d_discard = nodiscard,
     86 	.d_flag = D_DISK | D_MPSAFE
     87 };
     88 
     89 const struct cdevsw dm_cdevsw = {
     90 	.d_open = dmopen,
     91 	.d_close = dmclose,
     92 	.d_read = dmread,
     93 	.d_write = dmwrite,
     94 	.d_ioctl = dmioctl,
     95 	.d_stop = nostop,
     96 	.d_tty = notty,
     97 	.d_poll = nopoll,
     98 	.d_mmap = nommap,
     99 	.d_kqfilter = nokqfilter,
    100 	.d_discard = nodiscard,
    101 	.d_flag = D_DISK | D_MPSAFE
    102 };
    103 
    104 const struct dkdriver dmdkdriver = {
    105 	.d_strategy = dmstrategy
    106 };
    107 
    108 CFATTACH_DECL3_NEW(dm, 0,
    109      dm_match, dm_attach, dm_detach, NULL, NULL, NULL,
    110      DVF_DETACH_SHUTDOWN);
    111 
    112 /*
    113  * This structure is used to translate command sent to kernel driver in
    114  * <key>command</key>
    115  * <value></value>
    116  * to function which I can call, and if the command is allowed for
    117  * non-superusers.
    118  */
    119 /*
    120  * This array is used to translate cmd to function pointer.
    121  *
    122  * Interface between libdevmapper and lvm2tools uses different
    123  * names for one IOCTL call because libdevmapper do another thing
    124  * then. When I run "info" or "mknodes" libdevmapper will send same
    125  * ioctl to kernel but will do another things in userspace.
    126  *
    127  */
    128 static const struct cmd_function {
    129 	const char *cmd;
    130 	int  (*fn)(prop_dictionary_t);
    131 	int  allowed;
    132 } cmd_fn[] = {
    133 	{ .cmd = "version", .fn = NULL,                   .allowed = 1 },
    134 	{ .cmd = "targets", .fn = dm_list_versions_ioctl, .allowed = 1 },
    135 	{ .cmd = "create",  .fn = dm_dev_create_ioctl,    .allowed = 0 },
    136 	{ .cmd = "info",    .fn = dm_dev_status_ioctl,    .allowed = 1 },
    137 	{ .cmd = "mknodes", .fn = dm_dev_status_ioctl,    .allowed = 1 },
    138 	{ .cmd = "names",   .fn = dm_dev_list_ioctl,      .allowed = 1 },
    139 	{ .cmd = "suspend", .fn = dm_dev_suspend_ioctl,   .allowed = 0 },
    140 	{ .cmd = "remove",  .fn = dm_dev_remove_ioctl,    .allowed = 0 },
    141 	{ .cmd = "rename",  .fn = dm_dev_rename_ioctl,    .allowed = 0 },
    142 	{ .cmd = "resume",  .fn = dm_dev_resume_ioctl,    .allowed = 0 },
    143 	{ .cmd = "clear",   .fn = dm_table_clear_ioctl,   .allowed = 0 },
    144 	{ .cmd = "deps",    .fn = dm_table_deps_ioctl,    .allowed = 1 },
    145 	{ .cmd = "reload",  .fn = dm_table_load_ioctl,    .allowed = 0 },
    146 	{ .cmd = "status",  .fn = dm_table_status_ioctl,  .allowed = 1 },
    147 	{ .cmd = "table",   .fn = dm_table_status_ioctl,  .allowed = 1 },
    148 	{ .cmd = NULL,      .fn = NULL,                   .allowed = 0 },
    149 };
    150 
    151 #ifdef _MODULE
    152 #include <sys/module.h>
    153 
    154 /* Autoconf defines */
    155 CFDRIVER_DECL(dm, DV_DISK, NULL);
    156 
    157 MODULE(MODULE_CLASS_DRIVER, dm, "dk_subr");
    158 
    159 /* New module handle routine */
    160 static int
    161 dm_modcmd(modcmd_t cmd, void *arg)
    162 {
    163 #ifdef _MODULE
    164 	int error;
    165 	devmajor_t bmajor, cmajor;
    166 
    167 	error = 0;
    168 	bmajor = -1;
    169 	cmajor = -1;
    170 
    171 	switch (cmd) {
    172 	case MODULE_CMD_INIT:
    173 		error = devsw_attach(dm_cd.cd_name, &dm_bdevsw, &bmajor,
    174 		    &dm_cdevsw, &cmajor);
    175 		if (error == EEXIST)
    176 			error = 0;
    177 		if (error)
    178 			break;
    179 
    180 		error = config_cfdriver_attach(&dm_cd);
    181 		if (error) {
    182 			devsw_detach(&dm_bdevsw, &dm_cdevsw);
    183 			return error;
    184 		}
    185 
    186 		error = config_cfattach_attach(dm_cd.cd_name, &dm_ca);
    187 		if (error) {
    188 			config_cfdriver_detach(&dm_cd);
    189 			devsw_detach(&dm_bdevsw, &dm_cdevsw);
    190 			aprint_error("%s: unable to register cfattach\n",
    191 			    dm_cd.cd_name);
    192 			return error;
    193 		}
    194 
    195 		dm_doinit();
    196 		break;
    197 	case MODULE_CMD_FINI:
    198 		/*
    199 		 * Disable unloading of dm module if there are any devices
    200 		 * defined in driver. This is probably too strong we need
    201 		 * to disable auto-unload only if there is mounted dm device
    202 		 * present.
    203 		 */
    204 		if (dm_dev_counter > 0)
    205 			return EBUSY;
    206 		/* race window here */
    207 
    208 		error = dmdestroy();
    209 		if (error)
    210 			break;
    211 
    212 		config_cfdriver_detach(&dm_cd);
    213 		config_cfattach_detach(dm_cd.cd_name, &dm_ca);
    214 
    215 		devsw_detach(&dm_bdevsw, &dm_cdevsw);
    216 		break;
    217 	case MODULE_CMD_STAT:
    218 		return ENOTTY;
    219 	default:
    220 		return ENOTTY;
    221 	}
    222 
    223 	return error;
    224 #else
    225 	return ENOTTY;
    226 #endif
    227 }
    228 #endif /* _MODULE */
    229 
    230 /*
    231  * dm_match:
    232  *
    233  *	Autoconfiguration match function for pseudo-device glue.
    234  */
    235 static int
    236 dm_match(device_t parent, cfdata_t match, void *aux)
    237 {
    238 
    239 	/* Pseudo-device; always present. */
    240 	return 1;
    241 }
    242 
    243 /*
    244  * dm_attach:
    245  *
    246  *	Autoconfiguration attach function for pseudo-device glue.
    247  */
    248 static void
    249 dm_attach(device_t parent, device_t self, void *aux)
    250 {
    251 
    252 	if (!pmf_device_register(self, NULL, NULL))
    253 		aprint_error_dev(self, "couldn't establish power handler\n");
    254 }
    255 
    256 /*
    257  * dm_detach:
    258  *
    259  *	Autoconfiguration detach function for pseudo-device glue.
    260  * This routine is called by dm_ioctl::dm_dev_remove_ioctl and by autoconf to
    261  * remove devices created in device-mapper.
    262  */
    263 static int
    264 dm_detach(device_t self, int flags)
    265 {
    266 	bool busy;
    267 	dm_dev_t *dmv;
    268 
    269 	dmv = dm_dev_lookup(NULL, NULL, device_unit(self));
    270 	mutex_enter(&dmv->diskp->dk_openlock);
    271 	busy = (dmv->diskp->dk_openmask != 0 && (flags & DETACH_FORCE) == 0);
    272 	mutex_exit(&dmv->diskp->dk_openlock);
    273 	dm_dev_unbusy(dmv);
    274 	if (busy)
    275 		return EBUSY;
    276 
    277 	pmf_device_deregister(self);
    278 
    279 	/* Detach device from global device list */
    280 	if ((dmv = dm_dev_detach(self)) == NULL)
    281 		return ENOENT;
    282 
    283 	/* Destroy active table first.  */
    284 	dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE);
    285 
    286 	/* Destroy inactive table if exits, too. */
    287 	dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE);
    288 
    289 	dm_table_head_destroy(&dmv->table_head);
    290 
    291 	/* Destroy disk device structure */
    292 	disk_detach(dmv->diskp);
    293 	disk_destroy(dmv->diskp);
    294 
    295 	/* Destroy device */
    296 	dm_dev_free(dmv);
    297 
    298 	/* Decrement device counter After removing device */
    299 	atomic_dec_32(&dm_dev_counter);
    300 
    301 	return 0;
    302 }
    303 
    304 static void
    305 dm_doinit(void)
    306 {
    307 
    308 	dm_target_init();
    309 	dm_dev_init();
    310 	dm_pdev_init();
    311 }
    312 
    313 /* attach routine */
    314 void
    315 dmattach(int n)
    316 {
    317 	int error;
    318 
    319 	error = config_cfattach_attach(dm_cd.cd_name, &dm_ca);
    320 	if (error)
    321 		aprint_error("%s: unable to register cfattach\n",
    322 		    dm_cd.cd_name);
    323 	else
    324 		dm_doinit();
    325 }
    326 
    327 #ifdef _MODULE
    328 /* Destroy routine */
    329 static int
    330 dmdestroy(void)
    331 {
    332 	int error;
    333 
    334 	error = config_cfattach_detach(dm_cd.cd_name, &dm_ca);
    335 	if (error)
    336 		return error;
    337 
    338 	dm_dev_destroy();
    339 	dm_pdev_destroy();
    340 	dm_target_destroy();
    341 
    342 	return 0;
    343 }
    344 #endif /* _MODULE */
    345 
    346 static int
    347 dmopen(dev_t dev, int flags, int mode, struct lwp *l)
    348 {
    349 	dm_dev_t *dmv;
    350 	struct disk *dk;
    351 
    352 	dmv = dm_dev_lookup(NULL, NULL, minor(dev));
    353 	if (dmv) {
    354 		dk = dmv->diskp;
    355 		mutex_enter(&dk->dk_openlock);
    356 		switch (mode) {
    357 		case S_IFCHR:
    358 			dk->dk_copenmask |= 1;
    359 			break;
    360 		case S_IFBLK:
    361 			dk->dk_bopenmask |= 1;
    362 			break;
    363 		}
    364 		dk->dk_openmask = dk->dk_copenmask | dk->dk_bopenmask;
    365 		mutex_exit(&dk->dk_openlock);
    366 		dm_dev_unbusy(dmv);
    367 	}
    368 
    369 	aprint_debug("dm open routine called %" PRIu32 "\n", minor(dev));
    370 	return 0;
    371 }
    372 
    373 static int
    374 dmclose(dev_t dev, int flags, int mode, struct lwp *l)
    375 {
    376 	dm_dev_t *dmv;
    377 	struct disk *dk;
    378 
    379 	aprint_debug("dm close routine called %" PRIu32 "\n", minor(dev));
    380 
    381 	dmv = dm_dev_lookup(NULL, NULL, minor(dev));
    382 	if (dmv) {
    383 		dk = dmv->diskp;
    384 		mutex_enter(&dk->dk_openlock);
    385 		switch (mode) {
    386 		case S_IFCHR:
    387 			dk->dk_copenmask &= ~1;
    388 			break;
    389 		case S_IFBLK:
    390 			dk->dk_bopenmask &= ~1;
    391 			break;
    392 		}
    393 		dk->dk_openmask = dk->dk_copenmask | dk->dk_bopenmask;
    394 		mutex_exit(&dk->dk_openlock);
    395 		dm_dev_unbusy(dmv);
    396 	}
    397 	return 0;
    398 }
    399 
    400 
    401 static int
    402 dmioctl(dev_t dev, const u_long cmd, void *data, int flag, struct lwp *l)
    403 {
    404 	int r;
    405 	prop_dictionary_t dm_dict_in;
    406 
    407 	aprint_debug("dmioctl called\n");
    408 	KASSERT(data != NULL);
    409 
    410 	if ((r = disk_ioctl_switch(dev, cmd, data)) == ENOTTY) {
    411 		struct plistref *pref = (struct plistref *)data;
    412 
    413 		switch(cmd) {
    414 		case NETBSD_DM_IOCTL:
    415 			aprint_debug("dm NETBSD_DM_IOCTL called\n");
    416 			break;
    417 		default:
    418 			aprint_debug("dm unknown ioctl called\n");
    419 			return ENOTTY;
    420 			break; /* NOT REACHED */
    421 		}
    422 
    423 		if ((r = prop_dictionary_copyin_ioctl(pref, cmd, &dm_dict_in))
    424 		    != 0)
    425 			return r;
    426 
    427 		if ((r = dm_check_version(dm_dict_in)) != 0)
    428 			goto cleanup_exit;
    429 
    430 		/* run ioctl routine */
    431 		if ((r = dm_cmd_to_fun(dm_dict_in)) != 0)
    432 			goto cleanup_exit;
    433 
    434 cleanup_exit:
    435 		r = prop_dictionary_copyout_ioctl(pref, cmd, dm_dict_in);
    436 		prop_object_release(dm_dict_in);
    437 	}
    438 
    439 	return r;
    440 }
    441 
    442 /*
    443  * Translate command sent from libdevmapper to func.
    444  */
    445 static int
    446 dm_cmd_to_fun(prop_dictionary_t dm_dict)
    447 {
    448 	int i, r;
    449 	prop_string_t command;
    450 
    451 	if ((command = prop_dictionary_get(dm_dict, DM_IOCTL_COMMAND)) == NULL)
    452 		return EINVAL;
    453 
    454 	for (i = 0; cmd_fn[i].cmd != NULL; i++)
    455 		if (prop_string_equals_string(command, cmd_fn[i].cmd))
    456 			break;
    457 
    458 	if (!cmd_fn[i].allowed &&
    459 	    (r = kauth_authorize_system(kauth_cred_get(),
    460 	    KAUTH_SYSTEM_DEVMAPPER, 0, NULL, NULL, NULL)) != 0)
    461 		return r;
    462 
    463 	if (cmd_fn[i].cmd == NULL)
    464 		return EINVAL;
    465 
    466 	aprint_debug("ioctl %s called %p\n", cmd_fn[i].cmd, cmd_fn[i].fn);
    467 	if (cmd_fn[i].fn == NULL)
    468 		return 0;
    469 
    470 	return cmd_fn[i].fn(dm_dict);
    471 }
    472 
    473 /*
    474  * Check for disk specific ioctls.
    475  */
    476 static int
    477 disk_ioctl_switch(dev_t dev, unsigned long cmd, void *data)
    478 {
    479 	dm_dev_t *dmv;
    480 
    481 	/* disk ioctls make sense only on block devices */
    482 	if (minor(dev) == 0)
    483 		return ENOTTY;
    484 
    485 	switch(cmd) {
    486 	case DIOCGWEDGEINFO:
    487 	{
    488 		struct dkwedge_info *dkw = (void *) data;
    489 
    490 		if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL)
    491 			return ENODEV;
    492 
    493 		aprint_debug("DIOCGWEDGEINFO ioctl called\n");
    494 
    495 		strlcpy(dkw->dkw_devname, dmv->name, 16);
    496 		strlcpy(dkw->dkw_wname, dmv->name, DM_NAME_LEN);
    497 		strlcpy(dkw->dkw_parent, dmv->name, 16);
    498 
    499 		dkw->dkw_offset = 0;
    500 		dm_table_disksize(&dmv->table_head, &dkw->dkw_size, NULL);
    501 		strcpy(dkw->dkw_ptype, DKW_PTYPE_FFS);
    502 
    503 		dm_dev_unbusy(dmv);
    504 		break;
    505 	}
    506 	case DIOCGDISKINFO:
    507 	{
    508 		struct plistref *pref = (struct plistref *) data;
    509 
    510 		if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL)
    511 			return ENODEV;
    512 
    513 		aprint_debug("DIOCGDISKINFO ioctl called\n");
    514 
    515 		if (dmv->diskp->dk_info == NULL) {
    516 			dm_dev_unbusy(dmv);
    517 			return ENOTSUP;
    518 		} else
    519 			prop_dictionary_copyout_ioctl(pref, cmd,
    520 			    dmv->diskp->dk_info);
    521 
    522 		dm_dev_unbusy(dmv);
    523 		break;
    524 	}
    525 	case DIOCCACHESYNC:
    526 	{
    527 		dm_table_entry_t *table_en;
    528 		dm_table_t *tbl;
    529 
    530 		if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL)
    531 			return ENODEV;
    532 
    533 		aprint_debug("DIOCCACHESYNC ioctl called\n");
    534 
    535 		/* Select active table */
    536 		tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE);
    537 
    538 		/*
    539 		 * Call sync target routine for all table entries. Target sync
    540 		 * routine basically call DIOCCACHESYNC on underlying devices.
    541 		 */
    542 		SLIST_FOREACH(table_en, tbl, next)
    543 			if (table_en->target->sync)
    544 				table_en->target->sync(table_en);
    545 		dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE);
    546 		dm_dev_unbusy(dmv);
    547 		break;
    548 	}
    549 	case DIOCGSECTORSIZE:
    550 	{
    551 		unsigned int secsize, *valp = data;
    552 
    553 		if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL)
    554 			return ENODEV;
    555 
    556 		aprint_debug("DIOCGSECTORSIZE ioctl called\n");
    557 
    558 		dm_table_disksize(&dmv->table_head, NULL, &secsize);
    559 		*valp = secsize;
    560 
    561 		dm_dev_unbusy(dmv);
    562 		break;
    563 	}
    564 	case DIOCGMEDIASIZE:
    565 	{
    566 		off_t *valp = data;
    567 		uint64_t numsec;
    568 		unsigned secsize;
    569 
    570 		if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL)
    571 			return ENODEV;
    572 
    573 		aprint_debug("DIOCGMEDIASIZE ioctl called\n");
    574 
    575 		dm_table_disksize(&dmv->table_head, &numsec, &secsize);
    576 		*valp = (off_t) secsize * numsec;
    577 
    578 		dm_dev_unbusy(dmv);
    579 		break;
    580 	}
    581 	default:
    582 		aprint_debug("unknown disk_ioctl called\n");
    583 		return ENOTTY;
    584 		break; /* NOT REACHED */
    585 	}
    586 
    587 	return 0;
    588 }
    589 
    590 /*
    591  * Do all IO operations on dm logical devices.
    592  */
    593 static void
    594 dmstrategy(struct buf *bp)
    595 {
    596 	dm_dev_t *dmv;
    597 	dm_table_t *tbl;
    598 	dm_table_entry_t *table_en;
    599 	struct buf *nestbuf;
    600 
    601 	uint64_t buf_start, buf_len, issued_len;
    602 	uint64_t table_start, table_end;
    603 	uint64_t start, end;
    604 
    605 	buf_start = bp->b_blkno * DEV_BSIZE;
    606 	buf_len = bp->b_bcount;
    607 
    608 	table_end = 0;
    609 	issued_len = 0;
    610 
    611 	if ((dmv = dm_dev_lookup(NULL, NULL, minor(bp->b_dev))) == NULL) {
    612 		bp->b_error = EIO;
    613 		bp->b_resid = bp->b_bcount;
    614 		biodone(bp);
    615 		return;
    616 	}
    617 
    618 	if (bounds_check_with_mediasize(bp, DEV_BSIZE,
    619 	    dm_table_size(&dmv->table_head)) <= 0) {
    620 		dm_dev_unbusy(dmv);
    621 		bp->b_resid = bp->b_bcount;
    622 		biodone(bp);
    623 		return;
    624 	}
    625 
    626 	/*
    627 	 * disk(9) is part of device structure and it can't be used without
    628 	 * mutual exclusion, use diskp_mtx until it will be fixed.
    629 	 */
    630 	mutex_enter(&dmv->diskp_mtx);
    631 	disk_busy(dmv->diskp);
    632 	mutex_exit(&dmv->diskp_mtx);
    633 
    634 	/* Select active table */
    635 	tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE);
    636 
    637 	 /* Nested buffers count down to zero therefore I have
    638 	    to set bp->b_resid to maximal value. */
    639 	bp->b_resid = bp->b_bcount;
    640 
    641 	/*
    642 	 * Find out what tables I want to select.
    643 	 */
    644 	SLIST_FOREACH(table_en, tbl, next) {
    645 		/* I need number of bytes not blocks. */
    646 		table_start = table_en->start * DEV_BSIZE;
    647 		/*
    648 		 * I have to sub 1 from table_en->length to prevent
    649 		 * off by one error
    650 		 */
    651 		table_end = table_start + table_en->length * DEV_BSIZE;
    652 		start = MAX(table_start, buf_start);
    653 		end = MIN(table_end, buf_start + buf_len);
    654 
    655 		aprint_debug("----------------------------------------\n");
    656 		aprint_debug("table_start %010" PRIu64", table_end %010"
    657 		    PRIu64 "\n", table_start, table_end);
    658 		aprint_debug("buf_start %010" PRIu64", buf_len %010"
    659 		    PRIu64"\n", buf_start, buf_len);
    660 		aprint_debug("start-buf_start %010"PRIu64", end %010"
    661 		    PRIu64"\n", start - buf_start, end);
    662 		aprint_debug("start %010" PRIu64", end %010"
    663                     PRIu64"\n", start, end);
    664 		aprint_debug("----------------------------------------\n");
    665 
    666 		if (start < end) {
    667 			/* create nested buffer  */
    668 			nestbuf = getiobuf(NULL, true);
    669 			nestiobuf_setup(bp, nestbuf, start - buf_start,
    670 			    end - start);
    671 			issued_len += end - start;
    672 			/* I need number of blocks. */
    673 			nestbuf->b_blkno = (start - table_start) / DEV_BSIZE;
    674 			table_en->target->strategy(table_en, nestbuf);
    675 		}
    676 	}
    677 
    678 	if (issued_len < buf_len)
    679 		nestiobuf_done(bp, buf_len - issued_len, EINVAL);
    680 
    681 	mutex_enter(&dmv->diskp_mtx);
    682 	disk_unbusy(dmv->diskp, buf_len, bp ? (bp->b_flags & B_READ) : 0);
    683 	mutex_exit(&dmv->diskp_mtx);
    684 
    685 	dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE);
    686 	dm_dev_unbusy(dmv);
    687 }
    688 
    689 
    690 static int
    691 dmread(dev_t dev, struct uio *uio, int flag)
    692 {
    693 
    694 	return physio(dmstrategy, NULL, dev, B_READ, dmminphys, uio);
    695 }
    696 
    697 static int
    698 dmwrite(dev_t dev, struct uio *uio, int flag)
    699 {
    700 
    701 	return physio(dmstrategy, NULL, dev, B_WRITE, dmminphys, uio);
    702 }
    703 
    704 static int
    705 dmsize(dev_t dev)
    706 {
    707 	dm_dev_t *dmv;
    708 	uint64_t size;
    709 
    710 	if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL)
    711 		return -ENOENT;
    712 
    713 	size = dm_table_size(&dmv->table_head);
    714 	dm_dev_unbusy(dmv);
    715 
    716 	return size;
    717 }
    718 
    719 static void
    720 dmminphys(struct buf *bp)
    721 {
    722 
    723 	bp->b_bcount = MIN(bp->b_bcount, MAXPHYS);
    724 }
    725 
    726 void
    727 dmgetproperties(struct disk *disk, dm_table_head_t *head)
    728 {
    729 	uint64_t numsec;
    730 	unsigned int secsize;
    731 	struct disk_geom *dg;
    732 
    733 	dm_table_disksize(head, &numsec, &secsize);
    734 
    735 	dg = &disk->dk_geom;
    736 
    737 	memset(dg, 0, sizeof(*dg));
    738 	dg->dg_secperunit = numsec;
    739 	dg->dg_secsize = secsize;
    740 	dg->dg_nsectors = 32;
    741 	dg->dg_ntracks = 64;
    742 
    743 	disk_set_info(NULL, disk, "ESDI");
    744 }
    745 
    746 /*
    747  * Transform char s to uint64_t offset number.
    748  */
    749 uint64_t
    750 atoi64(const char *s)
    751 {
    752 	uint64_t n;
    753 	n = 0;
    754 
    755 	while (*s != '\0') {
    756 		if (!isdigit(*s))
    757 			break;
    758 
    759 		n = (10 * n) + (*s - '0');
    760 		s++;
    761 	}
    762 
    763 	return n;
    764 }
    765