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