1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <assert.h> 30 #include <string.h> 31 #include <libintl.h> 32 33 #include "volume_error.h" 34 #include "volume_defaults.h" 35 #include "volume_dlist.h" 36 #include "volume_output.h" 37 #include "volume_request.h" 38 39 #include "layout_device_cache.h" 40 #include "layout_discovery.h" 41 #include "layout_dlist_util.h" 42 #include "layout_request.h" 43 #include "layout_slice.h" 44 #include "layout_validate.h" 45 46 #define _LAYOUT_REQUEST_C 47 48 static char *_request_diskset = NULL; 49 static devconfig_t *_toplevel_request = NULL; 50 static defaults_t *_defaults = NULL; 51 52 /* 53 * This file contains code which handles various aspects of the 54 * request and defaults devconfig_t structs passed to the layout 55 * module. 56 * 57 * Functions are provided which determine what devices are available 58 * for use by the various volume layout mechanisms. These are based 59 * on the user specified available/unavailable devices included in 60 * a request or in the defaults associated with the destination diskset. 61 */ 62 63 /* 64 * A struct to hold device "specifications" extracted from a user 65 * specified device name. This struct is used to compare the user's 66 * available and unavailable device specifications against physical 67 * devices attached to the system. 68 * 69 * The spec struct holds one of two different specifications: if the 70 * user supplied device name is parsable as a CTD name, it is parsed 71 * into the component ids. Otherwise, it is stored as is. 72 * 73 * The CTD name space implies a device hierarchy and metassist 74 * supports an implied wildcarding scheme for the CTD name space. 75 * A CTD specification from the user is of the form cX, cXdX, 76 * cXdXsX, cXtX, cXtXdX, or cXtXdXsX, so it may or may nor 77 * correspond to an individual physical device depending on 78 * the context. 79 * 80 * For example, "c1" can mean the controller/HBA with the 81 * name "c1" or it can mean all devices attached to the 82 * controller named "c1". 83 * 84 * The ctd specs make matching physical devices against a 85 * user specification easier since the matching is based on 86 * the numeric values extracted from the cXtXdXsX string 87 * and not on the strings themselves. The strings are 88 * troublesome because of situations like "c1" being 89 * compared to "c11t1d0s0" and getting false matches. 90 * 91 * The ID_UNSPECIFIED value is used to flag components 92 * that were not in the CTD name: 93 * 94 * "c3" -> { ctrl=3, target=ID_UNSPECIFIED, 95 * lun=ID_UNSPECIFIED, slice=ID_UNSPECIFIED } 96 * 97 * "c3t2" -> { ctrl=3, target=2, 98 * lun=ID_UNSPECIFIED, slice=ID_UNSPECIFIED } 99 */ 100 101 #define ID_UNSPECIFIED -1 102 typedef struct { 103 int ctrl; 104 int target; 105 int lun; 106 int slice; 107 boolean_t is_ide; 108 } ctd_spec_t; 109 110 typedef enum { 111 SPEC_TYPE_CTD = 0, 112 SPEC_TYPE_RAW, 113 SPEC_TYPE_OTHER 114 } spec_type_t; 115 116 typedef struct { 117 spec_type_t type; 118 union { 119 ctd_spec_t *ctd; 120 char *raw; 121 } data; 122 } device_spec_t; 123 124 static int get_spec_for_name( 125 char *name, 126 device_spec_t **id); 127 128 static int create_device_spec( 129 char *name, 130 device_spec_t **spec); 131 132 static int create_device_ctd_spec( 133 char *name, 134 device_spec_t **spec); 135 136 static int create_device_raw_spec( 137 char *name, 138 device_spec_t **spec); 139 140 static void destroy_device_spec( 141 device_spec_t *spec); 142 143 static boolean_t ctd_spec_includes_device( 144 device_spec_t *spec, 145 device_spec_t *device); 146 147 static boolean_t raw_spec_includes_device( 148 device_spec_t *spec, 149 device_spec_t *device); 150 151 /* 152 * get_spec_for_name builds up a cached mapping of device 153 * names to the corresponding device_spec_t structs. 154 * 155 * This saves repeatedly converting the device names, which 156 * could get expensive since devices are checked against the 157 * user specified available/unavailable devices a lot. 158 * 159 * The cache is implemented as a list of these structs: 160 */ 161 typedef struct { 162 163 char *name; 164 device_spec_t *device_spec; 165 166 } spec_cache_t; 167 168 static dlist_t *_spec_cache = NULL; 169 170 static int destroy_spec_cache(); 171 static int compare_name_to_spec_cache_name( 172 void *name, void *list_item); 173 174 /* 175 * The user specified available/unavailable devices are 176 * accessed frequently during layout. To make this more 177 * efficient, the char *arrays of available/unavailable 178 * specifications for a request or defaults devconfig_t 179 * object are converted to device_spec_ts the first time 180 * they're accessed and then cached using this struct: 181 */ 182 typedef struct { 183 184 devconfig_t *request; 185 186 /* 187 * avail_specs_list is a list of device spec_t 188 * corresponding to available devices specified 189 * in the request object 190 */ 191 dlist_t *avail_specs_list; 192 193 /* 194 * unavail_specs_list is a list of device spec_t 195 * corresponding to unavailable devices specified 196 * in the request object 197 */ 198 dlist_t *unavail_specs_list; 199 200 } request_spec_list_t; 201 202 dlist_t *_request_spec_list_cache = NULL; 203 204 static int destroy_request_spec_list_cache(); 205 static void destroy_request_spec_list_entry(void *obj); 206 207 static int compare_request_to_request_spec_list_request( 208 void *object, 209 void *list_item); 210 211 static int convert_usernames_to_specs( 212 char **specs, 213 dlist_t **list); 214 215 /* other private functions */ 216 static int is_device_avail( 217 dm_descriptor_t desc, 218 devconfig_t *request, 219 boolean_t *avail); 220 221 static int is_named_device_avail( 222 devconfig_t *request, 223 char *device_name, 224 boolean_t check_aliases, 225 boolean_t *avail); 226 227 static int avail_list_includes_device_name( 228 dlist_t *list, 229 char *device_name, 230 boolean_t check_aliases, 231 boolean_t *includes); 232 233 static int unavail_list_includes_device_name( 234 dlist_t *list, 235 char *device_name, 236 boolean_t check_aliases, 237 boolean_t *includes); 238 239 static int spec_includes_device_name( 240 device_spec_t *spec, 241 char *device_name, 242 boolean_t check_aliases, 243 boolean_t *includes); 244 245 static boolean_t spec_includes_device( 246 device_spec_t *spec, 247 device_spec_t *device); 248 249 static int disk_get_avail_space( 250 devconfig_t *request, 251 dm_descriptor_t disk, 252 uint64_t *avail); 253 254 static int compare_hba_n_avail_disks( 255 void *obj1, 256 void *obj2); 257 258 /* 259 * FUNCTION: release_request_caches() 260 * 261 * RETURNS: 0 262 * 263 * PURPOSE: cleanup the module private caches. 264 */ 265 int 266 release_request_caches() 267 { 268 (void) destroy_request_spec_list_cache(); 269 (void) destroy_spec_cache(); 270 271 return (0); 272 } 273 /* 274 * FUNCTION: int set_request_diskset(char *) 275 * 276 * INPUT: char * - pointer to the diskset name 277 * OUTPUT: 0 - success 278 * !0 - validation failure 279 * RETURNS: 280 * 281 * PURPOSE: set the module global diskset name. 282 */ 283 int 284 set_request_diskset( 285 char *dsname) 286 { 287 _request_diskset = dsname; 288 289 if (dsname == NULL || dsname[0] == '\0') { 290 volume_set_error( 291 gettext("No disk set specified in request\n")); 292 return (-1); 293 } 294 295 return (0); 296 } 297 298 /* 299 * FUNCTION: char *get_request_diskset() 300 * 301 * INPUT: none - 302 * OUTPUT: none - 303 * RETURNS: char * - pointer to the currently set diskset name 304 * 305 * PURPOSE: get the global name of the current diskset. 306 */ 307 char * 308 get_request_diskset() 309 { 310 return (_request_diskset); 311 } 312 313 /* 314 * FUNCTION: void unset_request_diskset() 315 * 316 * PURPOSE: unset the module global diskset name. 317 */ 318 void 319 unset_request_diskset( 320 char *dsname) 321 { 322 _request_diskset = NULL; 323 } 324 325 /* 326 * FUNCTION: int set_toplevel_request(devconfig_t *) 327 * 328 * INPUT: devconfig_t * - pointer to the diskset request 329 * OUTPUT: 0 - success 330 * !0 - validation failure 331 * RETURNS: 332 * 333 * PURPOSE: set the module global toplevel request struct. 334 * this will be set within the only public entry 335 * point to the module -- get_layout() 336 * 337 * SIDEEFFECT: The devconfig_t's list of available and unavailable 338 * devices will be validated. 339 */ 340 int 341 set_toplevel_request( 342 devconfig_t *req) 343 { 344 _toplevel_request = req; 345 346 return (validate_request_avail_unavail(req)); 347 } 348 349 /* 350 * FUNCTION: void unset_toplevel_request() 351 * 352 * PURPOSE: unset the layout module global toplevel request struct. 353 * 354 */ 355 void 356 unset_toplevel_request() 357 { 358 _toplevel_request = NULL; 359 } 360 361 /* 362 * FUNCTION: int set_defaults(devconfig_t *) 363 * 364 * INPUT: devconfig_t * - pointer to the global defaults devconfig_t 365 * OUTPUT: 0 - success 366 * !0 - validation failure 367 * RETURNS: 368 * 369 * PURPOSE: set the module global defaults struct. 370 * this will be set within the only public entry 371 * point to the module -- get_layout() 372 * 373 * SIDEEFFECT: The devconfig_t's list of available and unavailable 374 * devices will be validated. 375 */ 376 int 377 set_request_defaults( 378 defaults_t *defaults) 379 { 380 int error = 0; 381 devconfig_t *diskset = NULL; 382 383 _defaults = defaults; 384 385 if ((error = defaults_get_diskset_by_name( 386 _defaults, get_request_diskset(), &diskset)) == 0) { 387 388 error = validate_request_avail_unavail(diskset); 389 390 } else if (error == ENOENT) { 391 /* no defaults to verify */ 392 error = 0; 393 } 394 395 return (error); 396 } 397 398 /* 399 * FUNCTION: void unset_request_defaults() 400 * 401 * PURPOSE: unset the layout module global defaults struct. 402 * 403 */ 404 void 405 unset_request_defaults() 406 { 407 _defaults = NULL; 408 } 409 410 /* 411 * FUNCTION: get_stripe_min_comp(devconfig_t *req, uint16_t *val) 412 * INPUT: req - a devconfig_t pointer to the current request 413 * val - pointer to a uint64_t to hold the result 414 * 415 * RETURNS: int - 0 - on success 416 * !0 - otherwise 417 * 418 * PURPOSE: helper which determines the minimum of components 419 * for striped volumes satisfying the input request. 420 * 421 * The value to use is taken from the input request, the 422 * toplevel diskset request, the diskset defaults or the 423 * global defaults. 424 */ 425 int 426 get_stripe_min_comp( 427 devconfig_t *req, 428 uint16_t *val) 429 { 430 int error = 0; 431 432 *val = 0; 433 434 if ((error = devconfig_get_stripe_mincomp(req, val)) != 0) { 435 if (error != ERR_ATTR_UNSET) { 436 return (error); 437 } 438 } 439 440 if (*val == 0) { 441 if ((error = defaults_get_stripe_mincomp( 442 _defaults, get_request_diskset(), val)) != 0) { 443 if (error != ERR_ATTR_UNSET) { 444 return (error); 445 } 446 } 447 } 448 449 return (error); 450 } 451 452 /* 453 * FUNCTION: get_stripe_max_comp(devconfig_t *req, uint16_t *val) 454 * INPUT: req - a devconfig_t pointer to the current request 455 * val - pointer to a uint64_t to hold the result 456 * 457 * RETURNS: int - 0 - on success 458 * !0 - otherwise 459 * 460 * PURPOSE: helper which determines the maximum number of components 461 * for striped volumes satisfying the input request. 462 * 463 * The value to use is taken from the input request, the 464 * toplevel diskset request, the diskset defaults or the 465 * global defaults. 466 */ 467 int 468 get_stripe_max_comp( 469 devconfig_t *req, 470 uint16_t *val) 471 { 472 int error = 0; 473 474 *val = 0; 475 476 if ((error = devconfig_get_stripe_maxcomp(req, val)) != 0) { 477 if (error != ERR_ATTR_UNSET) { 478 return (error); 479 } 480 } 481 482 if (*val == 0) { 483 if ((error = defaults_get_stripe_maxcomp( 484 _defaults, get_request_diskset(), val)) != 0) { 485 if (error != ERR_ATTR_UNSET) { 486 return (error); 487 } 488 } 489 } 490 491 return (error); 492 } 493 494 /* 495 * FUNCTION: get_stripe_interlace(devconfig_t *req, uint64_t *val) 496 * INPUT: req - a devconfig_t pointer to the current request 497 * val - pointer to a uint64_t to hold the result 498 * 499 * RETURNS: int - 0 - on success 500 * !0 - otherwise 501 * 502 * PURPOSE: helper which determines the interlace value for striped 503 * volumes satisfying the input request. 504 * 505 * The value to use is taken from the input request, the 506 * toplevel diskset request, the diskset defaults or the 507 * global defaults. 508 * 509 * If no value is explictly specified, ERR_ATTR_UNSET is 510 * returned. 511 */ 512 int 513 get_stripe_interlace( 514 devconfig_t *req, 515 uint64_t *val) 516 { 517 int error = 0; 518 519 *val = 0; 520 521 if ((error = devconfig_get_stripe_interlace(req, val)) != 0) { 522 if (error != ERR_ATTR_UNSET) { 523 return (error); 524 } 525 error = 0; 526 } 527 528 if (*val == 0) { 529 if ((error = defaults_get_stripe_interlace( 530 _defaults, get_request_diskset(), val)) != 0) { 531 if (error != ERR_ATTR_UNSET) { 532 return (error); 533 } 534 } 535 } 536 537 return (error); 538 } 539 540 /* 541 * FUNCTION: get_mirror_read_strategy(devconfig_t *req, 542 * mirror_read_strategy_t *val) 543 * INPUT: req - a devconfig_t pointer to the current request 544 * val - pointer to a mirror_read_strategy_t to hold the result 545 * 546 * RETURNS: int - 0 - on success 547 * !0 - otherwise 548 * 549 * PURPOSE: helper which determines the write strategy mirrored volumes 550 * should have for volumes satisfying the input request. 551 * 552 * The value to use is taken from the input request, the 553 * toplevel diskset request, the diskset defaults or the 554 * global defaults. 555 * 556 * If no value is explictly specified, ERR_ATTR_UNSET is 557 * returned. 558 */ 559 int 560 get_mirror_read_strategy( 561 devconfig_t *req, 562 mirror_read_strategy_t *val) 563 { 564 int error = 0; 565 566 *val = 0; 567 568 if ((error = devconfig_get_mirror_read(req, val)) != 0) { 569 if (error != ERR_ATTR_UNSET) { 570 return (error); 571 } 572 } 573 574 if (*val == 0) { 575 if ((error = defaults_get_mirror_read( 576 _defaults, get_request_diskset(), val)) != 0) { 577 if (error != ERR_ATTR_UNSET) { 578 return (error); 579 } 580 } 581 } 582 583 return (error); 584 } 585 586 /* 587 * FUNCTION: get_mirror_write_strategy(devconfig_t *req, 588 * mirror_write_strategy_t *val) 589 * INPUT: req - a devconfig_t pointer to the current request 590 * val - pointer to a mirror_write_strategy_t to hold result 591 * 592 * RETURNS: int - 0 - on success 593 * !0 - otherwise 594 * 595 * PURPOSE: helper which determines the write strategy mirrored volumes 596 * should have for volumes satisfying the input request. 597 * 598 * The value to use is taken from the input request, the 599 * toplevel diskset request, the diskset defaults or the 600 * global defaults. 601 * 602 * If no value is explictly specified, ERR_ATTR_UNSET is 603 * returned. 604 */ 605 int 606 get_mirror_write_strategy( 607 devconfig_t *req, 608 mirror_write_strategy_t *val) 609 { 610 int error = 0; 611 612 *val = 0; 613 614 if ((error = devconfig_get_mirror_write(req, val)) != 0) { 615 if (error != ERR_ATTR_UNSET) { 616 return (error); 617 } 618 } 619 620 if (*val == 0) { 621 if ((error = defaults_get_mirror_write( 622 _defaults, get_request_diskset(), val)) != 0) { 623 if (error != ERR_ATTR_UNSET) { 624 return (error); 625 } 626 } 627 } 628 629 return (error); 630 } 631 632 /* 633 * FUNCTION: get_mirror_pass(devconfig_t *req, uint16_t *val) 634 * INPUT: req - a devconfig_t pointer to the current request 635 * val - pointer to a uint16_t to hold the result 636 * 637 * RETURNS: int - 0 - on success 638 * !0 - otherwise 639 * 640 * PURPOSE: helper which determines the resync pass mirrored volumes 641 * should have for volumes satisfying the input request. 642 * 643 * The value to use is taken from the input request, the 644 * toplevel diskset request, the diskset defaults or the 645 * global defaults. 646 * 647 * If no value is explictly specified, ERR_ATTR_UNSET is 648 * returned. 649 */ 650 int 651 get_mirror_pass( 652 devconfig_t *req, 653 uint16_t *val) 654 { 655 int error = 0; 656 657 *val = 0; 658 659 if ((error = devconfig_get_mirror_pass(req, val)) != 0) { 660 if (error != ERR_ATTR_UNSET) { 661 return (error); 662 } 663 } 664 665 if (*val == 0) { 666 if ((error = defaults_get_mirror_pass( 667 _defaults, get_request_diskset(), val)) != 0) { 668 if (error != ERR_ATTR_UNSET) { 669 return (error); 670 } 671 } 672 } 673 674 return (error); 675 } 676 677 /* 678 * FUNCTION: get_mirror_nsubs(devconfig_t *req, uint16_t *val) 679 * INPUT: req - a devconfig_t pointer to the current request 680 * val - pointer to a uint16_t to hold the result 681 * 682 * RETURNS: int - 0 - on success 683 * !0 - otherwise 684 * 685 * PURPOSE: helper which determines how many submirrors mirrored 686 * volumes should have for volumes satisfying the input 687 * request. 688 * 689 * The value to use is taken from the input request, the 690 * toplevel diskset request, the diskset defaults or the 691 * global defaults. 692 */ 693 int 694 get_mirror_nsubs( 695 devconfig_t *req, 696 uint16_t *val) 697 { 698 int error = 0; 699 700 *val = 0; 701 702 if ((error = devconfig_get_mirror_nsubs(req, val)) != 0) { 703 if (error != ERR_ATTR_UNSET) { 704 return (error); 705 } 706 } 707 708 if (*val == 0) { 709 if ((error = defaults_get_mirror_nsubs( 710 _defaults, get_request_diskset(), val)) != 0) { 711 if (error != ERR_ATTR_UNSET) { 712 return (error); 713 } 714 } 715 } 716 717 return (error); 718 } 719 720 /* 721 * FUNCTION: get_volume_faultrecov(devconfig_t *req, boolean_t *val) 722 * INPUT: req - a devconfig_t pointer to the current request 723 * val - pointer to a boolean_t to hold the result 724 * 725 * RETURNS: int - 0 - on success 726 * !0 - otherwise 727 * 728 * PURPOSE: helper which determines whether data redundant volumes 729 * should also have fault recovery (e.g., HSPs) for volumes 730 * satisfying the input request. 731 * 732 * The value to use is taken from the input request, the 733 * toplevel diskset request, the diskset defaults or the 734 * global defaults. 735 */ 736 int 737 get_volume_faultrecov( 738 devconfig_t *req, 739 boolean_t *val) 740 { 741 int error = 0; 742 743 *val = B_FALSE; 744 745 if ((error = devconfig_get_volume_usehsp(req, val)) != 0) { 746 if (error == ERR_ATTR_UNSET) { 747 component_type_t type = TYPE_UNKNOWN; 748 (void) devconfig_get_type(req, &type); 749 750 switch (type) { 751 case TYPE_MIRROR: 752 error = defaults_get_mirror_usehsp( 753 _defaults, get_request_diskset(), val); 754 break; 755 756 case TYPE_STRIPE: 757 error = defaults_get_stripe_usehsp( 758 _defaults, get_request_diskset(), val); 759 break; 760 761 case TYPE_CONCAT: 762 error = defaults_get_concat_usehsp( 763 _defaults, get_request_diskset(), val); 764 break; 765 766 case TYPE_VOLUME: 767 error = defaults_get_volume_usehsp( 768 _defaults, get_request_diskset(), val); 769 break; 770 } 771 } 772 } 773 774 return (error); 775 } 776 777 /* 778 * FUNCTION: get_volume_redundancy_level(devconfig_t *req, uint16_t val) 779 * INPUT: req - a devconfig_t pointer to the current request 780 * val - pointer to a uint16-t to hold the result 781 * 782 * RETURNS: int - 0 - on success 783 * !0 - otherwise 784 * 785 * PURPOSE: helper which determines the appropriate level of data 786 * redundancy a volume should have for volumes satisfying 787 * the input request. 788 * 789 * The value to use is taken from the input request, the 790 * toplevel diskset request, the diskset defaults or the 791 * global defaults. 792 */ 793 int 794 get_volume_redundancy_level( 795 devconfig_t *req, 796 uint16_t *val) 797 { 798 int error = 0; 799 800 *val = 0; 801 802 if ((error = devconfig_get_volume_redundancy_level(req, val)) != 0) { 803 if (error != ERR_ATTR_UNSET) { 804 return (error); 805 } 806 } 807 808 if (*val == 0) { 809 if ((error = defaults_get_volume_redundancy_level( 810 _defaults, get_request_diskset(), val)) != 0) { 811 if (error != ERR_ATTR_UNSET) { 812 return (error); 813 } 814 } 815 } 816 817 return (error); 818 } 819 820 /* 821 * FUNCTION: get_volume_npaths(devconfig_t *req, uint16_t val) 822 * INPUT: req - a devconfig_t pointer to the current request 823 * val - pointer to a uint16-t to hold the result 824 * 825 * RETURNS: int - 0 - on success 826 * !0 - otherwise 827 * 828 * PURPOSE: helper which determines the appropriate level of datapath 829 * redundancy a slice component should have for volumes 830 * satisfying the input request. 831 * 832 * The value to use is taken from the input request, the 833 * toplevel diskset request, the diskset defaults or the 834 * global defaults. 835 */ 836 int 837 get_volume_npaths( 838 devconfig_t *req, 839 uint16_t *val) 840 { 841 int error = 0; 842 843 *val = 0; 844 845 if ((error = devconfig_get_volume_npaths(req, val)) != 0) { 846 if (error != ERR_ATTR_UNSET) { 847 return (error); 848 } 849 } 850 851 if (*val == 0) { 852 if ((error = defaults_get_volume_npaths( 853 _defaults, get_request_diskset(), val)) != 0) { 854 if (error != ERR_ATTR_UNSET) { 855 return (error); 856 } 857 } 858 } 859 860 return (error); 861 } 862 863 /* 864 * FUNCTION: get_default_hsp_name(devconfig_t *req, char **hspname) 865 * INPUT: req - a devconfig_t pointer to the current request 866 * hspname - pointer to a char * to hold the result, if any 867 * 868 * RETURNS: int - 0 - on success 869 * !0 - otherwise 870 * 871 * PURPOSE: helper which determines the default HSP name for the 872 * input request. 873 * 874 * The value to use is taken from the input request, the 875 * toplevel diskset request, the diskset defaults or the 876 * global defaults. 877 */ 878 int 879 get_default_hsp_name( 880 devconfig_t *req, 881 char **name) 882 { 883 int error = 0; 884 885 *name = NULL; 886 887 if ((error = defaults_get_hsp_name(_defaults, 888 get_request_diskset(), name)) != 0) { 889 if (error != ENOENT) { 890 return (error); 891 } 892 error = 0; 893 } 894 895 return (error); 896 } 897 898 /* 899 * FUNCTION: slice_is_available(char *sname, devconfig_t *request, 900 * boolean_t bool) 901 * INPUT: sname - a slice name 902 * request - pointer to a devconfig_t struct representing 903 * the current layout request being processed 904 * bool - pointer to a boolean to hold the result 905 * 906 * RETURNS: int - 0 - on success 907 * !0 - otherwise 908 * 909 * PURPOSE: Validation helper which determines if the named slice can 910 * be used as a volume component when satisfying the input 911 * request. 912 * 913 * Check if the slice appears in the known slice list, 914 * then check the request's available and unavailable 915 * device specifications. 916 */ 917 int 918 slice_is_available( 919 char *sname, 920 devconfig_t *request, 921 boolean_t *bool) 922 { 923 dm_descriptor_t slice = (dm_descriptor_t)0; 924 int error = 0; 925 926 *bool = B_FALSE; 927 928 if ((error = slice_get_by_name(sname, &slice)) != 0) { 929 return (error); 930 } 931 932 if (slice == (dm_descriptor_t)0) { 933 /* no slice found */ 934 return (ENODEV); 935 } 936 937 if (error == 0) { 938 error = is_named_device_avail(request, sname, B_TRUE, bool); 939 } 940 941 return (error); 942 } 943 944 /* 945 * FUNCTION: get_disks_for_target(char *name, dlist_t **disks) 946 * 947 * INPUT: name - a char* device CTD name 948 * 949 * OUTPUT: disks - disks matching the input target name 950 * 951 * RETURNS: int - 0 on success 952 * !0 otherwise 953 * 954 * PURPOSE: Validation helper function which finds all disks "on" the 955 * input target. 956 * 957 * The input name is assumed to be a target name, cXtX, and 958 * the list of known disks is searched to find any disk that 959 * looks to be "on" that target. 960 * 961 * "On" is determined by comparing a disk's name and 962 * aliases to the target to see if they match. 963 */ 964 int 965 get_disks_for_target( 966 char *name, 967 dlist_t **disks) 968 { 969 int error = 0; 970 device_spec_t *targetid = NULL; 971 972 error = get_spec_for_name(name, &targetid); 973 if (error == 0) { 974 dlist_t *known_disks = NULL; 975 dlist_t *iter = NULL; 976 977 get_known_disks(&known_disks); 978 for (iter = known_disks; 979 (iter != NULL) && (error == 0); 980 iter = iter->next) { 981 982 dm_descriptor_t disk = (uintptr_t)iter->obj; 983 device_spec_t *diskid = NULL; 984 char *diskname = NULL; 985 dlist_t *diskaliases = NULL; 986 dlist_t *item; 987 988 ((error = get_display_name(disk, &diskname)) != 0) || 989 (error = get_aliases(disk, &diskaliases)) || 990 (error = get_spec_for_name(diskname, &diskid)); 991 992 if (error == 0) { 993 if (spec_includes_device(targetid, diskid) == B_TRUE) { 994 /* add disk */ 995 if ((item = dlist_new_item((void *)(uintptr_t)disk)) == 996 NULL) { 997 error = ENOMEM; 998 } else { 999 *disks = dlist_append(item, *disks, AT_HEAD); 1000 } 1001 } else { 1002 /* check disk's aliases */ 1003 dlist_t *iter2; 1004 for (iter2 = diskaliases; 1005 (iter2 != NULL) && (error == 0); 1006 iter2 = iter2->next) { 1007 1008 char *aliasname = NULL; 1009 device_spec_t *aliasid = NULL; 1010 error = get_display_name(disk, &aliasname); 1011 error = get_spec_for_name(aliasname, &aliasid); 1012 1013 if (spec_includes_device( 1014 targetid, aliasid) == B_TRUE) { 1015 1016 /* alias matched, add disk */ 1017 item = dlist_new_item((void *)(uintptr_t)disk); 1018 if (item == NULL) { 1019 error = ENOMEM; 1020 } else { 1021 *disks = 1022 dlist_append(item, *disks, AT_HEAD); 1023 } 1024 } 1025 } 1026 } 1027 } 1028 } 1029 } 1030 1031 return (error); 1032 } 1033 1034 /* 1035 * FUNCTION: select_hbas_with_n_disks(devconfig_t *request, 1036 * dlist_t *hbas, int mindisks, dlist_t **selhbas, 1037 * dlist_t **seldisks) 1038 * 1039 * INPUT: request - pointer to a devconfig_t struct representing 1040 * the current layout request being processed 1041 * hbas - pointer to a list of HBAs 1042 * mindisks - minimum number of disks required on the HBAs 1043 * 1044 * OUTPUT: selhbas - pointer to a list containing the HBAs with at 1045 * least mindisks available disks. 1046 * seldisks - pointer to a list containing the available disks 1047 * for the HBAs in selhbas 1048 * 1049 * RETURNS: int - 0 - on success 1050 * !0 - otherwise 1051 * 1052 * PURPOSE: helper which counts the number of available disks associated 1053 * with each of the input HBAs and adds those that have at 1054 * least mindisks to the output list. 1055 * 1056 * Only available disks that have available space are counted. 1057 * 1058 * Disks connected thru multiple HBAs are only counted for 1059 * the first HBA they're accessed through. 1060 * 1061 * The list of HBAs returned will be in descending order, 1062 * i.e., HBAs with more disks come before those with fewer. 1063 * 1064 * The returned lists of HBAs and disks must be passed to 1065 * dlist_free_items() to recover the space allocated to hold 1066 * each list item. 1067 * 1068 * for (each HBA) { 1069 * 1070 * select HBA 1071 * get available disks on HBA 1072 * 1073 * for (each disk) { 1074 * if (disk is not in selected disk list) 1075 * add it to the list 1076 * else 1077 * count it as a distinct disk on this HBA 1078 * } 1079 * 1080 * if (this HBA has >= mindisks distinct disks) 1081 * add this HBA to the list of returned HBAs 1082 * 1083 * } 1084 */ 1085 int 1086 select_hbas_with_n_disks( 1087 devconfig_t *request, 1088 dlist_t *hbas, 1089 int mindisks, 1090 dlist_t **selhbas, 1091 dlist_t **seldisks) 1092 { 1093 dlist_t *iter = NULL; 1094 int error = 0; 1095 1096 *selhbas = NULL; 1097 *seldisks = NULL; 1098 1099 /* for each input HBA */ 1100 for (iter = hbas; (error == 0) && (iter != NULL); iter = iter->next) { 1101 1102 dm_descriptor_t hba = (uintptr_t)iter->obj; 1103 dlist_t *iter2 = NULL; 1104 dlist_t *disks = NULL; 1105 uint64_t space = 0; 1106 uint16_t ndistinct = 0; 1107 1108 error = hba_get_avail_disks_and_space(request, hba, &disks, &space); 1109 1110 /* for each of this HBA's disks */ 1111 for (iter2 = disks; 1112 (iter2 != NULL) && (error == 0); 1113 iter2 = iter2->next) { 1114 1115 dm_descriptor_t disk = (uintptr_t)iter2->obj; 1116 1117 /* unique disk? has it been seen thru some other HBA? */ 1118 if (dlist_contains(*seldisks, (void *)(uintptr_t)disk, 1119 compare_descriptor_names) != B_TRUE) { 1120 1121 /* distinct, add to list of all_distinct */ 1122 dlist_t *item = dlist_new_item((void *)(uintptr_t)disk); 1123 if (item == NULL) { 1124 error = ENOMEM; 1125 } else { 1126 1127 *seldisks = 1128 dlist_append(item, *seldisks, AT_HEAD); 1129 1130 /* increment this HBA's distinct disk count */ 1131 ++ndistinct; 1132 } 1133 } 1134 } 1135 1136 if (ndistinct >= mindisks) { 1137 1138 /* this HBA has minimum # of disks, add to output list */ 1139 dlist_t *item = dlist_new_item((void *)(uintptr_t)hba); 1140 if (item == NULL) { 1141 error = ENOMEM; 1142 } else { 1143 *selhbas = 1144 dlist_insert_ordered( 1145 item, *selhbas, DESCENDING, 1146 compare_hba_n_avail_disks); 1147 1148 /* save # of disks for ordering the list */ 1149 hba_set_n_avail_disks(hba, ndistinct); 1150 } 1151 } 1152 1153 dlist_free_items(disks, NULL); 1154 } 1155 1156 if (error != 0) { 1157 oprintf(OUTPUT_TERSE, 1158 gettext("failed selecting HBAs with n disks: %d\n"), 1159 error); 1160 1161 dlist_free_items(*selhbas, NULL); 1162 *selhbas = NULL; 1163 dlist_free_items(*seldisks, NULL); 1164 *seldisks = NULL; 1165 } 1166 1167 return (error); 1168 } 1169 1170 /* 1171 * FUNCTION: hba_get_avail_disks_and_space(devconfig_t *request, 1172 * dm_descriptor_t hba, dlist_t **disks, uint64_t *space) 1173 * 1174 * INPUT: request - pointer to a devconfig_t struct representing 1175 * the current layout request being processed 1176 * hba - dm_descriptor_t handle for an HBA 1177 * 1178 * OUTPUT: disks - pointer to a list to hold the computed available 1179 * disks 1180 * avail - pointer to a uint64_t to hold the aggregate 1181 * available space on the available disks 1182 * 1183 * RETURNS: int - 0 - on success 1184 * !0 - otherwise 1185 * 1186 * PURPOSE: helper which examines the disks associated with the 1187 * input HBA and assembles a list of those that are available. 1188 * 1189 * Available is defined as being in the usable list, having 1190 * unused space and not specifically excluded by the request's 1191 * list of unavailable devices. 1192 * 1193 * The returned list must be passed to dlist_free_items() 1194 * to recover the memory allocated to hold each list item. 1195 */ 1196 int 1197 hba_get_avail_disks_and_space( 1198 devconfig_t *request, 1199 dm_descriptor_t hba, 1200 dlist_t **disks, 1201 uint64_t *space) 1202 { 1203 dlist_t *usable_disks = NULL; 1204 dlist_t *iter = NULL; 1205 int error = 0; 1206 1207 *disks = NULL; 1208 1209 /* for each usable disk */ 1210 error = get_usable_disks(&usable_disks); 1211 for (iter = usable_disks; 1212 (error == 0) && (iter != NULL); 1213 iter = iter->next) { 1214 1215 dm_descriptor_t disk = (uintptr_t)iter->obj; 1216 boolean_t avail = B_FALSE; 1217 dlist_t *hbas = NULL; 1218 1219 /* is disk attached to HBA in question? */ 1220 error = disk_get_hbas(disk, &hbas); 1221 if (error != 0) { 1222 continue; 1223 } 1224 1225 if (dlist_contains(hbas, (void *)(uintptr_t)hba, 1226 compare_descriptor_names) == B_TRUE) { 1227 1228 /* is disk available? */ 1229 error = is_device_avail(disk, request, &avail); 1230 if ((error == 0) && (avail == B_TRUE)) { 1231 uint64_t disk_space = 0; 1232 1233 /* does disk have available space? */ 1234 error = disk_get_avail_space(request, disk, &disk_space); 1235 if ((error == 0) && (disk_space > 0)) { 1236 1237 dlist_t *item = dlist_new_item((void *)(uintptr_t)disk); 1238 if (item == NULL) { 1239 error = ENOMEM; 1240 } else { 1241 *disks = dlist_append(item, *disks, AT_HEAD); 1242 } 1243 1244 *space += disk_space; 1245 } 1246 } 1247 } 1248 1249 dlist_free_items(hbas, NULL); 1250 } 1251 1252 if (error != 0) { 1253 dlist_free_items(*disks, NULL); 1254 *disks = NULL; 1255 } 1256 1257 return (error); 1258 } 1259 1260 /* 1261 * FUNCTION: disk_get_avail_space(devconfig_t *request, 1262 * dlist_t *disks, uint64_t space) 1263 * 1264 * INPUT: request - pointer to a devconfig_t struct representing 1265 * the current layout request being processed 1266 * disks - pointer to a list of disks 1267 * space - pointer to a uint64_t to hold the computed available 1268 * space 1269 * 1270 * RETURNS: int - 0 - on success 1271 * !0 - otherwise 1272 * 1273 * PURPOSE: helper which iterates the input list of disks and determines 1274 * the aggregate amount of available space they represent. 1275 * 1276 * Only disk slices that are in the usable slice list and not 1277 * specifically excluded by the request's list of unavailable 1278 * devices will contribute to the aggregate space computation. 1279 */ 1280 static int 1281 disk_get_avail_space( 1282 devconfig_t *request, 1283 dm_descriptor_t disk, 1284 uint64_t *space) 1285 { 1286 dlist_t *usable_slices = NULL; 1287 dlist_t *iter = NULL; 1288 int error = 0; 1289 1290 *space = 0; 1291 1292 /* for each usable slice */ 1293 error = get_usable_slices(&usable_slices); 1294 for (iter = usable_slices; 1295 (error == 0) && (iter != NULL); 1296 iter = iter->next) { 1297 1298 dm_descriptor_t slice = (uintptr_t)iter->obj; 1299 dm_descriptor_t slice_disk; 1300 boolean_t avail = B_FALSE; 1301 boolean_t reserved = B_FALSE; 1302 boolean_t used = B_FALSE; 1303 1304 /* is slice on disk in question? */ 1305 if (((error = slice_get_disk(slice, &slice_disk)) != 0) || 1306 (compare_descriptor_names((void *)(uintptr_t)slice_disk, 1307 (void *)(uintptr_t)disk) != 0)) { 1308 continue; 1309 } 1310 1311 /* is slice reserved by an explicit layout request? */ 1312 if (((error = is_reserved_slice(slice, &reserved)) != 0) || 1313 (reserved == B_TRUE)) { 1314 continue; 1315 } 1316 1317 /* is slice used by a pending layout request? */ 1318 if (((error = is_used_slice(slice, &used)) != 0) || 1319 (used == B_TRUE)) { 1320 continue; 1321 } 1322 1323 /* is slice available? */ 1324 if (((error = is_device_avail(slice, request, &avail)) == 0) && 1325 (avail == B_TRUE)) { 1326 1327 /* does slice have usable space? */ 1328 uint64_t size = 0; 1329 if ((error = slice_get_size(slice, &size)) == 0) { 1330 *space += size; 1331 } 1332 } 1333 } 1334 1335 return (error); 1336 } 1337 1338 /* 1339 * FUNCTION: disks_get_avail_slices(devconfig_t *request, 1340 * dlist_t *disks, dlist_t **slices) 1341 * 1342 * INPUT: request - pointer to a devconfig_t struct representing 1343 * the current layout request being processed 1344 * disks - pointer to a list of disks 1345 * slices - pointer to an output list of disks 1346 * 1347 * RETURNS: int - 0 - on success 1348 * !0 - otherwise 1349 * 1350 * PURPOSE: helper which iterates the input list of disks and builds a 1351 * new list which contains disks that are determined to be 1352 * available for satisfying the input request. 1353 * 1354 * A disk must contain at least one slice in the available 1355 * slice list as well as have available space in order 1356 * to be available. 1357 */ 1358 int 1359 disks_get_avail_slices( 1360 devconfig_t *request, 1361 dlist_t *disks, 1362 dlist_t **slices) 1363 { 1364 dlist_t *usable_slices = NULL; 1365 dlist_t *iter = NULL; 1366 int error = 0; 1367 1368 *slices = NULL; 1369 1370 /* for each usable slice */ 1371 error = get_usable_slices(&usable_slices); 1372 for (iter = usable_slices; 1373 (error == 0) && (iter != NULL); 1374 iter = iter->next) { 1375 1376 dm_descriptor_t slice = (uintptr_t)iter->obj; 1377 dm_descriptor_t disk = (dm_descriptor_t)0; 1378 boolean_t avail = B_FALSE; 1379 boolean_t reserved = B_FALSE; 1380 boolean_t used = B_FALSE; 1381 1382 /* is slice on a disk in the input list? */ 1383 if (((error = slice_get_disk(slice, &disk)) != 0) || 1384 (dlist_contains(disks, (void *)(uintptr_t)disk, 1385 compare_descriptor_names) != B_TRUE)) { 1386 continue; 1387 } 1388 1389 /* is slice reserved by an explicit layout request? */ 1390 if (((error = is_reserved_slice(slice, &reserved)) != 0) || 1391 (reserved == B_TRUE)) { 1392 continue; 1393 } 1394 1395 /* is slice used by a pending layout request? */ 1396 if (((error = is_used_slice(slice, &used)) != 0) || 1397 (used == B_TRUE)) { 1398 continue; 1399 } 1400 1401 /* is slice available? */ 1402 if (((error = is_device_avail(slice, request, &avail)) == 0) && 1403 (avail == B_TRUE)) { 1404 1405 /* does slice have available space? */ 1406 uint64_t size = 0; 1407 error = slice_get_size(slice, &size); 1408 if ((error == 0) && (size > 0)) { 1409 dlist_t *item = dlist_new_item((void *)(uintptr_t)slice); 1410 if (item == NULL) { 1411 error = ENOMEM; 1412 } else { 1413 *slices = dlist_append(item, *slices, AT_TAIL); 1414 } 1415 } 1416 } 1417 } 1418 1419 if (error != 0) { 1420 dlist_free_items(*slices, NULL); 1421 *slices = NULL; 1422 } 1423 1424 return (error); 1425 } 1426 1427 1428 /* 1429 * FUNCTION: get_hbas_and_disks_used_by_volumes(dlist_t *volumes, 1430 * dlist_t **hbas, dlist_t **disks) 1431 * 1432 * INPUT: volumes - pointer to a list of devconfig_t volumes 1433 * 1434 * OUTPUT: hbas - a list of HBAs utilized by the input volumes 1435 * disks - a list of disks utilized by the input volumes 1436 * 1437 * RETURNS: int - 0 on success 1438 * !0 otherwise 1439 * 1440 * PURPOSE: An aggregate list of HBAs and disks used by the input volumes 1441 * is built up by iterating the list of volumes and calling 1442 * get_hbas_disks_used_by_volume() to determine the HBAs and disk 1443 * used by each volume. 1444 * 1445 * The returned lists of HBAs and disks may contain duplicates. 1446 */ 1447 int 1448 get_hbas_and_disks_used_by_volumes( 1449 dlist_t *volumes, 1450 dlist_t **hbas, 1451 dlist_t **disks) 1452 { 1453 dlist_t *iter = NULL; 1454 int error = 0; 1455 1456 for (iter = volumes; 1457 (iter != NULL) && (error == 0); 1458 iter = iter->next) { 1459 error = get_hbas_and_disks_used_by_volume( 1460 (devconfig_t *)iter->obj, hbas, disks); 1461 } 1462 1463 return (error); 1464 } 1465 1466 /* 1467 * FUNCTION: get_hbas_and_disks_used_by_volume(devconfig_t *volume, 1468 * dlist_t **hbas, dlist_t **disks) 1469 * 1470 * INPUT: volume - pointer to a devconfig_t volume 1471 * 1472 * OUTPUT: hbas - a list of HBAs updated to include those utilized 1473 * by the input volume 1474 * disks - a list of disks updated to inlclude those utilized 1475 * by the input volume 1476 * 1477 * RETURNS: int - 0 on success 1478 * !0 otherwise 1479 * 1480 * PURPOSE: The volume's components are iterated and the disks and HBAs 1481 * for each component are determined and appended to the input 1482 * lists of HBAs and disks. 1483 * 1484 * The returned lists of HBAs and disks may contain duplicates. 1485 */ 1486 int 1487 get_hbas_and_disks_used_by_volume( 1488 devconfig_t *volume, 1489 dlist_t **hbas, 1490 dlist_t **disks) 1491 { 1492 dlist_t *iter = NULL; 1493 int error = 0; 1494 1495 for (iter = devconfig_get_components(volume); 1496 (iter != NULL) && (error == 0); 1497 iter = iter->next) { 1498 1499 devconfig_t *dev = (devconfig_t *)iter->obj; 1500 if (devconfig_isA(dev, TYPE_SLICE)) { 1501 1502 dm_descriptor_t disk = NULL; 1503 char *name = NULL; 1504 1505 /* get disk for component slice */ 1506 ((error = devconfig_get_name(dev, &name)) != 0) || 1507 (error = get_disk_for_named_slice(name, &disk)); 1508 if (error == 0) { 1509 dlist_t *item = dlist_new_item((void *)(uintptr_t)disk); 1510 if (item == NULL) { 1511 error = ENOMEM; 1512 } else { 1513 *disks = dlist_append(item, *disks, AT_HEAD); 1514 } 1515 } 1516 1517 /* get HBAs for disk */ 1518 if (error == 0) { 1519 dlist_t *disk_hbas = NULL; 1520 if ((error = disk_get_hbas(disk, &disk_hbas)) == 0) { 1521 /* the hba list may contain dups, but that's ok */ 1522 *hbas = dlist_append(disk_hbas, *hbas, AT_HEAD); 1523 } 1524 } 1525 1526 } else if (devconfig_isA(dev, TYPE_MIRROR)) { 1527 1528 /* collect info for submirrors */ 1529 dlist_t *iter1; 1530 for (iter1 = devconfig_get_components(dev); 1531 (iter1 != NULL) && (error == 0); 1532 iter1 = iter1->next) { 1533 error = get_hbas_and_disks_used_by_volume( 1534 (devconfig_t *)iter1->obj, hbas, disks); 1535 } 1536 1537 } 1538 } 1539 1540 return (error); 1541 } 1542 1543 /* 1544 * FUNCTION: compare_hba_n_avail_disks(void *obj1, void *obj2) 1545 * 1546 * INPUT: obj1 - opaque pointer 1547 * obj2 - opaque pointer 1548 * 1549 * RETURNS: int - <0 - if obj1 has fewer available disks than obj2 1550 * 0 - if obj1 has the same # of available disks as obj2 1551 * >0 - if obj1 has more available disks than obj2 1552 * 1553 * PURPOSE: dlist_t helper which compares the number of available disks 1554 * for two HBAs represented as dm_descriptor_t handles. 1555 * 1556 * Both input objects are assumed to be dm_descriptor_t handles. 1557 * 1558 * The number of available disks associated with the HBAs was 1559 * computed and saved in select_hbas_with_n_disks(), this 1560 * function just checks the saved values. 1561 */ 1562 static int 1563 compare_hba_n_avail_disks( 1564 void *obj1, 1565 void *obj2) 1566 { 1567 uint16_t n1 = 0; 1568 uint16_t n2 = 0; 1569 1570 assert(obj1 != NULL); 1571 assert(obj2 != NULL); 1572 1573 (void) hba_get_n_avail_disks((uintptr_t)obj1, &n1); 1574 (void) hba_get_n_avail_disks((uintptr_t)obj2, &n2); 1575 1576 return ((int)n1 - n2); 1577 } 1578 1579 /* 1580 * FUNCTION: is_device_avail(dm_descriptor_t desc, 1581 * devconfig_t *request, boolean_t *avail) 1582 * 1583 * INPUT: desc - a dm_descriptor_t device handle 1584 * request - pointer to a devconfig_t struct representing 1585 * the current layout request being processed 1586 * avail - pointer to a boolean to hold the result 1587 * 1588 * RETURNS: int - 0 - on success 1589 * !0 - otherwise 1590 * 1591 * PURPOSE: Internal helper which determines if the input device can 1592 * be used as a volume component when satisfying the input 1593 * request. 1594 * 1595 * The device is assumed to be a known valid device. 1596 * 1597 * The function checks if the device passes the request's 1598 * available and unavailable device specifications. 1599 * 1600 * The input device name may be either a DID name or a CTD 1601 * name. All name comparisons are done using the CTD name. 1602 */ 1603 static int 1604 is_device_avail( 1605 dm_descriptor_t desc, 1606 devconfig_t *request, 1607 boolean_t *avail) 1608 { 1609 char *name = NULL; 1610 int error = 0; 1611 1612 *avail = B_FALSE; 1613 1614 if ((error = get_display_name(desc, &name)) == 0) { 1615 error = is_named_device_avail(request, name, B_TRUE, avail); 1616 } 1617 1618 return (error); 1619 } 1620 1621 /* 1622 * FUNCTION: compare_request_to_request_spec_list_request( 1623 * void *request, void *list_item) 1624 * 1625 * INPUT: request - opaque pointer to a devconfig_t 1626 * list_item - opaque pointer to a request_spec_list_t 1627 * 1628 * RETURNS: int - 0 - if request is the same as list_item->request 1629 * !0 - otherwise 1630 * 1631 * PURPOSE: dlist_t helper which compares the input request pointer 1632 * to the list_item's request pointer for equality. 1633 * 1634 * This function is the lookup mechanism for the lists of 1635 * cached device_spec_ts representing available/unavailable 1636 * devices for a given defaults_t request/defaults struct. 1637 * 1638 * The defaults_t struct pointer is the lookup key. 1639 */ 1640 static int 1641 compare_request_to_request_spec_list_request( 1642 void *request, 1643 void *list_item) 1644 { 1645 request_spec_list_t *entry = 1646 (request_spec_list_t *)list_item; 1647 1648 assert(request != NULL); 1649 assert(entry != NULL); 1650 1651 /* compare two devconfig_t pointers, if identical, return 0 */ 1652 return ((devconfig_t *)request != entry->request); 1653 } 1654 1655 /* 1656 * FUNCTION: compare_device_spec_specificity(void *spec1, void *spec2) 1657 * 1658 * INPUT: spec1 - opaque pointer to a device_spec_t 1659 * spec2 - opaque pointer to a device_spec_t 1660 * 1661 * RETURNS: int - <0 - if spec1 is less specific than spec2 1662 * 0 - if spec1 is as specific than spec2 1663 * >0 - if spec1 is more specific than spec2 1664 * 1665 * PURPOSE: dlist_t helper which compares the level of specificity 1666 * in the two input device_spec_t structs. The one 1667 * which specifies more "components" of a cXtXdXsX device 1668 * name is considered more specific. 1669 */ 1670 static int 1671 compare_device_spec_specificity( 1672 void *spec1, 1673 void *spec2) 1674 { 1675 if (spec1 == NULL || spec2 == NULL) { 1676 return (-1); 1677 } 1678 1679 if ((((device_spec_t *)spec1)->data.ctd->slice != ID_UNSPECIFIED) && 1680 (((device_spec_t *)spec2)->data.ctd->slice == ID_UNSPECIFIED)) { 1681 /* spec1 has slice, spec2 does not, spec1 more specific */ 1682 return (1); 1683 } 1684 1685 if ((((device_spec_t *)spec2)->data.ctd->slice != ID_UNSPECIFIED) && 1686 (((device_spec_t *)spec1)->data.ctd->slice == ID_UNSPECIFIED)) { 1687 /* spec2 has slice, spec1 does not, spec2 more specific */ 1688 return (-1); 1689 } 1690 1691 if ((((device_spec_t *)spec2)->data.ctd->slice != ID_UNSPECIFIED) && 1692 (((device_spec_t *)spec1)->data.ctd->slice != ID_UNSPECIFIED)) { 1693 /* both spec1 and spec2 have slice */ 1694 return (0); 1695 } 1696 1697 if ((((device_spec_t *)spec1)->data.ctd->lun != ID_UNSPECIFIED) && 1698 (((device_spec_t *)spec2)->data.ctd->lun == ID_UNSPECIFIED)) { 1699 /* spec1 has lun, spec2 does not, spec1 more specific */ 1700 return (1); 1701 } 1702 1703 if ((((device_spec_t *)spec2)->data.ctd->lun != ID_UNSPECIFIED) && 1704 (((device_spec_t *)spec1)->data.ctd->lun == ID_UNSPECIFIED)) { 1705 /* spec2 has lun, spec1 does not, spec2 more specific */ 1706 return (-1); 1707 } 1708 1709 if ((((device_spec_t *)spec2)->data.ctd->lun != ID_UNSPECIFIED) && 1710 (((device_spec_t *)spec1)->data.ctd->lun != ID_UNSPECIFIED)) { 1711 /* both spec1 and spec2 have lun */ 1712 return (0); 1713 } 1714 1715 if ((((device_spec_t *)spec1)->data.ctd->target != ID_UNSPECIFIED) && 1716 (((device_spec_t *)spec2)->data.ctd->target == ID_UNSPECIFIED)) { 1717 /* spec1 has target, spec2 does not, spec1 more specific */ 1718 return (1); 1719 } 1720 1721 if ((((device_spec_t *)spec2)->data.ctd->target != ID_UNSPECIFIED) && 1722 (((device_spec_t *)spec1)->data.ctd->target == ID_UNSPECIFIED)) { 1723 /* spec2 has target, spec1 does not, spec2 more specific */ 1724 return (-1); 1725 } 1726 1727 if ((((device_spec_t *)spec2)->data.ctd->target != ID_UNSPECIFIED) && 1728 (((device_spec_t *)spec1)->data.ctd->target != ID_UNSPECIFIED)) { 1729 /* both spec1 and spec2 have target */ 1730 return (0); 1731 } 1732 1733 /* both specify just ctrl */ 1734 return (0); 1735 } 1736 1737 /* 1738 * FUNCTION: find_request_spec_list_entry(devconfig_t *request) 1739 * 1740 * INPUT: request - pointer to a devconfig_t struct 1741 * 1742 * RETURNS: request_spec_list_entry - pointer to a 1743 * request_spec_list_entry struct 1744 * 1745 * PURPOSE: Lookup function which encapsulates the details of locating 1746 * the device_spec_list_t cache entry for the input request. 1747 */ 1748 static request_spec_list_t * 1749 find_request_spec_list_entry( 1750 devconfig_t *request) 1751 { 1752 dlist_t *list_item = NULL; 1753 request_spec_list_t *entry = NULL; 1754 1755 list_item = dlist_find( 1756 _request_spec_list_cache, 1757 (void *)request, 1758 compare_request_to_request_spec_list_request); 1759 1760 if (list_item != NULL) { 1761 entry = (request_spec_list_t *)list_item->obj; 1762 } 1763 1764 return (entry); 1765 } 1766 1767 /* 1768 * FUNCTION: add_request_spec_list_entry(devconfig_t *request, 1769 * char **avail_device_specs, char **unavail_device_specs, 1770 * request_spec_list_entry_t **entry) 1771 * 1772 * INPUT: entry - pointer to the request_spec_list_entry struct to be 1773 * added to the cache. 1774 * 1775 * RETURNS: int - 0 on success 1776 * !0 otherwise. 1777 * 1778 * PURPOSE: Function which encapsulates the details of adding a 1779 * device_spec_list_t cache entry. 1780 */ 1781 static int 1782 add_request_spec_list_entry( 1783 request_spec_list_t *entry) 1784 { 1785 dlist_t *list_item = dlist_new_item((void *)entry); 1786 1787 if (list_item == NULL) { 1788 return (ENOMEM); 1789 } 1790 1791 _request_spec_list_cache = dlist_append(list_item, 1792 _request_spec_list_cache, AT_HEAD); 1793 1794 return (0); 1795 } 1796 1797 /* 1798 * FUNCTION: make_request_spec_list_entry(devconfig_t *request, 1799 * char **avail_device_specs, char **unavail_device_specs, 1800 * request_spec_list_entry_t **entry) 1801 * 1802 * INPUT: request - pointer to a devconfig_t struct 1803 * avail_device_specs - char * array of user specified available 1804 * devices associated with the input request 1805 * unavail_device_specs - char * array of user specified 1806 * unavailable devices associated with the input 1807 * request 1808 * 1809 * RETURNS: int - 0 on success 1810 * !0 otherwise. 1811 * 1812 * PURPOSE: Function which encapsulates the details of generating a new 1813 * the device_spec_list_t cache entry for the input request 1814 * and its lists of avail/unavail devices. 1815 * 1816 * Converts the input arrays of (un)available device names into 1817 * equivalent lists of device_spec_t structs. 1818 * 1819 * Creates a new cache entry, populates it and adds it to the 1820 * cache. 1821 */ 1822 static int 1823 make_request_spec_list_entry( 1824 devconfig_t *request, 1825 char **avail_device_specs, 1826 char **unavail_device_specs, 1827 request_spec_list_t **entry) 1828 { 1829 int error = 0; 1830 dlist_t *list = NULL; 1831 1832 *entry = calloc(1, sizeof (request_spec_list_t)); 1833 if (*entry == NULL) { 1834 return (ENOMEM); 1835 } 1836 1837 (*entry)->request = request; 1838 1839 /* 1840 * map the avail_device_name array into a list of device_spec_t 1841 * and save the list as the entry's available list 1842 */ 1843 error = convert_usernames_to_specs( 1844 avail_device_specs, &list); 1845 1846 if (error == 0) { 1847 (*entry)->avail_specs_list = list; 1848 } 1849 1850 /* 1851 * map the unavail_device_name array into a list of device_spec_t 1852 * and save the list as the entry's unavailable list 1853 */ 1854 list = NULL; 1855 error = convert_usernames_to_specs( 1856 unavail_device_specs, &list); 1857 1858 if (error == 0) { 1859 (*entry)->unavail_specs_list = list; 1860 } 1861 1862 if (error != 0) { 1863 /* delete the partial entry */ 1864 destroy_request_spec_list_entry((void *)*entry); 1865 *entry = NULL; 1866 } 1867 1868 return (error); 1869 } 1870 1871 /* 1872 * FUNCTION: convert_usernames_to_specs(char **specs, dlist_t **list) 1873 * 1874 * INPUT: specs - char * array of device CTD names 1875 * 1876 * OUTPUT: list - pointer to a list of device_spec_t corresponding 1877 * to each name in the input array 1878 * 1879 * RETURNS: int - 0 on success 1880 * !0 otherwise. 1881 * 1882 * PURPOSE: Function which converts the input CTD device names to the 1883 * equivalent device_spec_t structs. 1884 * 1885 * Iterates the input array and converts each CTD name to a 1886 * device_spec_t using get_spec_for_name(). 1887 */ 1888 static int 1889 convert_usernames_to_specs( 1890 char **specs, 1891 dlist_t **list) 1892 { 1893 int i = 0; 1894 int error = 0; 1895 1896 /* 1897 * For each spec in the array, get the corresponding 1898 * device_spec_t and add it to the list. 1899 * 1900 * Any spec in the array that looks to be a DID name 1901 * is first converted to its equivalent CTD name. 1902 */ 1903 for (i = 0; 1904 (specs != NULL) && (specs[i] != NULL) && (error == 0); 1905 i++) { 1906 1907 device_spec_t *spec = NULL; 1908 char *userspec = specs[i]; 1909 1910 error = get_spec_for_name(userspec, &spec); 1911 if ((error == 0) && (spec != NULL)) { 1912 dlist_t *list_item = dlist_new_item((void *)spec); 1913 if (spec == NULL) { 1914 error = ENOMEM; 1915 } else { 1916 *list = dlist_insert_ordered 1917 (list_item, *list, DESCENDING, 1918 compare_device_spec_specificity); 1919 } 1920 } 1921 } 1922 1923 if (error != 0) { 1924 /* the device_spec_t in the list items are maintained */ 1925 /* in a cache elsewhere, so don't free them here. */ 1926 dlist_free_items(*list, NULL); 1927 *list = NULL; 1928 } 1929 1930 return (error); 1931 } 1932 1933 /* 1934 * FUNCTION: destroy_request_spec_list_entry(void *entry) 1935 * 1936 * INPUT: entry - opaque pointer to a request_spec_list_t 1937 * 1938 * RETURNS: nothing 1939 * 1940 * PURPOSE: Function which reclaims memory allocated to a 1941 * request_spec_list_t. 1942 * 1943 * Frees memory allocated to the avail_spec_list and 1944 * unavail_spec_list. Entries in the list are not freed, 1945 * since they are owned by the device_spec cache. 1946 */ 1947 static void 1948 destroy_request_spec_list_entry( 1949 void *obj) 1950 { 1951 request_spec_list_t *entry = (request_spec_list_t *)obj; 1952 1953 if (entry != NULL) { 1954 /* items in the list are in the spec_cache and will */ 1955 /* be cleaned up when it is destroyed. */ 1956 dlist_free_items(entry->avail_specs_list, NULL); 1957 dlist_free_items(entry->unavail_specs_list, NULL); 1958 free(entry); 1959 } 1960 } 1961 1962 /* 1963 * FUNCTION: destroy_request_spec_list_cache() 1964 * 1965 * RETURNS: int - 0 on success 1966 * !0 otherwise. 1967 * 1968 * PURPOSE: Function which destroys all entries in the request_spec_list 1969 * cache. 1970 */ 1971 static int 1972 destroy_request_spec_list_cache() 1973 { 1974 dlist_free_items(_request_spec_list_cache, 1975 destroy_request_spec_list_entry); 1976 _request_spec_list_cache = NULL; 1977 1978 return (0); 1979 } 1980 1981 /* 1982 * FUNCTION: get_request_avail_spec_list(devconfig_t *request, 1983 * dlist_t **list) 1984 * 1985 * INPUT: request - a pointer to a devconfig_t 1986 * 1987 * OUTPUT: list - pointer to a list of device_spec_t corresponding 1988 * to the devices specified as available by the 1989 * input request. 1990 * 1991 * RETURNS: int - 0 on success 1992 * !0 otherwise. 1993 * 1994 * PURPOSE: Function which locates or builds the list of device_spec_t 1995 * for the available devices specified in the input request. 1996 * 1997 * Looks up the input request in the request_spec_list cache. 1998 * If there is currently no entry in the cache for the request, 1999 * an entry is built and added. 2000 * 2001 * The entry's list of available device_spec_t is returned. 2002 */ 2003 static int 2004 get_request_avail_spec_list( 2005 devconfig_t *request, 2006 dlist_t **list) 2007 { 2008 request_spec_list_t *entry = NULL; 2009 int error = 0; 2010 2011 if ((entry = find_request_spec_list_entry(request)) == NULL) { 2012 2013 /* create cache entry for this request */ 2014 error = make_request_spec_list_entry( 2015 request, 2016 devconfig_get_available(request), 2017 devconfig_get_unavailable(request), 2018 &entry); 2019 2020 if ((error == 0) && (entry != NULL)) { 2021 if ((error = add_request_spec_list_entry(entry)) != 0) { 2022 destroy_request_spec_list_entry(entry); 2023 entry = NULL; 2024 } 2025 } 2026 } 2027 2028 if ((error == 0) && (entry != NULL)) { 2029 *list = entry->avail_specs_list; 2030 } 2031 2032 return (error); 2033 } 2034 2035 /* 2036 * FUNCTION: get_request_unavail_spec_list(devconfig_t *request, 2037 * dlist_t **list) 2038 * 2039 * INPUT: request - a pointer to a devconfig_t 2040 * 2041 * OUTPUT: list - pointer to a list of device_spec_t corresponding 2042 * to the devices specified as unavailable by the 2043 * input request. 2044 * 2045 * RETURNS: int - 0 on success 2046 * !0 otherwise. 2047 * 2048 * PURPOSE: Function which locates or builds the list of device_spec_t 2049 * for the unavailable devices specified in the input request. 2050 * 2051 * Looks up the input request in the request_spec_list cache. 2052 * If there is currently no entry in the cache for the request, 2053 * an entry is built and added. 2054 * 2055 * The entry's list of unavailable device_spec_t is returned. 2056 */ 2057 static int 2058 get_request_unavail_spec_list( 2059 devconfig_t *request, 2060 dlist_t **list) 2061 { 2062 request_spec_list_t *entry = NULL; 2063 int error = 0; 2064 2065 if ((entry = find_request_spec_list_entry(request)) == NULL) { 2066 2067 /* create new entry for this request */ 2068 error = make_request_spec_list_entry( 2069 request, 2070 devconfig_get_available(request), 2071 devconfig_get_unavailable(request), 2072 &entry); 2073 2074 if ((error == 0) && (entry != NULL)) { 2075 if ((error = add_request_spec_list_entry(entry)) != 0) { 2076 destroy_request_spec_list_entry(entry); 2077 entry = NULL; 2078 } 2079 } 2080 } 2081 2082 if ((error == 0) && (entry != NULL)) { 2083 *list = entry->unavail_specs_list; 2084 } 2085 2086 return (error); 2087 } 2088 2089 /* 2090 * FUNCTION: get_default_avail_spec_list(defaults_t *defaults, 2091 * char *dsname, dlist_t **list) 2092 * 2093 * INPUT: defaults - a pointer to a defaults_t struct 2094 * dsname - the name of the diskset whose defaults should be used 2095 * 2096 * OUTPUT: list - pointer to a list of device_spec_t corresponding 2097 * to the devices specified as available by the 2098 * defaults for the named diskset, or the global 2099 * defaults for all disksets. 2100 * 2101 * RETURNS: int - 0 on success 2102 * !0 otherwise. 2103 * 2104 * PURPOSE: Function which locates or builds the list of device_spec_t 2105 * for the available devices for the named diskset. 2106 * 2107 * Locates the defaults for the named diskset, if there are none, 2108 * locates the global defaults for all disksets. 2109 * 2110 * The defaults devconfig_t struct is then used to look up the 2111 * the corresponding entry in the request_spec_list cache. 2112 * 2113 * If there is currently no entry in the cache for the defaults, 2114 * an entry is built and added. 2115 * 2116 * The entry's list of available device_spec_t is returned. 2117 */ 2118 static int 2119 get_default_avail_spec_list( 2120 defaults_t *alldefaults, 2121 char *dsname, 2122 dlist_t **list) 2123 { 2124 request_spec_list_t *entry = NULL; 2125 devconfig_t *defaults = NULL; 2126 int error = 0; 2127 2128 /* Get diskset defaults, or global if none for diskset */ 2129 error = defaults_get_diskset_by_name( 2130 alldefaults, dsname, &defaults); 2131 2132 if (error != 0) { 2133 if (error == ENOENT) { 2134 /* to get global defaults, pass a NULL diskset name */ 2135 error = defaults_get_diskset_by_name( 2136 alldefaults, NULL, &defaults); 2137 } 2138 2139 if (error != 0) { 2140 if (error != ENOENT) { 2141 oprintf(OUTPUT_DEBUG, 2142 gettext("get defaults for %s returned %d\n"), 2143 dsname, error); 2144 } else { 2145 error = 0; 2146 } 2147 } 2148 } 2149 2150 if ((entry = find_request_spec_list_entry(defaults)) == NULL) { 2151 2152 /* create new entry for these defaults */ 2153 error = make_request_spec_list_entry( 2154 defaults, 2155 devconfig_get_available(defaults), 2156 devconfig_get_unavailable(defaults), 2157 &entry); 2158 2159 if ((error == 0) && (entry != NULL)) { 2160 if ((error = add_request_spec_list_entry(entry)) != 0) { 2161 destroy_request_spec_list_entry(entry); 2162 entry = NULL; 2163 } 2164 } 2165 } 2166 2167 if ((error == 0) && (entry != NULL)) { 2168 *list = entry->avail_specs_list; 2169 } 2170 2171 return (error); 2172 } 2173 2174 /* 2175 * FUNCTION: get_default_unavail_spec_list(defaults_t *defaults, 2176 * char *dsname, dlist_t **list) 2177 * 2178 * INPUT: defaults - a pointer to a defaults_t struct 2179 * dsname - the name of the diskset whose defaults should be used 2180 * 2181 * OUTPUT: list - pointer to a list of device_spec_t corresponding 2182 * to the devices specified as unavailable by the 2183 * defaults for the named diskset, or the global 2184 * defaults for all disksets. 2185 * 2186 * RETURNS: int - 0 on success 2187 * !0 otherwise. 2188 * 2189 * PURPOSE: Function which locates or builds the list of device_spec_t 2190 * for the unavailable devices for the named diskset. 2191 * 2192 * Locates the defaults for the named diskset, if there are none, 2193 * locates the global defaults for all disksets. 2194 * 2195 * The defaults devconfig_t struct is then used to look up the 2196 * the corresponding entry in the request_spec_list cache. 2197 * 2198 * If there is currently no entry in the cache for the defaults, 2199 * an entry is built and added. 2200 * 2201 * The entry's list of unavailable device_spec_t is returned. 2202 */ 2203 static int 2204 get_default_unavail_spec_list( 2205 defaults_t *alldefaults, 2206 char *dsname, 2207 dlist_t **list) 2208 { 2209 request_spec_list_t *entry = NULL; 2210 devconfig_t *defaults = NULL; 2211 int error = 0; 2212 2213 /* Get diskset defaults, or global if none for diskset */ 2214 error = defaults_get_diskset_by_name( 2215 alldefaults, dsname, &defaults); 2216 2217 if (error != 0) { 2218 2219 if (error == ENOENT) { 2220 /* to get global defaults, pass a NULL diskset name */ 2221 error = defaults_get_diskset_by_name( 2222 alldefaults, NULL, &defaults); 2223 } 2224 2225 if (error != 0) { 2226 if (error != ENOENT) { 2227 oprintf(OUTPUT_DEBUG, 2228 gettext("get defaults for %s returned %d\n"), 2229 dsname, error); 2230 } else { 2231 error = 0; 2232 } 2233 } 2234 } 2235 2236 if ((entry = find_request_spec_list_entry(defaults)) == NULL) { 2237 2238 /* create new entry for these defaults */ 2239 error = make_request_spec_list_entry( 2240 defaults, 2241 devconfig_get_available(defaults), 2242 devconfig_get_unavailable(defaults), 2243 &entry); 2244 2245 if ((error == 0) && (entry != NULL)) { 2246 if ((error = add_request_spec_list_entry(entry)) != 0) { 2247 destroy_request_spec_list_entry(entry); 2248 entry = NULL; 2249 } 2250 } 2251 } 2252 2253 if ((error == 0) && (entry != NULL)) { 2254 *list = entry->unavail_specs_list; 2255 } 2256 2257 return (error); 2258 } 2259 2260 /* 2261 * FUNCTION: is_named_device_avail(devconfig_t *request, char *device_name, 2262 * boolean_t check_aliases, boolean_t *avail) 2263 * 2264 * INPUT: request - the current request devconfig_t 2265 * device_name - char * device name 2266 * check_aliases - boolean_t which indicates whether the device's 2267 * aliases should be considered by the availability checks. 2268 * 2269 * OUTPUT: avail - a boolean_t * to hold the result 2270 * 2271 * RETURNS: int - !0 on error 2272 * 2273 * avail is set to B_TRUE if the named device is available for 2274 * the input request, B_FALSE otherwise. 2275 * 2276 * PURPOSE: Determine if the named device can be used to satisfy the 2277 * input request. 2278 * 2279 * There are several levels at which device availabiity or 2280 * unavailability may be specifed: 2281 * 2282 * 1. the volume subrequest, 2283 * 2. the toplevel (diskset) request, 2284 * 3. the diskset-specific defaults 2285 * 4. the global defaults 2286 * 2287 * If the diskset-specific defaults exist, only they are checked. 2288 * 2289 * The precedence ordering that is enforced: 2290 * 2291 * 1. if request has an avail list, the name must be in it 2292 * and not in the request's unavail list. 2293 * 2. if request has an unavail list, the name must not be in it. 2294 * 3. if toplevel request has an avail list, the name must be 2295 * in it and not in the toplevel request's unavailable 2296 * list. 2297 * 4. if toplevel request has an unavail list, the name must 2298 * not be in it. 2299 * 5. if defaults have an avail list, the name must be in it 2300 * and not in the defaults unavailable list. 2301 * 6. if defaults have an unavail list, the name must not be 2302 * in it. 2303 */ 2304 static int 2305 is_named_device_avail( 2306 devconfig_t *request, 2307 char *device_name, 2308 boolean_t check_aliases, 2309 boolean_t *avail) 2310 { 2311 typedef enum check_types { 2312 DEVICE_REQUEST = 0, 2313 DISKSET_REQUEST, 2314 DEFAULTS, 2315 N_CHECKS 2316 } check_type_t; 2317 2318 check_type_t check_type; 2319 2320 typedef enum list_types { 2321 AVAIL = 0, 2322 UNAVAIL, 2323 N_LISTS 2324 } list_type_t; 2325 2326 dlist_t *lists[N_CHECKS][N_LISTS]; 2327 boolean_t includes; 2328 int error = 0; 2329 2330 memset(lists, 0, (N_CHECKS * N_LISTS) * sizeof (dlist_t *)); 2331 2332 if (request != NULL) { 2333 /* get avail/unavail specs for request */ 2334 ((error = get_request_avail_spec_list( 2335 request, &lists[DEVICE_REQUEST][AVAIL])) != 0) || 2336 (error = get_request_unavail_spec_list( 2337 request, &lists[DEVICE_REQUEST][UNAVAIL])); 2338 } 2339 2340 if ((error == 0) && (_toplevel_request != NULL)) { 2341 /* diskset request */ 2342 ((error = get_request_avail_spec_list( 2343 _toplevel_request, &lists[DISKSET_REQUEST][AVAIL])) != 0) || 2344 (error = get_request_unavail_spec_list( 2345 _toplevel_request, &lists[DISKSET_REQUEST][UNAVAIL])); 2346 } 2347 2348 if ((error == 0) && (_defaults != NULL)) { 2349 /* and diskset/global defaults */ 2350 ((error = get_default_avail_spec_list(_defaults, 2351 get_request_diskset(), &lists[DEFAULTS][AVAIL])) != 0) || 2352 (error = get_default_unavail_spec_list(_defaults, 2353 get_request_diskset(), &lists[DEFAULTS][UNAVAIL])); 2354 } 2355 2356 if (error != 0) { 2357 return (error); 2358 } 2359 2360 *avail = B_TRUE; 2361 2362 for (check_type = DEVICE_REQUEST; 2363 (check_type < N_CHECKS) && (error == 0); 2364 check_type++) { 2365 2366 if (lists[check_type][AVAIL] != NULL) { 2367 2368 /* does avail spec list include named device? */ 2369 if ((error = avail_list_includes_device_name( 2370 lists[check_type][AVAIL], device_name, check_aliases, 2371 &includes)) == 0) { 2372 2373 if (includes != B_TRUE) { 2374 *avail = B_FALSE; 2375 } 2376 2377 if ((includes == B_TRUE) && 2378 (lists[check_type][UNAVAIL] != NULL)) { 2379 2380 /* device is available, is it in the unavail list? */ 2381 if ((error = unavail_list_includes_device_name( 2382 lists[check_type][UNAVAIL], device_name, 2383 check_aliases, &includes)) == 0) { 2384 2385 if (includes == B_TRUE) { 2386 *avail = B_FALSE; 2387 } 2388 } 2389 } 2390 } 2391 2392 /* lists at this level checked, skip remainder */ 2393 break; 2394 2395 } else if (lists[check_type][UNAVAIL] != NULL) { 2396 2397 /* does unavail spec list include named device? */ 2398 if ((error = unavail_list_includes_device_name( 2399 lists[check_type][UNAVAIL], device_name, 2400 check_aliases, &includes)) == 0) { 2401 2402 if (includes == B_TRUE) { 2403 *avail = B_FALSE; 2404 } 2405 } 2406 2407 /* list at this level checked, skip remainder */ 2408 break; 2409 } 2410 } 2411 2412 return (error); 2413 } 2414 2415 /* 2416 * FUNCTION: avail_list_includes_device_name(dlist_t *list, 2417 * char *device_name, boolean_t check_aliases, 2418 * boolean_t *includes) 2419 * 2420 * INPUT: list - a dlist_t list of available device_spec_t 2421 * device_name - a char * device CTD name 2422 * check_aliases - boolean_t which indicates if the device's 2423 * aliases should be considered in the availability 2424 * checking. 2425 * 2426 * OUTPUT: includes - B_TRUE - if named device is "included" by any 2427 * specification in the input list 2428 * B_FALSE - otherwise 2429 * 2430 * RETURNS: int - 0 on success 2431 * - !0 otherwise 2432 * 2433 * PURPOSE: Helper used by is_named_device_avail that determines 2434 * if the input list of device specifications "includes" 2435 * a specific device. 2436 * 2437 * Iterates the elements of the input array and searches 2438 * for a match using spec_includes_device_name(). 2439 */ 2440 static int 2441 avail_list_includes_device_name( 2442 dlist_t *list, 2443 char *device_name, 2444 boolean_t check_aliases, 2445 boolean_t *includes) 2446 { 2447 dlist_t *iter = NULL; 2448 int error = 0; 2449 2450 *includes = B_FALSE; 2451 2452 for (iter = list; 2453 (*includes == B_FALSE) && (iter != NULL) && (error == 0); 2454 iter = iter->next) { 2455 2456 device_spec_t *spec = (device_spec_t *)iter->obj; 2457 error = spec_includes_device_name(spec, device_name, 2458 check_aliases, includes); 2459 } 2460 2461 return (0); 2462 } 2463 2464 /* 2465 * FUNCTION: unavail_list_includes_device_name(dlist_t *list, 2466 * char *device_name, boolean_t check_aliases, 2467 * boolean_t *includes) 2468 * 2469 * INPUT: list - a dlist_t list of unavailable device_spec_t 2470 * device_name - a char * device CTD name 2471 * check_aliases - boolean_t which indicates if the device's 2472 * aliases should be considered in the availability 2473 * checking. 2474 * 2475 * OUTPUT: includes - B_TRUE - if named device is "included" by any 2476 * specification in the input list 2477 * B_FALSE - otherwise 2478 * 2479 * RETURNS: int - 0 on success 2480 * - !0 otherwise 2481 * 2482 * PURPOSE: Helper used by is_named_device_avail that determines 2483 * if the input list of device specifications "includes" 2484 * a specific device. 2485 * 2486 * Iterates the elements of the input array and searches 2487 * for a match using spec_includes_device_name_or_alias(). 2488 */ 2489 static int 2490 unavail_list_includes_device_name( 2491 dlist_t *list, 2492 char *device_name, 2493 boolean_t check_aliases, 2494 boolean_t *includes) 2495 { 2496 dlist_t *iter = NULL; 2497 int error = 0; 2498 device_spec_t *unavail_spec; 2499 boolean_t check_for_alternate_hba = B_FALSE; 2500 2501 *includes = B_FALSE; 2502 2503 /* 2504 * the specs in the list are in descending order of specificity. 2505 * so a more exact spec will rule the device out before a less 2506 * exact spec. 2507 * 2508 * Meaning: if the list has { "c3t0d0", ..., "c3", ... } and the 2509 * input device name is "c3t0d0s0", it will match "c3t0d0" 2510 * before "c3". 2511 * 2512 * This is important for the multi-path alias checking below. 2513 * If the input device name is ruled out by a non-controller 2514 * specification, it is really unavailable. 2515 */ 2516 for (iter = list; 2517 (*includes == B_FALSE) && (iter != NULL); 2518 iter = iter->next) { 2519 2520 unavail_spec = (device_spec_t *)iter->obj; 2521 error = spec_includes_device_name( 2522 unavail_spec, device_name, check_aliases, includes); 2523 2524 } 2525 2526 if ((error == 0) && (*includes == B_TRUE)) { 2527 2528 /* matched an unavailable spec, was it a controller/HBA? */ 2529 oprintf(OUTPUT_DEBUG, 2530 "device \"%s\" is unavailable, " 2531 "it matched \"c(%d)t(%d)d(%d)s(%d)\"\n", 2532 device_name, 2533 unavail_spec->data.ctd->ctrl, 2534 unavail_spec->data.ctd->target, 2535 unavail_spec->data.ctd->lun, 2536 unavail_spec->data.ctd->slice); 2537 2538 if ((unavail_spec->data.ctd->ctrl != ID_UNSPECIFIED) && 2539 (unavail_spec->data.ctd->target == ID_UNSPECIFIED) && 2540 (unavail_spec->data.ctd->lun == ID_UNSPECIFIED) && 2541 (unavail_spec->data.ctd->slice == ID_UNSPECIFIED)) { 2542 2543 /* 2544 * Need to see if the named device is a disk or slice, 2545 * and if so check to see if the it is multipathed 2546 * and possibly accessible thru another controller/HBA. 2547 */ 2548 check_for_alternate_hba = B_TRUE; 2549 } 2550 } 2551 2552 if ((error == 0) && (check_for_alternate_hba == B_TRUE)) { 2553 2554 dm_descriptor_t slice = (dm_descriptor_t)0; 2555 dm_descriptor_t disk = (dm_descriptor_t)0; 2556 2557 ((error = slice_get_by_name(device_name, &slice)) != 0) || 2558 (error = disk_get_by_name(device_name, &disk)); 2559 if (error != 0) { 2560 return (error); 2561 } 2562 2563 /* if it is a slice, get its disk */ 2564 if ((error == 0) && (slice != (dm_descriptor_t)0)) { 2565 error = slice_get_disk(slice, &disk); 2566 } 2567 2568 if ((error == 0) && (disk != (dm_descriptor_t)0)) { 2569 2570 /* see if all the disk's HBAs are unavailable */ 2571 dlist_t *hbas = NULL; 2572 dlist_t *iter = NULL; 2573 2574 error = disk_get_hbas(disk, &hbas); 2575 2576 if (hbas != NULL) { 2577 oprintf(OUTPUT_DEBUG, 2578 gettext(" checking alternate paths for %s\n"), 2579 device_name); 2580 } else { 2581 oprintf(OUTPUT_DEBUG, 2582 gettext(" no alternate paths for %s\n"), 2583 device_name); 2584 } 2585 2586 /* for each of the disk's HBAs */ 2587 for (iter = hbas; 2588 (iter != NULL) && (*includes == B_TRUE) && (error == 0); 2589 iter = iter->next) { 2590 2591 dm_descriptor_t hba = (uintptr_t)iter->obj; 2592 device_spec_t *hbaspec; 2593 char *hbaname = NULL; 2594 dlist_t *iter2 = NULL; 2595 2596 *includes = B_FALSE; 2597 2598 ((error = get_display_name(hba, &hbaname)) != 0) || 2599 (error = get_spec_for_name(hbaname, &hbaspec)); 2600 2601 /* is HBA unavailable? */ 2602 for (iter2 = list; 2603 (iter2 != NULL) && (error == 0) && 2604 (*includes == B_FALSE); 2605 iter2 = iter2->next) { 2606 2607 device_spec_t *spec = 2608 (device_spec_t *)iter2->obj; 2609 2610 *includes = spec_includes_device(spec, hbaspec); 2611 } 2612 } 2613 dlist_free_items(hbas, NULL); 2614 2615 /* if *includes==B_TRUE here, all HBAs are unavailable */ 2616 } 2617 } 2618 2619 return (error); 2620 } 2621 2622 /* 2623 * FUNCTION: spec_includes_device_name(device_spec_t *spec, 2624 * char *device_name, boolean_t check_aliases, 2625 * boolean_t *includes) 2626 * 2627 * INPUT: spec - a device_spec_t CTD specification. 2628 * device_name - a char * device CTD name 2629 * check_aliases - boolean_t which indicates if the device's 2630 * aliases should be considered in the checking. 2631 * 2632 * OUTPUT: includes - B_TRUE - if device is "included" by the input 2633 * specification 2634 * B_FALSE - otherwise 2635 * 2636 * RETURNS: int - 0 on success 2637 * - !0 otherwise 2638 * 2639 * PURPOSE: Helper used by (un)avail_specs_includes_device_name() that 2640 * determines if the input device specification "includes" 2641 * the named device. 2642 * 2643 * If check_aliases is true and the named device is a slice or 2644 * a disk drive, its multi-pathed aliases are also checked 2645 * against the spec. 2646 */ 2647 static int 2648 spec_includes_device_name( 2649 device_spec_t *spec, 2650 char *device_name, 2651 boolean_t check_aliases, 2652 boolean_t *includes) 2653 { 2654 device_spec_t *device_spec; 2655 int error = 0; 2656 2657 error = get_spec_for_name(device_name, &device_spec); 2658 if (error == 0) { 2659 2660 *includes = spec_includes_device(spec, device_spec); 2661 2662 if ((*includes == B_FALSE) && (check_aliases == B_TRUE)) { 2663 2664 /* spec doesn't include name, check aliases */ 2665 2666 dm_descriptor_t device = (dm_descriptor_t)0; 2667 dlist_t *aliases = NULL; 2668 2669 /* only slices and disks have aliases */ 2670 error = slice_get_by_name(device_name, &device); 2671 if (device != (dm_descriptor_t)0) { 2672 error = get_aliases(device, &aliases); 2673 } else if (error == 0) { 2674 error = disk_get_by_name(device_name, &device); 2675 if (device != (dm_descriptor_t)0) { 2676 error = get_aliases(device, &aliases); 2677 } 2678 } 2679 2680 if ((error == 0) && (aliases != NULL)) { 2681 2682 dlist_t *iter; 2683 for (iter = aliases; 2684 (iter != NULL) && (*includes == B_FALSE) && 2685 (error == 0); 2686 iter = iter->next) { 2687 2688 char *alias = (char *)iter->obj; 2689 device_spec_t *alias_spec; 2690 2691 error = get_spec_for_name(alias, &alias_spec); 2692 if (error == 0) { 2693 /* does spec include alias? */ 2694 *includes = spec_includes_device(spec, alias_spec); 2695 } 2696 } 2697 } 2698 dlist_free_items(aliases, free); 2699 } 2700 } 2701 2702 return (error); 2703 } 2704 2705 /* 2706 * FUNCTION: destroy_device_spec(device_spec_t *spec) 2707 * 2708 * INPUT: spec - pointer to a device_spec_t 2709 * 2710 * RETURNS: nothing 2711 * 2712 * PURPOSE: Function which reclaims memory allocated to a device_spec_t. 2713 * 2714 * Frees memory allocated to hold the specific data in the spec. 2715 */ 2716 static void 2717 destroy_device_spec( 2718 device_spec_t *spec) 2719 { 2720 if (spec != NULL) { 2721 if (spec->type == SPEC_TYPE_CTD) { 2722 free(spec->data.ctd); 2723 } else if (spec->type == SPEC_TYPE_RAW) { 2724 free(spec->data.raw); 2725 } 2726 free(spec); 2727 } 2728 } 2729 2730 /* 2731 * FUNCTION: create_device_spec(char *name, device_spec_t **spec); 2732 * 2733 * INPUT: name - pointer to a char* device name 2734 * 2735 * OUTPUT: spec - pointer to a device_spec_t to hold the result 2736 * 2737 * RETURNS: int - 0 on success 2738 * !0 otherwise 2739 * 2740 * PURPOSE: Function which creates a device_spec_t for the input 2741 * device name. 2742 * 2743 */ 2744 static int 2745 create_device_spec( 2746 char *name, 2747 device_spec_t **spec) 2748 { 2749 int error = 0; 2750 2751 /* allocate the device spec and try various parsing schemes */ 2752 *spec = (device_spec_t *)calloc(1, sizeof (device_spec_t)); 2753 if (*spec == NULL) { 2754 error = ENOMEM; 2755 } else { 2756 if (((error = create_device_ctd_spec(name, spec)) != 0) && 2757 (error != ENOMEM)) { 2758 /* CTD failed, try other parsing schemes */ 2759 error = create_device_raw_spec(name, spec); 2760 } 2761 } 2762 2763 return (error); 2764 } 2765 2766 /* 2767 * FUNCTION: create_device_ctd_spec(char *name, device_spec_t **spec); 2768 * 2769 * INPUT: name - pointer to a char* device name 2770 * 2771 * OUTPUT: spec - pointer to a device_spec_t updated with the parsed 2772 * CTD spec, if successful 2773 * 2774 * RETURNS: int - 0 on success 2775 * !0 otherwise 2776 * 2777 * PURPOSE: Function which atttempts to parse the input device name into 2778 * cXtXdXsX component ids. The ids are the integer values of each 2779 * specified segment of the input name. 2780 * 2781 * If the name doesn't contain a segment, the id is set to 2782 * ID_UNSPECIFIED. 2783 * 2784 * The input name must be well-formed. 2785 * 2786 * These are the acceptable forms: 2787 * 2788 * cXtXdXsX 2789 * cXtXdX 2790 * cXtX 2791 * cXdXsX 2792 * cXdX 2793 * cX 2794 */ 2795 static int 2796 create_device_ctd_spec( 2797 char *name, 2798 device_spec_t **spec) 2799 { 2800 uint_t ctrl; 2801 uint_t target; 2802 uint_t lun; 2803 uint_t slice; 2804 2805 uint_t nscan; 2806 uint_t nchars; 2807 2808 char *device_str; 2809 char *target_str; 2810 char *ctd_str; 2811 char *t_ptr; 2812 char *d_ptr; 2813 char *s_ptr; 2814 2815 boolean_t is_ide = B_FALSE; 2816 boolean_t got_slice = B_FALSE; 2817 boolean_t got_lun = B_FALSE; 2818 boolean_t got_target = B_FALSE; 2819 boolean_t got_ctrl = B_FALSE; 2820 2821 int error = 0; 2822 2823 ctd_str = strdup(name); 2824 if (ctd_str == NULL) { 2825 return (ENOMEM); 2826 } 2827 2828 /* trim any leading path (/dev/dsk/cXtXdXsX) */ 2829 if ((device_str = strrchr(ctd_str, '/')) != NULL) { 2830 ++device_str; 2831 } else { 2832 device_str = ctd_str; 2833 } 2834 2835 /* find each segment start position */ 2836 t_ptr = strrchr(device_str, 't'); 2837 d_ptr = strrchr(device_str, 'd'); 2838 s_ptr = strrchr(device_str, 's'); 2839 2840 /* 2841 * scan ids from each existing segment working backwards 2842 * so as to leave the device_str in the correct state 2843 * for the next expected segment 2844 */ 2845 if (s_ptr != NULL) { 2846 2847 /* found 's', try to get slice */ 2848 nchars = strlen(s_ptr); 2849 if ((sscanf(s_ptr, "s%u%n", &slice, &nscan) != 1) || 2850 (nscan != nchars)) { 2851 2852 error = -1; 2853 oprintf(OUTPUT_DEBUG, 2854 gettext("no slice component in device " 2855 "name \"%s\".\n"), 2856 name); 2857 2858 } else { 2859 got_slice = B_TRUE; 2860 *s_ptr = '\0'; 2861 } 2862 } 2863 2864 if ((error == 0) && (d_ptr != NULL)) { 2865 2866 /* found 'd', try to get disk/lun */ 2867 nchars = strlen(d_ptr); 2868 if ((sscanf(d_ptr, "d%u%n", &lun, &nscan) != 1) || 2869 (nscan != nchars)) { 2870 2871 error = -1; 2872 oprintf(OUTPUT_DEBUG, 2873 gettext("no disk/lun component " 2874 "in device name \"%s\".\n"), 2875 name); 2876 2877 } else { 2878 got_lun = B_TRUE; 2879 *d_ptr = '\0'; 2880 } 2881 } 2882 2883 if ((error == 0) && (t_ptr != NULL)) { 2884 2885 /* found 't', try to get target, it may be a hex WWN id */ 2886 2887 /* skip leading 't' and add two for the 'OX' */ 2888 nchars = strlen(t_ptr + 1) + 2; 2889 if ((target_str = (char *)malloc(nchars+1)) == NULL) { 2890 2891 error = ENOMEM; 2892 2893 } else { 2894 2895 strcpy(target_str, "0X"); 2896 strcpy(target_str+2, t_ptr + 1); 2897 target_str[nchars] = '\0'; 2898 2899 if ((sscanf(target_str, "%x%n", &target, &nscan) != 1) || 2900 (nscan != nchars)) { 2901 2902 error = -1; 2903 oprintf(OUTPUT_DEBUG, 2904 gettext("no target/WWN component " 2905 "in device name \"%s\".\n"), 2906 name); 2907 2908 } else { 2909 got_target = B_TRUE; 2910 *t_ptr = '\0'; 2911 } 2912 2913 free(target_str); 2914 } 2915 2916 } else { 2917 is_ide = B_TRUE; 2918 } 2919 2920 if ((error == 0) && (device_str != NULL)) { 2921 2922 /* get controller/hba/channel */ 2923 nchars = strlen(device_str); 2924 if ((sscanf(device_str, "c%u%n", &ctrl, &nscan) != 1) || 2925 (nscan != nchars)) { 2926 2927 error = -1; 2928 oprintf(OUTPUT_DEBUG, 2929 gettext("no channel/HBA component " 2930 "in device name \"%s\".\n"), 2931 name); 2932 2933 } else { 2934 got_ctrl = B_TRUE; 2935 } 2936 } 2937 2938 free(ctd_str); 2939 2940 if (error == 0) { 2941 2942 /* allocate the ctd_spec_t struct and store the ids */ 2943 (*spec)->type = SPEC_TYPE_CTD; 2944 (*spec)->data.ctd = (ctd_spec_t *)calloc(1, sizeof (ctd_spec_t)); 2945 2946 if ((*spec)->data.ctd == NULL) { 2947 error = ENOMEM; 2948 } 2949 2950 (*spec)->data.ctd->slice = ID_UNSPECIFIED; 2951 (*spec)->data.ctd->lun = ID_UNSPECIFIED; 2952 (*spec)->data.ctd->target = ID_UNSPECIFIED; 2953 (*spec)->data.ctd->ctrl = ID_UNSPECIFIED; 2954 2955 if (got_slice == B_TRUE) { 2956 (*spec)->data.ctd->slice = slice; 2957 } 2958 2959 if (got_lun == B_TRUE) { 2960 (*spec)->data.ctd->lun = lun; 2961 } 2962 2963 if (got_target == B_TRUE) { 2964 (*spec)->data.ctd->target = target; 2965 } 2966 2967 if (got_ctrl == B_TRUE) { 2968 (*spec)->data.ctd->ctrl = ctrl; 2969 } 2970 2971 (*spec)->data.ctd->is_ide = is_ide; 2972 } 2973 2974 return (error); 2975 } 2976 2977 /* 2978 * FUNCTION: create_device_raw_spec(char *name, device_spec_t **spec); 2979 * 2980 * INPUT: name - pointer to a char* device name 2981 * 2982 * OUTPUT: spec - pointer to a device_spec_t updated with the raw spec 2983 * 2984 * RETURNS: int - 0 on success 2985 * !0 otherwise 2986 * 2987 * PURPOSE: Function which creates a "raw" spec for the input name. 2988 * 2989 * This is a last resort if all other spec parsing schemes failed, 2990 * the "raw" spec is just the input device name. 2991 */ 2992 static int 2993 create_device_raw_spec( 2994 char *name, 2995 device_spec_t **spec) 2996 { 2997 int error = 0; 2998 char *ctd_str = strdup(name); 2999 3000 if (ctd_str == NULL) { 3001 return (ENOMEM); 3002 } 3003 3004 (*spec)->type = SPEC_TYPE_RAW; 3005 (*spec)->data.raw = ctd_str; 3006 3007 oprintf(OUTPUT_DEBUG, 3008 gettext("made raw device spec for \"%s\"\n"), ctd_str); 3009 3010 return (error); 3011 } 3012 3013 /* 3014 * FUNCTION: get_spec_for_name(char *name, device_spec_t **id); 3015 * 3016 * INPUT: name - pointer to a char* device name 3017 * 3018 * OUTPUT: id - pointer to a device_spec_t to hold the result 3019 * 3020 * RETURNS: int - 0 on success 3021 * !0 otherwise 3022 * 3023 * PURPOSE: Function which finds the device_spec_t that already 3024 * exists for the input name or creates it. 3025 * 3026 * The returned struct should not be freed, it is maintained 3027 * in a cache that will be purged when the layout process 3028 * is complete. 3029 */ 3030 int 3031 get_spec_for_name( 3032 char *name, 3033 device_spec_t **id) 3034 { 3035 dlist_t *item; 3036 int error = 0; 3037 3038 item = dlist_find(_spec_cache, (void *)name, 3039 compare_name_to_spec_cache_name); 3040 3041 if (item == NULL) { 3042 if ((error = create_device_spec(name, id)) == 0) { 3043 3044 spec_cache_t *entry = (spec_cache_t *) 3045 calloc(1, sizeof (spec_cache_t)); 3046 3047 if (entry == NULL) { 3048 destroy_device_spec(*id); 3049 error = ENOMEM; 3050 } else { 3051 char *dup = strdup(name); 3052 if (dup == NULL) { 3053 free(entry); 3054 destroy_device_spec(*id); 3055 *id = NULL; 3056 error = ENOMEM; 3057 } else { 3058 entry->name = dup; 3059 entry->device_spec = *id; 3060 } 3061 3062 if (error == 0) { 3063 dlist_t *item = dlist_new_item((void *)entry); 3064 if (item == NULL) { 3065 free(entry); 3066 destroy_device_spec(*id); 3067 *id = NULL; 3068 error = ENOMEM; 3069 } else { 3070 _spec_cache = 3071 dlist_append(item, _spec_cache, AT_HEAD); 3072 } 3073 } 3074 } 3075 } 3076 } else { 3077 *id = ((spec_cache_t *)item->obj)->device_spec; 3078 } 3079 3080 return (error); 3081 } 3082 3083 /* 3084 * FUNCTION: spec_includes_device(device_spec_t *spec, 3085 * device_spec_t *device) 3086 * 3087 * INPUT: spec - pointer to a device_spec struct 3088 * device - pointer to a device_spec struct 3089 * 3090 * RETURNS: boolean_t - B_TRUE if the device is included in the spec 3091 * B_FALSE otherwise 3092 * 3093 * PURPOSE: Function which determines if the input device matches the 3094 * input spec. 3095 * 3096 * If both specs are of the same type, the appropriate 3097 * comparison function is called. 3098 * 3099 * If the two specs are of different types, no comparison 3100 * is done and B_FALSE is returned. 3101 */ 3102 boolean_t 3103 spec_includes_device( 3104 device_spec_t *spec, 3105 device_spec_t *device) 3106 { 3107 if ((spec->type == SPEC_TYPE_CTD) && (device->type == SPEC_TYPE_CTD)) { 3108 return (ctd_spec_includes_device(spec, device)); 3109 } else if ((spec->type == SPEC_TYPE_RAW) && 3110 (device->type == SPEC_TYPE_RAW)) { 3111 return (raw_spec_includes_device(spec, device)); 3112 } 3113 3114 return (B_FALSE); 3115 } 3116 3117 /* 3118 * FUNCTION: ctd_spec_includes_device(device_spec_t *spec, 3119 * device_spec_t *device) 3120 * 3121 * INPUT: spec - pointer to a device_spec struct 3122 * device - pointer to a device_spec struct 3123 * 3124 * RETURNS: boolean_t - B_TRUE if the device is included in the spec 3125 * B_FALSE otherwise 3126 * 3127 * PURPOSE: Function which determines if the input CTD device spec 3128 * matches the input CTD spec. 3129 * 3130 * The device_spec_t structs contain component "ids" for 3131 * both the specification and the device. 3132 * 3133 * The device must match each of the ids in the spec that 3134 * are specified. 3135 * 3136 * spec devices matched 3137 * -------------------------------------------------------- 3138 * cX cX, cXtX, cXtXdX, cXtXdXsX, cXdX, cXdXsX 3139 * cXtX cXtX, cXtXdX, cXtXdXsX 3140 * cXtXdX cXtXdX, cXtXdXsX 3141 * cXtXdXsX cXtXdXsX 3142 * cXdX cXdX, cXdXsX 3143 * cXdXsX cXdXsX 3144 */ 3145 static boolean_t 3146 ctd_spec_includes_device( 3147 device_spec_t *spec, 3148 device_spec_t *device) 3149 { 3150 boolean_t match = B_FALSE; 3151 3152 if (spec->data.ctd->is_ide) { 3153 3154 /* valid IDE names are cX, cXdX, cXdXsX, no target */ 3155 3156 if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && 3157 (spec->data.ctd->lun != ID_UNSPECIFIED) && 3158 (spec->data.ctd->slice != ID_UNSPECIFIED)) { 3159 3160 match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && 3161 (spec->data.ctd->lun == device->data.ctd->lun) && 3162 (spec->data.ctd->slice == device->data.ctd->slice); 3163 3164 } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && 3165 (spec->data.ctd->lun != ID_UNSPECIFIED)) { 3166 3167 match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && 3168 (spec->data.ctd->lun == device->data.ctd->lun); 3169 3170 } else if (spec->data.ctd->ctrl != ID_UNSPECIFIED) { 3171 3172 match = (spec->data.ctd->ctrl == device->data.ctd->ctrl); 3173 3174 } 3175 3176 } else { 3177 3178 /* valid names are cX, cXtX, cXtXdX, cXtXdXsX */ 3179 3180 if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && 3181 (spec->data.ctd->target != ID_UNSPECIFIED) && 3182 (spec->data.ctd->lun != ID_UNSPECIFIED) && 3183 (spec->data.ctd->slice != ID_UNSPECIFIED)) { 3184 3185 match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && 3186 (spec->data.ctd->target == device->data.ctd->target) && 3187 (spec->data.ctd->lun == device->data.ctd->lun) && 3188 (spec->data.ctd->slice == device->data.ctd->slice); 3189 3190 } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && 3191 (spec->data.ctd->target != ID_UNSPECIFIED) && 3192 (spec->data.ctd->lun != ID_UNSPECIFIED)) { 3193 3194 match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && 3195 (spec->data.ctd->target == device->data.ctd->target) && 3196 (spec->data.ctd->lun == device->data.ctd->lun); 3197 3198 } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) && 3199 (spec->data.ctd->target != ID_UNSPECIFIED)) { 3200 3201 match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) && 3202 (spec->data.ctd->target == device->data.ctd->target); 3203 3204 } else if (spec->data.ctd->ctrl != ID_UNSPECIFIED) { 3205 3206 match = (spec->data.ctd->ctrl == device->data.ctd->ctrl); 3207 3208 } 3209 } 3210 3211 oprintf(OUTPUT_DEBUG, 3212 gettext("spec: c(%d) t(%d) d(%d) s(%d) " 3213 "%s: c(%d) t(%d) d(%d) s(%d)\n"), 3214 spec->data.ctd->ctrl, spec->data.ctd->target, 3215 spec->data.ctd->lun, spec->data.ctd->slice, 3216 (match ? gettext("includes") : gettext("does not include")), 3217 device->data.ctd->ctrl, device->data.ctd->target, 3218 device->data.ctd->lun, device->data.ctd->slice); 3219 3220 return (match); 3221 } 3222 3223 /* 3224 * FUNCTION: raw_spec_includes_device(device_spec_t *spec, 3225 * device_spec_t *device) 3226 * 3227 * INPUT: spec - pointer to a device_spec struct 3228 * device - pointer to a device_spec struct 3229 * 3230 * RETURNS: boolean_t - B_TRUE if the device is included in the spec 3231 * B_FALSE otherwise 3232 * 3233 * PURPOSE: Function which determines if the input raw device spec 3234 * matches the input spec. 3235 * 3236 * The device_spec_t raw elements are checked. 3237 * 3238 * If the spec's raw device name is exactly contained at the 3239 * beginning of the device spec's raw name, then the function 3240 * evaluates to true. 3241 */ 3242 static boolean_t 3243 raw_spec_includes_device( 3244 device_spec_t *spec, 3245 device_spec_t *device) 3246 { 3247 return (strncasecmp(spec->data.raw, 3248 device->data.raw, strlen(spec->data.raw)) == 0); 3249 } 3250 3251 /* 3252 * FUNCTION: compare_name_to_spec_cache_name(void *name, void *list_item) 3253 * 3254 * INPUT: name - opaque pointer to a char * device name 3255 * list_item - opaque pointer to a spec_cache_t entry 3256 * 3257 * RETURNS: int - 0 - if request is the same as list_item->request 3258 * !0 - otherwise 3259 * 3260 * PURPOSE: dlist_t helper which compares the input device name 3261 * to the list_item's device name for equality. 3262 * 3263 * This function is the lookup mechanism for the device_spec 3264 * associated with the name. 3265 */ 3266 static int 3267 compare_name_to_spec_cache_name( 3268 void *name, 3269 void *list_item) 3270 { 3271 spec_cache_t *entry = (spec_cache_t *)list_item; 3272 3273 assert(name != NULL); 3274 assert(entry != NULL); 3275 3276 return (string_case_compare((char *)name, entry->name)); 3277 } 3278 3279 /* 3280 * FUNCTION: destroy_spec_cache_entry(void *entry) 3281 * 3282 * INPUT: entry - opaque pointer to a spec_cache_t 3283 * 3284 * RETURNS: nothing 3285 * 3286 * PURPOSE: Function which reclaims memory allocated to a 3287 * spec_cache_t entry. 3288 * 3289 * Frees memory allocated to hold the CTD name and the 3290 * corresponding device_spec_t. 3291 */ 3292 static void 3293 destroy_spec_cache_entry( 3294 void *obj) 3295 { 3296 spec_cache_t *entry = (spec_cache_t *)obj; 3297 3298 if (entry != NULL) { 3299 free(entry->name); 3300 destroy_device_spec(entry->device_spec); 3301 free(entry); 3302 } 3303 } 3304 3305 /* 3306 * FUNCTION: destroy_spec_cache() 3307 * 3308 * RETURNS: int - 0 on success 3309 * !0 otherwise. 3310 * 3311 * PURPOSE: Function which destroys all entries in the device_spec 3312 * cache. 3313 */ 3314 static int 3315 destroy_spec_cache() 3316 { 3317 dlist_free_items(_spec_cache, destroy_spec_cache_entry); 3318 _spec_cache = NULL; 3319 3320 return (0); 3321 } 3322 3323 /* 3324 * FUNCTION: get_device_access_name(devconfig_t *request, 3325 * dm_descriptor_t desc, char **name) 3326 * 3327 * INPUT: request - a devconfig_t request 3328 * desc - a dm_descriptor_t device handle 3329 * 3330 * OUTPUT: name - a char * pointer to hold the preferred name 3331 * 3332 * RETURNS: int - 0 - if request is the same as list_item->request 3333 * !0 - otherwise 3334 * 3335 * PURPOSE: Utility function to determine which of the possible device 3336 * names should be used to access a known available device. 3337 * 3338 * Devices handled are slices and disks. 3339 * 3340 * If the input device is a multipathed disk or slice, it 3341 * can have several possible names. Determine which of the 3342 * names should be used based on the input request's available 3343 * or unavailable device specifications. 3344 * 3345 */ 3346 int 3347 get_device_access_name( 3348 devconfig_t *request, 3349 dm_descriptor_t desc, 3350 char **name) 3351 { 3352 int error = 0; 3353 boolean_t avail = B_FALSE; 3354 dlist_t *aliases = NULL; 3355 3356 assert(desc != (dm_descriptor_t)0); 3357 3358 *name = NULL; 3359 3360 if ((error = get_display_name(desc, name)) != 0) { 3361 return (error); 3362 } 3363 3364 if (is_did_name(*name) == B_TRUE) { 3365 oprintf(OUTPUT_DEBUG, 3366 gettext("device DID name %s is preferred\n"), 3367 *name); 3368 return (0); 3369 } 3370 3371 error = is_named_device_avail(request, *name, B_FALSE, &avail); 3372 if (error != 0) { 3373 return (error); 3374 } 3375 3376 if (avail == B_TRUE) { 3377 oprintf(OUTPUT_DEBUG, 3378 gettext("device name %s is accessible\n"), 3379 *name); 3380 return (0); 3381 } 3382 3383 /* search aliases for an 'available' name, prefer DID names */ 3384 if ((error = get_aliases(desc, &aliases)) == 0) { 3385 3386 dlist_t *iter = aliases; 3387 char *availname = NULL; 3388 char *didname = NULL; 3389 3390 for (; (iter != NULL) && (error == 0); iter = iter->next) { 3391 3392 char *alias = (char *)iter->obj; 3393 error = is_named_device_avail(request, alias, B_FALSE, &avail); 3394 3395 if ((error == 0) && (avail == B_TRUE)) { 3396 oprintf(OUTPUT_DEBUG, 3397 gettext("device alias %s is accessible for %s\n"), 3398 alias, *name); 3399 3400 availname = alias; 3401 3402 if (is_did_name(availname) == B_TRUE) { 3403 didname = alias; 3404 break; 3405 } 3406 } 3407 } 3408 3409 if (error == 0) { 3410 if (didname != NULL) { 3411 *name = didname; 3412 } else if (availname != NULL) { 3413 *name = availname; 3414 } 3415 } 3416 } 3417 3418 return (error); 3419 } 3420