1 /* $NetBSD: dm_ioctl.c,v 1.41 2019/12/12 16:28:24 tkusumi 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.41 2019/12/12 16:28:24 tkusumi 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 to calls tables can'tbe switched 57 * or destroyed. 58 * 59 * dm_table_head_init initialize talbe_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 86 #include <sys/device.h> 87 #include <sys/disk.h> 88 #include <sys/disklabel.h> 89 #include <sys/kmem.h> 90 #include <sys/malloc.h> 91 92 #include <machine/int_fmtio.h> 93 94 #include "netbsd-dm.h" 95 #include "dm.h" 96 97 static uint32_t sc_minor_num; 98 extern const struct dkdriver dmdkdriver; 99 uint32_t dm_dev_counter; 100 101 /* Generic cf_data for device-mapper driver */ 102 static struct cfdata dm_cfdata = { 103 .cf_name = "dm", 104 .cf_atname = "dm", 105 .cf_fstate = FSTATE_STAR, 106 .cf_unit = 0 107 }; 108 #define DM_REMOVE_FLAG(flag, name) do { \ 109 prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \ 110 flag &= ~name; \ 111 prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \ 112 } while (/*CONSTCOND*/0) 113 114 #define DM_ADD_FLAG(flag, name) do { \ 115 prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&flag); \ 116 flag |= name; \ 117 prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flag); \ 118 } while (/*CONSTCOND*/0) 119 120 static int dm_dbg_print_flags(int); 121 static int dm_table_init(dm_target_t *, dm_table_entry_t *, char *); 122 123 /* 124 * Print flags sent to the kernel from libevmapper. 125 */ 126 static int 127 dm_dbg_print_flags(int flags) 128 { 129 aprint_debug("dbg_print --- %d\n", flags); 130 131 if (flags & DM_READONLY_FLAG) 132 aprint_debug("dbg_flags: DM_READONLY_FLAG set In/Out\n"); 133 134 if (flags & DM_SUSPEND_FLAG) 135 aprint_debug("dbg_flags: DM_SUSPEND_FLAG set In/Out\n"); 136 137 if (flags & DM_PERSISTENT_DEV_FLAG) 138 aprint_debug("dbg_flags: DM_PERSISTENT_DEV_FLAG set In\n"); 139 140 if (flags & DM_STATUS_TABLE_FLAG) 141 aprint_debug("dbg_flags: DM_STATUS_TABLE_FLAG set In\n"); 142 143 if (flags & DM_ACTIVE_PRESENT_FLAG) 144 aprint_debug("dbg_flags: DM_ACTIVE_PRESENT_FLAG set Out\n"); 145 146 if (flags & DM_INACTIVE_PRESENT_FLAG) 147 aprint_debug("dbg_flags: DM_INACTIVE_PRESENT_FLAG set Out\n"); 148 149 if (flags & DM_BUFFER_FULL_FLAG) 150 aprint_debug("dbg_flags: DM_BUFFER_FULL_FLAG set Out\n"); 151 152 if (flags & DM_SKIP_BDGET_FLAG) 153 aprint_debug("dbg_flags: DM_SKIP_BDGET_FLAG set In\n"); 154 155 if (flags & DM_SKIP_LOCKFS_FLAG) 156 aprint_debug("dbg_flags: DM_SKIP_LOCKFS_FLAG set In\n"); 157 158 if (flags & DM_NOFLUSH_FLAG) 159 aprint_debug("dbg_flags: DM_NOFLUSH_FLAG set In\n"); 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 target_list = dm_target_prop_list(); 180 181 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, target_list); 182 prop_object_release(target_list); 183 184 return 0; 185 } 186 187 /* 188 * Create in-kernel entry for device. Device attributes such as name, uuid are 189 * taken from proplib dictionary. 190 * 191 */ 192 int 193 dm_dev_create_ioctl(prop_dictionary_t dm_dict) 194 { 195 dm_dev_t *dmv; 196 const char *name, *uuid; 197 int r; 198 uint32_t flags; 199 device_t devt; 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 if ((devt = config_attach_pseudo(&dm_cfdata)) == NULL) { 220 aprint_error("Unable to attach pseudo device dm/%s\n", name); 221 return (ENOMEM); 222 } 223 if ((dmv = dm_dev_alloc()) == 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 = (uint64_t)atomic_inc_32_nv(&sc_minor_num); 235 dmv->flags = 0; /* device flags are set when needed */ 236 dmv->ref_cnt = 0; 237 dmv->event_nr = 0; 238 dmv->devt = devt; 239 240 dm_table_head_init(&dmv->table_head); 241 242 mutex_init(&dmv->dev_mtx, MUTEX_DEFAULT, IPL_NONE); 243 mutex_init(&dmv->diskp_mtx, MUTEX_DEFAULT, IPL_NONE); 244 cv_init(&dmv->dev_cv, "dm_dev"); 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 dmv->diskp->dk_info = NULL; 255 256 if ((r = dm_dev_insert(dmv)) != 0) 257 dm_dev_free(dmv); 258 259 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 260 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 261 262 /* Increment device counter After creating device */ 263 atomic_inc_32(&dm_dev_counter); 264 265 return r; 266 } 267 268 /* 269 * Get list of created device-mapper devices fromglobal list and 270 * send it to kernel. 271 * 272 * Output dictionary: 273 * 274 * <key>cmd_data</key> 275 * <array> 276 * <dict> 277 * <key>name<key> 278 * <string>...</string> 279 * 280 * <key>dev</key> 281 * <integer>...</integer> 282 * </dict> 283 * </array> 284 * 285 */ 286 int 287 dm_dev_list_ioctl(prop_dictionary_t dm_dict) 288 { 289 prop_array_t dev_list; 290 uint32_t flags; 291 292 flags = 0; 293 294 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 295 296 dm_dbg_print_flags(flags); 297 298 dev_list = dm_dev_prop_list(); 299 300 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, dev_list); 301 prop_object_release(dev_list); 302 303 return 0; 304 } 305 306 /* 307 * Rename selected devices old name is in struct dm_ioctl. 308 * newname is taken from dictionary 309 * 310 * <key>cmd_data</key> 311 * <array> 312 * <string>...</string> 313 * </array> 314 */ 315 int 316 dm_dev_rename_ioctl(prop_dictionary_t dm_dict) 317 { 318 prop_array_t cmd_array; 319 dm_dev_t *dmv; 320 321 const char *name, *uuid, *n_name; 322 uint32_t flags, minor; 323 324 name = NULL; 325 uuid = NULL; 326 minor = 0; 327 328 /* Get needed values from dictionary. */ 329 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 330 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 331 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 332 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 333 334 dm_dbg_print_flags(flags); 335 336 cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA); 337 338 prop_array_get_cstring_nocopy(cmd_array, 0, &n_name); 339 340 if (strlen(n_name) + 1 > DM_NAME_LEN) 341 return EINVAL; 342 343 if ((dmv = dm_dev_rem(name, uuid, minor)) == NULL) { 344 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 345 return ENOENT; 346 } 347 /* change device name */ 348 /* 349 * XXX How to deal with this change, name only used in 350 * dm_dev_routines, should I add dm_dev_change_name which will run 351 * under the dm_dev_list mutex ? 352 */ 353 strlcpy(dmv->name, n_name, DM_NAME_LEN); 354 355 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 356 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 357 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); 358 359 dm_dev_insert(dmv); 360 361 return 0; 362 } 363 364 /* 365 * Remove device from global list I have to remove active 366 * and inactive tables first. 367 */ 368 int 369 dm_dev_remove_ioctl(prop_dictionary_t dm_dict) 370 { 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_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 382 prop_dictionary_get_cstring_nocopy(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 * atomicaly 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 return config_detach(devt, DETACH_QUIET); 405 } 406 407 /* 408 * Return actual state of device to libdevmapper. 409 */ 410 int 411 dm_dev_status_ioctl(prop_dictionary_t dm_dict) 412 { 413 dm_dev_t *dmv; 414 const char *name, *uuid; 415 uint32_t flags, j, minor; 416 417 name = NULL; 418 uuid = NULL; 419 flags = 0; 420 j = 0; 421 422 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 423 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 424 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 425 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 426 427 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 428 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 429 return ENOENT; 430 } 431 dm_dbg_print_flags(dmv->flags); 432 433 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 434 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 435 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); 436 437 if (dmv->flags & DM_SUSPEND_FLAG) 438 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG); 439 440 /* 441 * Add status flags for tables I have to check both active and 442 * inactive tables. 443 */ 444 if ((j = dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE))) 445 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 446 else 447 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 448 449 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_TARGET_COUNT, j); 450 451 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE)) 452 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 453 else 454 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 455 456 dm_dev_unbusy(dmv); 457 458 return 0; 459 } 460 461 /* 462 * Set only flag to suggest that device is suspended. This call is 463 * not supported in NetBSD. 464 * 465 */ 466 int 467 dm_dev_suspend_ioctl(prop_dictionary_t dm_dict) 468 { 469 dm_dev_t *dmv; 470 const char *name, *uuid; 471 uint32_t flags, minor; 472 473 name = NULL; 474 uuid = NULL; 475 flags = 0; 476 477 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 478 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 479 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 480 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 481 482 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 483 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 484 return ENOENT; 485 } 486 atomic_or_32(&dmv->flags, DM_SUSPEND_FLAG); 487 488 dm_dbg_print_flags(dmv->flags); 489 490 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 491 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, dmv->flags); 492 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 493 494 dm_dev_unbusy(dmv); 495 496 /* Add flags to dictionary flag after dmv -> dict copy */ 497 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 498 499 return 0; 500 } 501 502 /* 503 * Simulate Linux behaviour better and switch tables here and not in 504 * dm_table_load_ioctl. 505 */ 506 int 507 dm_dev_resume_ioctl(prop_dictionary_t dm_dict) 508 { 509 dm_dev_t *dmv; 510 const char *name, *uuid; 511 uint32_t flags, minor; 512 513 name = NULL; 514 uuid = NULL; 515 flags = 0; 516 517 /* 518 * char *xml; xml = prop_dictionary_externalize(dm_dict); 519 * printf("%s\n",xml); 520 */ 521 522 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 523 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 524 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 525 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 526 527 /* Remove device from global device list */ 528 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 529 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 530 return ENOENT; 531 } 532 533 /* Make inactive table active, if it exists */ 534 if (dmv->flags & DM_INACTIVE_PRESENT_FLAG) 535 dm_table_switch_tables(&dmv->table_head); 536 537 atomic_and_32(&dmv->flags, ~(DM_SUSPEND_FLAG | DM_INACTIVE_PRESENT_FLAG)); 538 atomic_or_32(&dmv->flags, DM_ACTIVE_PRESENT_FLAG); 539 540 DM_ADD_FLAG(flags, DM_EXISTS_FLAG); 541 542 dmgetproperties(dmv->diskp, &dmv->table_head); 543 544 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_OPEN, dmv->table_head.io_cnt); 545 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags); 546 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 547 548 dm_dev_unbusy(dmv); 549 550 /* Destroy inactive table after resume. */ 551 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 552 553 return 0; 554 } 555 556 /* 557 * Table management routines 558 * lvm2tools doens't send name/uuid to kernel with table 559 * for lookup I have to use minor number. 560 */ 561 562 /* 563 * Remove inactive table from device. Routines which work's with inactive tables 564 * doesn't need to synchronise with dmstrategy. They can synchronise themselves with mutex?. 565 * 566 */ 567 int 568 dm_table_clear_ioctl(prop_dictionary_t dm_dict) 569 { 570 dm_dev_t *dmv; 571 const char *name, *uuid; 572 uint32_t flags, minor; 573 574 dmv = NULL; 575 name = NULL; 576 uuid = NULL; 577 flags = 0; 578 minor = 0; 579 580 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 581 prop_dictionary_get_cstring_nocopy(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 620 int table_type; 621 622 name = NULL; 623 uuid = NULL; 624 dmv = NULL; 625 flags = 0; 626 627 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 628 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 629 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 630 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 631 632 /* create array for dev_t's */ 633 cmd_array = prop_array_create(); 634 635 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 636 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 637 return ENOENT; 638 } 639 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 640 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_NAME, dmv->name); 641 prop_dictionary_set_cstring(dm_dict, DM_IOCTL_UUID, dmv->uuid); 642 643 aprint_debug("Getting table deps for device: %s\n", dmv->name); 644 645 /* 646 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query 647 * INACTIVE TABLE 648 */ 649 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG) 650 table_type = DM_TABLE_INACTIVE; 651 else 652 table_type = DM_TABLE_ACTIVE; 653 654 tbl = dm_table_get_entry(&dmv->table_head, table_type); 655 656 SLIST_FOREACH(table_en, tbl, next) 657 table_en->target->deps(table_en, cmd_array); 658 659 dm_table_release(&dmv->table_head, table_type); 660 dm_dev_unbusy(dmv); 661 662 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array); 663 prop_object_release(cmd_array); 664 665 return 0; 666 } 667 668 /* 669 * Load new table/tables to device. 670 * Call apropriate target init routine open all physical pdev's and 671 * link them to device. For other targets mirror, strip, snapshot 672 * etc. also add dependency devices to upcalls list. 673 * 674 * Load table to inactive slot table are switched in dm_device_resume_ioctl. 675 * This simulates Linux behaviour better there should not be any difference. 676 * 677 */ 678 int 679 dm_table_load_ioctl(prop_dictionary_t dm_dict) 680 { 681 dm_dev_t *dmv; 682 dm_table_entry_t *table_en, *last_table; 683 dm_table_t *tbl; 684 dm_target_t *target; 685 686 prop_object_iterator_t iter; 687 prop_array_t cmd_array; 688 prop_dictionary_t target_dict; 689 690 const char *name, *uuid, *type; 691 uint32_t flags, ret, minor; 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 prop_object_iterator_release(iter); 719 return ENOENT; 720 } 721 aprint_debug("Loading table to device: %s--%d\n", name, 722 dmv->table_head.cur_active_table); 723 724 /* 725 * I have to check if this table slot is not used by another table list. 726 * if it is used I should free them. 727 */ 728 if (dmv->flags & DM_INACTIVE_PRESENT_FLAG) 729 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 730 731 dm_dbg_print_flags(dmv->flags); 732 tbl = dm_table_get_entry(&dmv->table_head, DM_TABLE_INACTIVE); 733 734 aprint_debug("dmv->name = %s\n", dmv->name); 735 736 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 737 738 while ((target_dict = prop_object_iterator_next(iter)) != NULL) { 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 prop_object_iterator_release(iter); 750 return ENOENT; 751 } 752 table_en = kmem_alloc(sizeof(dm_table_entry_t), KM_SLEEP); 753 prop_dictionary_get_uint64(target_dict, DM_TABLE_START, 754 &table_en->start); 755 prop_dictionary_get_uint64(target_dict, DM_TABLE_LENGTH, 756 &table_en->length); 757 758 table_en->target = target; 759 table_en->dm_dev = dmv; 760 table_en->target_config = NULL; 761 762 /* 763 * There is a parameter string after dm_target_spec 764 * structure which points to /dev/wd0a 284 part of 765 * table. String str points to this text. This can be 766 * null and therefore it should be checked before we try to 767 * use it. 768 */ 769 prop_dictionary_get_cstring(target_dict, 770 DM_TABLE_PARAMS, (char **) &str); 771 772 if (SLIST_EMPTY(tbl) || last_table == NULL) 773 /* insert this table to head */ 774 SLIST_INSERT_HEAD(tbl, table_en, next); 775 else 776 SLIST_INSERT_AFTER(last_table, table_en, next); 777 778 if ((ret = dm_table_init(target, table_en, str)) != 0) { 779 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 780 dm_table_destroy(&dmv->table_head, DM_TABLE_INACTIVE); 781 free(str, M_TEMP); 782 783 dm_dev_unbusy(dmv); 784 prop_object_iterator_release(iter); 785 return ret; 786 } 787 last_table = table_en; 788 free(str, M_TEMP); 789 } 790 prop_object_iterator_release(iter); 791 792 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 793 atomic_or_32(&dmv->flags, DM_INACTIVE_PRESENT_FLAG); 794 795 dm_table_release(&dmv->table_head, DM_TABLE_INACTIVE); 796 dm_dev_unbusy(dmv); 797 return 0; 798 } 799 800 static int 801 dm_table_init(dm_target_t *target, dm_table_entry_t *table_en, char *params) 802 { 803 int i, n, ret, argc; 804 char **ap, **argv; 805 806 if (params == NULL) 807 return EINVAL; 808 809 n = target->max_argc; 810 if (n) 811 aprint_debug("Max argc %d for %s target\n", n, target->name); 812 else 813 n = 32; /* large enough for most targets */ 814 815 argv = kmem_alloc(sizeof(*argv) * n, KM_SLEEP); 816 817 for (ap = argv; 818 ap < &argv[n] && (*ap = strsep(¶ms, " \t")) != NULL;) { 819 if (**ap != '\0') 820 ap++; 821 } 822 argc = ap - argv; 823 824 for (i = 0; i < argc; i++) 825 aprint_debug("DM: argv[%d] = \"%s\"\n", i, argv[i]); 826 827 KASSERT(target->init); 828 ret = target->init(table_en, argc, argv); 829 830 kmem_free(argv, sizeof(*argv) * n); 831 832 return ret; 833 } 834 835 /* 836 * Get description of all tables loaded to device from kernel 837 * and send it to libdevmapper. 838 * 839 * Output dictionary for every table: 840 * 841 * <key>cmd_data</key> 842 * <array> 843 * <dict> 844 * <key>type<key> 845 * <string>...</string> 846 * 847 * <key>start</key> 848 * <integer>...</integer> 849 * 850 * <key>length</key> 851 * <integer>...</integer> 852 * 853 * <key>params</key> 854 * <string>...</string> 855 * </dict> 856 * </array> 857 * 858 */ 859 int 860 dm_table_status_ioctl(prop_dictionary_t dm_dict) 861 { 862 dm_dev_t *dmv; 863 dm_table_t *tbl; 864 dm_table_entry_t *table_en; 865 866 prop_array_t cmd_array; 867 prop_dictionary_t target_dict; 868 869 uint32_t minor, flags; 870 871 const char *name, *uuid; 872 char *params; 873 int table_type; 874 875 dmv = NULL; 876 uuid = NULL; 877 name = NULL; 878 params = NULL; 879 flags = 0; 880 881 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_NAME, &name); 882 prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_UUID, &uuid); 883 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_FLAGS, &flags); 884 prop_dictionary_get_uint32(dm_dict, DM_IOCTL_MINOR, &minor); 885 886 cmd_array = prop_array_create(); 887 888 if ((dmv = dm_dev_lookup(name, uuid, minor)) == NULL) { 889 DM_REMOVE_FLAG(flags, DM_EXISTS_FLAG); 890 return ENOENT; 891 } 892 /* 893 * if DM_QUERY_INACTIVE_TABLE_FLAG is passed we need to query 894 * INACTIVE TABLE 895 */ 896 if (flags & DM_QUERY_INACTIVE_TABLE_FLAG) 897 table_type = DM_TABLE_INACTIVE; 898 else 899 table_type = DM_TABLE_ACTIVE; 900 901 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_ACTIVE)) { 902 DM_ADD_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 903 } else { 904 DM_REMOVE_FLAG(flags, DM_ACTIVE_PRESENT_FLAG); 905 906 if (dm_table_get_target_count(&dmv->table_head, DM_TABLE_INACTIVE)) 907 DM_ADD_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 908 else 909 DM_REMOVE_FLAG(flags, DM_INACTIVE_PRESENT_FLAG); 910 } 911 912 if (dmv->flags & DM_SUSPEND_FLAG) 913 DM_ADD_FLAG(flags, DM_SUSPEND_FLAG); 914 915 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_MINOR, dmv->minor); 916 917 aprint_debug("Status of device tables: %s--%d\n", 918 name, dmv->table_head.cur_active_table); 919 920 tbl = dm_table_get_entry(&dmv->table_head, table_type); 921 922 SLIST_FOREACH(table_en, tbl, next) { 923 target_dict = prop_dictionary_create(); 924 aprint_debug("%016" PRIu64 ", length %016" PRIu64 925 ", target %s\n", table_en->start, table_en->length, 926 table_en->target->name); 927 928 prop_dictionary_set_uint64(target_dict, DM_TABLE_START, 929 table_en->start); 930 prop_dictionary_set_uint64(target_dict, DM_TABLE_LENGTH, 931 table_en->length); 932 933 prop_dictionary_set_cstring(target_dict, DM_TABLE_TYPE, 934 table_en->target->name); 935 936 /* dm_table_get_cur_actv.table ?? */ 937 prop_dictionary_set_int32(target_dict, DM_TABLE_STAT, 938 dmv->table_head.cur_active_table); 939 940 /* 941 * Explicitly clear DM_TABLE_PARAMS to prevent dmsetup(8) from 942 * printing junk when DM_TABLE_PARAMS was never initialized. 943 */ 944 prop_dictionary_set_cstring(target_dict, DM_TABLE_PARAMS, ""); 945 946 if (flags & DM_STATUS_TABLE_FLAG) { 947 params = table_en->target->status 948 (table_en->target_config); 949 950 if (params != NULL) { 951 prop_dictionary_set_cstring(target_dict, 952 DM_TABLE_PARAMS, params); 953 954 kmem_free(params, DM_MAX_PARAMS_SIZE); 955 } 956 } 957 prop_array_add(cmd_array, target_dict); 958 prop_object_release(target_dict); 959 } 960 961 dm_table_release(&dmv->table_head, table_type); 962 dm_dev_unbusy(dmv); 963 964 prop_dictionary_set_uint32(dm_dict, DM_IOCTL_FLAGS, flags); 965 prop_dictionary_set(dm_dict, DM_IOCTL_CMD_DATA, cmd_array); 966 prop_object_release(cmd_array); 967 968 return 0; 969 } 970 971 /* 972 * For every call I have to set kernel driver version. 973 * Because I can have commands supported only in other 974 * newer/later version. This routine is called for every 975 * ioctl command. 976 */ 977 int 978 dm_check_version(prop_dictionary_t dm_dict) 979 { 980 size_t i; 981 uint32_t dm_version[3]; 982 prop_array_t ver; 983 984 ver = prop_dictionary_get(dm_dict, DM_IOCTL_VERSION); 985 986 for (i = 0; i < 3; i++) 987 prop_array_get_uint32(ver, i, &dm_version[i]); 988 989 if (DM_VERSION_MAJOR != dm_version[0] || DM_VERSION_MINOR < dm_version[1]) { 990 aprint_debug("libdevmapper/kernel version mismatch " 991 "kernel: %d.%d.%d libdevmapper: %d.%d.%d\n", 992 DM_VERSION_MAJOR, DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, 993 dm_version[0], dm_version[1], dm_version[2]); 994 995 return EIO; 996 } 997 prop_array_set_uint32(ver, 0, DM_VERSION_MAJOR); 998 prop_array_set_uint32(ver, 1, DM_VERSION_MINOR); 999 prop_array_set_uint32(ver, 2, DM_VERSION_PATCHLEVEL); 1000 1001 prop_dictionary_set(dm_dict, DM_IOCTL_VERSION, ver); 1002 1003 return 0; 1004 } 1005