1 /* $NetBSD: dm_ioctl.c,v 1.55 2021/08/21 22:23:33 andvar 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 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: dm_ioctl.c,v 1.55 2021/08/21 22:23:33 andvar Exp $"); 33 34 /* 35 * Locking is used to synchronise between ioctl calls and between dm_table's 36 * users. 37 * 38 * ioctl locking: 39 * Simple reference counting, to count users of device will be used routines 40 * dm_dev_busy/dm_dev_unbusy are used for that. 41 * dm_dev_lookup/dm_dev_rem call dm_dev_busy before return(caller is therefore 42 * holder of reference_counter last). 43 * 44 * ioctl routines which change/remove dm_dev parameters must wait on 45 * dm_dev::dev_cv and when last user will call dm_dev_unbusy they will wake 46 * up them. 47 * 48 * table_head locking: 49 * To access table entries dm_table_* routines must be used. 50 * 51 * dm_table_get_entry will increment table users reference 52 * counter. It will return active or inactive table depends 53 * on uint8_t argument. 54 * 55 * dm_table_release must be called for every table_entry from 56 * dm_table_get_entry. Between these two calls tables can't be switched 57 * or destroyed. 58 * 59 * dm_table_head_init initialize table_entries SLISTS and io_cv. 60 * 61 * dm_table_head_destroy destroy cv. 62 * 63 * There are two types of users for dm_table_head first type will 64 * only read list and try to do anything with it e.g. dmstrategy, 65 * dm_table_size etc. There is another user for table_head which wants 66 * to change table lists e.g. dm_dev_resume_ioctl, dm_dev_remove_ioctl, 67 * dm_table_clear_ioctl. 68 * 69 * NOTE: It is not allowed to call dm_table_destroy, dm_table_switch_tables 70 * with hold table reference counter. Table reference counter is hold 71 * after calling dm_table_get_entry routine. After calling this 72 * function user must call dm_table_release before any writer table 73 * operation. 74 * 75 * Example: dm_table_get_entry 76 * dm_table_destroy/dm_table_switch_tables 77 * This exaple will lead to deadlock situation because after dm_table_get_entry 78 * table reference counter is != 0 and dm_table_destroy have to wait on cv until 79 * reference counter is 0. 80 * 81 */ 82 83 #include <sys/types.h> 84 #include <sys/param.h> 85 #include <sys/device.h> 86 #include <sys/disk.h> 87 #include <sys/disklabel.h> 88 #include <sys/kmem.h> 89 #include <sys/malloc.h> 90 91 #include <machine/int_fmtio.h> 92 93 #include "netbsd-dm.h" 94 #include "dm.h" 95 #include "ioconf.h" 96 97 extern struct cfattach dm_ca; 98 static uint32_t sc_minor_num; 99 uint32_t dm_dev_counter; 100 101 #define DM_REMOVE_FLAG(flag, name) do { \ 102 prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \ 103 flag &= ~name; \ 104 prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \ 105 } while (/*CONSTCOND*/0) 106 107 #define DM_ADD_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 static int dm_table_deps(dm_table_entry_t *, prop_array_t); 114 static int dm_table_init(dm_target_t *, dm_table_entry_t *, char *); 115 116 /* 117 * Print flags sent to the kernel from libdevmapper. 118 */ 119 static int 120 dm_dbg_print_flags(uint32_t flags) 121 { 122 123 aprint_debug("dbg_print --- %d\n", flags); 124 125 if (flags & DM_READONLY_FLAG) 126 aprint_debug("dbg_flags: DM_READONLY_FLAG set In/Out\n"); 127 128 if (flags & DM_SUSPEND_FLAG) 129 aprint_debug("dbg_flags: DM_SUSPEND_FLAG set In/Out\n"); 130 131 if (flags & DM_PERSISTENT_DEV_FLAG) 132 aprint_debug("dbg_flags: DM_PERSISTENT_DEV_FLAG set In\n"); 133 134 if (flags & DM_STATUS_TABLE_FLAG) 135 aprint_debug("dbg_flags: DM_STATUS_TABLE_FLAG set In\n"); 136 137 if (flags & DM_ACTIVE_PRESENT_FLAG) 138 aprint_debug("dbg_flags: DM_ACTIVE_PRESENT_FLAG set Out\n"); 139 140 if (flags & DM_INACTIVE_PRESENT_FLAG) 141 aprint_debug("dbg_flags: DM_INACTIVE_PRESENT_FLAG set Out\n"); 142 143 if (flags & DM_BUFFER_FULL_FLAG) 144 aprint_debug("dbg_flags: DM_BUFFER_FULL_FLAG set Out\n"); 145 146 if (flags & DM_SKIP_BDGET_FLAG) 147 aprint_debug("dbg_flags: DM_SKIP_BDGET_FLAG set In\n"); 148 149 if (flags & DM_SKIP_LOCKFS_FLAG) 150 aprint_debug("dbg_flags: DM_SKIP_LOCKFS_FLAG set In\n"); 151 152 if (flags & DM_NOFLUSH_FLAG) 153 aprint_debug("dbg_flags: DM_NOFLUSH_FLAG set In\n"); 154 155 return 0; 156 } 157 158 /* 159 * Get list of all available targets from global 160 * target list and sent them back to libdevmapper. 161 */ 162 int 163 dm_list_versions_ioctl(prop_dictionary_t dm_dict) 164 { 165 prop_array_t target_list; 166 uint32_t flags; 167 168 flags = 0; 169 170 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 171 172 dm_dbg_print_flags(flags); 173 target_list = dm_target_prop_list(); 174 175 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, target_list); 176 prop_object_release(target_list); 177 178 return 0; 179 } 180 181 /* 182 * Create in-kernel entry for device. Device attributes such as name, uuid are 183 * taken from proplib dictionary. 184 */ 185 int 186 dm_dev_create_ioctl(prop_dictionary_t dm_dict) 187 { 188 dm_dev_t *dmv; 189 const char *name, *uuid; 190 int r; 191 uint32_t flags; 192 device_t devt; 193 cfdata_t cf; 194 195 flags = 0; 196 name = NULL; 197 uuid = NULL; 198 199 /* Get needed values from dictionary. */ 200 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name); 201 prop_dictionary_get_string(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(flags, DM_EXISTS_FLAG); /* Device already exists */ 209 dm_dev_unbusy(dmv); 210 return EEXIST; 211 } 212 cf = kmem_alloc(sizeof(*cf), KM_SLEEP); 213 cf->cf_name = dm_cd.cd_name; 214 cf->cf_atname = dm_ca.ca_name; 215 cf->cf_unit = atomic_inc_32_nv(&sc_minor_num); 216 cf->cf_fstate = FSTATE_NOTFOUND; 217 if ((devt = config_attach_pseudo(cf)) == NULL) { 218 kmem_free(cf, sizeof(*cf)); 219 aprint_error("Unable to attach pseudo device dm/%s\n", name); 220 return (ENOMEM); 221 } 222 if ((dmv = dm_dev_alloc()) == NULL) 223 return ENOMEM; 224 225 if (uuid) 226 strncpy(dmv->uuid, uuid, DM_UUID_LEN); 227 else 228 dmv->uuid[0] = '\0'; 229 230 if (name) 231 strlcpy(dmv->name, name, DM_NAME_LEN); 232 233 dmv->minor = cf->cf_unit; 234 dmv->flags = 0; /* device flags are set when needed */ 235 dmv->ref_cnt = 0; 236 dmv->event_nr = 0; 237 dmv->devt = devt; 238 239 dm_table_head_init(&dmv->table_head); 240 241 mutex_init(&dmv->dev_mtx, MUTEX_DEFAULT, IPL_NONE); 242 mutex_init(&dmv->diskp_mtx, MUTEX_DEFAULT, IPL_NONE); 243 cv_init(&dmv->dev_cv, "dm_dev"); 244 245 if (flags & DM_READONLY_FLAG) 246 dmv->flags |= DM_READONLY_FLAG; 247 248 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 249 250 disk_init(dmv->diskp, dmv->name, &dmdkdriver); 251 disk_attach(dmv->diskp); 252 253 dmv->diskp->dk_info = NULL; 254 255 if ((r = dm_dev_insert(dmv)) != 0) 256 dm_dev_free(dmv); 257 258 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 259 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 260 261 /* Increment device counter After creating device */ 262 atomic_inc_32(&dm_dev_counter); 263 264 return r; 265 } 266 267 /* 268 * Get list of created device-mapper devices from global list and 269 * send it to kernel. 270 * 271 * Output dictionary: 272 * 273 * <key>cmd_data</key> 274 * <array> 275 * <dict> 276 * <key>name<key> 277 * <string>...</string> 278 * 279 * <key>dev</key> 280 * <integer>...</integer> 281 * </dict> 282 * </array> 283 */ 284 int 285 dm_dev_list_ioctl(prop_dictionary_t dm_dict) 286 { 287 prop_array_t dev_list; 288 uint32_t flags; 289 290 flags = 0; 291 292 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 293 294 dm_dbg_print_flags(flags); 295 296 dev_list = dm_dev_prop_list(); 297 298 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, dev_list); 299 prop_object_release(dev_list); 300 301 return 0; 302 } 303 304 /* 305 * Rename selected devices old name is in struct dm_ioctl. 306 * newname is taken from dictionary 307 * 308 * <key>cmd_data</key> 309 * <array> 310 * <string>...</string> 311 * </array> 312 */ 313 int 314 dm_dev_rename_ioctl(prop_dictionary_t dm_dict) 315 { 316 prop_array_t cmd_array; 317 dm_dev_t *dmv; 318 319 const char *name, *uuid, *n_name; 320 uint32_t flags, minor; 321 322 name = NULL; 323 uuid = NULL; 324 minor = 0; 325 326 /* Get needed values from dictionary. */ 327 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name); 328 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid); 329 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 330 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 331 332 dm_dbg_print_flags(flags); 333 334 cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA); 335 336 prop_array_get_string(cmd_array, 0, &n_name); 337 338 if (strlen(n_name) + 1 > DM_NAME_LEN) 339 return EINVAL; 340 341 if ((dmv = dm_dev_rem(name, uuid, minor)) == NULL) { 342 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 343 return ENOENT; 344 } 345 /* change device name */ 346 /* 347 * XXX How to deal with this change, name only used in 348 * dm_dev_routines, should I add dm_dev_change_name which will run 349 * under the dm_dev_list mutex ? 350 */ 351 strlcpy(dmv->name, n_name, DM_NAME_LEN); 352 353 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 354 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 355 prop_dictionary_set_string(dm_dict, DM_IOCTL_UUID, dmv->uuid); 356 357 dm_dev_insert(dmv); 358 359 return 0; 360 } 361 362 /* 363 * Remove device from global list I have to remove active 364 * and inactive tables first. 365 */ 366 int 367 dm_dev_remove_ioctl(prop_dictionary_t dm_dict) 368 { 369 int error; 370 cfdata_t cf; 371 dm_dev_t *dmv; 372 const char *name, *uuid; 373 uint32_t flags, minor; 374 device_t devt; 375 376 flags = 0; 377 name = NULL; 378 uuid = NULL; 379 380 /* Get needed values from dictionary. */ 381 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name); 382 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid); 383 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 384 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 385 386 dm_dbg_print_flags(flags); 387 388 /* 389 * This seems as hack to me, probably use routine dm_dev_get_devt to 390 * atomically get devt from device. 391 */ 392 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 393 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 394 return ENOENT; 395 } 396 devt = dmv->devt; 397 398 dm_dev_unbusy(dmv); 399 400 /* 401 * This will call dm_detach routine which will actually removes 402 * device. 403 */ 404 cf = device_cfdata(devt); 405 error = config_detach(devt, DETACH_QUIET); 406 if (error == 0) 407 kmem_free(cf, sizeof(*cf)); 408 return error; 409 } 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 425 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name); 426 prop_dictionary_get_string(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_string(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 /* 465 * Set only flag to suggest that device is suspended. This call is 466 * not supported in NetBSD. 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_string(dm_dict, DM_IOCTL_NAME, &name); 480 prop_dictionary_get_string(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 /* 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_string(dm_dict, DM_IOCTL_NAME, &name); 525 prop_dictionary_get_string(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 535 /* Make inactive table active, if it exists */ 536 if (dmv->flags & DM_INACTIVE_PRESENT_FLAG) 537 dm_table_switch_tables(&dmv->table_head); 538 539 atomic_and_32(&dmv->flags, ~(DM_SUSPEND_FLAG | DM_INACTIVE_PRESENT_FLAG)); 540 atomic_or_32(&dmv->flags, DM_ACTIVE_PRESENT_FLAG); 541 542 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 543 544 dmgetproperties(dmv->diskp, &dmv->table_head); 545 546 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 547 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags); 548 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 549 550 dm_dev_unbusy(dmv); 551 552 /* Destroy inactive table after resume. */ 553 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 554 555 return 0; 556 } 557 558 /* 559 * Table management routines 560 * lvm2tools doesn't send name/uuid to kernel with table 561 * for lookup I have to use minor number. 562 */ 563 564 /* 565 * Remove inactive table from device. Routines which work's with inactive tables 566 * doesn't need to synchronise with dmstrategy. They can synchronise themselves with mutex?. 567 */ 568 int 569 dm_table_clear_ioctl(prop_dictionary_t dm_dict) 570 { 571 dm_dev_t *dmv; 572 const char *name, *uuid; 573 uint32_t flags, minor; 574 575 name = NULL; 576 uuid = NULL; 577 flags = 0; 578 minor = 0; 579 580 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name); 581 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid); 582 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 583 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 584 585 aprint_debug("Clearing inactive table from device: %s--%s\n", 586 name, uuid); 587 588 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 589 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 590 return ENOENT; 591 } 592 /* Select unused table */ 593 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 594 595 atomic_and_32(&dmv->flags, ~DM_INACTIVE_PRESENT_FLAG); 596 597 dm_dev_unbusy(dmv); 598 599 return 0; 600 } 601 602 /* 603 * Get list of physical devices for active table. 604 * Get dev_t from pdev vnode and insert it into cmd_array. 605 * 606 * XXX. This function is called from lvm2tools to get information 607 * about physical devices, too e.g. during vgcreate. 608 */ 609 int 610 dm_table_deps_ioctl(prop_dictionary_t dm_dict) 611 { 612 dm_dev_t *dmv; 613 dm_table_t *tbl; 614 dm_table_entry_t *table_en; 615 616 prop_array_t cmd_array; 617 const char *name, *uuid; 618 uint32_t flags, minor; 619 int table_type; 620 621 name = NULL; 622 uuid = NULL; 623 flags = 0; 624 625 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name); 626 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid); 627 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 628 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 629 630 /* create array for dev_t's */ 631 cmd_array = prop_array_create(); 632 633 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 634 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 635 return ENOENT; 636 } 637 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 638 prop_dictionary_set_string(dm_dict, DM_IOCTL_NAME, dmv->name); 639 prop_dictionary_set_string(dm_dict, DM_IOCTL_UUID, dmv->uuid); 640 641 aprint_debug("Getting table deps for device: %s\n", dmv->name); 642 643 /* 644 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query 645 * INACTIVE TABLE 646 */ 647 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG) 648 table_type = DM_TABLE_INACTIVE; 649 else 650 table_type = DM_TABLE_ACTIVE; 651 652 tbl = dm_table_get_entry(&dmv->table_head, table_type); 653 654 SLIST_FOREACH(table_en, tbl, next) 655 dm_table_deps(table_en, cmd_array); 656 657 dm_table_release(&dmv->table_head, table_type); 658 dm_dev_unbusy(dmv); 659 660 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array); 661 prop_object_release(cmd_array); 662 663 return 0; 664 } 665 666 static int 667 dm_table_deps(dm_table_entry_t *table_en, prop_array_t array) 668 { 669 dm_mapping_t *map; 670 int i, size; 671 uint64_t rdev, tmp; 672 673 size = prop_array_count(array); 674 675 TAILQ_FOREACH(map, &table_en->pdev_maps, next) { 676 rdev = map->data.pdev->pdev_vnode->v_rdev; 677 for (i = 0; i < size; i++) { 678 if (prop_array_get_uint64(array, i, &tmp) == true) 679 if (rdev == tmp) 680 break; /* exists */ 681 } 682 /* 683 * Ignore if the device has already been added by 684 * other tables. 685 */ 686 if (i == size) { 687 prop_array_add_uint64(array, rdev); 688 aprint_debug("%s: %d:%d\n", __func__, major(rdev), 689 minor(rdev)); 690 } 691 } 692 693 return 0; 694 } 695 696 /* 697 * Load new table/tables to device. 698 * Call appropriate target init routine to open all physical pdev's and 699 * link them to device. For other targets mirror, strip, snapshot 700 * etc. also add dependency devices to upcalls list. 701 * 702 * Load table to inactive slot table are switched in dm_device_resume_ioctl. 703 * This simulates Linux behaviour better there should not be any difference. 704 */ 705 int 706 dm_table_load_ioctl(prop_dictionary_t dm_dict) 707 { 708 dm_dev_t *dmv; 709 dm_table_entry_t *table_en, *last_table; 710 dm_table_t *tbl; 711 dm_target_t *target; 712 713 prop_object_iterator_t iter; 714 prop_array_t cmd_array; 715 prop_dictionary_t target_dict; 716 717 const char *name, *uuid, *type; 718 uint32_t flags, minor; 719 720 flags = 0; 721 name = NULL; 722 uuid = NULL; 723 last_table = NULL; 724 725 /* 726 * char *xml; xml = prop_dictionary_externalize(dm_dict); 727 * printf("%s\n",xml); 728 */ 729 730 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name); 731 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid); 732 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 733 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 734 735 cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA); 736 iter = prop_array_iterator(cmd_array); 737 dm_dbg_print_flags(flags); 738 739 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 740 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 741 prop_object_iterator_release(iter); 742 return ENOENT; 743 } 744 aprint_debug("Loading table to device: %s--%d\n", name, 745 dmv->table_head.cur_active_table); 746 747 /* 748 * I have to check if this table slot is not used by another table list. 749 * if it is used I should free them. 750 */ 751 if (dmv->flags & DM_INACTIVE_PRESENT_FLAG) 752 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 753 754 dm_dbg_print_flags(dmv->flags); 755 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_INACTIVE); 756 757 aprint_debug("dmv->name = %s\n", dmv->name); 758 759 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 760 761 while ((target_dict = prop_object_iterator_next(iter)) != NULL) { 762 int ret; 763 const char *cp; 764 char *str; 765 size_t strsz; 766 767 prop_dictionary_get_string(target_dict, 768 DM_TABLE_TYPE, &type); 769 /* 770 * If we want to deny table with 2 or more different 771 * target we should do it here 772 */ 773 if (((target = dm_target_lookup(type)) == NULL) && 774 ((target = dm_target_autoload(type)) == NULL)) { 775 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 776 dm_dev_unbusy(dmv); 777 prop_object_iterator_release(iter); 778 return ENOENT; 779 } 780 table_en = kmem_alloc(sizeof(dm_table_entry_t), KM_SLEEP); 781 prop_dictionary_get_uint64(target_dict, DM_TABLE_START, 782 &table_en->start); 783 prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH, 784 &table_en->length); 785 786 table_en->target = target; 787 table_en->dm_dev = dmv; 788 table_en->target_config = NULL; 789 TAILQ_INIT(&table_en->pdev_maps); 790 791 /* 792 * There is a parameter string after dm_target_spec 793 * structure which points to /dev/wd0a 284 part of 794 * table. String str points to this text. This can be 795 * null and therefore it should be checked before we try to 796 * use it. 797 */ 798 cp = NULL; 799 prop_dictionary_get_string(target_dict, 800 DM_TABLE_PARAMS, &cp); 801 if (cp == NULL) 802 str = NULL; 803 else 804 str = kmem_strdupsize(cp, &strsz, KM_SLEEP); 805 806 if (SLIST_EMPTY(tbl) || last_table == NULL) 807 /* insert this table to head */ 808 SLIST_INSERT_HEAD(tbl, table_en, next); 809 else 810 SLIST_INSERT_AFTER(last_table, table_en, next); 811 812 if ((ret = dm_table_init(target, table_en, str)) != 0) { 813 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 814 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 815 816 if (str != NULL) 817 kmem_free(str, strsz); 818 819 dm_dev_unbusy(dmv); 820 prop_object_iterator_release(iter); 821 return ret; 822 } 823 last_table = table_en; 824 if (str != NULL) 825 kmem_free(str, strsz); 826 } 827 prop_object_iterator_release(iter); 828 829 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 830 atomic_or_32(&dmv->flags, DM_INACTIVE_PRESENT_FLAG); 831 832 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 833 dm_dev_unbusy(dmv); 834 return 0; 835 } 836 837 static int 838 dm_table_init(dm_target_t *target, dm_table_entry_t *table_en, char *params) 839 { 840 int i, n, ret, argc; 841 char **ap, **argv; 842 843 if (params == NULL) 844 return EINVAL; 845 846 n = target->max_argc; 847 if (n) 848 aprint_debug("Max argc %d for %s target\n", n, target->name); 849 else 850 n = 32; /* large enough for most targets */ 851 852 argv = kmem_alloc(sizeof(*argv) * n, KM_SLEEP); 853 854 for (ap = argv; 855 ap < &argv[n] && (*ap = strsep(¶ms, " \t")) != NULL;) { 856 if (**ap != '\0') 857 ap++; 858 } 859 argc = ap - argv; 860 861 for (i = 0; i < argc; i++) 862 aprint_debug("DM: argv[%d] = \"%s\"\n", i, argv[i]); 863 864 KASSERT(target->init); 865 ret = target->init(table_en, argc, argv); 866 867 kmem_free(argv, sizeof(*argv) * n); 868 869 return ret; 870 } 871 872 /* 873 * Get description of all tables loaded to device from kernel 874 * and send it to libdevmapper. 875 * 876 * Output dictionary for every table: 877 * 878 * <key>cmd_data</key> 879 * <array> 880 * <dict> 881 * <key>type<key> 882 * <string>...</string> 883 * 884 * <key>start</key> 885 * <integer>...</integer> 886 * 887 * <key>length</key> 888 * <integer>...</integer> 889 * 890 * <key>params</key> 891 * <string>...</string> 892 * </dict> 893 * </array> 894 */ 895 int 896 dm_table_status_ioctl(prop_dictionary_t dm_dict) 897 { 898 dm_dev_t *dmv; 899 dm_table_t *tbl; 900 dm_table_entry_t *table_en; 901 902 prop_array_t cmd_array; 903 prop_dictionary_t target_dict; 904 905 uint32_t minor, flags; 906 const char *name, *uuid; 907 int table_type; 908 909 uuid = NULL; 910 name = NULL; 911 flags = 0; 912 913 prop_dictionary_get_string(dm_dict, DM_IOCTL_NAME, &name); 914 prop_dictionary_get_string(dm_dict, DM_IOCTL_UUID, &uuid); 915 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 916 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 917 918 cmd_array = prop_array_create(); 919 920 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 921 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 922 return ENOENT; 923 } 924 /* 925 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query 926 * INACTIVE TABLE 927 */ 928 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG) 929 table_type = DM_TABLE_INACTIVE; 930 else 931 table_type = DM_TABLE_ACTIVE; 932 933 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE)) { 934 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 935 } else { 936 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 937 938 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE)) 939 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 940 else 941 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 942 } 943 944 if (dmv->flags & DM_SUSPEND_FLAG) 945 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG); 946 947 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 948 949 aprint_debug("Status of device tables: %s--%d\n", 950 name, dmv->table_head.cur_active_table); 951 952 tbl = dm_table_get_entry(&dmv->table_head, table_type); 953 954 SLIST_FOREACH(table_en, tbl, next) { 955 char *params; 956 int is_table; 957 958 target_dict = prop_dictionary_create(); 959 aprint_debug("%016" PRIu64 ", length %016" PRIu64 960 ", target %s\n", table_en->start, table_en->length, 961 table_en->target->name); 962 963 prop_dictionary_set_uint64(target_dict, DM_TABLE_START, 964 table_en->start); 965 prop_dictionary_set_uint64(target_dict, DM_TABLE_LENGTH, 966 table_en->length); 967 968 prop_dictionary_set_string(target_dict, DM_TABLE_TYPE, 969 table_en->target->name); 970 971 /* dm_table_get_cur_actv.table ?? */ 972 prop_dictionary_set_int32(target_dict, DM_TABLE_STAT, 973 dmv->table_head.cur_active_table); 974 975 /* 976 * Explicitly clear DM_TABLE_PARAMS to prevent dmsetup(8) from 977 * printing junk when DM_TABLE_PARAMS was never initialized. 978 */ 979 prop_dictionary_set_string(target_dict, DM_TABLE_PARAMS, ""); 980 981 is_table = (flags & DM_STATUS_TABLE_FLAG) ? 1 : 0; 982 if (is_table && table_en->target->table) 983 params = table_en->target->table( 984 table_en->target_config); 985 else if (!is_table && table_en->target->info) 986 params = table_en->target->info( 987 table_en->target_config); 988 else 989 params = NULL; 990 991 if (params != NULL) { 992 prop_dictionary_set_string(target_dict, 993 DM_TABLE_PARAMS, params); 994 kmem_free(params, DM_MAX_PARAMS_SIZE); 995 } 996 997 prop_array_add(cmd_array, target_dict); 998 prop_object_release(target_dict); 999 } 1000 1001 dm_table_release(&dmv->table_head, table_type); 1002 dm_dev_unbusy(dmv); 1003 1004 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags); 1005 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array); 1006 prop_object_release(cmd_array); 1007 1008 return 0; 1009 } 1010 1011 /* 1012 * For every call I have to set kernel driver version. 1013 * Because I can have commands supported only in other 1014 * newer/later version. This routine is called for every 1015 * ioctl command. 1016 */ 1017 int 1018 dm_check_version(prop_dictionary_t dm_dict) 1019 { 1020 int i; 1021 uint32_t dm_version[3]; 1022 prop_array_t ver; 1023 1024 ver = prop_dictionary_get(dm_dict, DM_IOCTL_VERSION); 1025 1026 for (i = 0; i < 3; i++) 1027 prop_array_get_uint32(ver, i, &dm_version[i]); 1028 1029 if (DM_VERSION_MAJOR != dm_version[0] || DM_VERSION_MINOR < dm_version[1]) { 1030 aprint_debug("libdevmapper/kernel version mismatch " 1031 "kernel: %d.%d.%d libdevmapper: %d.%d.%d\n", 1032 DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, 1033 dm_version[0], dm_version[1], dm_version[2]); 1034 1035 return EIO; 1036 } 1037 prop_array_set_uint32(ver, 0, DM_VERSION_MAJOR); 1038 prop_array_set_uint32(ver, 1, DM_VERSION_MINOR); 1039 prop_array_set_uint32(ver, 2, DM_VERSION_PATCHLEVEL); 1040 1041 prop_dictionary_set(dm_dict, DM_IOCTL_VERSION, ver); 1042 1043 return 0; 1044 } 1045