1 /* $NetBSD: dm_ioctl.c,v 1.21 2010/02/25 20:48:58 jakllsch Exp $ */ 2 3 /* 4 * Copyright (c) 2010-2011 Alex Hornung <alex@alexhornung.com> 5 * Copyright (c) 2008 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Adam Hamsik. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Locking is used to synchronise between ioctl calls and between dm_table's 35 * users. 36 * 37 * ioctl locking: 38 * Simple reference counting, to count users of device will be used routines 39 * dm_dev_busy/dm_dev_unbusy are used for that. 40 * dm_dev_lookup/dm_dev_rem call dm_dev_busy before return(caller is therefore 41 * holder of reference_counter last). 42 * 43 * ioctl routines which change/remove dm_dev parameters must wait on 44 * dm_dev::dev_cv and when last user will call dm_dev_unbusy they will wake 45 * up them. 46 * 47 * table_head locking: 48 * To access table entries dm_table_* routines must be used. 49 * 50 * dm_table_get_entry will increment table users reference 51 * counter. It will return active or inactive table depends 52 * on uint8_t argument. 53 * 54 * dm_table_release must be called for every table_entry from 55 * dm_table_get_entry. Between these to calls tables can'tbe switched 56 * or destroyed. 57 * 58 * dm_table_head_init initialize talbe_entries TAILQS and io_cv. 59 * 60 * dm_table_head_destroy destroy cv. 61 * 62 * There are two types of users for dm_table_head first type will 63 * only read list and try to do anything with it e.g. dmstrategy, 64 * dm_table_size etc. There is another user for table_head which wants 65 * to change table lists e.g. dm_dev_resume_ioctl, dm_dev_remove_ioctl, 66 * dm_table_clear_ioctl. 67 * 68 * NOTE: It is not allowed to call dm_table_destroy, dm_table_switch_tables 69 * with hold table reference counter. Table reference counter is hold 70 * after calling dm_table_get_entry routine. After calling this 71 * function user must call dm_table_release before any writer table 72 * operation. 73 * 74 * Example: dm_table_get_entry 75 * dm_table_destroy/dm_table_switch_tables 76 * This exaple will lead to deadlock situation because after dm_table_get_entry 77 * table reference counter is != 0 and dm_table_destroy have to wait on cv until 78 * reference counter is 0. 79 * 80 */ 81 82 #include <sys/types.h> 83 #include <sys/device.h> 84 #include <sys/malloc.h> 85 #include <sys/vnode.h> 86 #include <dev/disk/dm/dm.h> 87 88 #include "netbsd-dm.h" 89 90 #define DM_REMOVE_FLAG(flag, name) do { \ 91 prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \ 92 flag &= ~name; \ 93 prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \ 94 } while (/*CONSTCOND*/0) 95 96 #define DM_ADD_FLAG(flag, name) do { \ 97 prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \ 98 flag |= name; \ 99 prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \ 100 } while (/*CONSTCOND*/0) 101 102 static int 103 dm_table_deps(dm_table_entry_t *, prop_array_t); 104 static int 105 dm_table_init(dm_target_t *, dm_table_entry_t *, char *); 106 107 /* 108 * Print flags sent to the kernel from libevmapper. 109 */ 110 static int 111 dm_dbg_print_flags(int flags) 112 { 113 aprint_debug("dbg_print --- %d\n", flags); 114 115 if (flags & DM_READONLY_FLAG) 116 aprint_debug("dbg_flags: DM_READONLY_FLAG set In/Out\n"); 117 118 if (flags & DM_SUSPEND_FLAG) 119 aprint_debug("dbg_flags: DM_SUSPEND_FLAG set In/Out\n"); 120 121 if (flags & DM_PERSISTENT_DEV_FLAG) 122 aprint_debug("dbg_flags: DM_PERSISTENT_DEV_FLAG set In\n"); 123 124 if (flags & DM_STATUS_TABLE_FLAG) 125 aprint_debug("dbg_flags: DM_STATUS_TABLE_FLAG set In\n"); 126 127 if (flags & DM_ACTIVE_PRESENT_FLAG) 128 aprint_debug("dbg_flags: DM_ACTIVE_PRESENT_FLAG set Out\n"); 129 130 if (flags & DM_INACTIVE_PRESENT_FLAG) 131 aprint_debug("dbg_flags: DM_INACTIVE_PRESENT_FLAG set Out\n"); 132 133 if (flags & DM_BUFFER_FULL_FLAG) 134 aprint_debug("dbg_flags: DM_BUFFER_FULL_FLAG set Out\n"); 135 136 if (flags & DM_SKIP_BDGET_FLAG) 137 aprint_debug("dbg_flags: DM_SKIP_BDGET_FLAG set In\n"); 138 139 if (flags & DM_SKIP_LOCKFS_FLAG) 140 aprint_debug("dbg_flags: DM_SKIP_LOCKFS_FLAG set In\n"); 141 142 if (flags & DM_NOFLUSH_FLAG) 143 aprint_debug("dbg_flags: DM_NOFLUSH_FLAG set In\n"); 144 145 return 0; 146 } 147 /* 148 * Get list of all available targets from global 149 * target list and sent them back to libdevmapper. 150 */ 151 int 152 dm_list_versions_ioctl(prop_dictionary_t dm_dict) 153 { 154 prop_array_t target_list; 155 uint32_t flags; 156 157 flags = 0; 158 159 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 160 161 dm_dbg_print_flags(flags); 162 target_list = dm_target_prop_list(); 163 164 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, target_list); 165 prop_object_release(target_list); 166 167 return 0; 168 } 169 /* 170 * Create in-kernel entry for device. Device attributes such as name, uuid are 171 * taken from proplib dictionary. 172 * 173 */ 174 int 175 dm_dev_create_ioctl(prop_dictionary_t dm_dict) 176 { 177 dm_dev_t *dmv; 178 const char *name, *uuid; 179 int r, flags; 180 181 r = 0; 182 flags = 0; 183 name = NULL; 184 uuid = NULL; 185 186 /* Get needed values from dictionary. */ 187 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 188 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 189 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 190 191 dm_dbg_print_flags(flags); 192 193 /* Lookup name and uuid if device already exist quit. */ 194 if ((dmv = dm_dev_lookup(name, uuid, -1)) != NULL) { 195 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); /* Device already exists */ 196 dm_dev_unbusy(dmv); 197 return EEXIST; 198 } 199 200 r = dm_dev_create(&dmv, name, uuid, flags); 201 if (r == 0) { 202 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 203 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 204 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 205 } 206 207 return r; 208 } 209 /* 210 * Get list of created device-mapper devices fromglobal list and 211 * send it to kernel. 212 * 213 * Output dictionary: 214 * 215 * <key>cmd_data</key> 216 * <array> 217 * <dict> 218 * <key>name<key> 219 * <string>...</string> 220 * 221 * <key>dev</key> 222 * <integer>...</integer> 223 * </dict> 224 * </array> 225 * 226 */ 227 int 228 dm_dev_list_ioctl(prop_dictionary_t dm_dict) 229 { 230 prop_array_t dev_list; 231 232 uint32_t flags; 233 234 flags = 0; 235 236 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 237 238 dm_dbg_print_flags(flags); 239 240 dev_list = dm_dev_prop_list(); 241 242 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, dev_list); 243 prop_object_release(dev_list); 244 245 return 0; 246 } 247 /* 248 * Rename selected devices old name is in struct dm_ioctl. 249 * newname is taken from dictionary 250 * 251 * <key>cmd_data</key> 252 * <array> 253 * <string>...</string> 254 * </array> 255 */ 256 int 257 dm_dev_rename_ioctl(prop_dictionary_t dm_dict) 258 { 259 #if 0 260 prop_array_t cmd_array; 261 dm_dev_t *dmv; 262 263 const char *name, *uuid, *n_name; 264 uint32_t flags, minor; 265 266 name = NULL; 267 uuid = NULL; 268 minor = 0; 269 270 /* Get needed values from dictionary. */ 271 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 272 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 273 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 274 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 275 276 dm_dbg_print_flags(flags); 277 278 cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA); 279 280 prop_array_get_cstring_nocopy(cmd_array, 0, &n_name); 281 282 if (strlen(n_name) + 1 > DM_NAME_LEN) 283 return EINVAL; 284 285 if ((dmv = dm_dev_rem(name, uuid, minor)) == NULL) { 286 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 287 return ENOENT; 288 } 289 /* change device name */ 290 /* 291 * XXX How to deal with this change, name only used in 292 * dm_dev_routines, should I add dm_dev_change_name which will run 293 * under the dm_dev_list mutex ? 294 */ 295 strlcpy(dmv->name, n_name, DM_NAME_LEN); 296 297 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 298 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 299 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); 300 301 dm_dev_insert(dmv); 302 #endif 303 304 /* 305 * XXX: the rename is not yet implemented. The main complication 306 * here is devfs. We'd probably need a new function, rename_dev() 307 * that would trigger a node rename in devfs. 308 */ 309 kprintf("dm_dev_rename_ioctl called, but not implemented!\n"); 310 return ENOSYS; 311 } 312 313 /* 314 * Remove device 315 */ 316 int 317 dm_dev_remove_ioctl(prop_dictionary_t dm_dict) 318 { 319 dm_dev_t *dmv; 320 const char *name, *uuid; 321 uint32_t flags, minor, is_open; 322 323 flags = 0; 324 name = NULL; 325 uuid = NULL; 326 327 /* Get needed values from dictionary. */ 328 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 329 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 330 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 331 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 332 333 dm_dbg_print_flags(flags); 334 335 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 336 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 337 return ENOENT; 338 } 339 340 is_open = dmv->is_open; 341 342 dm_dev_unbusy(dmv); 343 344 if (is_open) 345 return EBUSY; 346 347 /* 348 * This will call dm_dev_rem_dev routine which will actually remove 349 * device. 350 */ 351 return dm_dev_remove(dmv); 352 } 353 354 /* 355 * Try to remove all devices 356 */ 357 int 358 dm_dev_remove_all_ioctl(prop_dictionary_t dm_dict) 359 { 360 uint32_t flags = 0; 361 362 /* Get needed values from dictionary. */ 363 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 364 365 dm_dbg_print_flags(flags); 366 367 /* Gently remove all devices, if possible */ 368 return dm_dev_remove_all(1); 369 } 370 371 /* 372 * Return actual state of device to libdevmapper. 373 */ 374 int 375 dm_dev_status_ioctl(prop_dictionary_t dm_dict) 376 { 377 dm_dev_t *dmv; 378 const char *name, *uuid; 379 uint32_t flags, j, minor; 380 381 name = NULL; 382 uuid = NULL; 383 flags = 0; 384 j = 0; 385 386 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 387 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 388 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 389 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 390 391 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 392 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 393 return ENOENT; 394 } 395 dm_dbg_print_flags(dmv->flags); 396 397 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 398 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 399 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); 400 401 if (dmv->flags & DM_SUSPEND_FLAG) 402 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG); 403 404 /* 405 * Add status flags for tables I have to check both active and 406 * inactive tables. 407 */ 408 if ((j = dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE))) { 409 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 410 } else 411 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 412 413 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_TARGET_COUNT, j); 414 415 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE)) 416 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 417 else 418 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 419 420 dm_dev_unbusy(dmv); 421 422 return 0; 423 } 424 /* 425 * Set only flag to suggest that device is suspended. This call is 426 * not supported in NetBSD. 427 * 428 */ 429 int 430 dm_dev_suspend_ioctl(prop_dictionary_t dm_dict) 431 { 432 dm_dev_t *dmv; 433 const char *name, *uuid; 434 uint32_t flags, minor; 435 436 name = NULL; 437 uuid = NULL; 438 flags = 0; 439 440 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 441 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 442 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 443 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 444 445 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 446 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 447 return ENOENT; 448 } 449 atomic_set_int(&dmv->flags, DM_SUSPEND_FLAG); 450 451 dm_dbg_print_flags(dmv->flags); 452 453 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 454 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, dmv->flags); 455 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 456 457 dm_dev_unbusy(dmv); 458 459 /* Add flags to dictionary flag after dmv -> dict copy */ 460 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 461 462 return 0; 463 } 464 /* 465 * Simulate Linux behaviour better and switch tables here and not in 466 * dm_table_load_ioctl. 467 */ 468 int 469 dm_dev_resume_ioctl(prop_dictionary_t dm_dict) 470 { 471 dm_dev_t *dmv; 472 const char *name, *uuid; 473 uint32_t flags, minor; 474 475 name = NULL; 476 uuid = NULL; 477 flags = 0; 478 479 /* 480 * char *xml; xml = prop_dictionary_externalize(dm_dict); 481 * kprintf("%s\n",xml); 482 */ 483 484 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 485 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 486 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 487 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 488 489 /* Remove device from global device list */ 490 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 491 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 492 return ENOENT; 493 } 494 atomic_clear_int(&dmv->flags, (DM_SUSPEND_FLAG | DM_INACTIVE_PRESENT_FLAG)); 495 atomic_set_int(&dmv->flags, DM_ACTIVE_PRESENT_FLAG); 496 497 dm_table_switch_tables(&dmv->table_head); 498 499 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 500 501 dmsetdiskinfo(dmv->diskp, &dmv->table_head); 502 503 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 504 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags); 505 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 506 507 dm_dev_unbusy(dmv); 508 509 /* Destroy inactive table after resume. */ 510 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 511 512 return 0; 513 } 514 /* 515 * Table management routines 516 * lvm2tools doens't send name/uuid to kernel with table 517 * for lookup I have to use minor number. 518 */ 519 520 /* 521 * Remove inactive table from device. Routines which work's with inactive tables 522 * doesn't need to synchronise with dmstrategy. They can synchronise themselves with mutex?. 523 * 524 */ 525 int 526 dm_table_clear_ioctl(prop_dictionary_t dm_dict) 527 { 528 dm_dev_t *dmv; 529 const char *name, *uuid; 530 uint32_t flags, minor; 531 532 dmv = NULL; 533 name = NULL; 534 uuid = NULL; 535 flags = 0; 536 minor = 0; 537 538 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 539 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 540 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 541 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 542 543 aprint_debug("Clearing inactive table from device: %s--%s\n", 544 name, uuid); 545 546 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 547 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 548 return ENOENT; 549 } 550 /* Select unused table */ 551 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 552 553 atomic_clear_int(&dmv->flags, DM_INACTIVE_PRESENT_FLAG); 554 555 dm_dev_unbusy(dmv); 556 557 return 0; 558 } 559 /* 560 * Get list of physical devices for active table. 561 * Get dev_t from pdev vnode and insert it into cmd_array. 562 * 563 * XXX. This function is called from lvm2tools to get information 564 * about physical devices, too e.g. during vgcreate. 565 */ 566 int 567 dm_table_deps_ioctl(prop_dictionary_t dm_dict) 568 { 569 dm_dev_t *dmv; 570 dm_table_t *tbl; 571 dm_table_entry_t *table_en; 572 573 prop_array_t cmd_array; 574 const char *name, *uuid; 575 uint32_t flags, minor; 576 577 int table_type; 578 579 name = NULL; 580 uuid = NULL; 581 dmv = NULL; 582 flags = 0; 583 584 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 585 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 586 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 587 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 588 589 /* create array for dev_t's */ 590 cmd_array = prop_array_create(); 591 592 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 593 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 594 return ENOENT; 595 } 596 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 597 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_NAME, dmv->name); 598 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); 599 600 aprint_debug("Getting table deps for device: %s\n", dmv->name); 601 602 /* 603 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query 604 * INACTIVE TABLE 605 */ 606 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG) 607 table_type = DM_TABLE_INACTIVE; 608 else 609 table_type = DM_TABLE_ACTIVE; 610 611 tbl = dm_table_get_entry(&dmv->table_head, table_type); 612 613 TAILQ_FOREACH(table_en, tbl, next) 614 dm_table_deps(table_en, cmd_array); 615 616 dm_table_release(&dmv->table_head, table_type); 617 dm_dev_unbusy(dmv); 618 619 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array); 620 prop_object_release(cmd_array); 621 622 return 0; 623 } 624 625 static int 626 dm_table_deps(dm_table_entry_t *table_en, prop_array_t array) 627 { 628 dm_mapping_t *map; 629 int i, size; 630 uint64_t ret, tmp; 631 632 size = prop_array_count(array); 633 634 TAILQ_FOREACH(map, &table_en->pdev_maps, next) { 635 ret = map->data.pdev->udev; 636 for (i = 0; i < size; i++) { 637 if (prop_array_get_uint64(array, i, &tmp) == true) 638 if (ret == tmp) 639 break; /* exists */ 640 } 641 /* 642 * Ignore if the device has already been added by 643 * other tables. 644 */ 645 if (i == size) 646 prop_array_add_uint64(array, ret); 647 } 648 649 return 0; 650 } 651 652 /* 653 * Load new table/tables to device. 654 * Call apropriate target init routine open all physical pdev's and 655 * link them to device. For other targets mirror, strip, snapshot 656 * etc. also add dependency devices to upcalls list. 657 * 658 * Load table to inactive slot table are switched in dm_device_resume_ioctl. 659 * This simulates Linux behaviour better there should not be any difference. 660 * 661 */ 662 int 663 dm_table_load_ioctl(prop_dictionary_t dm_dict) 664 { 665 dm_dev_t *dmv; 666 dm_table_entry_t *table_en; 667 dm_table_t *tbl; 668 dm_target_t *target; 669 670 prop_object_iterator_t iter; 671 prop_array_t cmd_array; 672 prop_dictionary_t target_dict; 673 674 const char *name, *uuid, *type; 675 676 uint32_t flags, ret, minor; 677 678 char *str; 679 680 ret = 0; 681 flags = 0; 682 name = NULL; 683 uuid = NULL; 684 dmv = NULL; 685 str = NULL; 686 687 /* 688 * char *xml; xml = prop_dictionary_externalize(dm_dict); 689 * kprintf("%s\n",xml); 690 */ 691 692 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 693 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 694 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 695 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 696 697 cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA); 698 iter = prop_array_iterator(cmd_array); 699 dm_dbg_print_flags(flags); 700 701 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 702 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 703 return ENOENT; 704 } 705 aprint_debug("Loading table to device: %s--%d\n", name, 706 dmv->table_head.cur_active_table); 707 708 /* 709 * I have to check if this table slot is not used by another table list. 710 * if it is used I should free them. 711 */ 712 if (dmv->flags & DM_INACTIVE_PRESENT_FLAG) 713 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 714 715 dm_dbg_print_flags(dmv->flags); 716 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_INACTIVE); 717 718 aprint_debug("dmv->name = %s\n", dmv->name); 719 720 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 721 722 while ((target_dict = prop_object_iterator_next(iter)) != NULL) { 723 prop_dictionary_get_cstring_nocopy(target_dict, 724 DM_TABLE_TYPE, &type); 725 /* 726 * If we want to deny table with 2 or more different 727 * target we should do it here 728 */ 729 if (((target = dm_target_lookup(type)) == NULL) && 730 ((target = dm_target_autoload(type)) == NULL)) { 731 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 732 dm_dev_unbusy(dmv); 733 return ENOENT; 734 } 735 if ((table_en = kmalloc(sizeof(dm_table_entry_t), 736 M_DM, M_WAITOK)) == NULL) { 737 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 738 dm_dev_unbusy(dmv); 739 dm_target_unbusy(target); 740 return ENOMEM; 741 } 742 prop_dictionary_get_uint64(target_dict, DM_TABLE_START, 743 &table_en->start); 744 prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH, 745 &table_en->length); 746 747 aprint_debug("dm_ioctl.c... table_en->start = %ju, " 748 "table_en->length = %ju\n", 749 (uintmax_t)table_en->start, 750 (uintmax_t)table_en->length); 751 752 table_en->target = target; 753 table_en->dev = dmv; 754 table_en->target_config = NULL; 755 TAILQ_INIT(&table_en->pdev_maps); 756 757 /* 758 * There is a parameter string after dm_target_spec 759 * structure which points to /dev/wd0a 284 part of 760 * table. String str points to this text. This can be 761 * null and therefore it should be checked before we try to 762 * use it. 763 */ 764 prop_dictionary_get_cstring(target_dict, 765 DM_TABLE_PARAMS, &str); 766 767 TAILQ_INSERT_TAIL(tbl, table_en, next); 768 769 /* 770 * Params string is different for every target, 771 * therfore I have to pass it to target init 772 * routine and parse parameters there. 773 */ 774 aprint_debug("DM: str passed in is: \"%s\"\n", str); 775 776 if ((ret = dm_table_init(target, table_en, str)) != 0) { 777 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 778 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 779 kfree(str, M_TEMP); 780 781 dm_dev_unbusy(dmv); 782 return ret; 783 } 784 kfree(str, M_TEMP); 785 } 786 prop_object_iterator_release(iter); 787 788 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 789 atomic_set_int(&dmv->flags, DM_INACTIVE_PRESENT_FLAG); 790 791 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 792 793 dm_dev_unbusy(dmv); 794 #if 0 795 dmsetdiskinfo(dmv->diskp, &dmv->table_head); 796 #endif 797 return 0; 798 } 799 800 static int 801 dm_table_init(dm_target_t *target, dm_table_entry_t *table_en, char *params) 802 { 803 int i, n, ret, argc; 804 char **ap, **argv; 805 806 if (params == NULL) 807 return EINVAL; 808 809 n = target->max_argc; 810 if (n) { 811 aprint_debug("Max argc %d for %s target\n", n, target->name); 812 } else { 813 n = 20; /* large enough slots for most targets */ 814 } 815 816 argv = kmalloc(sizeof(*argv) * n, M_DM, M_WAITOK | M_ZERO); 817 818 for (ap = argv; 819 ap < &argv[n] && (*ap = strsep(¶ms, " \t")) != NULL;) { 820 if (**ap != '\0') 821 ap++; 822 } 823 argc = ap - argv; 824 825 if (dm_debug_level) { 826 for (i = 0; i < argc; i++) 827 kprintf("DM: argv[%d] = \"%s\"\n", i, argv[i]); 828 } 829 830 KKASSERT(target->init); 831 ret = target->init(table_en, argc, argv); 832 833 kfree(argv, M_DM); 834 835 return ret; 836 } 837 838 /* 839 * Get description of all tables loaded to device from kernel 840 * and send it to libdevmapper. 841 * 842 * Output dictionary for every table: 843 * 844 * <key>cmd_data</key> 845 * <array> 846 * <dict> 847 * <key>type<key> 848 * <string>...</string> 849 * 850 * <key>start</key> 851 * <integer>...</integer> 852 * 853 * <key>length</key> 854 * <integer>...</integer> 855 * 856 * <key>params</key> 857 * <string>...</string> 858 * </dict> 859 * </array> 860 * 861 */ 862 int 863 dm_table_status_ioctl(prop_dictionary_t dm_dict) 864 { 865 dm_dev_t *dmv; 866 dm_table_t *tbl; 867 dm_table_entry_t *table_en; 868 869 prop_array_t cmd_array; 870 prop_dictionary_t target_dict; 871 872 uint32_t minor; 873 874 const char *name, *uuid; 875 char *params; 876 int flags; 877 int table_type; 878 879 dmv = NULL; 880 uuid = NULL; 881 name = NULL; 882 params = NULL; 883 flags = 0; 884 885 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 886 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 887 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 888 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 889 890 cmd_array = prop_array_create(); 891 892 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 893 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 894 return ENOENT; 895 } 896 /* 897 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query 898 * INACTIVE TABLE 899 */ 900 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG) 901 table_type = DM_TABLE_INACTIVE; 902 else 903 table_type = DM_TABLE_ACTIVE; 904 905 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE)) 906 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 907 else { 908 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 909 910 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE)) 911 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 912 else { 913 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 914 } 915 } 916 917 if (dmv->flags & DM_SUSPEND_FLAG) 918 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG); 919 920 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 921 922 aprint_debug("Status of device tables: %s--%d\n", 923 name, dmv->table_head.cur_active_table); 924 925 tbl = dm_table_get_entry(&dmv->table_head, table_type); 926 927 TAILQ_FOREACH(table_en, tbl, next) { 928 target_dict = prop_dictionary_create(); 929 aprint_debug("%016" PRIu64 ", length %016" PRIu64 930 ", target %s\n", table_en->start, table_en->length, 931 table_en->target->name); 932 933 prop_dictionary_set_uint64(target_dict, DM_TABLE_START, 934 table_en->start); 935 prop_dictionary_set_uint64(target_dict, DM_TABLE_LENGTH, 936 table_en->length); 937 938 prop_dictionary_set_cstring(target_dict, DM_TABLE_TYPE, 939 table_en->target->name); 940 941 /* dm_table_get_cur_actv.table ?? */ 942 prop_dictionary_set_int32(target_dict, DM_TABLE_STAT, 943 dmv->table_head.cur_active_table); 944 945 if (flags & DM_STATUS_TABLE_FLAG) { 946 params = table_en->target->table 947 (table_en->target_config); 948 } else if (table_en->target->info) { 949 params = table_en->target->info 950 (table_en->target_config); 951 } else { 952 params = NULL; 953 } 954 955 if (params != NULL) { 956 prop_dictionary_set_cstring(target_dict, 957 DM_TABLE_PARAMS, params); 958 kfree(params, M_DM); 959 } else { 960 prop_dictionary_set_cstring(target_dict, 961 DM_TABLE_PARAMS, ""); 962 } 963 964 prop_array_add(cmd_array, target_dict); 965 prop_object_release(target_dict); 966 } 967 968 dm_table_release(&dmv->table_head, table_type); 969 dm_dev_unbusy(dmv); 970 971 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags); 972 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array); 973 prop_object_release(cmd_array); 974 975 return 0; 976 } 977 978 int 979 dm_message_ioctl(prop_dictionary_t dm_dict) 980 { 981 dm_table_t *tbl; 982 dm_table_entry_t *table_en; 983 dm_dev_t *dmv; 984 const char *name, *uuid; 985 uint32_t flags, minor; 986 uint64_t table_start, table_end, sector; 987 char *msg; 988 int ret, found = 0; 989 990 flags = 0; 991 name = NULL; 992 uuid = NULL; 993 994 /* Get needed values from dictionary. */ 995 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 996 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 997 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 998 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 999 prop_dictionary_get_uint64(dm_dict, DM_MESSAGE_SECTOR, §or); 1000 1001 dm_dbg_print_flags(flags); 1002 1003 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 1004 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 1005 return ENOENT; 1006 } 1007 1008 /* Get message string */ 1009 prop_dictionary_get_cstring(dm_dict, DM_MESSAGE_STR, &msg); 1010 1011 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_ACTIVE); 1012 1013 ret = EINVAL; 1014 1015 if (sector == 0) { 1016 if (!TAILQ_EMPTY(tbl)) { 1017 table_en = TAILQ_FIRST(tbl); 1018 found = 1; 1019 } 1020 } else { 1021 TAILQ_FOREACH(table_en, tbl, next) { 1022 table_start = table_en->start; 1023 table_end = table_start + table_en->length; 1024 1025 if ((sector >= table_start) && (sector < table_end)) { 1026 found = 1; 1027 break; 1028 } 1029 } 1030 } 1031 1032 if (found) { 1033 if (table_en->target->message != NULL) 1034 ret = table_en->target->message(table_en, msg); 1035 } 1036 1037 dm_table_release(&dmv->table_head, DM_TABLE_ACTIVE); 1038 1039 1040 kfree(msg, M_TEMP); 1041 dm_dev_unbusy(dmv); 1042 1043 return ret; 1044 } 1045 1046 /* 1047 * For every call I have to set kernel driver version. 1048 * Because I can have commands supported only in other 1049 * newer/later version. This routine is called for every 1050 * ioctl command. 1051 */ 1052 int 1053 dm_check_version(prop_dictionary_t dm_dict) 1054 { 1055 size_t i; 1056 int dm_version[3]; 1057 prop_array_t ver; 1058 1059 ver = prop_dictionary_get(dm_dict, DM_IOCTL_VERSION); 1060 1061 for (i = 0; i < 3; i++) 1062 prop_array_get_uint32(ver, i, &dm_version[i]); 1063 1064 if (DM_VERSION_MAJOR != dm_version[0] || DM_VERSION_MINOR < dm_version[1]) { 1065 aprint_debug("libdevmapper/kernel version mismatch " 1066 "kernel: %d.%d.%d libdevmapper: %d.%d.%d\n", 1067 DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, 1068 dm_version[0], dm_version[1], dm_version[2]); 1069 1070 return EIO; 1071 } 1072 prop_array_set_uint32(ver, 0, DM_VERSION_MAJOR); 1073 prop_array_set_uint32(ver, 1, DM_VERSION_MINOR); 1074 prop_array_set_uint32(ver, 2, DM_VERSION_PATCHLEVEL); 1075 1076 prop_dictionary_set(dm_dict, DM_IOCTL_VERSION, ver); 1077 1078 return 0; 1079 } 1080