1 /* $NetBSD: device-mapper.c,v 1.40 2018/10/06 14:59:11 mlelstv 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 case DIOCGSECTORSIZE: 512 { 513 u_int *valp = data; 514 uint64_t numsec; 515 unsigned int secsize; 516 517 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 518 return ENODEV; 519 520 aprint_debug("DIOCGSECTORSIZE ioctl called\n"); 521 522 dm_table_disksize(&dmv->table_head, &numsec, &secsize); 523 *valp = secsize; 524 525 dm_dev_unbusy(dmv); 526 break; 527 } 528 529 case DIOCGMEDIASIZE: 530 { 531 off_t *valp = data; 532 uint64_t numsec; 533 unsigned int secsize; 534 535 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 536 return ENODEV; 537 538 aprint_debug("DIOCGMEDIASIZE ioctl called\n"); 539 540 dm_table_disksize(&dmv->table_head, &numsec, &secsize); 541 *valp = numsec; 542 543 dm_dev_unbusy(dmv); 544 break; 545 } 546 547 548 default: 549 aprint_debug("unknown disk_ioctl called\n"); 550 return ENOTTY; 551 break; /* NOT REACHED */ 552 } 553 554 return 0; 555 } 556 557 /* 558 * Do all IO operations on dm logical devices. 559 */ 560 static void 561 dmstrategy(struct buf *bp) 562 { 563 dm_dev_t *dmv; 564 dm_table_t *tbl; 565 dm_table_entry_t *table_en; 566 struct buf *nestbuf; 567 568 uint64_t buf_start, buf_len, issued_len; 569 uint64_t table_start, table_end; 570 uint64_t start, end; 571 572 buf_start = bp->b_blkno * DEV_BSIZE; 573 buf_len = bp->b_bcount; 574 575 tbl = NULL; 576 577 table_end = 0; 578 issued_len = 0; 579 580 if ((dmv = dm_dev_lookup(NULL, NULL, minor(bp->b_dev))) == NULL) { 581 bp->b_error = EIO; 582 bp->b_resid = bp->b_bcount; 583 biodone(bp); 584 return; 585 } 586 587 if (bounds_check_with_mediasize(bp, DEV_BSIZE, 588 dm_table_size(&dmv->table_head)) <= 0) { 589 dm_dev_unbusy(dmv); 590 bp->b_resid = bp->b_bcount; 591 biodone(bp); 592 return; 593 } 594 595 /* 596 * disk(9) is part of device structure and it can't be used without 597 * mutual exclusion, use diskp_mtx until it will be fixed. 598 */ 599 mutex_enter(&dmv->diskp_mtx); 600 disk_busy(dmv->diskp); 601 mutex_exit(&dmv->diskp_mtx); 602 603 /* Select active table */ 604 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); 605 606 /* Nested buffers count down to zero therefore I have 607 to set bp->b_resid to maximal value. */ 608 bp->b_resid = bp->b_bcount; 609 610 /* 611 * Find out what tables I want to select. 612 */ 613 SLIST_FOREACH(table_en, tbl, next) 614 { 615 /* I need need number of bytes not blocks. */ 616 table_start = table_en->start * DEV_BSIZE; 617 /* 618 * I have to sub 1 from table_en->length to prevent 619 * off by one error 620 */ 621 table_end = table_start + (table_en->length)* DEV_BSIZE; 622 623 start = MAX(table_start, buf_start); 624 625 end = MIN(table_end, buf_start + buf_len); 626 627 aprint_debug("----------------------------------------\n"); 628 aprint_debug("table_start %010" PRIu64", table_end %010" 629 PRIu64 "\n", table_start, table_end); 630 aprint_debug("buf_start %010" PRIu64", buf_len %010" 631 PRIu64"\n", buf_start, buf_len); 632 aprint_debug("start-buf_start %010"PRIu64", end %010" 633 PRIu64"\n", start - buf_start, end); 634 aprint_debug("start %010" PRIu64" , end %010" 635 PRIu64"\n", start, end); 636 aprint_debug("\n----------------------------------------\n"); 637 638 if (start < end) { 639 /* create nested buffer */ 640 nestbuf = getiobuf(NULL, true); 641 642 nestiobuf_setup(bp, nestbuf, start - buf_start, 643 (end - start)); 644 645 issued_len += end - start; 646 647 /* I need number of blocks. */ 648 nestbuf->b_blkno = (start - table_start) / DEV_BSIZE; 649 650 table_en->target->strategy(table_en, nestbuf); 651 } 652 } 653 654 if (issued_len < buf_len) 655 nestiobuf_done(bp, buf_len - issued_len, EINVAL); 656 657 mutex_enter(&dmv->diskp_mtx); 658 disk_unbusy(dmv->diskp, buf_len, bp != NULL ? bp->b_flags & B_READ : 0); 659 mutex_exit(&dmv->diskp_mtx); 660 661 dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); 662 dm_dev_unbusy(dmv); 663 664 return; 665 } 666 667 668 static int 669 dmread(dev_t dev, struct uio *uio, int flag) 670 { 671 672 return (physio(dmstrategy, NULL, dev, B_READ, dmminphys, uio)); 673 } 674 675 static int 676 dmwrite(dev_t dev, struct uio *uio, int flag) 677 { 678 679 return (physio(dmstrategy, NULL, dev, B_WRITE, dmminphys, uio)); 680 } 681 682 static int 683 dmsize(dev_t dev) 684 { 685 dm_dev_t *dmv; 686 uint64_t size; 687 688 size = 0; 689 690 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 691 return -ENOENT; 692 693 size = dm_table_size(&dmv->table_head); 694 dm_dev_unbusy(dmv); 695 696 return size; 697 } 698 699 static void 700 dmminphys(struct buf *bp) 701 { 702 703 bp->b_bcount = MIN(bp->b_bcount, MAXPHYS); 704 } 705 706 void 707 dmgetproperties(struct disk *disk, dm_table_head_t *head) 708 { 709 uint64_t numsec; 710 unsigned secsize; 711 712 dm_table_disksize(head, &numsec, &secsize); 713 714 struct disk_geom *dg = &disk->dk_geom; 715 716 memset(dg, 0, sizeof(*dg)); 717 dg->dg_secperunit = numsec; 718 dg->dg_secsize = secsize; 719 dg->dg_nsectors = 32; 720 dg->dg_ntracks = 64; 721 722 disk_set_info(NULL, disk, "ESDI"); 723 } 724