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