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