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