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