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