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/disk.h> 43 #include <sys/disklabel.h> 44 #include <sys/dtype.h> 45 #include <sys/ioccom.h> 46 #include <sys/malloc.h> 47 #include <sys/module.h> 48 49 #include "netbsd-dm.h" 50 #include "dm.h" 51 52 static d_ioctl_t dmioctl; 53 static d_open_t dmopen; 54 static d_close_t dmclose; 55 static d_psize_t dmsize; 56 static d_strategy_t dmstrategy; 57 58 /* attach and detach routines */ 59 void dmattach(int); 60 static int dm_modcmd(module_t mod, int cmd, void *unused); 61 static int dmdestroy(void); 62 63 static void dm_doinit(void); 64 65 static int dm_cmd_to_fun(prop_dictionary_t); 66 static int disk_ioctl_switch(cdev_t, u_long, void *); 67 static int dm_ioctl_switch(u_long); 68 #if 0 69 static void dmminphys(struct buf *); 70 #endif 71 72 /* ***Variable-definitions*** */ 73 struct dev_ops dm_ops = { 74 { "dm", 0, D_DISK | 75 D_MPSAFE_READ | D_MPSAFE_WRITE | D_MPSAFE_IOCTL }, 76 .d_open = dmopen, 77 .d_close = dmclose, 78 .d_read = physread, 79 .d_write = physwrite, 80 .d_ioctl = dmioctl, 81 .d_strategy = dmstrategy, 82 .d_psize = dmsize, 83 /* D_DISK */ 84 }; 85 86 MALLOC_DEFINE(M_DM, "dm", "Device Mapper allocations"); 87 88 extern uint64_t dm_dev_counter; 89 90 static cdev_t dmcdev; 91 92 static moduledata_t dm_mod = { 93 "dm", 94 dm_modcmd, 95 NULL 96 }; 97 DECLARE_MODULE(dm, dm_mod, SI_SUB_RAID, SI_ORDER_ANY); 98 99 /* 100 * This array is used to translate cmd to function pointer. 101 * 102 * Interface between libdevmapper and lvm2tools uses different 103 * names for one IOCTL call because libdevmapper do another thing 104 * then. When I run "info" or "mknodes" libdevmapper will send same 105 * ioctl to kernel but will do another things in userspace. 106 * 107 */ 108 struct cmd_function cmd_fn[] = { 109 { .cmd = "version", .fn = dm_get_version_ioctl}, 110 { .cmd = "targets", .fn = dm_list_versions_ioctl}, 111 { .cmd = "create", .fn = dm_dev_create_ioctl}, 112 { .cmd = "info", .fn = dm_dev_status_ioctl}, 113 { .cmd = "mknodes", .fn = dm_dev_status_ioctl}, 114 { .cmd = "names", .fn = dm_dev_list_ioctl}, 115 { .cmd = "suspend", .fn = dm_dev_suspend_ioctl}, 116 { .cmd = "remove", .fn = dm_dev_remove_ioctl}, 117 { .cmd = "rename", .fn = dm_dev_rename_ioctl}, 118 { .cmd = "resume", .fn = dm_dev_resume_ioctl}, 119 { .cmd = "clear", .fn = dm_table_clear_ioctl}, 120 { .cmd = "deps", .fn = dm_table_deps_ioctl}, 121 { .cmd = "reload", .fn = dm_table_load_ioctl}, 122 { .cmd = "status", .fn = dm_table_status_ioctl}, 123 { .cmd = "table", .fn = dm_table_status_ioctl}, 124 {NULL, NULL} 125 }; 126 127 /* New module handle routine */ 128 static int 129 dm_modcmd(module_t mod, int cmd, void *unused) 130 { 131 int error, bmajor, cmajor; 132 133 error = 0; 134 bmajor = -1; 135 cmajor = -1; 136 137 switch (cmd) { 138 case MOD_LOAD: 139 dm_doinit(); 140 kprintf("Device Mapper version %d.%d.%d loaded\n", 141 DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL); 142 break; 143 144 case MOD_UNLOAD: 145 /* 146 * Disable unloading of dm module if there are any devices 147 * defined in driver. This is probably too strong we need 148 * to disable auto-unload only if there is mounted dm device 149 * present. 150 */ 151 if (dm_dev_counter > 0) 152 return EBUSY; 153 154 error = dmdestroy(); 155 if (error) 156 break; 157 kprintf("Device Mapper unloaded\n"); 158 break; 159 160 default: 161 break; 162 } 163 164 return error; 165 } 166 167 /* 168 * dm_detach: 169 * 170 * Autoconfiguration detach function for pseudo-device glue. 171 * This routine is called by dm_ioctl::dm_dev_remove_ioctl and by autoconf to 172 * remove devices created in device-mapper. 173 */ 174 int 175 dm_detach(dm_dev_t *dmv) 176 { 177 disable_dev(dmv); 178 179 /* Destroy active table first. */ 180 dm_table_destroy(&dmv->table_head, DM_TABLE_ACTIVE); 181 182 /* Destroy inactive table if exits, too. */ 183 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 184 185 dm_table_head_destroy(&dmv->table_head); 186 187 destroy_dev(dmv->devt); 188 189 /* Destroy device */ 190 (void)dm_dev_free(dmv); 191 192 /* Decrement device counter After removing device */ 193 --dm_dev_counter; /* XXX: was atomic 64 */ 194 195 return 0; 196 } 197 198 static void 199 dm_doinit(void) 200 { 201 dm_target_init(); 202 dm_dev_init(); 203 dm_pdev_init(); 204 dmcdev = make_dev(&dm_ops, 0, UID_ROOT, GID_OPERATOR, 0640, "mapper/control"); 205 } 206 207 /* Destroy routine */ 208 static int 209 dmdestroy(void) 210 { 211 destroy_dev(dmcdev); 212 213 dm_dev_destroy(); 214 dm_pdev_destroy(); 215 dm_target_destroy(); 216 217 return 0; 218 } 219 220 static int 221 dmopen(struct dev_open_args *ap) 222 { 223 224 aprint_debug("dm open routine called %" PRIu32 "\n", 225 minor(ap->a_head.a_dev)); 226 return 0; 227 } 228 229 static int 230 dmclose(struct dev_close_args *ap) 231 { 232 233 aprint_debug("dm close routine called %" PRIu32 "\n", 234 minor(ap->a_head.a_dev)); 235 return 0; 236 } 237 238 239 static int 240 dmioctl(struct dev_ioctl_args *ap) 241 { 242 cdev_t dev = ap->a_head.a_dev; 243 u_long cmd = ap->a_cmd; 244 void *data = ap->a_data; 245 246 int r; 247 prop_dictionary_t dm_dict_in; 248 249 r = 0; 250 251 aprint_debug("dmioctl called\n"); 252 253 KKASSERT(data != NULL); 254 255 if (( r = disk_ioctl_switch(dev, cmd, data)) == ENOTTY) { 256 struct plistref *pref = (struct plistref *) data; 257 258 /* Check if we were called with NETBSD_DM_IOCTL ioctl 259 otherwise quit. */ 260 if ((r = dm_ioctl_switch(cmd)) != 0) 261 return r; 262 263 if((r = prop_dictionary_copyin_ioctl(pref, cmd, &dm_dict_in)) != 0) 264 return r; 265 266 if ((r = dm_check_version(dm_dict_in)) != 0) 267 goto cleanup_exit; 268 269 /* run ioctl routine */ 270 if ((r = dm_cmd_to_fun(dm_dict_in)) != 0) 271 goto cleanup_exit; 272 273 cleanup_exit: 274 r = prop_dictionary_copyout_ioctl(pref, cmd, dm_dict_in); 275 prop_object_release(dm_dict_in); 276 } 277 278 return r; 279 } 280 281 /* 282 * Translate command sent from libdevmapper to func. 283 */ 284 static int 285 dm_cmd_to_fun(prop_dictionary_t dm_dict){ 286 int i, r; 287 prop_string_t command; 288 289 r = 0; 290 291 if ((command = prop_dictionary_get(dm_dict, DM_IOCTL_COMMAND)) == NULL) 292 return EINVAL; 293 294 for(i = 0; cmd_fn[i].cmd != NULL; i++) 295 if (prop_string_equals_cstring(command, cmd_fn[i].cmd)) 296 break; 297 298 if (cmd_fn[i].cmd == NULL) 299 return EINVAL; 300 301 aprint_debug("ioctl %s called\n", cmd_fn[i].cmd); 302 r = cmd_fn[i].fn(dm_dict); 303 304 return r; 305 } 306 307 /* Call apropriate ioctl handler function. */ 308 static int 309 dm_ioctl_switch(u_long cmd) 310 { 311 312 switch(cmd) { 313 314 case NETBSD_DM_IOCTL: 315 aprint_debug("dm NetBSD_DM_IOCTL called\n"); 316 break; 317 default: 318 aprint_debug("dm unknown ioctl called\n"); 319 return ENOTTY; 320 break; /* NOT REACHED */ 321 } 322 323 return 0; 324 } 325 326 /* 327 * Check for disk specific ioctls. 328 */ 329 330 static int 331 disk_ioctl_switch(cdev_t dev, u_long cmd, void *data) 332 { 333 dm_dev_t *dmv; 334 335 /* disk ioctls make sense only on block devices */ 336 if (minor(dev) == 0) 337 return ENOTTY; 338 339 switch(cmd) { 340 case DIOCGPART: 341 { 342 struct partinfo *dpart; 343 u_int64_t size; 344 dpart = (void *)data; 345 bzero(dpart, sizeof(*dpart)); 346 347 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 348 return ENODEV; 349 if (dmv->diskp->d_info.d_media_blksize == 0) { 350 dm_dev_unbusy(dmv); 351 return ENOTSUP; 352 } else { 353 size = dm_table_size(&dmv->table_head); 354 kprintf("fooi, size = %"PRIu64" ...\n", size); 355 dpart->media_offset = 0; 356 dpart->media_size = size * DEV_BSIZE; 357 dpart->media_blocks = size; 358 dpart->media_blksize = DEV_BSIZE; 359 dpart->fstype = FS_BSDFFS; 360 } 361 dm_dev_unbusy(dmv); 362 break; 363 } 364 365 default: 366 aprint_debug("unknown disk_ioctl called\n"); 367 return ENOTTY; 368 break; /* NOT REACHED */ 369 } 370 371 return 0; 372 } 373 374 /* 375 * Do all IO operations on dm logical devices. 376 */ 377 static int 378 dmstrategy(struct dev_strategy_args *ap) 379 { 380 cdev_t dev = ap->a_head.a_dev; 381 struct bio *bio = ap->a_bio; 382 struct buf *bp = bio->bio_buf; 383 384 dm_dev_t *dmv; 385 dm_table_t *tbl; 386 dm_table_entry_t *table_en; 387 struct buf *nestbuf; 388 389 uint32_t dev_type; 390 391 uint64_t buf_start, buf_len, issued_len; 392 uint64_t table_start, table_end; 393 uint64_t start, end; 394 395 buf_start = bio->bio_offset; 396 buf_len = bp->b_bcount; 397 398 tbl = NULL; 399 400 table_end = 0; 401 dev_type = 0; 402 issued_len = 0; 403 404 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) { 405 bp->b_error = EIO; 406 bp->b_resid = bp->b_bcount; 407 biodone(bio); 408 return 0; 409 } 410 411 if (bounds_check_with_mediasize(bio, DEV_BSIZE, 412 dm_table_size(&dmv->table_head)) <= 0) { 413 dm_dev_unbusy(dmv); 414 bp->b_resid = bp->b_bcount; 415 biodone(bio); 416 return 0; 417 } 418 419 /* Select active table */ 420 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); 421 422 /* Nested buffers count down to zero therefore I have 423 to set bp->b_resid to maximal value. */ 424 bp->b_resid = bp->b_bcount; 425 426 /* 427 * Find out what tables I want to select. 428 */ 429 SLIST_FOREACH(table_en, tbl, next) 430 { 431 /* I need need number of bytes not blocks. */ 432 table_start = table_en->start * DEV_BSIZE; 433 /* 434 * I have to sub 1 from table_en->length to prevent 435 * off by one error 436 */ 437 table_end = table_start + (table_en->length)* DEV_BSIZE; 438 439 start = MAX(table_start, buf_start); 440 441 end = MIN(table_end, buf_start + buf_len); 442 443 aprint_debug("----------------------------------------\n"); 444 aprint_debug("table_start %010" PRIu64", table_end %010" 445 PRIu64 "\n", table_start, table_end); 446 aprint_debug("buf_start %010" PRIu64", buf_len %010" 447 PRIu64"\n", buf_start, buf_len); 448 aprint_debug("start-buf_start %010"PRIu64", end %010" 449 PRIu64"\n", start - buf_start, end); 450 aprint_debug("start %010" PRIu64" , end %010" 451 PRIu64"\n", start, end); 452 aprint_debug("\n----------------------------------------\n"); 453 454 if (start < end) { 455 /* create nested buffer */ 456 nestbuf = getpbuf(NULL); 457 458 nestiobuf_setup(bio, nestbuf, start - buf_start, 459 (end - start)); 460 461 issued_len += end - start; 462 463 /* I need number of bytes. */ 464 nestbuf->b_bio1.bio_offset = (start - table_start); 465 466 table_en->target->strategy(table_en, nestbuf); 467 } 468 } 469 470 if (issued_len < buf_len) 471 nestiobuf_done(bio, buf_len - issued_len, EINVAL); 472 473 dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); 474 dm_dev_unbusy(dmv); 475 476 return 0; 477 } 478 479 static int 480 dmsize(struct dev_psize_args *ap) 481 { 482 cdev_t dev = ap->a_head.a_dev; 483 dm_dev_t *dmv; 484 uint64_t size; 485 486 size = 0; 487 488 if ((dmv = dm_dev_lookup(NULL, NULL, minor(dev))) == NULL) 489 return -ENOENT; 490 491 size = dm_table_size(&dmv->table_head); 492 dm_dev_unbusy(dmv); 493 494 return size; 495 } 496 497 #if 0 498 static void 499 dmminphys(struct buf *bp) 500 { 501 502 bp->b_bcount = MIN(bp->b_bcount, MAXPHYS); 503 } 504 #endif 505 506 void 507 dmsetdiskinfo(struct disk *disk, dm_table_head_t *head) 508 { 509 struct disk_info info; 510 int dmp_size; 511 512 dmp_size = dm_table_size(head); 513 514 bzero(&info, sizeof(struct disk_info)); 515 info.d_media_blksize = DEV_BSIZE; 516 info.d_media_blocks = dmp_size; 517 info.d_media_size = dmp_size * DEV_BSIZE; 518 info.d_dsflags = DSO_MBRQUIET; /* XXX */ 519 info.d_secpertrack = 32; 520 info.d_nheads = 64; 521 info.d_secpercyl = info.d_secpertrack * info.d_nheads; 522 info.d_ncylinders = dmp_size / 2048; 523 bcopy(&info, &disk->d_info, sizeof(disk->d_info)); 524 } 525 526 prop_dictionary_t 527 dmgetdiskinfo(struct disk *disk) 528 { 529 prop_dictionary_t disk_info, geom; 530 struct disk_info *pinfo; 531 532 pinfo = &disk->d_info; 533 534 disk_info = prop_dictionary_create(); 535 geom = prop_dictionary_create(); 536 537 prop_dictionary_set_cstring_nocopy(disk_info, "type", "ESDI"); 538 prop_dictionary_set_uint64(geom, "sectors-per-unit", pinfo->d_media_blocks); 539 prop_dictionary_set_uint32(geom, "sector-size", 540 DEV_BSIZE /* XXX 512? */); 541 prop_dictionary_set_uint32(geom, "sectors-per-track", 32); 542 prop_dictionary_set_uint32(geom, "tracks-per-cylinder", 64); 543 prop_dictionary_set_uint32(geom, "cylinders-per-unit", 544 pinfo->d_media_blocks / 2048); 545 prop_dictionary_set(disk_info, "geometry", geom); 546 prop_object_release(geom); 547 548 return disk_info; 549 } 550 551 void 552 dmgetproperties(struct disk *disk, dm_table_head_t *head) 553 { 554 #if 0 555 prop_dictionary_t disk_info, odisk_info, geom; 556 int dmp_size; 557 558 dmp_size = dm_table_size(head); 559 disk_info = prop_dictionary_create(); 560 geom = prop_dictionary_create(); 561 562 prop_dictionary_set_cstring_nocopy(disk_info, "type", "ESDI"); 563 prop_dictionary_set_uint64(geom, "sectors-per-unit", dmp_size); 564 prop_dictionary_set_uint32(geom, "sector-size", 565 DEV_BSIZE /* XXX 512? */); 566 prop_dictionary_set_uint32(geom, "sectors-per-track", 32); 567 prop_dictionary_set_uint32(geom, "tracks-per-cylinder", 64); 568 prop_dictionary_set_uint32(geom, "cylinders-per-unit", dmp_size / 2048); 569 prop_dictionary_set(disk_info, "geometry", geom); 570 prop_object_release(geom); 571 572 odisk_info = disk->dk_info; 573 disk->dk_info = disk_info; 574 575 if (odisk_info != NULL) 576 prop_object_release(odisk_info); 577 #endif 578 } 579