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