1 /* $NetBSD: device-mapper.c,v 1.39 2017/10/28 04:53:55 riastradh 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 39 #include <sys/buf.h> 40 #include <sys/conf.h> 41 #include <sys/device.h> 42 #include <sys/dkio.h> 43 #include <sys/disk.h> 44 #include <sys/disklabel.h> 45 #include <sys/ioctl.h> 46 #include <sys/ioccom.h> 47 #include <sys/kmem.h> 48 #include <sys/kauth.h> 49 50 #include "netbsd-dm.h" 51 #include "dm.h" 52 #include "ioconf.h" 53 54 static dev_type_open(dmopen); 55 static dev_type_close(dmclose); 56 static dev_type_read(dmread); 57 static dev_type_write(dmwrite); 58 static dev_type_ioctl(dmioctl); 59 static dev_type_strategy(dmstrategy); 60 static dev_type_size(dmsize); 61 62 /* attach and detach routines */ 63 #ifdef _MODULE 64 static int dmdestroy(void); 65 #endif 66 67 static void dm_doinit(void); 68 69 static int dm_cmd_to_fun(prop_dictionary_t); 70 static int disk_ioctl_switch(dev_t, u_long, void *); 71 static int dm_ioctl_switch(u_long); 72 static void dmminphys(struct buf *); 73 74 /* CF attach/detach functions used for power management */ 75 static int dm_detach(device_t, int); 76 static void dm_attach(device_t, device_t, void *); 77 static int dm_match(device_t, cfdata_t, void *); 78 79 /* ***Variable-definitions*** */ 80 const struct bdevsw dm_bdevsw = { 81 .d_open = dmopen, 82 .d_close = dmclose, 83 .d_strategy = dmstrategy, 84 .d_ioctl = dmioctl, 85 .d_dump = nodump, 86 .d_psize = dmsize, 87 .d_discard = nodiscard, 88 .d_flag = D_DISK | D_MPSAFE 89 }; 90 91 const struct cdevsw dm_cdevsw = { 92 .d_open = dmopen, 93 .d_close = dmclose, 94 .d_read = dmread, 95 .d_write = dmwrite, 96 .d_ioctl = dmioctl, 97 .d_stop = nostop, 98 .d_tty = notty, 99 .d_poll = nopoll, 100 .d_mmap = nommap, 101 .d_kqfilter = nokqfilter, 102 .d_discard = nodiscard, 103 .d_flag = D_DISK | D_MPSAFE 104 }; 105 106 const struct dkdriver dmdkdriver = { 107 .d_strategy = dmstrategy 108 }; 109 110 CFATTACH_DECL3_NEW(dm, 0, 111 dm_match, dm_attach, dm_detach, NULL, NULL, NULL, 112 DVF_DETACH_SHUTDOWN); 113 114 extern uint32_t dm_dev_counter; 115 116 /* 117 * This array is used to translate cmd to function pointer. 118 * 119 * Interface between libdevmapper and lvm2tools uses different 120 * names for one IOCTL call because libdevmapper do another thing 121 * then. When I run "info" or "mknodes" libdevmapper will send same 122 * ioctl to kernel but will do another things in userspace. 123 * 124 */ 125 static const struct cmd_function cmd_fn[] = { 126 { .cmd = "version", .fn = dm_get_version_ioctl, .allowed = 1 }, 127 { .cmd = "targets", .fn = dm_list_versions_ioctl, .allowed = 1 }, 128 { .cmd = "create", .fn = dm_dev_create_ioctl, .allowed = 0 }, 129 { .cmd = "info", .fn = dm_dev_status_ioctl, .allowed = 1 }, 130 { .cmd = "mknodes", .fn = dm_dev_status_ioctl, .allowed = 1 }, 131 { .cmd = "names", .fn = dm_dev_list_ioctl, .allowed = 1 }, 132 { .cmd = "suspend", .fn = dm_dev_suspend_ioctl, .allowed = 0 }, 133 { .cmd = "remove", .fn = dm_dev_remove_ioctl, .allowed = 0 }, 134 { .cmd = "rename", .fn = dm_dev_rename_ioctl, .allowed = 0 }, 135 { .cmd = "resume", .fn = dm_dev_resume_ioctl, .allowed = 0 }, 136 { .cmd = "clear", .fn = dm_table_clear_ioctl, .allowed = 0 }, 137 { .cmd = "deps", .fn = dm_table_deps_ioctl, .allowed = 1 }, 138 { .cmd = "reload", .fn = dm_table_load_ioctl, .allowed = 0 }, 139 { .cmd = "status", .fn = dm_table_status_ioctl, .allowed = 1 }, 140 { .cmd = "table", .fn = dm_table_status_ioctl, .allowed = 1 }, 141 { .cmd = NULL, .fn = NULL, .allowed = 0 } 142 }; 143 144 #ifdef _MODULE 145 #include <sys/module.h> 146 147 /* Autoconf defines */ 148 CFDRIVER_DECL(dm, DV_DISK, NULL); 149 150 MODULE(MODULE_CLASS_DRIVER, dm, "dk_subr"); 151 152 /* New module handle routine */ 153 static int 154 dm_modcmd(modcmd_t cmd, void *arg) 155 { 156 #ifdef _MODULE 157 int error; 158 devmajor_t bmajor, cmajor; 159 160 error = 0; 161 bmajor = -1; 162 cmajor = -1; 163 164 switch (cmd) { 165 case MODULE_CMD_INIT: 166 error = config_cfdriver_attach(&dm_cd); 167 if (error) 168 break; 169 170 error = config_cfattach_attach(dm_cd.cd_name, &dm_ca); 171 if (error) { 172 aprint_error("%s: unable to register cfattach\n", 173 dm_cd.cd_name); 174 return error; 175 } 176 177 error = devsw_attach(dm_cd.cd_name, &dm_bdevsw, &bmajor, 178 &dm_cdevsw, &cmajor); 179 if (error == EEXIST) 180 error = 0; 181 if (error) { 182 config_cfattach_detach(dm_cd.cd_name, &dm_ca); 183 config_cfdriver_detach(&dm_cd); 184 break; 185 } 186 187 dm_doinit(); 188 189 break; 190 191 case MODULE_CMD_FINI: 192 /* 193 * Disable unloading of dm module if there are any devices 194 * defined in driver. This is probably too strong we need 195 * to disable auto-unload only if there is mounted dm device 196 * present. 197 */ 198 if (dm_dev_counter > 0) 199 return EBUSY; 200 201 error = dmdestroy(); 202 if (error) 203 break; 204 205 config_cfdriver_detach(&dm_cd); 206 207 devsw_detach(&dm_bdevsw, &dm_cdevsw); 208 break; 209 case MODULE_CMD_STAT: 210 return ENOTTY; 211 212 default: 213 return ENOTTY; 214 } 215 216 return error; 217 #else 218 return ENOTTY; 219 #endif 220 } 221 #endif /* _MODULE */ 222 223 /* 224 * dm_match: 225 * 226 * Autoconfiguration match function for pseudo-device glue. 227 */ 228 static int 229 dm_match(device_t parent, cfdata_t match, void *aux) 230 { 231 232 /* Pseudo-device; always present. */ 233 return (1); 234 } 235 236 /* 237 * dm_attach: 238 * 239 * Autoconfiguration attach function for pseudo-device glue. 240 */ 241 static void 242 dm_attach(device_t parent, device_t self, void *aux) 243 { 244 return; 245 } 246 247 248 /* 249 * dm_detach: 250 * 251 * Autoconfiguration detach function for pseudo-device glue. 252 * This routine is called by dm_ioctl::dm_dev_remove_ioctl and by autoconf to 253 * remove devices created in device-mapper. 254 */ 255 static int 256 dm_detach(device_t self, int flags) 257 { 258 dm_dev_t *dmv; 259 260 /* Detach device from global device list */ 261 if ((dmv = dm_dev_detach(self)) == NULL) 262 return ENOENT; 263 264 /* Destroy active table first. */ 265 dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE); 266 267 /* Destroy inactive table if exits, too. */ 268 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 269 270 dm_table_head_destroy(&dmv->table_head); 271 272 /* Destroy disk device structure */ 273 disk_detach(dmv->diskp); 274 disk_destroy(dmv->diskp); 275 276 /* Destroy device */ 277 (void)dm_dev_free(dmv); 278 279 /* Decrement device counter After removing device */ 280 atomic_dec_32(&dm_dev_counter); 281 282 return 0; 283 } 284 285 static void 286 dm_doinit(void) 287 { 288 dm_target_init(); 289 dm_dev_init(); 290 dm_pdev_init(); 291 } 292 293 /* attach routine */ 294 void 295 dmattach(int n) 296 { 297 int error; 298 299 error = config_cfattach_attach(dm_cd.cd_name, &dm_ca); 300 if (error) { 301 aprint_error("%s: unable to register cfattach\n", 302 dm_cd.cd_name); 303 } else { 304 dm_doinit(); 305 } 306 } 307 308 #ifdef _MODULE 309 /* Destroy routine */ 310 static int 311 dmdestroy(void) 312 { 313 int error; 314 315 error = config_cfattach_detach(dm_cd.cd_name, &dm_ca); 316 if (error) 317 return error; 318 319 dm_dev_destroy(); 320 dm_pdev_destroy(); 321 dm_target_destroy(); 322 323 return 0; 324 } 325 #endif /* _MODULE */ 326 327 static int 328 dmopen(dev_t dev, int flags, int mode, struct lwp *l) 329 { 330 331 aprint_debug("dm open routine called %" PRIu32 "\n", minor(dev)); 332 return 0; 333 } 334 335 static int 336 dmclose(dev_t dev, int flags, int mode, struct lwp *l) 337 { 338 339 aprint_debug("dm close routine called %" PRIu32 "\n", minor(dev)); 340 return 0; 341 } 342 343 344 static int 345 dmioctl(dev_t dev, const u_long cmd, void *data, int flag, struct lwp *l) 346 { 347 int r; 348 prop_dictionary_t dm_dict_in; 349 350 r = 0; 351 352 aprint_debug("dmioctl called\n"); 353 KASSERT(data != NULL); 354 355 if (( r = disk_ioctl_switch(dev, cmd, data)) == ENOTTY) { 356 struct plistref *pref = (struct plistref *) data; 357 358 /* Check if we were called with NETBSD_DM_IOCTL ioctl 359 otherwise quit. */ 360 if ((r = dm_ioctl_switch(cmd)) != 0) 361 return r; 362 363 if((r = prop_dictionary_copyin_ioctl(pref, cmd, &dm_dict_in)) 364 != 0) 365 return r; 366 367 if ((r = dm_check_version(dm_dict_in)) != 0) 368 goto cleanup_exit; 369 370 /* run ioctl routine */ 371 if ((r = dm_cmd_to_fun(dm_dict_in)) != 0) 372 goto cleanup_exit; 373 374 cleanup_exit: 375 r = prop_dictionary_copyout_ioctl(pref, cmd, dm_dict_in); 376 prop_object_release(dm_dict_in); 377 } 378 379 return r; 380 } 381 382 /* 383 * Translate command sent from libdevmapper to func. 384 */ 385 static int 386 dm_cmd_to_fun(prop_dictionary_t dm_dict) 387 { 388 int i, r; 389 prop_string_t command; 390 391 r = 0; 392 393 if ((command = prop_dictionary_get(dm_dict, DM_IOCTL_COMMAND)) == NULL) 394 return EINVAL; 395 396 for(i = 0; cmd_fn[i].cmd != NULL; i++) 397 if (prop_string_equals_cstring(command, cmd_fn[i].cmd)) 398 break; 399 400 if (!cmd_fn[i].allowed && 401 (r = kauth_authorize_system(kauth_cred_get(), 402 KAUTH_SYSTEM_DEVMAPPER, 0, NULL, NULL, NULL)) != 0) 403 return r; 404 405 if (cmd_fn[i].cmd == NULL) 406 return EINVAL; 407 408 aprint_debug("ioctl %s called\n", cmd_fn[i].cmd); 409 r = cmd_fn[i].fn(dm_dict); 410 411 return r; 412 } 413 414 /* Call apropriate ioctl handler function. */ 415 static int 416 dm_ioctl_switch(u_long cmd) 417 { 418 419 switch(cmd) { 420 421 case NETBSD_DM_IOCTL: 422 aprint_debug("dm NetBSD_DM_IOCTL called\n"); 423 break; 424 default: 425 aprint_debug("dm unknown ioctl called\n"); 426 return ENOTTY; 427 break; /* NOT REACHED */ 428 } 429 430 return 0; 431 } 432 433 /* 434 * Check for disk specific ioctls. 435 */ 436 437 static int 438 disk_ioctl_switch(dev_t dev, u_long cmd, void *data) 439 { 440 dm_dev_t *dmv; 441 442 /* disk ioctls make sense only on block devices */ 443 if (minor(dev) == 0) 444 return ENOTTY; 445 446 switch(cmd) { 447 case DIOCGWEDGEINFO: 448 { 449 struct dkwedge_info *dkw = (void *) data; 450 unsigned secsize; 451 452 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 453 return ENODEV; 454 455 aprint_debug("DIOCGWEDGEINFO ioctl called\n"); 456 457 strlcpy(dkw->dkw_devname, dmv->name, 16); 458 strlcpy(dkw->dkw_wname, dmv->name, DM_NAME_LEN); 459 strlcpy(dkw->dkw_parent, dmv->name, 16); 460 461 dkw->dkw_offset = 0; 462 dm_table_disksize(&dmv->table_head, &dkw->dkw_size, &secsize); 463 strcpy(dkw->dkw_ptype, DKW_PTYPE_FFS); 464 465 dm_dev_unbusy(dmv); 466 break; 467 } 468 469 case DIOCGDISKINFO: 470 { 471 struct plistref *pref = (struct plistref *) data; 472 473 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 474 return ENODEV; 475 476 if (dmv->diskp->dk_info == NULL) { 477 dm_dev_unbusy(dmv); 478 return ENOTSUP; 479 } else 480 prop_dictionary_copyout_ioctl(pref, cmd, 481 dmv->diskp->dk_info); 482 483 dm_dev_unbusy(dmv); 484 break; 485 } 486 487 case DIOCCACHESYNC: 488 { 489 dm_table_entry_t *table_en; 490 dm_table_t *tbl; 491 492 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 493 return ENODEV; 494 495 /* Select active table */ 496 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); 497 498 /* 499 * Call sync target routine for all table entries. Target sync 500 * routine basically call DIOCCACHESYNC on underlying devices. 501 */ 502 SLIST_FOREACH(table_en, tbl, next) 503 { 504 (void)table_en->target->sync(table_en); 505 } 506 dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); 507 dm_dev_unbusy(dmv); 508 break; 509 } 510 511 512 default: 513 aprint_debug("unknown disk_ioctl called\n"); 514 return ENOTTY; 515 break; /* NOT REACHED */ 516 } 517 518 return 0; 519 } 520 521 /* 522 * Do all IO operations on dm logical devices. 523 */ 524 static void 525 dmstrategy(struct buf *bp) 526 { 527 dm_dev_t *dmv; 528 dm_table_t *tbl; 529 dm_table_entry_t *table_en; 530 struct buf *nestbuf; 531 532 uint64_t buf_start, buf_len, issued_len; 533 uint64_t table_start, table_end; 534 uint64_t start, end; 535 536 buf_start = bp->b_blkno * DEV_BSIZE; 537 buf_len = bp->b_bcount; 538 539 tbl = NULL; 540 541 table_end = 0; 542 issued_len = 0; 543 544 if ((dmv = dm_dev_lookup(NULL, NULL, minor(bp->b_dev))) == NULL) { 545 bp->b_error = EIO; 546 bp->b_resid = bp->b_bcount; 547 biodone(bp); 548 return; 549 } 550 551 if (bounds_check_with_mediasize(bp, DEV_BSIZE, 552 dm_table_size(&dmv->table_head)) <= 0) { 553 dm_dev_unbusy(dmv); 554 bp->b_resid = bp->b_bcount; 555 biodone(bp); 556 return; 557 } 558 559 /* 560 * disk(9) is part of device structure and it can't be used without 561 * mutual exclusion, use diskp_mtx until it will be fixed. 562 */ 563 mutex_enter(&dmv->diskp_mtx); 564 disk_busy(dmv->diskp); 565 mutex_exit(&dmv->diskp_mtx); 566 567 /* Select active table */ 568 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); 569 570 /* Nested buffers count down to zero therefore I have 571 to set bp->b_resid to maximal value. */ 572 bp->b_resid = bp->b_bcount; 573 574 /* 575 * Find out what tables I want to select. 576 */ 577 SLIST_FOREACH(table_en, tbl, next) 578 { 579 /* I need need number of bytes not blocks. */ 580 table_start = table_en->start * DEV_BSIZE; 581 /* 582 * I have to sub 1 from table_en->length to prevent 583 * off by one error 584 */ 585 table_end = table_start + (table_en->length)* DEV_BSIZE; 586 587 start = MAX(table_start, buf_start); 588 589 end = MIN(table_end, buf_start + buf_len); 590 591 aprint_debug("----------------------------------------\n"); 592 aprint_debug("table_start %010" PRIu64", table_end %010" 593 PRIu64 "\n", table_start, table_end); 594 aprint_debug("buf_start %010" PRIu64", buf_len %010" 595 PRIu64"\n", buf_start, buf_len); 596 aprint_debug("start-buf_start %010"PRIu64", end %010" 597 PRIu64"\n", start - buf_start, end); 598 aprint_debug("start %010" PRIu64" , end %010" 599 PRIu64"\n", start, end); 600 aprint_debug("\n----------------------------------------\n"); 601 602 if (start < end) { 603 /* create nested buffer */ 604 nestbuf = getiobuf(NULL, true); 605 606 nestiobuf_setup(bp, nestbuf, start - buf_start, 607 (end - start)); 608 609 issued_len += end - start; 610 611 /* I need number of blocks. */ 612 nestbuf->b_blkno = (start - table_start) / DEV_BSIZE; 613 614 table_en->target->strategy(table_en, nestbuf); 615 } 616 } 617 618 if (issued_len < buf_len) 619 nestiobuf_done(bp, buf_len - issued_len, EINVAL); 620 621 mutex_enter(&dmv->diskp_mtx); 622 disk_unbusy(dmv->diskp, buf_len, bp != NULL ? bp->b_flags & B_READ : 0); 623 mutex_exit(&dmv->diskp_mtx); 624 625 dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); 626 dm_dev_unbusy(dmv); 627 628 return; 629 } 630 631 632 static int 633 dmread(dev_t dev, struct uio *uio, int flag) 634 { 635 636 return (physio(dmstrategy, NULL, dev, B_READ, dmminphys, uio)); 637 } 638 639 static int 640 dmwrite(dev_t dev, struct uio *uio, int flag) 641 { 642 643 return (physio(dmstrategy, NULL, dev, B_WRITE, dmminphys, uio)); 644 } 645 646 static int 647 dmsize(dev_t dev) 648 { 649 dm_dev_t *dmv; 650 uint64_t size; 651 652 size = 0; 653 654 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 655 return -ENOENT; 656 657 size = dm_table_size(&dmv->table_head); 658 dm_dev_unbusy(dmv); 659 660 return size; 661 } 662 663 static void 664 dmminphys(struct buf *bp) 665 { 666 667 bp->b_bcount = MIN(bp->b_bcount, MAXPHYS); 668 } 669 670 void 671 dmgetproperties(struct disk *disk, dm_table_head_t *head) 672 { 673 uint64_t numsec; 674 unsigned secsize; 675 676 dm_table_disksize(head, &numsec, &secsize); 677 678 struct disk_geom *dg = &disk->dk_geom; 679 680 memset(dg, 0, sizeof(*dg)); 681 dg->dg_secperunit = numsec; 682 dg->dg_secsize = secsize; 683 dg->dg_nsectors = 32; 684 dg->dg_ntracks = 64; 685 686 disk_set_info(NULL, disk, "ESDI"); 687 } 688