1 /* $NetBSD: dm_ioctl.c,v 1.22 2010/05/18 15:01:17 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 depends 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/device.h> 85 #include <sys/disk.h> 86 #include <sys/disklabel.h> 87 #include <sys/kmem.h> 88 #include <sys/malloc.h> 89 #include <sys/vnode.h> 90 91 #include <machine/int_fmtio.h> 92 93 #include "netbsd-dm.h" 94 #include "dm.h" 95 96 static uint64_t sc_minor_num; 97 extern const struct dkdriver dmdkdriver; 98 uint64_t dm_dev_counter; 99 100 /* Generic cf_data for device-mapper driver */ 101 static struct cfdata dm_cfdata = { 102 .cf_name = "dm", 103 .cf_atname = "dm", 104 .cf_fstate = FSTATE_STAR, 105 .cf_unit = 0 106 }; 107 #define DM_REMOVE_FLAG(flag, name) do { \ 108 prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \ 109 flag &= ~name; \ 110 prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \ 111 } while (/*CONSTCOND*/0) 112 113 #define DM_ADD_FLAG(flag, name) do { \ 114 prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \ 115 flag |= name; \ 116 prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \ 117 } while (/*CONSTCOND*/0) 118 119 static int dm_dbg_print_flags(int); 120 121 /* 122 * Print flags sent to the kernel from libevmapper. 123 */ 124 static int 125 dm_dbg_print_flags(int flags) 126 { 127 aprint_debug("dbg_print --- %d\n", flags); 128 129 if (flags & DM_READONLY_FLAG) 130 aprint_debug("dbg_flags: DM_READONLY_FLAG set In/Out\n"); 131 132 if (flags & DM_SUSPEND_FLAG) 133 aprint_debug("dbg_flags: DM_SUSPEND_FLAG set In/Out \n"); 134 135 if (flags & DM_PERSISTENT_DEV_FLAG) 136 aprint_debug("db_flags: DM_PERSISTENT_DEV_FLAG set In\n"); 137 138 if (flags & DM_STATUS_TABLE_FLAG) 139 aprint_debug("dbg_flags: DM_STATUS_TABLE_FLAG set In\n"); 140 141 if (flags & DM_ACTIVE_PRESENT_FLAG) 142 aprint_debug("dbg_flags: DM_ACTIVE_PRESENT_FLAG set Out\n"); 143 144 if (flags & DM_INACTIVE_PRESENT_FLAG) 145 aprint_debug("dbg_flags: DM_INACTIVE_PRESENT_FLAG set Out\n"); 146 147 if (flags & DM_BUFFER_FULL_FLAG) 148 aprint_debug("dbg_flags: DM_BUFFER_FULL_FLAG set Out\n"); 149 150 if (flags & DM_SKIP_BDGET_FLAG) 151 aprint_debug("dbg_flags: DM_SKIP_BDGET_FLAG set In\n"); 152 153 if (flags & DM_SKIP_LOCKFS_FLAG) 154 aprint_debug("dbg_flags: DM_SKIP_LOCKFS_FLAG set In\n"); 155 156 if (flags & DM_NOFLUSH_FLAG) 157 aprint_debug("dbg_flags: DM_NOFLUSH_FLAG set In\n"); 158 159 return 0; 160 } 161 /* 162 * Get version ioctl call I do it as default therefore this 163 * function is unused now. 164 */ 165 int 166 dm_get_version_ioctl(prop_dictionary_t dm_dict) 167 { 168 169 return 0; 170 } 171 /* 172 * Get list of all available targets from global 173 * target list and sent them back to libdevmapper. 174 */ 175 int 176 dm_list_versions_ioctl(prop_dictionary_t dm_dict) 177 { 178 prop_array_t target_list; 179 uint32_t flags; 180 181 flags = 0; 182 183 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 184 185 dm_dbg_print_flags(flags); 186 target_list = dm_target_prop_list(); 187 188 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, target_list); 189 prop_object_release(target_list); 190 191 return 0; 192 } 193 /* 194 * Create in-kernel entry for device. Device attributes such as name, uuid are 195 * taken from proplib dictionary. 196 * 197 */ 198 int 199 dm_dev_create_ioctl(prop_dictionary_t dm_dict) 200 { 201 dm_dev_t *dmv; 202 const char *name, *uuid; 203 int r, flags; 204 device_t devt; 205 206 r = 0; 207 flags = 0; 208 name = NULL; 209 uuid = NULL; 210 211 /* Get needed values from dictionary. */ 212 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 213 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 214 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 215 216 dm_dbg_print_flags(flags); 217 218 /* Lookup name and uuid if device already exist quit. */ 219 if ((dmv = dm_dev_lookup(name, uuid, -1)) != NULL) { 220 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); /* Device already exists */ 221 dm_dev_unbusy(dmv); 222 return EEXIST; 223 } 224 if ((devt = config_attach_pseudo(&dm_cfdata)) == NULL) { 225 aprint_error("Unable to attach pseudo device dm/%s\n", name); 226 return (ENOMEM); 227 } 228 if ((dmv = dm_dev_alloc()) == NULL) 229 return ENOMEM; 230 231 if (uuid) 232 strncpy(dmv->uuid, uuid, DM_UUID_LEN); 233 else 234 dmv->uuid[0] = '\0'; 235 236 if (name) 237 strlcpy(dmv->name, name, DM_NAME_LEN); 238 239 dmv->minor = atomic_inc_64_nv(&sc_minor_num); 240 dmv->flags = 0; /* device flags are set when needed */ 241 dmv->ref_cnt = 0; 242 dmv->event_nr = 0; 243 dmv->dev_type = 0; 244 dmv->devt = devt; 245 246 dm_table_head_init(&dmv->table_head); 247 248 mutex_init(&dmv->dev_mtx, MUTEX_DEFAULT, IPL_NONE); 249 mutex_init(&dmv->diskp_mtx, MUTEX_DEFAULT, IPL_NONE); 250 cv_init(&dmv->dev_cv, "dm_dev"); 251 252 if (flags & DM_READONLY_FLAG) 253 dmv->flags |= DM_READONLY_FLAG; 254 255 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 256 257 disk_init(dmv->diskp, dmv->name, &dmdkdriver); 258 disk_attach(dmv->diskp); 259 260 dmv->diskp->dk_info = NULL; 261 262 if ((r = dm_dev_insert(dmv)) != 0) 263 dm_dev_free(dmv); 264 265 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 266 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 267 268 /* Increment device counter After creating device */ 269 atomic_inc_64(&dm_dev_counter); 270 271 return r; 272 } 273 /* 274 * Get list of created device-mapper devices fromglobal list and 275 * send it to kernel. 276 * 277 * Output dictionary: 278 * 279 * <key>cmd_data</key> 280 * <array> 281 * <dict> 282 * <key>name<key> 283 * <string>...</string> 284 * 285 * <key>dev</key> 286 * <integer>...</integer> 287 * </dict> 288 * </array> 289 * 290 */ 291 int 292 dm_dev_list_ioctl(prop_dictionary_t dm_dict) 293 { 294 prop_array_t dev_list; 295 296 uint32_t flags; 297 298 flags = 0; 299 300 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 301 302 dm_dbg_print_flags(flags); 303 304 dev_list = dm_dev_prop_list(); 305 306 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, dev_list); 307 prop_object_release(dev_list); 308 309 return 0; 310 } 311 /* 312 * Rename selected devices old name is in struct dm_ioctl. 313 * newname is taken from dictionary 314 * 315 * <key>cmd_data</key> 316 * <array> 317 * <string>...</string> 318 * </array> 319 */ 320 int 321 dm_dev_rename_ioctl(prop_dictionary_t dm_dict) 322 { 323 prop_array_t cmd_array; 324 dm_dev_t *dmv; 325 326 const char *name, *uuid, *n_name; 327 uint32_t flags, minor; 328 329 name = NULL; 330 uuid = NULL; 331 minor = 0; 332 333 /* Get needed values from dictionary. */ 334 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 335 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 336 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 337 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 338 339 dm_dbg_print_flags(flags); 340 341 cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA); 342 343 prop_array_get_cstring_nocopy(cmd_array, 0, &n_name); 344 345 if (strlen(n_name) + 1 > DM_NAME_LEN) 346 return EINVAL; 347 348 if ((dmv = dm_dev_rem(name, uuid, minor)) == NULL) { 349 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 350 return ENOENT; 351 } 352 /* change device name */ 353 /* 354 * XXX How to deal with this change, name only used in 355 * dm_dev_routines, should I add dm_dev_change_name which will run 356 * under the dm_dev_list mutex ? 357 */ 358 strlcpy(dmv->name, n_name, DM_NAME_LEN); 359 360 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 361 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 362 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); 363 364 dm_dev_insert(dmv); 365 366 return 0; 367 } 368 /* 369 * Remove device from global list I have to remove active 370 * and inactive tables first. 371 */ 372 int 373 dm_dev_remove_ioctl(prop_dictionary_t dm_dict) 374 { 375 dm_dev_t *dmv; 376 const char *name, *uuid; 377 uint32_t flags, minor; 378 device_t devt; 379 380 flags = 0; 381 name = NULL; 382 uuid = NULL; 383 384 /* Get needed values from dictionary. */ 385 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 386 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 387 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 388 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 389 390 dm_dbg_print_flags(flags); 391 392 /* 393 * This seems as hack to me, probably use routine dm_dev_get_devt to 394 * atomicaly get devt from device. 395 */ 396 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 397 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 398 return ENOENT; 399 } 400 devt = dmv->devt; 401 402 dm_dev_unbusy(dmv); 403 404 /* 405 * This will call dm_detach routine which will actually removes 406 * device. 407 */ 408 return config_detach(devt, DETACH_QUIET); 409 } 410 /* 411 * Return actual state of device to libdevmapper. 412 */ 413 int 414 dm_dev_status_ioctl(prop_dictionary_t dm_dict) 415 { 416 dm_dev_t *dmv; 417 const char *name, *uuid; 418 uint32_t flags, j, minor; 419 420 name = NULL; 421 uuid = NULL; 422 flags = 0; 423 j = 0; 424 425 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 426 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 427 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 428 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 429 430 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 431 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 432 return ENOENT; 433 } 434 dm_dbg_print_flags(dmv->flags); 435 436 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 437 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 438 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); 439 440 if (dmv->flags & DM_SUSPEND_FLAG) 441 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG); 442 443 /* 444 * Add status flags for tables I have to check both active and 445 * inactive tables. 446 */ 447 if ((j = dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE))) { 448 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 449 } else 450 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 451 452 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_TARGET_COUNT, j); 453 454 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE)) 455 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 456 else 457 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 458 459 dm_dev_unbusy(dmv); 460 461 return 0; 462 } 463 /* 464 * Set only flag to suggest that device is suspended. This call is 465 * not supported in NetBSD. 466 * 467 */ 468 int 469 dm_dev_suspend_ioctl(prop_dictionary_t dm_dict) 470 { 471 dm_dev_t *dmv; 472 const char *name, *uuid; 473 uint32_t flags, minor; 474 475 name = NULL; 476 uuid = NULL; 477 flags = 0; 478 479 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 480 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 481 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 482 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 483 484 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 485 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 486 return ENOENT; 487 } 488 atomic_or_32(&dmv->flags, DM_SUSPEND_FLAG); 489 490 dm_dbg_print_flags(dmv->flags); 491 492 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 493 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, dmv->flags); 494 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 495 496 dm_dev_unbusy(dmv); 497 498 /* Add flags to dictionary flag after dmv -> dict copy */ 499 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 500 501 return 0; 502 } 503 /* 504 * Simulate Linux behaviour better and switch tables here and not in 505 * dm_table_load_ioctl. 506 */ 507 int 508 dm_dev_resume_ioctl(prop_dictionary_t dm_dict) 509 { 510 dm_dev_t *dmv; 511 const char *name, *uuid; 512 uint32_t flags, minor; 513 514 name = NULL; 515 uuid = NULL; 516 flags = 0; 517 518 /* 519 * char *xml; xml = prop_dictionary_externalize(dm_dict); 520 * printf("%s\n",xml); 521 */ 522 523 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 524 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 525 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 526 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 527 528 /* Remove device from global device list */ 529 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 530 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 531 return ENOENT; 532 } 533 atomic_and_32(&dmv->flags, ~(DM_SUSPEND_FLAG | DM_INACTIVE_PRESENT_FLAG)); 534 atomic_or_32(&dmv->flags, DM_ACTIVE_PRESENT_FLAG); 535 536 dm_table_switch_tables(&dmv->table_head); 537 538 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 539 540 dmgetproperties(dmv->diskp, &dmv->table_head); 541 542 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 543 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags); 544 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 545 546 dm_dev_unbusy(dmv); 547 548 /* Destroy inactive table after resume. */ 549 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 550 551 return 0; 552 } 553 /* 554 * Table management routines 555 * lvm2tools doens't send name/uuid to kernel with table 556 * for lookup I have to use minor number. 557 */ 558 559 /* 560 * Remove inactive table from device. Routines which work's with inactive tables 561 * doesn't need to synchronise with dmstrategy. They can synchronise themselves with mutex?. 562 * 563 */ 564 int 565 dm_table_clear_ioctl(prop_dictionary_t dm_dict) 566 { 567 dm_dev_t *dmv; 568 const char *name, *uuid; 569 uint32_t flags, minor; 570 571 dmv = NULL; 572 name = NULL; 573 uuid = NULL; 574 flags = 0; 575 minor = 0; 576 577 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 578 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 579 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 580 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 581 582 aprint_debug("Clearing inactive table from device: %s--%s\n", 583 name, uuid); 584 585 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 586 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 587 return ENOENT; 588 } 589 /* Select unused table */ 590 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 591 592 atomic_and_32(&dmv->flags, ~DM_INACTIVE_PRESENT_FLAG); 593 594 dm_dev_unbusy(dmv); 595 596 return 0; 597 } 598 /* 599 * Get list of physical devices for active table. 600 * Get dev_t from pdev vnode and insert it into cmd_array. 601 * 602 * XXX. This function is called from lvm2tools to get information 603 * about physical devices, too e.g. during vgcreate. 604 */ 605 int 606 dm_table_deps_ioctl(prop_dictionary_t dm_dict) 607 { 608 dm_dev_t *dmv; 609 dm_table_t *tbl; 610 dm_table_entry_t *table_en; 611 612 prop_array_t cmd_array; 613 const char *name, *uuid; 614 uint32_t flags, minor; 615 616 int table_type; 617 size_t i; 618 619 name = NULL; 620 uuid = NULL; 621 dmv = NULL; 622 flags = 0; 623 624 i = 0; 625 626 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 627 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 628 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 629 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 630 631 /* create array for dev_t's */ 632 cmd_array = prop_array_create(); 633 634 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 635 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 636 return ENOENT; 637 } 638 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 639 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_NAME, dmv->name); 640 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); 641 642 aprint_debug("Getting table deps for device: %s\n", dmv->name); 643 644 /* 645 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query 646 * INACTIVE TABLE 647 */ 648 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG) 649 table_type = DM_TABLE_INACTIVE; 650 else 651 table_type = DM_TABLE_ACTIVE; 652 653 tbl = dm_table_get_entry(&dmv->table_head, table_type); 654 655 SLIST_FOREACH(table_en, tbl, next) 656 table_en->target->deps(table_en, cmd_array); 657 658 dm_table_release(&dmv->table_head, table_type); 659 dm_dev_unbusy(dmv); 660 661 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array); 662 prop_object_release(cmd_array); 663 664 return 0; 665 } 666 /* 667 * Load new table/tables to device. 668 * Call apropriate target init routine open all physical pdev's and 669 * link them to device. For other targets mirror, strip, snapshot 670 * etc. also add dependency devices to upcalls list. 671 * 672 * Load table to inactive slot table are switched in dm_device_resume_ioctl. 673 * This simulates Linux behaviour better there should not be any difference. 674 * 675 */ 676 int 677 dm_table_load_ioctl(prop_dictionary_t dm_dict) 678 { 679 dm_dev_t *dmv; 680 dm_table_entry_t *table_en, *last_table; 681 dm_table_t *tbl; 682 dm_target_t *target; 683 684 prop_object_iterator_t iter; 685 prop_array_t cmd_array; 686 prop_dictionary_t target_dict; 687 688 const char *name, *uuid, *type; 689 690 uint32_t flags, ret, minor; 691 692 char *str; 693 694 ret = 0; 695 flags = 0; 696 name = NULL; 697 uuid = NULL; 698 dmv = NULL; 699 last_table = NULL; 700 str = NULL; 701 702 /* 703 * char *xml; xml = prop_dictionary_externalize(dm_dict); 704 * printf("%s\n",xml); 705 */ 706 707 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 708 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 709 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 710 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 711 712 cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA); 713 iter = prop_array_iterator(cmd_array); 714 dm_dbg_print_flags(flags); 715 716 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 717 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 718 return ENOENT; 719 } 720 aprint_debug("Loading table to device: %s--%d\n", name, 721 dmv->table_head.cur_active_table); 722 723 /* 724 * I have to check if this table slot is not used by another table list. 725 * if it is used I should free them. 726 */ 727 if (dmv->flags & DM_INACTIVE_PRESENT_FLAG) 728 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 729 730 dm_dbg_print_flags(dmv->flags); 731 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_INACTIVE); 732 733 aprint_debug("dmv->name = %s\n", dmv->name); 734 735 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 736 737 while ((target_dict = prop_object_iterator_next(iter)) != NULL) { 738 739 prop_dictionary_get_cstring_nocopy(target_dict, 740 DM_TABLE_TYPE, &type); 741 /* 742 * If we want to deny table with 2 or more different 743 * target we should do it here 744 */ 745 if (((target = dm_target_lookup(type)) == NULL) && 746 ((target = dm_target_autoload(type)) == NULL)) { 747 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 748 dm_dev_unbusy(dmv); 749 return ENOENT; 750 } 751 if ((table_en = kmem_alloc(sizeof(dm_table_entry_t), 752 KM_SLEEP)) == NULL) { 753 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 754 dm_dev_unbusy(dmv); 755 return ENOMEM; 756 } 757 prop_dictionary_get_uint64(target_dict, DM_TABLE_START, 758 &table_en->start); 759 prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH, 760 &table_en->length); 761 762 table_en->target = target; 763 table_en->dm_dev = dmv; 764 table_en->target_config = NULL; 765 766 /* 767 * There is a parameter string after dm_target_spec 768 * structure which points to /dev/wd0a 284 part of 769 * table. String str points to this text. This can be 770 * null and therefore it should be checked before we try to 771 * use it. 772 */ 773 prop_dictionary_get_cstring(target_dict, 774 DM_TABLE_PARAMS, (char **) &str); 775 776 if (SLIST_EMPTY(tbl)) 777 /* insert this table to head */ 778 SLIST_INSERT_HEAD(tbl, table_en, next); 779 else 780 SLIST_INSERT_AFTER(last_table, table_en, next); 781 782 /* 783 * Params string is different for every target, 784 * therfore I have to pass it to target init 785 * routine and parse parameters there. 786 */ 787 788 if ((ret = target->init(dmv, &table_en->target_config, 789 str)) != 0) { 790 791 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 792 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 793 free(str, M_TEMP); 794 795 dm_dev_unbusy(dmv); 796 return ret; 797 } 798 last_table = table_en; 799 free(str, M_TEMP); 800 } 801 prop_object_iterator_release(iter); 802 803 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 804 atomic_or_32(&dmv->flags, DM_INACTIVE_PRESENT_FLAG); 805 806 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 807 dm_dev_unbusy(dmv); 808 return 0; 809 } 810 /* 811 * Get description of all tables loaded to device from kernel 812 * and send it to libdevmapper. 813 * 814 * Output dictionary for every table: 815 * 816 * <key>cmd_data</key> 817 * <array> 818 * <dict> 819 * <key>type<key> 820 * <string>...</string> 821 * 822 * <key>start</key> 823 * <integer>...</integer> 824 * 825 * <key>length</key> 826 * <integer>...</integer> 827 * 828 * <key>params</key> 829 * <string>...</string> 830 * </dict> 831 * </array> 832 * 833 */ 834 int 835 dm_table_status_ioctl(prop_dictionary_t dm_dict) 836 { 837 dm_dev_t *dmv; 838 dm_table_t *tbl; 839 dm_table_entry_t *table_en; 840 841 prop_array_t cmd_array; 842 prop_dictionary_t target_dict; 843 844 uint32_t rec_size, minor; 845 846 const char *name, *uuid; 847 char *params; 848 int flags; 849 int table_type; 850 851 dmv = NULL; 852 uuid = NULL; 853 name = NULL; 854 params = NULL; 855 flags = 0; 856 rec_size = 0; 857 858 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 859 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 860 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 861 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 862 863 cmd_array = prop_array_create(); 864 865 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 866 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 867 return ENOENT; 868 } 869 /* 870 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query 871 * INACTIVE TABLE 872 */ 873 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG) 874 table_type = DM_TABLE_INACTIVE; 875 else 876 table_type = DM_TABLE_ACTIVE; 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 aprint_debug("Status of device tables: %s--%d\n", 896 name, dmv->table_head.cur_active_table); 897 898 tbl = dm_table_get_entry(&dmv->table_head, table_type); 899 900 SLIST_FOREACH(table_en, tbl, next) { 901 target_dict = prop_dictionary_create(); 902 aprint_debug("%016" PRIu64 ", length %016" PRIu64 903 ", target %s\n", table_en->start, table_en->length, 904 table_en->target->name); 905 906 prop_dictionary_set_uint64(target_dict, DM_TABLE_START, 907 table_en->start); 908 prop_dictionary_set_uint64(target_dict, DM_TABLE_LENGTH, 909 table_en->length); 910 911 prop_dictionary_set_cstring(target_dict, DM_TABLE_TYPE, 912 table_en->target->name); 913 914 /* dm_table_get_cur_actv.table ?? */ 915 prop_dictionary_set_int32(target_dict, DM_TABLE_STAT, 916 dmv->table_head.cur_active_table); 917 918 if (flags |= DM_STATUS_TABLE_FLAG) { 919 params = table_en->target->status 920 (table_en->target_config); 921 922 if (params != NULL) { 923 prop_dictionary_set_cstring(target_dict, 924 DM_TABLE_PARAMS, params); 925 926 kmem_free(params, DM_MAX_PARAMS_SIZE); 927 } 928 } 929 prop_array_add(cmd_array, target_dict); 930 prop_object_release(target_dict); 931 } 932 933 dm_table_release(&dmv->table_head, table_type); 934 dm_dev_unbusy(dmv); 935 936 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags); 937 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array); 938 prop_object_release(cmd_array); 939 940 return 0; 941 } 942 943 944 /* 945 * For every call I have to set kernel driver version. 946 * Because I can have commands supported only in other 947 * newer/later version. This routine is called for every 948 * ioctl command. 949 */ 950 int 951 dm_check_version(prop_dictionary_t dm_dict) 952 { 953 size_t i; 954 int dm_version[3]; 955 prop_array_t ver; 956 957 ver = prop_dictionary_get(dm_dict, DM_IOCTL_VERSION); 958 959 for (i = 0; i < 3; i++) 960 prop_array_get_uint32(ver, i, &dm_version[i]); 961 962 if (DM_VERSION_MAJOR != dm_version[0] || DM_VERSION_MINOR < dm_version[1]) { 963 aprint_debug("libdevmapper/kernel version mismatch " 964 "kernel: %d.%d.%d libdevmapper: %d.%d.%d\n", 965 DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, 966 dm_version[0], dm_version[1], dm_version[2]); 967 968 return EIO; 969 } 970 prop_array_set_uint32(ver, 0, DM_VERSION_MAJOR); 971 prop_array_set_uint32(ver, 1, DM_VERSION_MINOR); 972 prop_array_set_uint32(ver, 2, DM_VERSION_PATCHLEVEL); 973 974 prop_dictionary_set(dm_dict, DM_IOCTL_VERSION, ver); 975 976 return 0; 977 } 978