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