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