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