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