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 2004 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 <limits.h> 30 #include <libdiskmgt.h> 31 #include <libintl.h> 32 33 #include <meta.h> 34 35 #define _LAYOUT_DISCOVERY_C 36 37 #include "volume_dlist.h" 38 #include "volume_error.h" 39 #include "volume_nvpair.h" 40 #include "volume_output.h" 41 42 #include "layout_device_cache.h" 43 #include "layout_device_util.h" 44 #include "layout_dlist_util.h" 45 #include "layout_discovery.h" 46 #include "layout_request.h" 47 #include "layout_slice.h" 48 #include "layout_svm_util.h" 49 50 /* 51 * lists of device dm_descriptor_t handles discovered during 52 * the initial system probe. Lists are populated by 53 * discover_known_devices. 54 * 55 * "bad" slices are those that are known to libdiskmgt but 56 * cannot be accessed. An example would be a slice that has 57 * disappeared due to disk re-slicing: libdiskmgt may have a 58 * cached handle for it, but the slice no longer exists. 59 * 60 * "bad" disks are thoese that are known to libdiskmgt but 61 * cannot be accessed. An example would be a disk that has 62 * failed or has gone offline: libdiskmgt may have a cached 63 * handle for it, but the disk does not respond. 64 */ 65 static dlist_t *_bad_slices = NULL; 66 static dlist_t *_bad_disks = NULL; 67 68 static dlist_t *_known_slices = NULL; 69 static dlist_t *_known_disks = NULL; 70 static dlist_t *_known_hbas = NULL; 71 72 /* 73 * helper functions for building known device lists, used by 74 * discover_known_devices. 75 */ 76 static int generate_known_slices(dlist_t *disks, dlist_t **known, 77 dlist_t **bad); 78 static int generate_known_disks(dlist_t **known, dlist_t **bad); 79 static int generate_known_hbas(dlist_t *disks, dlist_t **known); 80 static int generate_known_hba_name( 81 dm_descriptor_t hba, 82 dm_descriptor_t alias, 83 dm_descriptor_t disk); 84 85 static void print_known_devices(); 86 static void print_device_list(dlist_t *devices); 87 88 /* 89 * lists of device dm_descriptor_t handles that are usable by layout. 90 * These devices must still pass the user specified available/unavailable 91 * filter before they're actually considered available. 92 * 93 * Lists are populated by discover_usable_devices. 94 */ 95 static dlist_t *_usable_slices = NULL; 96 static dlist_t *_usable_disks = NULL; 97 static dlist_t *_usable_hbas = NULL; 98 99 /* 100 * private flag that remembers if any HBA is known to support MPXIO 101 */ 102 static boolean_t _mpxio_enabled = B_FALSE; 103 104 /* 105 * The slice_class struct is used to group slices by usage class. 106 */ 107 typedef struct { 108 char *usage; /* usage description */ 109 dlist_t *sliceinfo; /* list with info about each slice with usage */ 110 } slice_class_t; 111 112 #define USE_DISKSET "diskset" 113 114 static int check_slice_usage( 115 char *dsname, 116 dm_descriptor_t slice, 117 dm_descriptor_t disk, 118 boolean_t *avail, 119 dlist_t **bad, 120 dlist_t **classes); 121 122 static int check_svm_slice_usage( 123 char *dsname, 124 dm_descriptor_t slice, 125 dm_descriptor_t disk, 126 boolean_t *avail, 127 dlist_t **classes); 128 129 static int save_slice_classification( 130 char *dsname, 131 dm_descriptor_t slice, 132 dm_descriptor_t disk, 133 char *usage, 134 char *usage_detail, 135 dlist_t **classes); 136 137 static int generate_usable_disks_and_slices_in_local_set( 138 dlist_t **classes, 139 dlist_t **bad_disks, 140 dlist_t **usable_disks, 141 dlist_t **usable_slices); 142 143 static int generate_usable_disks_and_slices_in_named_set( 144 char *dsname, 145 dlist_t **classes, 146 dlist_t **bad_slices, 147 dlist_t **usable_disks, 148 dlist_t **usable_slices); 149 150 static int create_usable_slices( 151 dm_descriptor_t disk, 152 dlist_t *used, 153 dlist_t *unused, 154 dlist_t **usable); 155 156 static int add_new_usable( 157 dm_descriptor_t disk, 158 uint64_t stblk, 159 uint64_t nblks, 160 dlist_t **next_unused, 161 dlist_t **usable); 162 163 static int update_slice_attributes( 164 dm_descriptor_t slice, 165 uint64_t stblk, 166 uint64_t nblks, 167 uint64_t nbytes); 168 169 static int generate_usable_hbas( 170 dlist_t *disks, 171 dlist_t **usable); 172 173 static void print_usable_devices(); 174 175 static void print_unusable_devices( 176 dlist_t *badslices, 177 dlist_t *baddisks, 178 dlist_t *usedslices); 179 180 static char *get_slice_usage_msg( 181 char *usage); 182 183 /* 184 * virtual slices... 185 */ 186 static int generate_virtual_slices( 187 dlist_t *avail_disks_local_set, 188 dlist_t **usable); 189 190 /* 191 * multipathed disks have aliases, as do slices on those disks. 192 * these need to be tracked since the user may specify them. 193 * A multi-pathed disk is one connected to the system thru 194 * more than one physical HBA, each connection gets a distinct 195 * name in the device tree and they're all more or less equivalent. 196 * No indication as to how many possible physical connections a 197 * disk may have, so we pick an arbitrary number of aliases to 198 * support. There is nothing significant about this number, 199 * it just controls the number of alias slots that get allocated. 200 */ 201 #define MAX_ALIASES 8 202 203 /* 204 * attribute name for layout private information stored in 205 * device nvpair attribute lists. 206 */ 207 static char *ATTR_DEVICE_ALIASES = "layout_device_aliases"; 208 209 static int compare_start_blocks( 210 void *desc1, void *desc2); 211 212 static int compare_desc_display_names( 213 void *desc1, void *desc2); 214 215 /* 216 * FUNCTION: is_mpxio_enabled() 217 * 218 * RETURNS: boolean_t - B_TRUE - if MPXIO appears enabled for the system 219 * B_FALSE - otherwise 220 * 221 * PURPOSE: returns the value of _mpxio_enabled which is set to B_TRUE 222 * during system configuration discovery if any of the knwon 223 * HBAs advertises itself as a "multiplex" controller. 224 */ 225 boolean_t 226 is_mpxio_enabled() 227 { 228 return (_mpxio_enabled); 229 } 230 231 /* 232 * FUNCTION: discover_known_devices() 233 * 234 * SIDEEFFECT: populates the module private lists of known devices 235 * (_known_slices, _known_disks, _known_hbas). 236 * 237 * All known devices will also have had their CTD 238 * short names inferred and stored. 239 * 240 * RETURNS: int - 0 on success 241 * !0 otherwise 242 * 243 * PURPOSE: Load physical devices discovered thru libdiskmgt. 244 */ 245 int 246 discover_known_devices() 247 { 248 int error = 0; 249 250 oprintf(OUTPUT_TERSE, 251 gettext("\nScanning system physical " 252 "device configuration...\n")); 253 254 /* initialize layout_device_cache */ 255 ((error = create_device_caches()) != 0) || 256 257 (error = generate_known_disks(&_known_disks, &_bad_disks)) || 258 (error = generate_known_slices(_known_disks, &_known_slices, 259 &_bad_slices)) || 260 (error = generate_known_hbas(_known_disks, &_known_hbas)); 261 262 if (error == 0) { 263 print_known_devices(); 264 } 265 266 return (error); 267 } 268 269 /* 270 * FUNCTION: release_known_devices() 271 * 272 * RETURNS: int - 0 on success 273 * !0 otherwise 274 * 275 * PURPOSE: Unloads all state currently held for known 276 * physical devices. 277 */ 278 int 279 release_known_devices( 280 char *diskset) 281 { 282 /* these lists are module private */ 283 dlist_free_items(_bad_slices, NULL); 284 dlist_free_items(_bad_disks, NULL); 285 dlist_free_items(_known_slices, NULL); 286 dlist_free_items(_known_disks, NULL); 287 dlist_free_items(_known_hbas, NULL); 288 289 _bad_slices = NULL; 290 _bad_disks = NULL; 291 _known_slices = NULL; 292 _known_disks = NULL; 293 _known_hbas = NULL; 294 295 /* clean up state kept in layout_device_cache */ 296 release_device_caches(); 297 298 return (0); 299 } 300 301 /* 302 * FUNCTION: discover_usable_devices(char *diskset) 303 * 304 * INPUT: diskset - a char * diskset name. 305 * 306 * SIDEEFFECT: Traverses the lists of known devices and populates the 307 * module private lists of usable devices (_usable_slices, 308 * _usable_disks, _usable_hbas), as well as the module 309 * private list of used slices. 310 * 311 * RETURNS: int - 0 on success 312 * !0 otherwise 313 * 314 * PURPOSE: Process the known devices and determine which of them are 315 * usable for generating volumes in the specified diskset. 316 * 317 * The specified diskset's name cannot be NULL or 0 length. 318 */ 319 int 320 discover_usable_devices( 321 char *diskset) 322 { 323 int error = 0; 324 325 dlist_t *used_classes = NULL; 326 dlist_t *iter = NULL; 327 328 if (diskset == NULL || diskset[0] == '\0') { 329 volume_set_error( 330 gettext("a diskset name must be specified in " 331 "the request\n")); 332 return (-1); 333 } 334 335 oprintf(OUTPUT_TERSE, 336 gettext("\nDetermining usable physical devices " 337 "for disk set \"%s\"...\n"), 338 diskset); 339 340 error = generate_usable_disks_and_slices_in_local_set( 341 &used_classes, &_bad_slices, &_usable_disks, &_usable_slices); 342 if (error == 0) { 343 344 error = generate_usable_disks_and_slices_in_named_set( 345 diskset, &used_classes, &_bad_slices, &_usable_disks, 346 &_usable_slices); 347 if (error == 0) { 348 349 error = generate_usable_hbas(_usable_disks, &_usable_hbas); 350 if (error == 0) { 351 352 print_usable_devices(); 353 print_unusable_devices( 354 _bad_slices, _bad_disks, used_classes); 355 } 356 } 357 } 358 359 /* 360 * free slice classification usage and lists, items are char* 361 * the used_classes structure is only filled in if verbose 362 * output was requested. 363 */ 364 for (iter = used_classes; iter != NULL; iter = iter->next) { 365 slice_class_t *class = (slice_class_t *)iter->obj; 366 free(class->usage); 367 dlist_free_items(class->sliceinfo, free); 368 } 369 370 dlist_free_items(used_classes, free); 371 return (error); 372 } 373 374 /* 375 * FUNCTION: release_usable_devices() 376 * 377 * RETURNS: int - 0 on success 378 * !0 otherwise 379 * 380 * PURPOSE: Unloads all state currently held for usable 381 * physical devices. 382 */ 383 int 384 release_usable_devices() 385 { 386 /* list items are shared with _known_XXX lists */ 387 388 dlist_free_items(_usable_slices, NULL); 389 dlist_free_items(_usable_disks, NULL); 390 dlist_free_items(_usable_hbas, NULL); 391 392 _usable_slices = NULL; 393 _usable_disks = NULL; 394 _usable_hbas = NULL; 395 396 /* clean up state kept in layout_device_util */ 397 release_virtual_slices(); 398 399 return (0); 400 } 401 402 /* 403 * FUNCTION: get_known_slices(dlist_t **list) 404 * get_known_disks(dlist_t **list) 405 * get_known_hbas(dlist_t **list) 406 * 407 * OUTPUT: list - a dlist_t pointer to hold the returned list of 408 * devices. 409 * 410 * RETURNS: int - 0 on success 411 * !0 otherwise 412 * 413 * PURPOSE: Public accessors for the module private lists of 414 * available devices. 415 */ 416 int 417 get_known_slices( 418 dlist_t **list) 419 { 420 *list = _known_slices; 421 422 return (0); 423 } 424 425 int 426 get_known_disks( 427 dlist_t **list) 428 { 429 *list = _known_disks; 430 431 return (0); 432 } 433 434 int 435 get_known_hbas( 436 dlist_t **list) 437 { 438 *list = _known_hbas; 439 440 return (0); 441 } 442 443 /* make fully qualified DID device name */ 444 static char * 445 make_fully_qualified_did_device_name( 446 char *device) 447 { 448 static char buf[MAXPATHLEN]; 449 450 if (device != NULL && strrchr(device, '/') == NULL) { 451 (void) snprintf(buf, MAXPATHLEN-1, "%s/%s", 452 "/dev/did/dsk", device); 453 return (buf); 454 } 455 456 return (device); 457 } 458 459 /* 460 * FUNCTION: generate_known_disks(dlist_t **known, 461 * dlist_t **bad) 462 * 463 * INPUT: NONE 464 * 465 * OUTPUT: known - populated list of known disks 466 * bad - populated list of known bad disks 467 * 468 * RETURNS: int - 0 on success 469 * !0 otherwise 470 * 471 * PURPOSE: Does the system configuration discovery to determine 472 * what disks are known to be attached to the system. 473 * 474 * Determines the CTD name for each disk and saves it. 475 */ 476 static int 477 generate_known_disks( 478 dlist_t **known, 479 dlist_t **bad) 480 { 481 int i; 482 int error = 0; 483 dm_descriptor_t *ddp; 484 485 ddp = dm_get_descriptors(DM_DRIVE, NULL, &error); 486 (void) add_descriptors_to_free(ddp); 487 488 *known = NULL; 489 490 if (error != 0) { 491 volume_set_error( 492 gettext("Error discovering system hardware configuration,\n" 493 "unable to communicate with libdiskmgt or diskmgtd.\n")); 494 return (-1); 495 } 496 497 if ((ddp == NULL) || (ddp[0] == NULL)) { 498 volume_set_error(gettext("there are no known disks\n")); 499 return (-1); 500 } 501 502 /* iterate all returned disks and add them to the known list */ 503 for (i = 0; (ddp[i] != NULL) && (error == 0); i++) { 504 dm_descriptor_t disk = (dm_descriptor_t)ddp[i]; 505 dlist_t *aliases = NULL; 506 uint32_t mtype = DM_MT_UNKNOWN; 507 uint32_t dtype = DM_DT_UNKNOWN; 508 boolean_t bad_disk = B_FALSE; 509 boolean_t online = B_TRUE; 510 511 #if defined(i386) 512 /* on X86, disks must have a solaris FDISK partition */ 513 boolean_t solpart = B_FALSE; 514 #endif /* defined(i386) */ 515 516 if (((error = disk_get_is_online(disk, &online)) == 0 && 517 online == B_FALSE) || error == ENODEV) { 518 /* if the disk is offline, report it as bad */ 519 bad_disk = B_TRUE; 520 error = 0; 521 } else 522 523 if (error == 0 && 524 (((error = disk_get_media_type(disk, &mtype)) != 0) || 525 ((error = disk_get_drive_type(disk, &dtype)) != 0)) && 526 error == ENODEV) { 527 /* 528 * if any disk attribute access fails with ENODEV 529 * report it as bad 530 */ 531 bad_disk = B_TRUE; 532 error = 0; 533 } else { 534 535 /* 536 * Determine whether disk is fixed by checking its 537 * drive type. If drive type is unknown, check media 538 * type. 539 */ 540 int isfixed = (dtype == DM_DT_FIXED || 541 (dtype == DM_DT_UNKNOWN && mtype == DM_MT_FIXED)); 542 543 if (!isfixed) { 544 continue; /* ignore non-fixed disks */ 545 } 546 547 #if defined(i386) 548 if (((error = disk_get_has_solaris_partition(disk, 549 &solpart)) != 0) || (solpart != B_TRUE)) { 550 551 /* X86 drive has no solaris partition, report as bad */ 552 oprintf(OUTPUT_DEBUG, 553 gettext("%s has no solaris FDISK partition.\n")); 554 555 bad_disk = B_TRUE; 556 } 557 #endif /* defined(i386) */ 558 559 } 560 561 if (bad_disk) { 562 /* remember bad disks and continue */ 563 if (dlist_contains(*bad, 564 (void *)disk, compare_descriptor_names) != B_TRUE) { 565 dlist_t *item = dlist_new_item((void *)disk); 566 if (item == NULL) { 567 error = ENOMEM; 568 } else { 569 *bad = dlist_append(item, *bad, AT_TAIL); 570 } 571 } 572 continue; 573 } 574 575 /* get disk name and multipath aliases */ 576 if ((error = disk_get_aliases(disk, &aliases)) == 0) { 577 dlist_t *iter; 578 boolean_t disk_name_set = B_FALSE; 579 580 for (iter = aliases; 581 (iter != NULL) && (error == 0); 582 iter = iter->next) { 583 584 dm_descriptor_t ap = (uintptr_t)iter->obj; 585 char *alias; 586 587 if ((error = get_name(ap, &alias)) == 0) { 588 /* save first alias as display name */ 589 if (disk_name_set != B_TRUE) { 590 /* make sure DID disk alias is fully qualified */ 591 592 if (is_did_disk_name(alias) == B_TRUE) { 593 char *qual_name = 594 make_fully_qualified_did_device_name(alias); 595 596 set_display_name(disk, qual_name); 597 oprintf(OUTPUT_DEBUG, 598 gettext("DID disk name: %s\n"), 599 qual_name); 600 } else { 601 set_display_name(disk, alias); 602 oprintf(OUTPUT_DEBUG, 603 gettext("disk name: %s\n"), 604 alias); 605 } 606 disk_name_set = B_TRUE; 607 608 } else { 609 /* save others as aliases */ 610 set_alias(disk, alias); 611 oprintf(OUTPUT_DEBUG, 612 gettext(" alias: %s\n"), 613 alias); 614 } 615 } 616 } 617 618 dlist_free_items(aliases, NULL); 619 } 620 621 if (error == 0) { 622 dlist_t *item = dlist_new_item((void *)disk); 623 if (item == NULL) { 624 error = ENOMEM; 625 } else { 626 *known = 627 dlist_insert_ordered(item, *known, 628 ASCENDING, compare_desc_display_names); 629 } 630 } 631 } 632 633 if (ddp != NULL) { 634 free(ddp); 635 } 636 637 return (error); 638 } 639 640 /* 641 * FUNCTION: generate_known_slices(dlist_t *disks, 642 * dlist_t **known, dlist_t **bad) 643 * 644 * OUTPUT: disks - a pointer to a list of known disks 645 * known - a pointer to a dlist_t list to hold the known slices 646 * bad - a pointer to a dlist_t to hold the bad slices 647 * 648 * RETURNS: int - 0 on success 649 * !0 otherwise. 650 * 651 * PURPOSE: Examines input list of known disks and determines the slices 652 * attached to each. 653 * 654 * Some slices returned from libdiskmgt may not really exist, 655 * this is detected when trying to get more information about 656 * the slice -- ENODEV is returned. Any such slices will be 657 * added to the bad slice list. 658 */ 659 static int 660 generate_known_slices( 661 dlist_t *disks, 662 dlist_t **known, 663 dlist_t **bad) 664 { 665 dlist_t *iter; 666 int error = 0; 667 668 /* iterate list of disks and add their slices to the known list */ 669 for (iter = disks; (iter != NULL) && (error == 0); iter = iter->next) { 670 671 dm_descriptor_t disk = (uintptr_t)iter->obj; 672 dlist_t *slices = NULL; 673 dlist_t *iter1; 674 char *dname = NULL; 675 boolean_t disk_ctd_alias_derived = B_FALSE; 676 677 if (((error = disk_get_slices(disk, &slices)) != 0) || 678 ((error = get_display_name(disk, &dname)) != 0)) { 679 continue; 680 } 681 682 for (iter1 = slices; 683 (iter1 != NULL) && (error == 0); 684 iter1 = iter1->next) { 685 686 dm_descriptor_t slice = (uintptr_t)iter1->obj; 687 uint32_t index = 0; 688 nvlist_t *attrs = NULL; 689 char *sname = NULL; 690 691 if (((error = get_name(slice, &sname)) != 0) || 692 ((error = slice_get_index(slice, &index)) != 0) || 693 ((error = get_cached_attributes(slice, &attrs)) != 0)) { 694 695 if (error == ENODEV) { 696 /* bad slice, remember it and continue */ 697 dlist_t *item = dlist_new_item((void *)slice); 698 if (item == NULL) { 699 error = ENOMEM; 700 } else { 701 *bad = dlist_insert_ordered( 702 item, *bad, 703 ASCENDING, compare_descriptor_names); 704 error = 0; 705 } 706 } 707 continue; 708 } 709 710 if ((error == 0) && (is_did_slice_name(sname) == B_TRUE) && 711 (disk_ctd_alias_derived == B_FALSE)) { 712 /* BEGIN CSTYLED */ 713 /* 714 * If the slice name is a DID name, get the local CTD 715 * name for slice, extract the disk name and add it as 716 * an alias for the disk. 717 * 718 * This is the only way to derive the CTD alias for the 719 * disk when DID is enabled. 720 * 721 * The disk_ctd_alias_derived flag ensure the disk's 722 * CTD alias is only set once. 723 * 724 * The slice's CTD aliases are then derived from the 725 * disk's CTD alias in the normal, non-DID name processing 726 * which happens below. 727 */ 728 /* END CSTYLED */ 729 char *local = NULL; 730 if ((error = nvlist_lookup_string(attrs, DM_LOCALNAME, 731 &local)) != 0) { 732 if (error == ENOENT) { 733 /* no local name -> no DID */ 734 error = 0; 735 } 736 } else { 737 char *localdisk = NULL; 738 char *diskonly = NULL; 739 if ((error = extract_diskname(local, 740 &localdisk)) == 0) { 741 if ((diskonly = strrchr(localdisk, '/')) != NULL) { 742 ++diskonly; 743 } else { 744 diskonly = localdisk; 745 } 746 oprintf(OUTPUT_DEBUG, 747 gettext(" set DID disk CTD alias: %s\n"), 748 diskonly); 749 error = set_alias(disk, diskonly); 750 free(localdisk); 751 disk_ctd_alias_derived = B_TRUE; 752 } 753 } 754 } 755 756 /* derive slice display name from disk's display name */ 757 if (error == 0) { 758 if ((error = make_slicename_for_diskname_and_index( 759 dname, index, &sname)) == 0) { 760 error = set_display_name(slice, sname); 761 } 762 } 763 764 /* set slice aliases using disk aliases */ 765 if (error == 0) { 766 dlist_t *aliases = NULL; 767 if ((error = get_aliases(disk, &aliases)) == 0) { 768 769 dlist_t *iter2 = aliases; 770 for (; (iter2 != NULL) && (error == 0); 771 iter2 = iter2->next) { 772 773 char *dalias = (char *)iter2->obj; 774 char *salias = NULL; 775 776 if ((error = make_slicename_for_diskname_and_index( 777 dalias, index, &salias)) == 0) { 778 error = set_alias(slice, salias); 779 free(salias); 780 } 781 } 782 dlist_free_items(aliases, free); 783 } 784 } 785 786 if (error == 0) { 787 dlist_t *item = dlist_new_item((void *)slice); 788 if (item == NULL) { 789 error = ENOMEM; 790 } else { 791 *known = 792 dlist_insert_ordered( 793 item, *known, 794 ASCENDING, compare_desc_display_names); 795 } 796 } 797 } 798 799 dlist_free_items(slices, NULL); 800 } 801 802 return (error); 803 } 804 805 /* 806 * FUNCTION: generate_known_hbas(dlist_t *disks, dlist_t **known) 807 * 808 * INPUT: diskset - a char * diskset name. 809 * 810 * OUTPUT: populates the list of known HBAs. 811 * 812 * RETURNS: int - 0 on success 813 * !0 otherwise 814 * 815 * PURPOSE: Examines known disk list and derives the list of known HBAs. 816 * 817 * Determines the CTD name for an HBA and saves it. 818 */ 819 static int 820 generate_known_hbas( 821 dlist_t *disks, 822 dlist_t **known) 823 { 824 dlist_t *iter; 825 int error = 0; 826 827 /* 828 * for each known disk follow its HBA connections and 829 * assemble the list of known HBAs. 830 */ 831 for (iter = disks; 832 (iter != NULL) && (error == 0); 833 iter = iter->next) { 834 835 dm_descriptor_t disk = (uintptr_t)iter->obj; 836 dlist_t *hbas = NULL; 837 dlist_t *iter2 = NULL; 838 dlist_t *iter3 = NULL; 839 dlist_t *aliases = NULL; 840 char *dname = NULL; 841 842 ((error = get_display_name(disk, &dname)) != 0) || 843 (error = disk_get_aliases(disk, &aliases)) || 844 (error = disk_get_hbas(disk, &hbas)); 845 846 if (error == 0) { 847 848 if ((hbas == NULL) || (dlist_length(hbas) == 0)) { 849 850 oprintf(OUTPUT_DEBUG, 851 gettext("Disk %s has no HBA/Controller?!\n"), 852 dname); 853 error = -1; 854 855 dlist_free_items(hbas, NULL); 856 dlist_free_items(aliases, NULL); 857 858 continue; 859 } 860 861 for (iter2 = hbas, iter3 = aliases; 862 iter2 != NULL && iter3 != NULL; 863 iter2 = iter2->next, iter3 = iter3->next) { 864 865 dm_descriptor_t hba = (uintptr_t)iter2->obj; 866 dm_descriptor_t alias = (uintptr_t)iter3->obj; 867 dlist_t *item = NULL; 868 869 /* scan list of known HBAs and see if known */ 870 if (dlist_contains(*known, 871 (void*)hba, compare_descriptor_names) == B_TRUE) { 872 /* known HBA */ 873 continue; 874 } 875 876 /* see if HBA supports MPXIO */ 877 if ((error == 0) && (_mpxio_enabled != B_TRUE)) { 878 hba_is_multiplex(hba, &_mpxio_enabled); 879 } 880 881 /* generate a CTD name for HBA */ 882 error = generate_known_hba_name(hba, alias, disk); 883 if (error == 0) { 884 /* add to known HBA list */ 885 if ((item = dlist_new_item((void *)hba)) == NULL) { 886 error = ENOMEM; 887 } else { 888 *known = 889 dlist_insert_ordered(item, *known, 890 ASCENDING, compare_desc_display_names); 891 } 892 } 893 } 894 } 895 896 dlist_free_items(aliases, NULL); 897 dlist_free_items(hbas, NULL); 898 } 899 900 return (error); 901 } 902 903 /* 904 * FUNCTION: generate_known_hba_name(dm_descriptor_t hba, 905 * dm_descriptor_t alias, char *diskname) 906 * 907 * INPUT: hba - a dm_descriptor_t HBA handle. 908 * alias - a dm_descriptor_t disk alias handle. 909 * diskname - a char * disk name 910 * 911 * RETURNS: int - 0 on success 912 * !0 otherwise 913 * 914 * PURPOSE: Sets the CTD name for the input HBA. 915 * 916 * The CTD name for the HBA is generated from the input 917 * disk alias (ex: cXdXtXsX) or from the disk name if 918 * the input alias is a DID name (ex: dX). 919 */ 920 static int 921 generate_known_hba_name( 922 dm_descriptor_t hba, 923 dm_descriptor_t alias, 924 dm_descriptor_t disk) 925 { 926 char *hbaname = NULL; 927 char *aliasname = NULL; 928 int error = 0; 929 930 ((error = get_name(alias, &aliasname)) != 0) || 931 (error = extract_hbaname(aliasname, &hbaname)); 932 if (error != 0) { 933 free(hbaname); 934 return (error); 935 } 936 937 /* see if the input alias is a DID name... */ 938 if (is_did_disk_name(aliasname) == B_TRUE) { 939 940 /* look for a non-DID name in disk's aliases */ 941 dlist_t *aliases = NULL; 942 error = get_aliases(disk, &aliases); 943 944 for (; (error == 0) && (aliases != NULL); 945 aliases = aliases->next) { 946 947 aliasname = (char *)aliases->obj; 948 if (is_did_disk_name(aliasname) != B_TRUE) { 949 /* this is the "local" CTD name generated by */ 950 /* generate_known_disks() above */ 951 error = extract_hbaname(aliasname, &hbaname); 952 if ((error == 0) && (hbaname != NULL)) { 953 set_display_name(hba, hbaname); 954 break; 955 } 956 } 957 } 958 dlist_free_items(aliases, free); 959 960 } else { 961 /* use whatever was derived from the alias name */ 962 set_display_name(hba, hbaname); 963 } 964 965 return (error); 966 } 967 968 /* 969 * FUNCTION: print_known_devices() 970 * 971 * PURPOSE: Print out the known devices. 972 * 973 * Iterates the lists of known slices, disks and HBAs 974 * and prints out their CTD and device names. 975 */ 976 static void 977 print_known_devices( 978 char *diskset) 979 { 980 int i = 0; 981 struct { 982 char *msg; 983 dlist_t *list; 984 } devs[3]; 985 986 devs[0].msg = gettext("HBA/Controllers"); 987 devs[0].list = _known_hbas; 988 devs[1].msg = gettext("disks"); 989 devs[1].list = _known_disks; 990 devs[2].msg = gettext("slices"); 991 devs[2].list = _known_slices; 992 993 for (i = 0; i < 3; i++) { 994 995 oprintf(OUTPUT_VERBOSE, 996 gettext("\n These %s are known:\n\n"), 997 devs[i].msg); 998 999 print_device_list(devs[i].list); 1000 } 1001 } 1002 1003 /* 1004 * FUNCTION: get_usable_slices(dlist_t **list) 1005 * 1006 * OUTPUT: list - a dlist_t pointer to hold the returned list of 1007 * devices. 1008 * 1009 * RETURNS: int - 0 on success 1010 * !0 otherwise 1011 * 1012 * PURPOSE: Public accessors the the modules private lists of 1013 * available devices. 1014 * 1015 * The functions are keyed by diskset name in the event 1016 * objects in different disksets are loaded concurrently. 1017 */ 1018 int 1019 get_usable_slices( 1020 dlist_t **list) 1021 { 1022 *list = _usable_slices; 1023 1024 return (0); 1025 } 1026 1027 int 1028 get_usable_disks( 1029 dlist_t **list) 1030 { 1031 *list = _usable_disks; 1032 1033 return (0); 1034 } 1035 1036 int 1037 get_usable_hbas( 1038 dlist_t **list) 1039 { 1040 *list = _usable_hbas; 1041 1042 return (0); 1043 } 1044 1045 /* 1046 * FUNCTION: generate_usable_disks_and_slices_in_local_set(dlist_t **classes, 1047 * dlist_t **bad_disks, dlist_t **usable_disks, 1048 * dlist_t **usable_slices) 1049 * 1050 * OUTPUT: used_classes - a pointer to a list of slice_class_t structs 1051 * updated with known slices that have detected uses 1052 * added to the correct class'e list of slices. 1053 * bad_disks - a pointer to a list of bad/unusable disks updated 1054 * with any bad disks that were detected 1055 * useable_disks - a pointer to a list of usable disks 1056 * useable_slices - a pointer to a list of usable slices 1057 * 1058 * RETURNS: int - 0 on success 1059 * !0 otherwise. 1060 * 1061 * PURPOSE: Scans the disks in the local set to determine which are 1062 * usable during layout processing. 1063 * 1064 * Determines which are usable by layout using usages detected 1065 * by libdiskmgt. 1066 */ 1067 static int 1068 generate_usable_disks_and_slices_in_local_set( 1069 dlist_t **classes, 1070 dlist_t **bad_slices, 1071 dlist_t **usable_disks, 1072 dlist_t **usable_slices) 1073 { 1074 char *dsname = MD_LOCAL_NAME; 1075 dlist_t *disks; 1076 dlist_t *iter; 1077 int error; 1078 1079 /* Get disks in local set */ 1080 error = get_disks_in_diskset(dsname, &disks); 1081 if (error != 0) { 1082 return (error); 1083 } 1084 1085 /* For each disk in this set... */ 1086 for (iter = disks; iter != NULL && error == 0; iter = iter->next) { 1087 dm_descriptor_t disk = (uintptr_t)iter->obj; 1088 dlist_t *slices; 1089 1090 /* Get slices on this disk */ 1091 error = disk_get_slices(disk, &slices); 1092 if (error == 0) { 1093 dlist_t *iter2; 1094 1095 /* 1096 * Assume disk is available until a bad or unavailable 1097 * slice is found 1098 */ 1099 boolean_t avail = B_TRUE; 1100 boolean_t bad_disk = B_FALSE; 1101 1102 /* For each slice on this disk... */ 1103 for (iter2 = slices; 1104 iter2 != NULL && error == 0 && 1105 avail == B_TRUE && bad_disk == B_FALSE; 1106 iter2 = iter2->next) { 1107 1108 dm_descriptor_t slice = (uintptr_t)iter2->obj; 1109 dlist_t *bad_slices_on_this_disk = NULL; 1110 1111 /* Is this slice available? */ 1112 error = check_slice_usage(dsname, slice, 1113 disk, &avail, &bad_slices_on_this_disk, classes); 1114 1115 /* Is the slice bad (inaccessible)? */ 1116 if (error != 0 && bad_slices_on_this_disk != NULL) { 1117 bad_disk = B_TRUE; 1118 *bad_slices = dlist_append_list( 1119 *bad_slices, bad_slices_on_this_disk); 1120 } 1121 } 1122 1123 /* Is the disk available? */ 1124 if (error == 0 && bad_disk == B_FALSE && avail == B_TRUE) { 1125 error = dlist_append_object( 1126 (void *)disk, usable_disks, AT_TAIL); 1127 } 1128 1129 dlist_free_items(slices, NULL); 1130 } 1131 } 1132 1133 dlist_free_items(disks, NULL); 1134 1135 if (error == 0) { 1136 /* BEGIN CSTYLED */ 1137 /* 1138 * Now reslice usable disks in the local set to 1139 * simulate the slices they'll have when they're added 1140 * to the named disk set, and add these resulting 1141 * virtual slices to the list of available slices. 1142 */ 1143 /* END CSTYLED */ 1144 error = generate_virtual_slices(*usable_disks, usable_slices); 1145 } 1146 1147 return (error); 1148 } 1149 1150 /* 1151 * FUNCTION: generate_virtual_slices(dlist_t *unused, dlist_t **usable) 1152 * 1153 * INPUT: slice_classes - a list of unused slice dm_descriptor_t handles. 1154 * 1155 * OUTPUT: usable - pointer to the list of usable slices, updated 1156 * with any created virtual slices. 1157 * 1158 * RETURNS: int - 0 on success 1159 * !0 otherwise. 1160 * 1161 * PURPOSE: Helper which creates virtual slices for each disk which 1162 * could be added to a diskset if necessary... 1163 * 1164 * Search the input list of slice classes for the entry 1165 * containing slices known to be available for use by layout. 1166 * 1167 * Iterate the list of unused slices and determine the set 1168 * of unique disks. 1169 * 1170 * For each unique disk, create virtual slice descriptors to 1171 * represent those that will exist if/when the disk is added 1172 * to the diskset. 1173 * 1174 * Add theese virtual slices to the list of usable slices. 1175 */ 1176 static int 1177 generate_virtual_slices( 1178 dlist_t *avail_disks_local_set, 1179 dlist_t **usable) 1180 { 1181 dlist_t *iter = NULL; 1182 int error = 0; 1183 1184 /* generate virtual slices */ 1185 error = create_virtual_slices(avail_disks_local_set); 1186 if (error == 0) { 1187 1188 get_virtual_slices(&iter); 1189 for (; (iter != NULL) && (error == 0); iter = iter->next) { 1190 1191 dlist_t *item = dlist_new_item((void *) iter->obj); 1192 if (item == NULL) { 1193 error = ENOMEM; 1194 } else { 1195 *usable = 1196 dlist_insert_ordered(item, *usable, 1197 ASCENDING, compare_desc_display_names); 1198 } 1199 } 1200 } 1201 1202 return (error); 1203 } 1204 1205 /* 1206 * FUNCTION: generate_usable_disks_and_slices_in_named_set(char *dsname, 1207 * dlist_t **classes, dlist_t **bad_slices, 1208 * dlist_t **usable_slices, dlist_t **usable_disks) 1209 * 1210 * INPUT: dsname - a char * diskset name. 1211 * 1212 * OUTPUT: classes - pointer to a list of slice_class_t structs, 1213 * updated to include slices in the disk set with 1214 * known uses. 1215 * bad_slices - pointer to a list of bad/unusable slices, 1216 * updated to include slices in the disk set that 1217 * are inaccessible or no longer existent. 1218 * usable_slices - pointer to a list of usable slices in the 1219 * disk set. 1220 * usable_disks - pointer to a list of usable disks in the 1221 * disk set. 1222 * 1223 * RETURNS: int - 0 on success 1224 * !0 otherwise. 1225 * 1226 * PURPOSE: 1. determine the disks in the named disk set 1227 * 2. determine the used slices on the disks 1228 * 3. determine the unused slices on the disks 1229 * 4. look for unused space on the disks and collect it 1230 * into an existing unused slice, or create a new 1231 * virtual slice. 1232 */ 1233 static int 1234 generate_usable_disks_and_slices_in_named_set( 1235 char *dsname, 1236 dlist_t **classes, 1237 dlist_t **bad_slices, 1238 dlist_t **usable_disks, 1239 dlist_t **usable_slices) 1240 { 1241 dlist_t *disks = NULL; 1242 dlist_t *iter = NULL; 1243 int error = 0; 1244 1245 error = get_disks_in_diskset(dsname, &disks); 1246 if (error != 0) { 1247 return (error); 1248 } 1249 1250 /* For each disk... */ 1251 for (iter = disks; 1252 iter != NULL && error == 0; 1253 iter = iter->next) { 1254 1255 dm_descriptor_t disk = (uintptr_t)iter->obj; 1256 dlist_t *iter2; 1257 dlist_t *slices = NULL; 1258 dlist_t *bad_slices_on_this_disk = NULL; 1259 dlist_t *used_slices_on_this_disk = NULL; 1260 dlist_t *unused_slices_on_this_disk = NULL; 1261 boolean_t bad_disk = B_FALSE; 1262 1263 error = disk_get_slices(disk, &slices); 1264 if (error != 0) { 1265 break; 1266 } 1267 1268 /* Determine the used, unused, and bad slices on the disk */ 1269 1270 /* For each slice... */ 1271 for (iter2 = slices; 1272 iter2 != NULL && error == 0 && bad_disk == B_FALSE; 1273 iter2 = iter2->next) { 1274 1275 dm_descriptor_t slice = (uintptr_t)iter2->obj; 1276 1277 boolean_t rsvd = B_FALSE; 1278 boolean_t avail = B_FALSE; 1279 1280 /* Get slice usage */ 1281 if (((error = is_reserved_slice(slice, &rsvd)) == 0) && 1282 ((error = check_slice_usage(dsname, slice, disk, &avail, 1283 &bad_slices_on_this_disk, classes)) == 0)) { 1284 1285 /* Is the slice bad (inaccessible)? */ 1286 if (bad_slices_on_this_disk != NULL) { 1287 *bad_slices = dlist_append_list( 1288 *bad_slices, bad_slices_on_this_disk); 1289 /* 1290 * Since one slice on this disk is bad, don't 1291 * use any slices on this disk 1292 */ 1293 bad_disk = B_TRUE; 1294 } else { 1295 1296 dlist_t *item = dlist_new_item((void *)slice); 1297 if (item == NULL) { 1298 error = ENOMEM; 1299 } else { 1300 /* Add slice to used/unused list as appropriate */ 1301 if (avail == B_TRUE && rsvd == B_FALSE) { 1302 unused_slices_on_this_disk = dlist_append( 1303 item, unused_slices_on_this_disk, AT_TAIL); 1304 } else { 1305 used_slices_on_this_disk = 1306 dlist_insert_ordered(item, 1307 used_slices_on_this_disk, 1308 ASCENDING, compare_start_blocks); 1309 } 1310 } 1311 } 1312 } 1313 } 1314 1315 /* Done iterating slices */ 1316 1317 if (error == 0 && bad_disk == B_FALSE) { 1318 /* For each unused slice... */ 1319 for (iter2 = unused_slices_on_this_disk; 1320 iter2 != NULL && error == 0; 1321 iter2 = iter2->next) { 1322 1323 dm_descriptor_t slice = (uintptr_t)iter2->obj; 1324 error = update_slice_attributes(slice, 0, 0, 0); 1325 1326 /* Only do this once */ 1327 if (error == 0 && iter2 == unused_slices_on_this_disk) { 1328 error = add_modified_disk(NULL, disk); 1329 } 1330 } 1331 1332 if (error == 0) { 1333 /* Create usable slices from the used/unused slice lists */ 1334 error = create_usable_slices(disk, used_slices_on_this_disk, 1335 unused_slices_on_this_disk, usable_slices); 1336 if (error == 0) { 1337 error = dlist_append_object( 1338 (void *)disk, usable_disks, AT_TAIL); 1339 } 1340 } 1341 } 1342 1343 dlist_free_items(slices, NULL); 1344 dlist_free_items(used_slices_on_this_disk, NULL); 1345 dlist_free_items(unused_slices_on_this_disk, NULL); 1346 } 1347 1348 return (error); 1349 } 1350 1351 /* 1352 * FUNCTION: create_usable_slices(dm_descriptor_t disk, dlist_t *used, 1353 * dlist_t *unused, dlist_t **usable); 1354 * 1355 * INPUT: disk - a dm_descriptor_t disk handle 1356 * used - pointer to a list of pvt_t structs 1357 * representing existing used slices 1358 * on the input disk. 1359 * unused - pointer to a list of pvt_t structs 1360 * representing existing unused slices 1361 * on the input disk. 1362 * 1363 * OUTPUT: usable - pointer to a list of pvts representing slices 1364 * which can be used for new volume layouts. 1365 * 1366 * Slices in this list have any available space on the 1367 * disk collected into the fewest, lowest indexed slices 1368 * possible. 1369 * 1370 * RETURNS: int - 0 on success 1371 * !0 otherwise. 1372 * 1373 * PURPOSE: helper for generate_usable_slices_and_disks_in_diskset() which 1374 * turns any detected free space on the input disk into one or 1375 * more slices. 1376 */ 1377 static int 1378 create_usable_slices( 1379 dm_descriptor_t disk, 1380 dlist_t *used, 1381 dlist_t *unused, 1382 dlist_t **usable) 1383 { 1384 dlist_t *iter; 1385 int error = 0; 1386 boolean_t first = B_TRUE; 1387 dlist_t *next_unused = unused; 1388 1389 char *dname = NULL; 1390 uint64_t disk_firstblk = 0; 1391 uint64_t disk_nblks = 0; 1392 uint64_t disk_endblk = 0; 1393 1394 oprintf(OUTPUT_DEBUG, 1395 gettext("\n create_usable_slices for disk\n")); 1396 1397 /* get necessary info about disk: */ 1398 error = get_display_name(disk, &dname); 1399 if (error != 0) { 1400 return (error); 1401 } 1402 1403 /* disk start block is first usable block */ 1404 error = disk_get_start_block(disk, &disk_firstblk); 1405 if (error != 0) { 1406 return (error); 1407 } 1408 1409 /* disk size determines last usable disk block */ 1410 error = disk_get_size_in_blocks(disk, &disk_nblks); 1411 if (error != 0) { 1412 return (error); 1413 } 1414 1415 disk_endblk = disk_firstblk + disk_nblks - 1; 1416 1417 /* search for gaps before, between and after used slices */ 1418 for (iter = used; iter != NULL && error == 0; iter = iter->next) { 1419 1420 dm_descriptor_t cur = (uintptr_t)iter->obj; 1421 1422 uint64_t cur_stblk = 0; 1423 uint64_t cur_nblks = 0; 1424 uint64_t cur_endblk = 0; 1425 uint32_t cur_index = 0; 1426 1427 uint64_t new_stblk = 0; 1428 uint64_t new_endblk = 0; 1429 1430 char *sname = NULL; 1431 (void) get_display_name(cur, &sname); 1432 1433 if (((error = slice_get_index(cur, &cur_index)) != 0) || 1434 ((error = slice_get_start_block(cur, &cur_stblk)) != 0) || 1435 ((error = slice_get_size_in_blocks(cur, &cur_nblks)) != 0)) { 1436 continue; 1437 } 1438 1439 cur_endblk = cur_stblk + cur_nblks - 1; 1440 1441 oprintf(OUTPUT_DEBUG, 1442 gettext(" used slice %d (%10llu to %10llu)\n"), 1443 cur_index, cur_stblk, cur_endblk); 1444 1445 if (first == B_TRUE) { 1446 /* first slice: make sure it starts at disk_firstblk */ 1447 first = B_FALSE; 1448 if (cur_stblk != disk_firstblk) { 1449 /* close gap at beginning of disk */ 1450 new_stblk = disk_firstblk; 1451 new_endblk = cur_stblk - 1; 1452 1453 oprintf(OUTPUT_DEBUG, 1454 gettext(" unused space before first " 1455 "used slice\n")); 1456 } 1457 } 1458 1459 if (iter->next != NULL) { 1460 /* check for gap between slices */ 1461 dm_descriptor_t next = (uintptr_t)iter->next->obj; 1462 uint64_t next_stblk = 0; 1463 uint32_t next_index = 0; 1464 1465 if (((error = slice_get_start_block(next, &next_stblk)) == 0) && 1466 ((error = slice_get_index(next, &next_index)) == 0)) { 1467 if (cur_endblk != next_stblk - 1) { 1468 /* close gap between slices */ 1469 new_stblk = cur_endblk + 1; 1470 new_endblk = next_stblk - 1; 1471 1472 oprintf(OUTPUT_DEBUG, 1473 gettext(" unused space between slices " 1474 "%d and %d\n"), cur_index, next_index); 1475 } 1476 } 1477 1478 } else { 1479 /* last slice: make sure it includes last block on disk */ 1480 if (cur_endblk != disk_endblk) { 1481 /* close gap at end of disk */ 1482 new_stblk = cur_endblk + 1; 1483 new_endblk = disk_endblk; 1484 1485 oprintf(OUTPUT_DEBUG, 1486 gettext(" unused space after last slice " 1487 "cur_endblk: %llu disk_endblk: %llu\n"), 1488 cur_endblk, disk_endblk); 1489 } 1490 } 1491 1492 if ((error == 0) && (new_endblk != 0)) { 1493 error = add_new_usable(disk, new_stblk, 1494 new_endblk - new_stblk + 1, &next_unused, usable); 1495 } 1496 } 1497 1498 if (error != 0) { 1499 dlist_free_items(*usable, free); 1500 *usable = NULL; 1501 } 1502 1503 return (error); 1504 } 1505 1506 /* 1507 * FUNCTION: add_new_usable(dm_descriptor_t disk, uint64_t stblk, 1508 * uint64_t nblks, dlist_t **next_unused, 1509 * dlist_t **usable); 1510 * 1511 * INPUT: disk - a dm_descriptor_t disk handle 1512 * stblk - start block of the usable space 1513 * nblks - number of usable blocks 1514 * next_unused - pointer to the next unused slice 1515 * 1516 * OUTPUT: next_unused - updated pointer to the next unused slice 1517 * usable - possibly updated pointer to a list of slices on 1518 * the disk with usable space 1519 * 1520 * RETURNS: int - 0 on success 1521 * !0 otherwise. 1522 * 1523 * PURPOSE: helper for create_usable_slices() which turns free space 1524 * on the input disk into a usable slice. 1525 * 1526 * If possible an existing unused slice will be recycled 1527 * into a usable slice. If there are none, a new virtual 1528 * slice will be created. 1529 */ 1530 static int 1531 add_new_usable( 1532 dm_descriptor_t disk, 1533 uint64_t stblk, 1534 uint64_t nblks, 1535 dlist_t **next_unused, 1536 dlist_t **usable) 1537 { 1538 dm_descriptor_t new_usable = 0; 1539 int error = 0; 1540 1541 /* try to use an existing unused slice for the usable slice */ 1542 if (*next_unused != NULL) { 1543 new_usable = (uintptr_t)((*next_unused)->obj); 1544 *next_unused = (*next_unused)->next; 1545 1546 oprintf(OUTPUT_DEBUG, 1547 gettext("\trecyling used slice into usable slice " 1548 "start: %llu, end: %llu\n"), 1549 stblk, stblk + nblks + 1); 1550 } 1551 1552 if (new_usable == NULL) { 1553 /* no unused slices, try to make a new virtual slice */ 1554 uint32_t index = UINT32_MAX; 1555 error = disk_get_available_slice_index(disk, &index); 1556 if ((error == 0) && (index != UINT32_MAX)) { 1557 1558 char *dname = NULL; 1559 error = get_display_name(disk, &dname); 1560 if (error == 0) { 1561 1562 char buf[MAXNAMELEN]; 1563 (void) snprintf(buf, MAXNAMELEN-1, "%ss%d", dname, index); 1564 error = add_virtual_slice(buf, index, 0, 0, disk); 1565 if (error == 0) { 1566 /* retrieve the virtual slice */ 1567 error = slice_get_by_name(buf, &new_usable); 1568 } 1569 } 1570 } 1571 } 1572 1573 if ((error == 0) && (new_usable != (dm_descriptor_t)0)) { 1574 /* BEGIN CSTYLED */ 1575 /* 1576 * have an unused slice, update its attributes to reflect 1577 * the usable space it represents 1578 */ 1579 /* END CSTYLED */ 1580 uint64_t disk_blksz = 0; 1581 error = disk_get_blocksize(disk, &disk_blksz); 1582 if (error == 0) { 1583 error = update_slice_attributes(new_usable, stblk, 1584 nblks, nblks * disk_blksz); 1585 if (error == 0) { 1586 error = dlist_append_object( 1587 (void *)new_usable, usable, AT_TAIL); 1588 } 1589 } 1590 } 1591 1592 return (error); 1593 } 1594 1595 /* 1596 * FUNCTION: update_slice_attributes(dm_descriptor_t slice, uint64_t stblk, 1597 * uint64_t nblks, uint64_t nbytes) 1598 * 1599 * INPUT: slice - a dm_descriptor_t slice handle 1600 * stblk - start block of the usable space 1601 * nblks - size of slice in blocks 1602 * nbytes - size of slice in bytes 1603 * 1604 * SIDEEFFECT: adds a modification record for the slice. 1605 * 1606 * RETURNS: int - 0 on success 1607 * !0 otherwise. 1608 * 1609 * PURPOSE: utility which updates several slice attributes in one call. 1610 */ 1611 static int 1612 update_slice_attributes( 1613 dm_descriptor_t slice, 1614 uint64_t stblk, 1615 uint64_t nblks, 1616 uint64_t nbytes) 1617 { 1618 char *sname = NULL; 1619 uint32_t index = 0; 1620 int error = 0; 1621 1622 if ((error = get_display_name(slice, &sname)) == 0) { 1623 if ((error = slice_get_index(slice, &index)) == 0) { 1624 if ((error = slice_set_start_block(slice, stblk)) == 0) { 1625 if ((error = slice_set_size_in_blocks(slice, nblks)) == 0) { 1626 if (nblks == 0) { 1627 error = add_slice_to_remove(sname, index); 1628 } else { 1629 error = assemble_modified_slice((dm_descriptor_t)0, 1630 sname, index, stblk, nblks, nbytes, NULL); 1631 } 1632 } 1633 } 1634 } 1635 } 1636 1637 return (error); 1638 } 1639 1640 /* 1641 * FUNCTION: generate_usable_hbas(dlist_t *slices, 1642 * dlist_t **usable) 1643 * 1644 * INPUT: disks - a list of usable disks. 1645 * 1646 * OUTPUT: usable - a populated list of usable HBAs. 1647 * 1648 * RETURNS: int - 0 on success 1649 * !0 otherwise 1650 * 1651 * PURPOSE: Examines usable disk list and derives the list of usable HBAs. 1652 * 1653 */ 1654 static int 1655 generate_usable_hbas( 1656 dlist_t *disks, 1657 dlist_t **usable) 1658 { 1659 dlist_t *iter; 1660 int error = 0; 1661 1662 /* 1663 * for each usable disk, follow its HBA connections and 1664 * add them to the list of usable HBAs. 1665 */ 1666 for (iter = disks; (iter != NULL) && (error == 0); iter = iter->next) { 1667 1668 dm_descriptor_t dp = NULL; 1669 dlist_t *hbas = NULL; 1670 dlist_t *iter2 = NULL; 1671 1672 dp = (uintptr_t)iter->obj; 1673 1674 error = disk_get_hbas(dp, &hbas); 1675 if (error == 0) { 1676 1677 for (iter2 = hbas; 1678 (iter2 != NULL) && (error == 0); 1679 iter2 = iter2->next) { 1680 1681 dm_descriptor_t hba = (uintptr_t)iter2->obj; 1682 dlist_t *item = NULL; 1683 1684 /* scan list of usable HBAs and see if known */ 1685 if (dlist_contains(*usable, 1686 (void*)hba, compare_descriptor_names) == B_TRUE) { 1687 /* known HBA, continue to next HBA/alias */ 1688 continue; 1689 } 1690 1691 /* add this HBA to the usable list */ 1692 if ((item = dlist_new_item((void *)hba)) == NULL) { 1693 error = ENOMEM; 1694 } else { 1695 *usable = 1696 dlist_insert_ordered(item, *usable, 1697 ASCENDING, compare_desc_display_names); 1698 } 1699 } 1700 } 1701 1702 dlist_free_items(hbas, NULL); 1703 } 1704 1705 return (error); 1706 } 1707 1708 /* 1709 * FUNCTION: check_slice_usage(char *dsname, dm_descriptor_t slice, 1710 * dm_descriptor_t disk, boolean_t *avail, 1711 * dlist_t **bad, dlist_t **classes) 1712 * 1713 * INPUT: dsname - a char * diskset name. 1714 * slice - a dm_descriptor_t handle for a known slices. 1715 * disk - a dm_descriptor_t handle the slice's disk. 1716 * 1717 * OUTPUT: avail - a boolean_t to hold the slice's availability. 1718 * bad - pointer to a list of bad/unusable slices, 1719 * possibly updated if the input slice 1720 * was determined to be inaccessible. 1721 * classes - pointer to a list of slice_class_t structs, 1722 * possibly updated to include the input slice 1723 * if it has a known use. 1724 * 1725 * RETURNS: int - 0 on success 1726 * !0 otherwise. 1727 * 1728 * PURPOSE: Handles the details of 1729 * determining usage and/or availability of a single slice. 1730 * 1731 * Queries the device library for the input slice's detectable 1732 * usage status. 1733 * 1734 * If the slice has a detected usage, its name is added to 1735 * the appropriate slice_class_t list in the input list of 1736 * slice classes, this is only done if verbose output was 1737 * requested. 1738 */ 1739 static int 1740 check_slice_usage( 1741 char *dsname, 1742 dm_descriptor_t slice, 1743 dm_descriptor_t disk, 1744 boolean_t *avail, 1745 dlist_t **bad, 1746 dlist_t **classes) 1747 { 1748 boolean_t online = B_FALSE; 1749 boolean_t used = B_FALSE; 1750 nvlist_t *stats = NULL; 1751 char *name = NULL; 1752 char *used_by = NULL; 1753 char *use_detail = NULL; 1754 int error = 0; 1755 1756 *avail = B_FALSE; 1757 1758 if (((error = get_display_name(slice, &name)) != 0) || 1759 (error = disk_get_is_online(disk, &online))) { 1760 return (error); 1761 } 1762 1763 /* 1764 * if the disk is known to be offline, skip getting status 1765 * for the slice since it will just fail and return ENODEV. 1766 */ 1767 if (online != B_TRUE) { 1768 error = ENODEV; 1769 } else { 1770 stats = dm_get_stats(slice, DM_SLICE_STAT_USE, &error); 1771 } 1772 1773 if (error != 0) { 1774 if (error == ENODEV) { 1775 dlist_t *item = dlist_new_item((void *)slice); 1776 oprintf(OUTPUT_TERSE, 1777 gettext("Warning: unable to get slice information " 1778 "for %s, it will not be used.\n"), name); 1779 1780 if (item == NULL) { 1781 error = ENOMEM; 1782 } else { 1783 error = 0; 1784 *bad = dlist_insert_ordered(item, *bad, ASCENDING, 1785 compare_desc_display_names); 1786 } 1787 } else { 1788 oprintf(OUTPUT_TERSE, 1789 gettext("check_slice_usage: dm_get_stats for " 1790 "%s failed %d\n"), 1791 name, error); 1792 } 1793 1794 return (error); 1795 } 1796 1797 /* 1798 * check if/how the slice is currently being used, 1799 * device library provides this info in the nvpair_t list: 1800 * 1801 * stat_type is DM_SLICE_STAT_USE 1802 * used_by: string (mount, svm, lu, vxvm, fs) 1803 * used_name: string 1804 * 1805 */ 1806 if (stats != NULL) { 1807 error = get_string(stats, DM_USED_BY, &used_by); 1808 if (error != 0) { 1809 if (error == ENOENT) { 1810 used_by = NULL; 1811 error = 0; 1812 } else { 1813 oprintf(OUTPUT_TERSE, 1814 gettext("check_slice_usage: dm_get_stats.%s for " 1815 "%s failed %d\n"), 1816 DM_USED_BY, name, error); 1817 } 1818 } 1819 1820 if (error == 0) { 1821 error = get_string(stats, DM_USED_NAME, &use_detail); 1822 if (error != 0) { 1823 if (error == ENOENT) { 1824 use_detail = NULL; 1825 error = 0; 1826 } else { 1827 oprintf(OUTPUT_TERSE, 1828 gettext("check_slice_usage: " 1829 "dm_get_stats.%s for " 1830 "%s failed %d\n"), 1831 DM_USED_NAME, name, error); 1832 } 1833 } 1834 } 1835 } 1836 1837 if ((error == 0) && (used_by != NULL) && (used_by[0] != '\0')) { 1838 1839 /* was detected usage SVM? */ 1840 if (string_case_compare(used_by, DM_USE_SVM) == 0) { 1841 1842 /* check use_detail, it is in the form diskset:name */ 1843 if (strncmp("diskset:", use_detail, 8) == 0) { 1844 1845 /* check disk set name */ 1846 char *str = strrchr(use_detail, ':'); 1847 if ((str != NULL) && 1848 (string_case_compare(str+1, dsname) == 0)) { 1849 1850 /* slice in the right diskset */ 1851 error = check_svm_slice_usage( 1852 dsname, slice, disk, &used, classes); 1853 1854 } else { 1855 1856 /* slice in other diskset */ 1857 save_slice_classification( 1858 dsname, slice, disk, used_by, use_detail, 1859 classes); 1860 used = B_TRUE; 1861 } 1862 1863 } else { 1864 1865 /* slice is volume component */ 1866 save_slice_classification( 1867 dsname, slice, disk, used_by, use_detail, 1868 classes); 1869 used = B_TRUE; 1870 } 1871 1872 } else { 1873 1874 /* save usage */ 1875 save_slice_classification( 1876 dsname, slice, disk, used_by, use_detail, 1877 classes); 1878 used = B_TRUE; 1879 } 1880 } 1881 1882 nvlist_free(stats); 1883 1884 if (error == 0) { 1885 if (used == B_TRUE) { 1886 *avail = B_FALSE; 1887 } else { 1888 *avail = B_TRUE; 1889 } 1890 } 1891 1892 return (error); 1893 } 1894 1895 /* 1896 * FUNCTION: check_svm_slice_usage(char *dsname, dm_descriptor_t slice, 1897 * dm_descriptor_t disk, boolean_t *used, 1898 * dlist_t **classes) 1899 * 1900 * INPUT: dsname - a char * diskset name. 1901 * slice - a dm_descriptor_t handle for a known slices. 1902 * disk - a dm_descriptor_t handle the slice's disk. 1903 * 1904 * OUTPUT: used - a boolean_t to hold the slice usage status. 1905 * classes - pointer to a list of slice_class_t possibly updated 1906 * with the input slice's SVM specific usage 1907 * classification. 1908 * 1909 * RETURNS: int - 0 on success 1910 * !0 otherwise. 1911 * 1912 * PURPOSE: Handles the finer details of 1913 * a single slice is being used in the context of SVM. 1914 * 1915 * Currently, one thing is checked: 1916 * 1917 * 1. determine if the slice is reserved for metadb replicas. 1918 * The convention for disks in disksets is that a single slice 1919 * (index 6 or 7) is set aside for metadb replicas. 1920 * 1921 * If this condition does not hold, the slice is considered 1922 * available for use by layout and 'used' is set to B_FALSE. 1923 */ 1924 static int 1925 check_svm_slice_usage( 1926 char *dsname, 1927 dm_descriptor_t slice, 1928 dm_descriptor_t disk, 1929 boolean_t *used, 1930 dlist_t **classes) 1931 { 1932 boolean_t is_replica = B_FALSE; 1933 uint32_t index = 0; 1934 char *diskname = NULL; 1935 int error = 0; 1936 1937 ((error = slice_get_index(slice, &index)) != 0) || 1938 (error = get_display_name(disk, &diskname)) || 1939 (error = is_reserved_replica_slice_index( 1940 dsname, diskname, index, &is_replica)); 1941 1942 if (error == 0) { 1943 if (is_replica == B_TRUE) { 1944 /* is replica slice -> used */ 1945 save_slice_classification(dsname, slice, disk, DM_USE_SVM, 1946 gettext("reserved for metadb replicas"), classes); 1947 *used = B_TRUE; 1948 } else { 1949 *used = B_FALSE; 1950 } 1951 } 1952 1953 return (error); 1954 } 1955 1956 /* 1957 * FUNCTION: save_slice_classification(char *dsname, dm_descriptor_t slice, 1958 * dm_descriptor_t disk, char *used_by, char *usage_detail, 1959 * dlist_t **classes) 1960 * 1961 * INPUT: dsname - a char * disk set name 1962 * slice - a dm_descriptor_t slice handle. 1963 * disk - a dm_descriptor_t handle for the slice's disk. 1964 * used_by - a char * usage classification. 1965 * usage_detail - a char * usage description for the slice. 1966 * 1967 * OUTPUT: classes - a list of slice_class_t updated to hold a usage 1968 * entry for the input slicexs. 1969 * 1970 * SIDEEFFECT: adds the input slice to the list of known, used slices. 1971 * 1972 * RETURNS: int - 0 on success 1973 * !0 otherwise. 1974 * 1975 * PURPOSE: Adds an entry to the 1976 * appropriate slice_class_t list of slices. If there is 1977 * not an appropriate slice_class_t entry in the input list 1978 * of classes, one is added. 1979 * 1980 * As a performance optimization the slice usage classification 1981 * information is only saved if verbose output was requested by 1982 * the user. 1983 */ 1984 static int 1985 save_slice_classification( 1986 char *dsname, 1987 dm_descriptor_t slice, 1988 dm_descriptor_t disk, 1989 char *usage, 1990 char *usage_detail, 1991 dlist_t **classes) 1992 { 1993 int error = 0; 1994 1995 error = add_used_slice(slice); 1996 1997 if ((error == 0) && (get_max_verbosity() >= OUTPUT_VERBOSE)) { 1998 1999 dlist_t *iter; 2000 dlist_t *item; 2001 slice_class_t *class = NULL; 2002 2003 /* locate class struct matching 'usage' */ 2004 for (iter = *classes; iter != NULL; iter = iter->next) { 2005 class = (slice_class_t *)iter->obj; 2006 if (string_case_compare(usage, class->usage) == 0) { 2007 break; 2008 } 2009 } 2010 2011 if (iter == NULL) { 2012 /* add a new class to the list of classes */ 2013 class = (slice_class_t *)calloc(1, sizeof (slice_class_t)); 2014 if (class == NULL) { 2015 error = ENOMEM; 2016 } else { 2017 class->usage = strdup(usage); 2018 if (class->usage == NULL) { 2019 free(class); 2020 class = NULL; 2021 error = ENOMEM; 2022 } else { 2023 item = dlist_new_item((void *)class); 2024 if (item == NULL) { 2025 free(class->usage); 2026 free(class); 2027 class = NULL; 2028 error = ENOMEM; 2029 } else { 2030 *classes = dlist_append(item, *classes, AT_TAIL); 2031 } 2032 } 2033 } 2034 } 2035 2036 if ((error == 0) && (class != NULL)) { 2037 2038 char buf[BUFSIZ]; 2039 char *dup = NULL; 2040 char *slicename = NULL; 2041 2042 (void) get_display_name(slice, &slicename); 2043 (void) snprintf(buf, BUFSIZ-1, " %s: %s", 2044 slicename, usage_detail); 2045 if ((dup = strdup(buf)) == NULL) { 2046 error = ENOMEM; 2047 } else { 2048 if ((item = dlist_new_item((void *)dup)) == NULL) { 2049 free(dup); 2050 error = ENOMEM; 2051 } else { 2052 class->sliceinfo = 2053 dlist_insert_ordered( 2054 item, class->sliceinfo, 2055 ASCENDING, compare_strings); 2056 } 2057 } 2058 } 2059 } 2060 2061 return (error); 2062 } 2063 2064 /* 2065 * FUNCTION: print_usable_devices() 2066 * 2067 * PURPOSE: Print out the devices determined to be available for 2068 * use by layout. 2069 * 2070 * Iterates the lists of usable slices, disks and HBAs 2071 * and prints out their CTD and device names. 2072 */ 2073 static void 2074 print_usable_devices() 2075 { 2076 int i = 0; 2077 2078 struct { 2079 char *msg; 2080 dlist_t *list; 2081 } devs[3]; 2082 2083 devs[0].msg = gettext("HBA/Controllers"); 2084 devs[0].list = _usable_hbas; 2085 devs[1].msg = gettext("disks"); 2086 devs[1].list = _usable_disks; 2087 devs[2].msg = gettext("slices"); 2088 devs[2].list = _usable_slices; 2089 2090 for (i = 0; i < 3; i++) { 2091 2092 oprintf(OUTPUT_VERBOSE, 2093 gettext("\n These %s are usable:\n\n"), 2094 devs[i].msg); 2095 2096 print_device_list(devs[i].list); 2097 } 2098 } 2099 2100 /* 2101 * FUNCTION: print_device_list(dlist_t *devices) 2102 * 2103 * INPUT: devices - a list of device descriptor handles 2104 * 2105 * PURPOSE: A helper for the print_XXX_devices() routines which iterates 2106 * the input list and prints out each device name, CTD name and 2107 * alias(es). 2108 */ 2109 static void 2110 print_device_list( 2111 dlist_t *devices) 2112 { 2113 dlist_t *iter = NULL; 2114 2115 for (iter = devices; iter != NULL; iter = iter->next) { 2116 2117 dm_descriptor_t device = ((uintptr_t)iter->obj); 2118 char *name = NULL; 2119 char *ctd = NULL; 2120 dlist_t *aliases = NULL; 2121 2122 (void) get_display_name(device, &ctd); 2123 (void) get_name(device, &name); 2124 oprintf(OUTPUT_VERBOSE, 2125 " %-25s %s\n", (ctd != NULL ? ctd : ""), name); 2126 2127 (void) get_aliases(device, &aliases); 2128 for (; aliases != NULL; aliases = aliases->next) { 2129 oprintf(OUTPUT_VERBOSE, 2130 gettext(" (alias: %s)\n"), 2131 (char *)aliases->obj); 2132 } 2133 2134 dlist_free_items(aliases, free); 2135 } 2136 } 2137 2138 /* 2139 * FUNCTION: print_unusable_devices( 2140 * dlist_t *bad_slices, dlist_t *bad_disks, 2141 * dlist_t *used_classes) 2142 * 2143 * INPUT: used_classes - a list of slice_class_t structs 2144 * 2145 * PURPOSE: Print out the devices determined to be unavailable for 2146 * use by layout. 2147 * 2148 * Iterates the input list of slice classifications and prints 2149 * out a description of the class and the slices so classified. 2150 * 2151 * Also iterates the lists of bad slices and disks (those that 2152 * libdiskmgt returned descriptors for but cannot be accessed) 2153 * and notes them as unusable. 2154 */ 2155 static void 2156 print_unusable_devices( 2157 dlist_t *bad_slices, 2158 dlist_t *bad_disks, 2159 dlist_t *used_classes) 2160 { 2161 dlist_t *iter = NULL; 2162 dlist_t *slices = NULL; 2163 char *preamble; 2164 2165 struct { 2166 char *msg; 2167 dlist_t *list; 2168 } devs[2]; 2169 2170 /* report bad disks and slices */ 2171 devs[0].msg = gettext("disks"); 2172 devs[0].list = bad_disks; 2173 devs[1].msg = gettext("slices"); 2174 devs[1].list = bad_slices; 2175 2176 if (bad_disks != NULL) { 2177 oprintf(OUTPUT_VERBOSE, 2178 #if defined(sparc) 2179 gettext("\n These disks are not usable, they may " 2180 "may be offline or cannot be accessed:\n\n")); 2181 #elif defined(i386) 2182 gettext("\n These disks are not usable, they may " 2183 "may be offline,\n missing a Solaris FDISK " 2184 "partition or cannot be accessed:\n\n")); 2185 #endif 2186 print_device_list(bad_disks); 2187 } 2188 2189 if (bad_slices != NULL) { 2190 oprintf(OUTPUT_VERBOSE, gettext( 2191 "\n These slices, and subsequently the disks on which they\n" 2192 "reside, are not usable, they cannot be accessed:\n\n")); 2193 print_device_list(bad_slices); 2194 } 2195 2196 /* report used slices and usages */ 2197 preamble = gettext("\n These slices are not usable, %s:\n\n"); 2198 for (iter = used_classes; iter != NULL; iter = iter->next) { 2199 slice_class_t *class = (slice_class_t *)iter->obj; 2200 2201 if (class->sliceinfo != NULL) { 2202 2203 oprintf(OUTPUT_VERBOSE, preamble, 2204 get_slice_usage_msg(class->usage)); 2205 2206 slices = class->sliceinfo; 2207 for (; slices != NULL; slices = slices->next) { 2208 oprintf(OUTPUT_VERBOSE, " %s\n", (char *)slices->obj); 2209 } 2210 } 2211 } 2212 2213 } 2214 2215 /* 2216 * FUNCTION: char * get_slice_usage_msg(char *usage) 2217 * 2218 * INPUT: usage - char * string representing a slice usage classification 2219 * 2220 * OUTPUT: char * "friendly" usage message 2221 * 2222 * PURPOSE: the input usage string comes from libdiskmgt and is very terse. 2223 * 2224 * Convert it into a friendlier usage description suitable for user 2225 * consumption. 2226 */ 2227 static char * 2228 get_slice_usage_msg( 2229 char *usage) 2230 { 2231 char *str = NULL; 2232 2233 if (string_case_compare(usage, DM_USE_MOUNT) == 0) { 2234 str = gettext("they have mounted filesystems"); 2235 } else if (string_case_compare(usage, DM_USE_FS) == 0) { 2236 str = gettext("they appear to have unmounted filesystems"); 2237 } else if (string_case_compare(usage, DM_USE_SVM) == 0) { 2238 str = gettext("they are utilized by SVM"); 2239 } else if (string_case_compare(usage, DM_USE_VXVM) == 0) { 2240 str = gettext("they are utilized by VxVm"); 2241 } else if (string_case_compare(usage, DM_USE_LU) == 0) { 2242 str = gettext("they are utilized by LiveUpgrade"); 2243 } else if (string_case_compare(usage, DM_USE_DUMP) == 0) { 2244 str = gettext("they are reserved as dump devices"); 2245 } else if (string_case_compare(usage, USE_DISKSET) == 0) { 2246 str = gettext("they have disk set issues"); 2247 } else { 2248 /* libdiskmgt has detected a usage unknown to layout */ 2249 str = usage; 2250 } 2251 2252 return (str); 2253 } 2254 2255 /* 2256 * FUNCTION: set_alias(dm_descriptor_t desc, char *alias) 2257 * 2258 * INPUT: desc - a dm_descriptor_t handle. 2259 * alias - a char * alias for the device represented 2260 * by the descriptor. 2261 * 2262 * RETURNS: int - 0 on success 2263 * !0 otherwise 2264 * 2265 * PURPOSE: Adds the specified alias to the known aliases for the 2266 * device associated with the input descriptor. 2267 */ 2268 int 2269 set_alias( 2270 dm_descriptor_t desc, 2271 char *alias) 2272 { 2273 nvlist_t *attrs = NULL; 2274 char **old_aliases = NULL; 2275 char **new_aliases = NULL; 2276 uint_t nelem = 0; 2277 int error = 0; 2278 int i = 0; 2279 2280 if ((error = get_cached_attributes(desc, &attrs)) != 0) { 2281 return (error); 2282 } 2283 2284 if ((error = get_string_array( 2285 attrs, ATTR_DEVICE_ALIASES, &old_aliases, &nelem)) != 0) { 2286 if (error != ENOENT) { 2287 return (error); 2288 } 2289 /* no aliases yet */ 2290 error = 0; 2291 } 2292 2293 /* add new alias */ 2294 new_aliases = (char **)calloc(MAX_ALIASES, sizeof (char *)); 2295 if (new_aliases != NULL) { 2296 2297 for (i = 0; i < nelem && i < MAX_ALIASES; i++) { 2298 char *dup = strdup(old_aliases[i]); 2299 if (dup != NULL) { 2300 new_aliases[i] = dup; 2301 } else { 2302 error = ENOMEM; 2303 } 2304 } 2305 2306 if (error == 0) { 2307 if (i == MAX_ALIASES) { 2308 volume_set_error( 2309 gettext("Maximum number of device aliases " 2310 "(8) reached\n"), 2311 MAX_ALIASES); 2312 error = -1; 2313 2314 } else { 2315 new_aliases[i] = alias; 2316 error = set_string_array(attrs, ATTR_DEVICE_ALIASES, 2317 new_aliases, i + 1); 2318 } 2319 } 2320 2321 free(new_aliases); 2322 } 2323 2324 if (error == 0) { 2325 /* cache descriptor under this alias */ 2326 error = add_cached_descriptor(alias, desc); 2327 } 2328 2329 return (error); 2330 } 2331 2332 /* 2333 * FUNCTION: get_aliases(dm_descriptor_t desc, dlist_t **list) 2334 * 2335 * INPUT: desc - a dm_descriptor_t handle. 2336 * 2337 * OUTPUT: list - a dlist_t list pointing to the list of 2338 * aliases associated with the device 2339 * represented by the descriptor. 2340 * 2341 * RETURNS: int - 0 on success 2342 * !0 otherwise 2343 * 2344 * PURPOSE: Retrieves aliases for the input descriptor and 2345 * appends them to the input list. 2346 * 2347 * The list of returned items must be freed by calling 2348 * dlist_free_items(list, free) 2349 */ 2350 int 2351 get_aliases( 2352 dm_descriptor_t desc, 2353 dlist_t **list) 2354 { 2355 nvlist_t *attrs = NULL; 2356 char **aliases = NULL; 2357 uint_t nelem = 0; 2358 int error = 0; 2359 int i; 2360 2361 if ((error = get_cached_attributes(desc, &attrs)) != 0) { 2362 return (error); 2363 } 2364 2365 if ((error = get_string_array( 2366 attrs, ATTR_DEVICE_ALIASES, &aliases, &nelem)) != 0) { 2367 if (error == ENOENT) { 2368 /* no aliases */ 2369 return (0); 2370 } 2371 } 2372 2373 for (i = 0; i < nelem; i++) { 2374 dlist_t *item; 2375 char *dup; 2376 2377 if ((dup = strdup(aliases[i])) == NULL) { 2378 error = ENOMEM; 2379 break; 2380 } 2381 2382 if ((item = dlist_new_item(dup)) == NULL) { 2383 free(dup); 2384 error = ENOMEM; 2385 break; 2386 } 2387 2388 *list = dlist_append(item, *list, AT_TAIL); 2389 } 2390 2391 return (error); 2392 } 2393 2394 /* 2395 * FUNCTION: compare_start_blocks( 2396 * void *obj1, void *obj2) 2397 * 2398 * INPUT: desc1 - opaque pointer to a dm_descriptor_t 2399 * desc2 - opaque pointer to a dm_descriptor_t 2400 * 2401 * RETURNS: int - <0 - if desc1.stblk < desc2.stblk 2402 * 0 - if desc1.stblk == desc2.stblk 2403 * >0 - if desc1.stblk > desc.stblk 2404 * 2405 * PURPOSE: dlist_t helper which compares the start blocks of 2406 * the two input dm_descriptor_t slice handles. 2407 */ 2408 static int 2409 compare_start_blocks( 2410 void *desc1, 2411 void *desc2) 2412 { 2413 uint64_t stblk1 = 0; 2414 uint64_t stblk2 = 0; 2415 2416 assert(desc1 != (dm_descriptor_t)0); 2417 assert(desc2 != (dm_descriptor_t)0); 2418 2419 (void) slice_get_start_block((uintptr_t)desc1, &stblk1); 2420 (void) slice_get_start_block((uintptr_t)desc2, &stblk2); 2421 2422 return (stblk1 - stblk2); 2423 } 2424 2425 /* 2426 * FUNCTION: compare_desc_display_names( 2427 * void *desc1, void *desc2) 2428 * 2429 * INPUT: desc1 - opaque pointer to a dm_descriptor_t 2430 * desc2 - opaque pointer to a dm_descriptor_t 2431 * 2432 * RETURNS: int - <0 - if desc1.name < desc2.name 2433 * 0 - if desc1.name == desc2.name 2434 * >0 - if desc1.name > desc.name 2435 * 2436 * PURPOSE: dlist_t helper which compares the CTD names of the 2437 * two input dm_descriptor_t objects. 2438 */ 2439 static int 2440 compare_desc_display_names( 2441 void *desc1, 2442 void *desc2) 2443 { 2444 char *name1 = NULL; 2445 char *name2 = NULL; 2446 2447 assert(desc1 != (dm_descriptor_t)0); 2448 assert(desc2 != (dm_descriptor_t)0); 2449 2450 (void) get_display_name((uintptr_t)desc1, &name1); 2451 (void) get_display_name((uintptr_t)desc2, &name2); 2452 2453 return (string_case_compare(name1, name2)); 2454 } 2455