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 <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, (void *)(uintptr_t)disk, 564 compare_descriptor_names) != B_TRUE) { 565 dlist_t *item = dlist_new_item((void *)(uintptr_t)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 *)(uintptr_t)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 = 698 dlist_new_item((void *)(uintptr_t)slice); 699 if (item == NULL) { 700 error = ENOMEM; 701 } else { 702 *bad = dlist_insert_ordered( 703 item, *bad, 704 ASCENDING, compare_descriptor_names); 705 error = 0; 706 } 707 } 708 continue; 709 } 710 711 if ((error == 0) && (is_did_slice_name(sname) == B_TRUE) && 712 (disk_ctd_alias_derived == B_FALSE)) { 713 /* BEGIN CSTYLED */ 714 /* 715 * If the slice name is a DID name, get the local CTD 716 * name for slice, extract the disk name and add it as 717 * an alias for the disk. 718 * 719 * This is the only way to derive the CTD alias for the 720 * disk when DID is enabled. 721 * 722 * The disk_ctd_alias_derived flag ensure the disk's 723 * CTD alias is only set once. 724 * 725 * The slice's CTD aliases are then derived from the 726 * disk's CTD alias in the normal, non-DID name processing 727 * which happens below. 728 */ 729 /* END CSTYLED */ 730 char *local = NULL; 731 if ((error = nvlist_lookup_string(attrs, DM_LOCALNAME, 732 &local)) != 0) { 733 if (error == ENOENT) { 734 /* no local name -> no DID */ 735 error = 0; 736 } 737 } else { 738 char *localdisk = NULL; 739 char *diskonly = NULL; 740 if ((error = extract_diskname(local, 741 &localdisk)) == 0) { 742 if ((diskonly = strrchr(localdisk, '/')) != NULL) { 743 ++diskonly; 744 } else { 745 diskonly = localdisk; 746 } 747 oprintf(OUTPUT_DEBUG, 748 gettext(" set DID disk CTD alias: %s\n"), 749 diskonly); 750 error = set_alias(disk, diskonly); 751 free(localdisk); 752 disk_ctd_alias_derived = B_TRUE; 753 } 754 } 755 } 756 757 /* derive slice display name from disk's display name */ 758 if (error == 0) { 759 if ((error = make_slicename_for_diskname_and_index( 760 dname, index, &sname)) == 0) { 761 error = set_display_name(slice, sname); 762 } 763 } 764 765 /* set slice aliases using disk aliases */ 766 if (error == 0) { 767 dlist_t *aliases = NULL; 768 if ((error = get_aliases(disk, &aliases)) == 0) { 769 770 dlist_t *iter2 = aliases; 771 for (; (iter2 != NULL) && (error == 0); 772 iter2 = iter2->next) { 773 774 char *dalias = (char *)iter2->obj; 775 char *salias = NULL; 776 777 if ((error = make_slicename_for_diskname_and_index( 778 dalias, index, &salias)) == 0) { 779 error = set_alias(slice, salias); 780 free(salias); 781 } 782 } 783 dlist_free_items(aliases, free); 784 } 785 } 786 787 if (error == 0) { 788 dlist_t *item = dlist_new_item((void *)(uintptr_t)slice); 789 if (item == NULL) { 790 error = ENOMEM; 791 } else { 792 *known = 793 dlist_insert_ordered( 794 item, *known, 795 ASCENDING, compare_desc_display_names); 796 } 797 } 798 } 799 800 dlist_free_items(slices, NULL); 801 } 802 803 return (error); 804 } 805 806 /* 807 * FUNCTION: generate_known_hbas(dlist_t *disks, dlist_t **known) 808 * 809 * INPUT: diskset - a char * diskset name. 810 * 811 * OUTPUT: populates the list of known HBAs. 812 * 813 * RETURNS: int - 0 on success 814 * !0 otherwise 815 * 816 * PURPOSE: Examines known disk list and derives the list of known HBAs. 817 * 818 * Determines the CTD name for an HBA and saves it. 819 */ 820 static int 821 generate_known_hbas( 822 dlist_t *disks, 823 dlist_t **known) 824 { 825 dlist_t *iter; 826 int error = 0; 827 828 /* 829 * for each known disk follow its HBA connections and 830 * assemble the list of known HBAs. 831 */ 832 for (iter = disks; 833 (iter != NULL) && (error == 0); 834 iter = iter->next) { 835 836 dm_descriptor_t disk = (uintptr_t)iter->obj; 837 dlist_t *hbas = NULL; 838 dlist_t *iter2 = NULL; 839 dlist_t *iter3 = NULL; 840 dlist_t *aliases = NULL; 841 char *dname = NULL; 842 843 ((error = get_display_name(disk, &dname)) != 0) || 844 (error = disk_get_aliases(disk, &aliases)) || 845 (error = disk_get_hbas(disk, &hbas)); 846 847 if (error == 0) { 848 849 if ((hbas == NULL) || (dlist_length(hbas) == 0)) { 850 851 oprintf(OUTPUT_DEBUG, 852 gettext("Disk %s has no HBA/Controller?!\n"), 853 dname); 854 error = -1; 855 856 dlist_free_items(hbas, NULL); 857 dlist_free_items(aliases, NULL); 858 859 continue; 860 } 861 862 for (iter2 = hbas, iter3 = aliases; 863 iter2 != NULL && iter3 != NULL; 864 iter2 = iter2->next, iter3 = iter3->next) { 865 866 dm_descriptor_t hba = (uintptr_t)iter2->obj; 867 dm_descriptor_t alias = (uintptr_t)iter3->obj; 868 dlist_t *item = NULL; 869 870 /* scan list of known HBAs and see if known */ 871 if (dlist_contains(*known, (void*)(uintptr_t)hba, 872 compare_descriptor_names) == B_TRUE) { 873 /* known HBA */ 874 continue; 875 } 876 877 /* see if HBA supports MPXIO */ 878 if ((error == 0) && (_mpxio_enabled != B_TRUE)) { 879 hba_is_multiplex(hba, &_mpxio_enabled); 880 } 881 882 /* generate a CTD name for HBA */ 883 error = generate_known_hba_name(hba, alias, disk); 884 if (error == 0) { 885 /* add to known HBA list */ 886 if ((item = dlist_new_item((void *)(uintptr_t)hba)) == 887 NULL) { 888 error = ENOMEM; 889 } else { 890 *known = 891 dlist_insert_ordered(item, *known, 892 ASCENDING, compare_desc_display_names); 893 } 894 } 895 } 896 } 897 898 dlist_free_items(aliases, NULL); 899 dlist_free_items(hbas, NULL); 900 } 901 902 return (error); 903 } 904 905 /* 906 * FUNCTION: generate_known_hba_name(dm_descriptor_t hba, 907 * dm_descriptor_t alias, char *diskname) 908 * 909 * INPUT: hba - a dm_descriptor_t HBA handle. 910 * alias - a dm_descriptor_t disk alias handle. 911 * diskname - a char * disk name 912 * 913 * RETURNS: int - 0 on success 914 * !0 otherwise 915 * 916 * PURPOSE: Sets the CTD name for the input HBA. 917 * 918 * The CTD name for the HBA is generated from the input 919 * disk alias (ex: cXdXtXsX) or from the disk name if 920 * the input alias is a DID name (ex: dX). 921 */ 922 static int 923 generate_known_hba_name( 924 dm_descriptor_t hba, 925 dm_descriptor_t alias, 926 dm_descriptor_t disk) 927 { 928 char *hbaname = NULL; 929 char *aliasname = NULL; 930 int error = 0; 931 932 ((error = get_name(alias, &aliasname)) != 0) || 933 (error = extract_hbaname(aliasname, &hbaname)); 934 if (error != 0) { 935 free(hbaname); 936 return (error); 937 } 938 939 /* see if the input alias is a DID name... */ 940 if (is_did_disk_name(aliasname) == B_TRUE) { 941 942 /* look for a non-DID name in disk's aliases */ 943 dlist_t *aliases = NULL; 944 error = get_aliases(disk, &aliases); 945 946 for (; (error == 0) && (aliases != NULL); 947 aliases = aliases->next) { 948 949 aliasname = (char *)aliases->obj; 950 if (is_did_disk_name(aliasname) != B_TRUE) { 951 /* this is the "local" CTD name generated by */ 952 /* generate_known_disks() above */ 953 error = extract_hbaname(aliasname, &hbaname); 954 if ((error == 0) && (hbaname != NULL)) { 955 set_display_name(hba, hbaname); 956 break; 957 } 958 } 959 } 960 dlist_free_items(aliases, free); 961 962 } else { 963 /* use whatever was derived from the alias name */ 964 set_display_name(hba, hbaname); 965 } 966 967 return (error); 968 } 969 970 /* 971 * FUNCTION: print_known_devices() 972 * 973 * PURPOSE: Print out the known devices. 974 * 975 * Iterates the lists of known slices, disks and HBAs 976 * and prints out their CTD and device names. 977 */ 978 static void 979 print_known_devices( 980 char *diskset) 981 { 982 int i = 0; 983 struct { 984 char *msg; 985 dlist_t *list; 986 } devs[3]; 987 988 devs[0].msg = gettext("HBA/Controllers"); 989 devs[0].list = _known_hbas; 990 devs[1].msg = gettext("disks"); 991 devs[1].list = _known_disks; 992 devs[2].msg = gettext("slices"); 993 devs[2].list = _known_slices; 994 995 for (i = 0; i < 3; i++) { 996 997 oprintf(OUTPUT_VERBOSE, 998 gettext("\n These %s are known:\n\n"), 999 devs[i].msg); 1000 1001 print_device_list(devs[i].list); 1002 } 1003 } 1004 1005 /* 1006 * FUNCTION: get_usable_slices(dlist_t **list) 1007 * 1008 * OUTPUT: list - a dlist_t pointer to hold the returned list of 1009 * devices. 1010 * 1011 * RETURNS: int - 0 on success 1012 * !0 otherwise 1013 * 1014 * PURPOSE: Public accessors the the modules private lists of 1015 * available devices. 1016 * 1017 * The functions are keyed by diskset name in the event 1018 * objects in different disksets are loaded concurrently. 1019 */ 1020 int 1021 get_usable_slices( 1022 dlist_t **list) 1023 { 1024 *list = _usable_slices; 1025 1026 return (0); 1027 } 1028 1029 int 1030 get_usable_disks( 1031 dlist_t **list) 1032 { 1033 *list = _usable_disks; 1034 1035 return (0); 1036 } 1037 1038 int 1039 get_usable_hbas( 1040 dlist_t **list) 1041 { 1042 *list = _usable_hbas; 1043 1044 return (0); 1045 } 1046 1047 /* 1048 * FUNCTION: generate_usable_disks_and_slices_in_local_set(dlist_t **classes, 1049 * dlist_t **bad_disks, dlist_t **usable_disks, 1050 * dlist_t **usable_slices) 1051 * 1052 * OUTPUT: used_classes - a pointer to a list of slice_class_t structs 1053 * updated with known slices that have detected uses 1054 * added to the correct class'e list of slices. 1055 * bad_disks - a pointer to a list of bad/unusable disks updated 1056 * with any bad disks that were detected 1057 * useable_disks - a pointer to a list of usable disks 1058 * useable_slices - a pointer to a list of usable slices 1059 * 1060 * RETURNS: int - 0 on success 1061 * !0 otherwise. 1062 * 1063 * PURPOSE: Scans the disks in the local set to determine which are 1064 * usable during layout processing. 1065 * 1066 * Determines which are usable by layout using usages detected 1067 * by libdiskmgt. 1068 */ 1069 static int 1070 generate_usable_disks_and_slices_in_local_set( 1071 dlist_t **classes, 1072 dlist_t **bad_slices, 1073 dlist_t **usable_disks, 1074 dlist_t **usable_slices) 1075 { 1076 char *dsname = MD_LOCAL_NAME; 1077 dlist_t *disks; 1078 dlist_t *iter; 1079 int error; 1080 1081 /* Get disks in local set */ 1082 error = get_disks_in_diskset(dsname, &disks); 1083 if (error != 0) { 1084 return (error); 1085 } 1086 1087 /* For each disk in this set... */ 1088 for (iter = disks; iter != NULL && error == 0; iter = iter->next) { 1089 dm_descriptor_t disk = (uintptr_t)iter->obj; 1090 dlist_t *slices; 1091 1092 /* Get slices on this disk */ 1093 error = disk_get_slices(disk, &slices); 1094 if (error == 0) { 1095 dlist_t *iter2; 1096 1097 /* 1098 * Assume disk is available until a bad or unavailable 1099 * slice is found 1100 */ 1101 boolean_t avail = B_TRUE; 1102 boolean_t bad_disk = B_FALSE; 1103 1104 /* For each slice on this disk... */ 1105 for (iter2 = slices; 1106 iter2 != NULL && error == 0 && 1107 avail == B_TRUE && bad_disk == B_FALSE; 1108 iter2 = iter2->next) { 1109 1110 dm_descriptor_t slice = (uintptr_t)iter2->obj; 1111 dlist_t *bad_slices_on_this_disk = NULL; 1112 1113 /* Is this slice available? */ 1114 error = check_slice_usage(dsname, slice, 1115 disk, &avail, &bad_slices_on_this_disk, classes); 1116 1117 /* Is the slice bad (inaccessible)? */ 1118 if (error != 0 && bad_slices_on_this_disk != NULL) { 1119 bad_disk = B_TRUE; 1120 *bad_slices = dlist_append_list( 1121 *bad_slices, bad_slices_on_this_disk); 1122 } 1123 } 1124 1125 /* Is the disk available? */ 1126 if (error == 0 && bad_disk == B_FALSE && avail == B_TRUE) { 1127 error = dlist_append_object( 1128 (void *)(uintptr_t)disk, usable_disks, AT_TAIL); 1129 } 1130 1131 dlist_free_items(slices, NULL); 1132 } 1133 } 1134 1135 dlist_free_items(disks, NULL); 1136 1137 if (error == 0) { 1138 /* BEGIN CSTYLED */ 1139 /* 1140 * Now reslice usable disks in the local set to 1141 * simulate the slices they'll have when they're added 1142 * to the named disk set, and add these resulting 1143 * virtual slices to the list of available slices. 1144 */ 1145 /* END CSTYLED */ 1146 error = generate_virtual_slices(*usable_disks, usable_slices); 1147 } 1148 1149 return (error); 1150 } 1151 1152 /* 1153 * FUNCTION: generate_virtual_slices(dlist_t *unused, dlist_t **usable) 1154 * 1155 * INPUT: slice_classes - a list of unused slice dm_descriptor_t handles. 1156 * 1157 * OUTPUT: usable - pointer to the list of usable slices, updated 1158 * with any created virtual slices. 1159 * 1160 * RETURNS: int - 0 on success 1161 * !0 otherwise. 1162 * 1163 * PURPOSE: Helper which creates virtual slices for each disk which 1164 * could be added to a diskset if necessary... 1165 * 1166 * Search the input list of slice classes for the entry 1167 * containing slices known to be available for use by layout. 1168 * 1169 * Iterate the list of unused slices and determine the set 1170 * of unique disks. 1171 * 1172 * For each unique disk, create virtual slice descriptors to 1173 * represent those that will exist if/when the disk is added 1174 * to the diskset. 1175 * 1176 * Add theese virtual slices to the list of usable slices. 1177 */ 1178 static int 1179 generate_virtual_slices( 1180 dlist_t *avail_disks_local_set, 1181 dlist_t **usable) 1182 { 1183 dlist_t *iter = NULL; 1184 int error = 0; 1185 1186 /* generate virtual slices */ 1187 error = create_virtual_slices(avail_disks_local_set); 1188 if (error == 0) { 1189 1190 get_virtual_slices(&iter); 1191 for (; (iter != NULL) && (error == 0); iter = iter->next) { 1192 1193 dlist_t *item = dlist_new_item((void *) iter->obj); 1194 if (item == NULL) { 1195 error = ENOMEM; 1196 } else { 1197 *usable = 1198 dlist_insert_ordered(item, *usable, 1199 ASCENDING, compare_desc_display_names); 1200 } 1201 } 1202 } 1203 1204 return (error); 1205 } 1206 1207 /* 1208 * FUNCTION: generate_usable_disks_and_slices_in_named_set(char *dsname, 1209 * dlist_t **classes, dlist_t **bad_slices, 1210 * dlist_t **usable_slices, dlist_t **usable_disks) 1211 * 1212 * INPUT: dsname - a char * diskset name. 1213 * 1214 * OUTPUT: classes - pointer to a list of slice_class_t structs, 1215 * updated to include slices in the disk set with 1216 * known uses. 1217 * bad_slices - pointer to a list of bad/unusable slices, 1218 * updated to include slices in the disk set that 1219 * are inaccessible or no longer existent. 1220 * usable_slices - pointer to a list of usable slices in the 1221 * disk set. 1222 * usable_disks - pointer to a list of usable disks in the 1223 * disk set. 1224 * 1225 * RETURNS: int - 0 on success 1226 * !0 otherwise. 1227 * 1228 * PURPOSE: 1. determine the disks in the named disk set 1229 * 2. determine the used slices on the disks 1230 * 3. determine the unused slices on the disks 1231 * 4. look for unused space on the disks and collect it 1232 * into an existing unused slice, or create a new 1233 * virtual slice. 1234 */ 1235 static int 1236 generate_usable_disks_and_slices_in_named_set( 1237 char *dsname, 1238 dlist_t **classes, 1239 dlist_t **bad_slices, 1240 dlist_t **usable_disks, 1241 dlist_t **usable_slices) 1242 { 1243 dlist_t *disks = NULL; 1244 dlist_t *iter = NULL; 1245 int error = 0; 1246 1247 error = get_disks_in_diskset(dsname, &disks); 1248 if (error != 0) { 1249 return (error); 1250 } 1251 1252 /* For each disk... */ 1253 for (iter = disks; 1254 iter != NULL && error == 0; 1255 iter = iter->next) { 1256 1257 dm_descriptor_t disk = (uintptr_t)iter->obj; 1258 dlist_t *iter2; 1259 dlist_t *slices = NULL; 1260 dlist_t *bad_slices_on_this_disk = NULL; 1261 dlist_t *used_slices_on_this_disk = NULL; 1262 dlist_t *unused_slices_on_this_disk = NULL; 1263 boolean_t bad_disk = B_FALSE; 1264 1265 error = disk_get_slices(disk, &slices); 1266 if (error != 0) { 1267 break; 1268 } 1269 1270 /* Determine the used, unused, and bad slices on the disk */ 1271 1272 /* For each slice... */ 1273 for (iter2 = slices; 1274 iter2 != NULL && error == 0 && bad_disk == B_FALSE; 1275 iter2 = iter2->next) { 1276 1277 dm_descriptor_t slice = (uintptr_t)iter2->obj; 1278 1279 boolean_t rsvd = B_FALSE; 1280 boolean_t avail = B_FALSE; 1281 1282 /* Get slice usage */ 1283 if (((error = is_reserved_slice(slice, &rsvd)) == 0) && 1284 ((error = check_slice_usage(dsname, slice, disk, &avail, 1285 &bad_slices_on_this_disk, classes)) == 0)) { 1286 1287 /* Is the slice bad (inaccessible)? */ 1288 if (bad_slices_on_this_disk != NULL) { 1289 *bad_slices = dlist_append_list( 1290 *bad_slices, bad_slices_on_this_disk); 1291 /* 1292 * Since one slice on this disk is bad, don't 1293 * use any slices on this disk 1294 */ 1295 bad_disk = B_TRUE; 1296 } else { 1297 1298 dlist_t *item = 1299 dlist_new_item((void *)(uintptr_t)slice); 1300 if (item == NULL) { 1301 error = ENOMEM; 1302 } else { 1303 /* Add slice to used/unused list as appropriate */ 1304 if (avail == B_TRUE && rsvd == B_FALSE) { 1305 unused_slices_on_this_disk = dlist_append( 1306 item, unused_slices_on_this_disk, AT_TAIL); 1307 } else { 1308 used_slices_on_this_disk = 1309 dlist_insert_ordered(item, 1310 used_slices_on_this_disk, 1311 ASCENDING, compare_start_blocks); 1312 } 1313 } 1314 } 1315 } 1316 } 1317 1318 /* Done iterating slices */ 1319 1320 if (error == 0 && bad_disk == B_FALSE) { 1321 /* For each unused slice... */ 1322 for (iter2 = unused_slices_on_this_disk; 1323 iter2 != NULL && error == 0; 1324 iter2 = iter2->next) { 1325 1326 dm_descriptor_t slice = (uintptr_t)iter2->obj; 1327 error = update_slice_attributes(slice, 0, 0, 0); 1328 1329 /* Only do this once */ 1330 if (error == 0 && iter2 == unused_slices_on_this_disk) { 1331 error = add_modified_disk(NULL, disk); 1332 } 1333 } 1334 1335 if (error == 0) { 1336 /* Create usable slices from the used/unused slice lists */ 1337 error = create_usable_slices(disk, used_slices_on_this_disk, 1338 unused_slices_on_this_disk, usable_slices); 1339 if (error == 0) { 1340 error = dlist_append_object((void *)(uintptr_t)disk, 1341 usable_disks, AT_TAIL); 1342 } 1343 } 1344 } 1345 1346 dlist_free_items(slices, NULL); 1347 dlist_free_items(used_slices_on_this_disk, NULL); 1348 dlist_free_items(unused_slices_on_this_disk, NULL); 1349 } 1350 1351 return (error); 1352 } 1353 1354 /* 1355 * FUNCTION: create_usable_slices(dm_descriptor_t disk, dlist_t *used, 1356 * dlist_t *unused, dlist_t **usable); 1357 * 1358 * INPUT: disk - a dm_descriptor_t disk handle 1359 * used - pointer to a list of pvt_t structs 1360 * representing existing used slices 1361 * on the input disk. 1362 * unused - pointer to a list of pvt_t structs 1363 * representing existing unused slices 1364 * on the input disk. 1365 * 1366 * OUTPUT: usable - pointer to a list of pvts representing slices 1367 * which can be used for new volume layouts. 1368 * 1369 * Slices in this list have any available space on the 1370 * disk collected into the fewest, lowest indexed slices 1371 * possible. 1372 * 1373 * RETURNS: int - 0 on success 1374 * !0 otherwise. 1375 * 1376 * PURPOSE: helper for generate_usable_slices_and_disks_in_diskset() which 1377 * turns any detected free space on the input disk into one or 1378 * more slices. 1379 */ 1380 static int 1381 create_usable_slices( 1382 dm_descriptor_t disk, 1383 dlist_t *used, 1384 dlist_t *unused, 1385 dlist_t **usable) 1386 { 1387 dlist_t *iter; 1388 int error = 0; 1389 boolean_t first = B_TRUE; 1390 dlist_t *next_unused = unused; 1391 1392 char *dname = NULL; 1393 uint64_t disk_firstblk = 0; 1394 uint64_t disk_nblks = 0; 1395 uint64_t disk_endblk = 0; 1396 1397 oprintf(OUTPUT_DEBUG, 1398 gettext("\n create_usable_slices for disk\n")); 1399 1400 /* get necessary info about disk: */ 1401 error = get_display_name(disk, &dname); 1402 if (error != 0) { 1403 return (error); 1404 } 1405 1406 /* disk start block is first usable block */ 1407 error = disk_get_start_block(disk, &disk_firstblk); 1408 if (error != 0) { 1409 return (error); 1410 } 1411 1412 /* disk size determines last usable disk block */ 1413 error = disk_get_size_in_blocks(disk, &disk_nblks); 1414 if (error != 0) { 1415 return (error); 1416 } 1417 1418 disk_endblk = disk_firstblk + disk_nblks - 1; 1419 1420 /* search for gaps before, between and after used slices */ 1421 for (iter = used; iter != NULL && error == 0; iter = iter->next) { 1422 1423 dm_descriptor_t cur = (uintptr_t)iter->obj; 1424 1425 uint64_t cur_stblk = 0; 1426 uint64_t cur_nblks = 0; 1427 uint64_t cur_endblk = 0; 1428 uint32_t cur_index = 0; 1429 1430 uint64_t new_stblk = 0; 1431 uint64_t new_endblk = 0; 1432 1433 char *sname = NULL; 1434 (void) get_display_name(cur, &sname); 1435 1436 if (((error = slice_get_index(cur, &cur_index)) != 0) || 1437 ((error = slice_get_start_block(cur, &cur_stblk)) != 0) || 1438 ((error = slice_get_size_in_blocks(cur, &cur_nblks)) != 0)) { 1439 continue; 1440 } 1441 1442 cur_endblk = cur_stblk + cur_nblks - 1; 1443 1444 oprintf(OUTPUT_DEBUG, 1445 gettext(" used slice %d (%10llu to %10llu)\n"), 1446 cur_index, cur_stblk, cur_endblk); 1447 1448 if (first == B_TRUE) { 1449 /* first slice: make sure it starts at disk_firstblk */ 1450 first = B_FALSE; 1451 if (cur_stblk != disk_firstblk) { 1452 /* close gap at beginning of disk */ 1453 new_stblk = disk_firstblk; 1454 new_endblk = cur_stblk - 1; 1455 1456 oprintf(OUTPUT_DEBUG, 1457 gettext(" unused space before first " 1458 "used slice\n")); 1459 } 1460 } 1461 1462 if (iter->next != NULL) { 1463 /* check for gap between slices */ 1464 dm_descriptor_t next = (uintptr_t)iter->next->obj; 1465 uint64_t next_stblk = 0; 1466 uint32_t next_index = 0; 1467 1468 if (((error = slice_get_start_block(next, &next_stblk)) == 0) && 1469 ((error = slice_get_index(next, &next_index)) == 0)) { 1470 if (cur_endblk != next_stblk - 1) { 1471 /* close gap between slices */ 1472 new_stblk = cur_endblk + 1; 1473 new_endblk = next_stblk - 1; 1474 1475 oprintf(OUTPUT_DEBUG, 1476 gettext(" unused space between slices " 1477 "%d and %d\n"), cur_index, next_index); 1478 } 1479 } 1480 1481 } else { 1482 /* last slice: make sure it includes last block on disk */ 1483 if (cur_endblk != disk_endblk) { 1484 /* close gap at end of disk */ 1485 new_stblk = cur_endblk + 1; 1486 new_endblk = disk_endblk; 1487 1488 oprintf(OUTPUT_DEBUG, 1489 gettext(" unused space after last slice " 1490 "cur_endblk: %llu disk_endblk: %llu\n"), 1491 cur_endblk, disk_endblk); 1492 } 1493 } 1494 1495 if ((error == 0) && (new_endblk != 0)) { 1496 error = add_new_usable(disk, new_stblk, 1497 new_endblk - new_stblk + 1, &next_unused, usable); 1498 } 1499 } 1500 1501 if (error != 0) { 1502 dlist_free_items(*usable, free); 1503 *usable = NULL; 1504 } 1505 1506 return (error); 1507 } 1508 1509 /* 1510 * FUNCTION: add_new_usable(dm_descriptor_t disk, uint64_t stblk, 1511 * uint64_t nblks, dlist_t **next_unused, 1512 * dlist_t **usable); 1513 * 1514 * INPUT: disk - a dm_descriptor_t disk handle 1515 * stblk - start block of the usable space 1516 * nblks - number of usable blocks 1517 * next_unused - pointer to the next unused slice 1518 * 1519 * OUTPUT: next_unused - updated pointer to the next unused slice 1520 * usable - possibly updated pointer to a list of slices on 1521 * the disk with usable space 1522 * 1523 * RETURNS: int - 0 on success 1524 * !0 otherwise. 1525 * 1526 * PURPOSE: helper for create_usable_slices() which turns free space 1527 * on the input disk into a usable slice. 1528 * 1529 * If possible an existing unused slice will be recycled 1530 * into a usable slice. If there are none, a new virtual 1531 * slice will be created. 1532 */ 1533 static int 1534 add_new_usable( 1535 dm_descriptor_t disk, 1536 uint64_t stblk, 1537 uint64_t nblks, 1538 dlist_t **next_unused, 1539 dlist_t **usable) 1540 { 1541 dm_descriptor_t new_usable = 0; 1542 int error = 0; 1543 1544 /* try to use an existing unused slice for the usable slice */ 1545 if (*next_unused != NULL) { 1546 new_usable = (uintptr_t)((*next_unused)->obj); 1547 *next_unused = (*next_unused)->next; 1548 1549 oprintf(OUTPUT_DEBUG, 1550 gettext("\trecyling used slice into usable slice " 1551 "start: %llu, end: %llu\n"), 1552 stblk, stblk + nblks + 1); 1553 } 1554 1555 if (new_usable == NULL) { 1556 /* no unused slices, try to make a new virtual slice */ 1557 uint32_t index = UINT32_MAX; 1558 error = disk_get_available_slice_index(disk, &index); 1559 if ((error == 0) && (index != UINT32_MAX)) { 1560 1561 char *dname = NULL; 1562 error = get_display_name(disk, &dname); 1563 if (error == 0) { 1564 1565 char buf[MAXNAMELEN]; 1566 (void) snprintf(buf, MAXNAMELEN-1, "%ss%d", dname, index); 1567 error = add_virtual_slice(buf, index, 0, 0, disk); 1568 if (error == 0) { 1569 /* retrieve the virtual slice */ 1570 error = slice_get_by_name(buf, &new_usable); 1571 } 1572 } 1573 } 1574 } 1575 1576 if ((error == 0) && (new_usable != (dm_descriptor_t)0)) { 1577 /* BEGIN CSTYLED */ 1578 /* 1579 * have an unused slice, update its attributes to reflect 1580 * the usable space it represents 1581 */ 1582 /* END CSTYLED */ 1583 uint64_t disk_blksz = 0; 1584 error = disk_get_blocksize(disk, &disk_blksz); 1585 if (error == 0) { 1586 error = update_slice_attributes(new_usable, stblk, 1587 nblks, nblks * disk_blksz); 1588 if (error == 0) { 1589 error = dlist_append_object( 1590 (void *)(uintptr_t)new_usable, usable, AT_TAIL); 1591 } 1592 } 1593 } 1594 1595 return (error); 1596 } 1597 1598 /* 1599 * FUNCTION: update_slice_attributes(dm_descriptor_t slice, uint64_t stblk, 1600 * uint64_t nblks, uint64_t nbytes) 1601 * 1602 * INPUT: slice - a dm_descriptor_t slice handle 1603 * stblk - start block of the usable space 1604 * nblks - size of slice in blocks 1605 * nbytes - size of slice in bytes 1606 * 1607 * SIDEEFFECT: adds a modification record for the slice. 1608 * 1609 * RETURNS: int - 0 on success 1610 * !0 otherwise. 1611 * 1612 * PURPOSE: utility which updates several slice attributes in one call. 1613 */ 1614 static int 1615 update_slice_attributes( 1616 dm_descriptor_t slice, 1617 uint64_t stblk, 1618 uint64_t nblks, 1619 uint64_t nbytes) 1620 { 1621 char *sname = NULL; 1622 uint32_t index = 0; 1623 int error = 0; 1624 1625 if ((error = get_display_name(slice, &sname)) == 0) { 1626 if ((error = slice_get_index(slice, &index)) == 0) { 1627 if ((error = slice_set_start_block(slice, stblk)) == 0) { 1628 if ((error = slice_set_size_in_blocks(slice, nblks)) == 0) { 1629 if (nblks == 0) { 1630 error = add_slice_to_remove(sname, index); 1631 } else { 1632 error = assemble_modified_slice((dm_descriptor_t)0, 1633 sname, index, stblk, nblks, nbytes, NULL); 1634 } 1635 } 1636 } 1637 } 1638 } 1639 1640 return (error); 1641 } 1642 1643 /* 1644 * FUNCTION: generate_usable_hbas(dlist_t *slices, 1645 * dlist_t **usable) 1646 * 1647 * INPUT: disks - a list of usable disks. 1648 * 1649 * OUTPUT: usable - a populated list of usable HBAs. 1650 * 1651 * RETURNS: int - 0 on success 1652 * !0 otherwise 1653 * 1654 * PURPOSE: Examines usable disk list and derives the list of usable HBAs. 1655 * 1656 */ 1657 static int 1658 generate_usable_hbas( 1659 dlist_t *disks, 1660 dlist_t **usable) 1661 { 1662 dlist_t *iter; 1663 int error = 0; 1664 1665 /* 1666 * for each usable disk, follow its HBA connections and 1667 * add them to the list of usable HBAs. 1668 */ 1669 for (iter = disks; (iter != NULL) && (error == 0); iter = iter->next) { 1670 1671 dm_descriptor_t dp = NULL; 1672 dlist_t *hbas = NULL; 1673 dlist_t *iter2 = NULL; 1674 1675 dp = (uintptr_t)iter->obj; 1676 1677 error = disk_get_hbas(dp, &hbas); 1678 if (error == 0) { 1679 1680 for (iter2 = hbas; 1681 (iter2 != NULL) && (error == 0); 1682 iter2 = iter2->next) { 1683 1684 dm_descriptor_t hba = (uintptr_t)iter2->obj; 1685 dlist_t *item = NULL; 1686 1687 /* scan list of usable HBAs and see if known */ 1688 if (dlist_contains(*usable, (void*)(uintptr_t)hba, 1689 compare_descriptor_names) == B_TRUE) { 1690 /* known HBA, continue to next HBA/alias */ 1691 continue; 1692 } 1693 1694 /* add this HBA to the usable list */ 1695 if ((item = dlist_new_item((void *)(uintptr_t)hba)) == 1696 NULL) { 1697 error = ENOMEM; 1698 } else { 1699 *usable = 1700 dlist_insert_ordered(item, *usable, 1701 ASCENDING, compare_desc_display_names); 1702 } 1703 } 1704 } 1705 1706 dlist_free_items(hbas, NULL); 1707 } 1708 1709 return (error); 1710 } 1711 1712 /* 1713 * FUNCTION: check_slice_usage(char *dsname, dm_descriptor_t slice, 1714 * dm_descriptor_t disk, boolean_t *avail, 1715 * dlist_t **bad, dlist_t **classes) 1716 * 1717 * INPUT: dsname - a char * diskset name. 1718 * slice - a dm_descriptor_t handle for a known slices. 1719 * disk - a dm_descriptor_t handle the slice's disk. 1720 * 1721 * OUTPUT: avail - a boolean_t to hold the slice's availability. 1722 * bad - pointer to a list of bad/unusable slices, 1723 * possibly updated if the input slice 1724 * was determined to be inaccessible. 1725 * classes - pointer to a list of slice_class_t structs, 1726 * possibly updated to include the input slice 1727 * if it has a known use. 1728 * 1729 * RETURNS: int - 0 on success 1730 * !0 otherwise. 1731 * 1732 * PURPOSE: Handles the details of 1733 * determining usage and/or availability of a single slice. 1734 * 1735 * Queries the device library for the input slice's detectable 1736 * usage status. 1737 * 1738 * If the slice has a detected usage, its name is added to 1739 * the appropriate slice_class_t list in the input list of 1740 * slice classes, this is only done if verbose output was 1741 * requested. 1742 */ 1743 static int 1744 check_slice_usage( 1745 char *dsname, 1746 dm_descriptor_t slice, 1747 dm_descriptor_t disk, 1748 boolean_t *avail, 1749 dlist_t **bad, 1750 dlist_t **classes) 1751 { 1752 boolean_t online = B_FALSE; 1753 boolean_t used = B_FALSE; 1754 nvlist_t *stats = NULL; 1755 char *name = NULL; 1756 char *used_by = NULL; 1757 char *use_detail = NULL; 1758 int error = 0; 1759 1760 *avail = B_FALSE; 1761 1762 if (((error = get_display_name(slice, &name)) != 0) || 1763 (error = disk_get_is_online(disk, &online))) { 1764 return (error); 1765 } 1766 1767 /* 1768 * if the disk is known to be offline, skip getting status 1769 * for the slice since it will just fail and return ENODEV. 1770 */ 1771 if (online != B_TRUE) { 1772 error = ENODEV; 1773 } else { 1774 stats = dm_get_stats(slice, DM_SLICE_STAT_USE, &error); 1775 } 1776 1777 if (error != 0) { 1778 if (error == ENODEV) { 1779 dlist_t *item = dlist_new_item((void *)(uintptr_t)slice); 1780 oprintf(OUTPUT_TERSE, 1781 gettext("Warning: unable to get slice information " 1782 "for %s, it will not be used.\n"), name); 1783 1784 if (item == NULL) { 1785 error = ENOMEM; 1786 } else { 1787 error = 0; 1788 *bad = dlist_insert_ordered(item, *bad, ASCENDING, 1789 compare_desc_display_names); 1790 } 1791 } else { 1792 oprintf(OUTPUT_TERSE, 1793 gettext("check_slice_usage: dm_get_stats for " 1794 "%s failed %d\n"), 1795 name, error); 1796 } 1797 1798 return (error); 1799 } 1800 1801 /* 1802 * check if/how the slice is currently being used, 1803 * device library provides this info in the nvpair_t list: 1804 * 1805 * stat_type is DM_SLICE_STAT_USE 1806 * used_by: string (mount, svm, lu, vxvm, fs) 1807 * used_name: string 1808 * 1809 */ 1810 if (stats != NULL) { 1811 error = get_string(stats, DM_USED_BY, &used_by); 1812 if (error != 0) { 1813 if (error == ENOENT) { 1814 used_by = NULL; 1815 error = 0; 1816 } else { 1817 oprintf(OUTPUT_TERSE, 1818 gettext("check_slice_usage: dm_get_stats.%s for " 1819 "%s failed %d\n"), 1820 DM_USED_BY, name, error); 1821 } 1822 } 1823 1824 if (error == 0) { 1825 error = get_string(stats, DM_USED_NAME, &use_detail); 1826 if (error != 0) { 1827 if (error == ENOENT) { 1828 use_detail = NULL; 1829 error = 0; 1830 } else { 1831 oprintf(OUTPUT_TERSE, 1832 gettext("check_slice_usage: " 1833 "dm_get_stats.%s for " 1834 "%s failed %d\n"), 1835 DM_USED_NAME, name, error); 1836 } 1837 } 1838 } 1839 } 1840 1841 if ((error == 0) && (used_by != NULL) && (used_by[0] != '\0')) { 1842 1843 /* was detected usage SVM? */ 1844 if (string_case_compare(used_by, DM_USE_SVM) == 0) { 1845 1846 /* check use_detail, it is in the form diskset:name */ 1847 if (strncmp("diskset:", use_detail, 8) == 0) { 1848 1849 /* check disk set name */ 1850 char *str = strrchr(use_detail, ':'); 1851 if ((str != NULL) && 1852 (string_case_compare(str+1, dsname) == 0)) { 1853 1854 /* slice in the right diskset */ 1855 error = check_svm_slice_usage( 1856 dsname, slice, disk, &used, classes); 1857 1858 } else { 1859 1860 /* slice in other diskset */ 1861 save_slice_classification( 1862 dsname, slice, disk, used_by, use_detail, 1863 classes); 1864 used = B_TRUE; 1865 } 1866 1867 } else { 1868 1869 /* slice is volume component */ 1870 save_slice_classification( 1871 dsname, slice, disk, used_by, use_detail, 1872 classes); 1873 used = B_TRUE; 1874 } 1875 1876 } else { 1877 1878 /* save usage */ 1879 save_slice_classification( 1880 dsname, slice, disk, used_by, use_detail, 1881 classes); 1882 used = B_TRUE; 1883 } 1884 } 1885 1886 nvlist_free(stats); 1887 1888 if (error == 0) { 1889 if (used == B_TRUE) { 1890 *avail = B_FALSE; 1891 } else { 1892 *avail = B_TRUE; 1893 } 1894 } 1895 1896 return (error); 1897 } 1898 1899 /* 1900 * FUNCTION: check_svm_slice_usage(char *dsname, dm_descriptor_t slice, 1901 * dm_descriptor_t disk, boolean_t *used, 1902 * dlist_t **classes) 1903 * 1904 * INPUT: dsname - a char * diskset name. 1905 * slice - a dm_descriptor_t handle for a known slices. 1906 * disk - a dm_descriptor_t handle the slice's disk. 1907 * 1908 * OUTPUT: used - a boolean_t to hold the slice usage status. 1909 * classes - pointer to a list of slice_class_t possibly updated 1910 * with the input slice's SVM specific usage 1911 * classification. 1912 * 1913 * RETURNS: int - 0 on success 1914 * !0 otherwise. 1915 * 1916 * PURPOSE: Handles the finer details of 1917 * a single slice is being used in the context of SVM. 1918 * 1919 * Currently, one thing is checked: 1920 * 1921 * 1. determine if the slice is reserved for metadb replicas. 1922 * The convention for disks in disksets is that a single slice 1923 * (index 6 or 7) is set aside for metadb replicas. 1924 * 1925 * If this condition does not hold, the slice is considered 1926 * available for use by layout and 'used' is set to B_FALSE. 1927 */ 1928 static int 1929 check_svm_slice_usage( 1930 char *dsname, 1931 dm_descriptor_t slice, 1932 dm_descriptor_t disk, 1933 boolean_t *used, 1934 dlist_t **classes) 1935 { 1936 boolean_t is_replica = B_FALSE; 1937 uint32_t index = 0; 1938 char *diskname = NULL; 1939 int error = 0; 1940 1941 ((error = slice_get_index(slice, &index)) != 0) || 1942 (error = get_display_name(disk, &diskname)) || 1943 (error = is_reserved_replica_slice_index( 1944 dsname, diskname, index, &is_replica)); 1945 1946 if (error == 0) { 1947 if (is_replica == B_TRUE) { 1948 /* is replica slice -> used */ 1949 save_slice_classification(dsname, slice, disk, DM_USE_SVM, 1950 gettext("reserved for metadb replicas"), classes); 1951 *used = B_TRUE; 1952 } else { 1953 *used = B_FALSE; 1954 } 1955 } 1956 1957 return (error); 1958 } 1959 1960 /* 1961 * FUNCTION: save_slice_classification(char *dsname, dm_descriptor_t slice, 1962 * dm_descriptor_t disk, char *used_by, char *usage_detail, 1963 * dlist_t **classes) 1964 * 1965 * INPUT: dsname - a char * disk set name 1966 * slice - a dm_descriptor_t slice handle. 1967 * disk - a dm_descriptor_t handle for the slice's disk. 1968 * used_by - a char * usage classification. 1969 * usage_detail - a char * usage description for the slice. 1970 * 1971 * OUTPUT: classes - a list of slice_class_t updated to hold a usage 1972 * entry for the input slicexs. 1973 * 1974 * SIDEEFFECT: adds the input slice to the list of known, used slices. 1975 * 1976 * RETURNS: int - 0 on success 1977 * !0 otherwise. 1978 * 1979 * PURPOSE: Adds an entry to the 1980 * appropriate slice_class_t list of slices. If there is 1981 * not an appropriate slice_class_t entry in the input list 1982 * of classes, one is added. 1983 * 1984 * As a performance optimization the slice usage classification 1985 * information is only saved if verbose output was requested by 1986 * the user. 1987 */ 1988 static int 1989 save_slice_classification( 1990 char *dsname, 1991 dm_descriptor_t slice, 1992 dm_descriptor_t disk, 1993 char *usage, 1994 char *usage_detail, 1995 dlist_t **classes) 1996 { 1997 int error = 0; 1998 1999 error = add_used_slice(slice); 2000 2001 if ((error == 0) && (get_max_verbosity() >= OUTPUT_VERBOSE)) { 2002 2003 dlist_t *iter; 2004 dlist_t *item; 2005 slice_class_t *class = NULL; 2006 2007 /* locate class struct matching 'usage' */ 2008 for (iter = *classes; iter != NULL; iter = iter->next) { 2009 class = (slice_class_t *)iter->obj; 2010 if (string_case_compare(usage, class->usage) == 0) { 2011 break; 2012 } 2013 } 2014 2015 if (iter == NULL) { 2016 /* add a new class to the list of classes */ 2017 class = (slice_class_t *)calloc(1, sizeof (slice_class_t)); 2018 if (class == NULL) { 2019 error = ENOMEM; 2020 } else { 2021 class->usage = strdup(usage); 2022 if (class->usage == NULL) { 2023 free(class); 2024 class = NULL; 2025 error = ENOMEM; 2026 } else { 2027 item = dlist_new_item((void *)class); 2028 if (item == NULL) { 2029 free(class->usage); 2030 free(class); 2031 class = NULL; 2032 error = ENOMEM; 2033 } else { 2034 *classes = dlist_append(item, *classes, AT_TAIL); 2035 } 2036 } 2037 } 2038 } 2039 2040 if ((error == 0) && (class != NULL)) { 2041 2042 char buf[BUFSIZ]; 2043 char *dup = NULL; 2044 char *slicename = NULL; 2045 2046 (void) get_display_name(slice, &slicename); 2047 (void) snprintf(buf, BUFSIZ-1, " %s: %s", 2048 slicename, usage_detail); 2049 if ((dup = strdup(buf)) == NULL) { 2050 error = ENOMEM; 2051 } else { 2052 if ((item = dlist_new_item((void *)dup)) == NULL) { 2053 free(dup); 2054 error = ENOMEM; 2055 } else { 2056 class->sliceinfo = 2057 dlist_insert_ordered( 2058 item, class->sliceinfo, 2059 ASCENDING, compare_strings); 2060 } 2061 } 2062 } 2063 } 2064 2065 return (error); 2066 } 2067 2068 /* 2069 * FUNCTION: print_usable_devices() 2070 * 2071 * PURPOSE: Print out the devices determined to be available for 2072 * use by layout. 2073 * 2074 * Iterates the lists of usable slices, disks and HBAs 2075 * and prints out their CTD and device names. 2076 */ 2077 static void 2078 print_usable_devices() 2079 { 2080 int i = 0; 2081 2082 struct { 2083 char *msg; 2084 dlist_t *list; 2085 } devs[3]; 2086 2087 devs[0].msg = gettext("HBA/Controllers"); 2088 devs[0].list = _usable_hbas; 2089 devs[1].msg = gettext("disks"); 2090 devs[1].list = _usable_disks; 2091 devs[2].msg = gettext("slices"); 2092 devs[2].list = _usable_slices; 2093 2094 for (i = 0; i < 3; i++) { 2095 2096 oprintf(OUTPUT_VERBOSE, 2097 gettext("\n These %s are usable:\n\n"), 2098 devs[i].msg); 2099 2100 print_device_list(devs[i].list); 2101 } 2102 } 2103 2104 /* 2105 * FUNCTION: print_device_list(dlist_t *devices) 2106 * 2107 * INPUT: devices - a list of device descriptor handles 2108 * 2109 * PURPOSE: A helper for the print_XXX_devices() routines which iterates 2110 * the input list and prints out each device name, CTD name and 2111 * alias(es). 2112 */ 2113 static void 2114 print_device_list( 2115 dlist_t *devices) 2116 { 2117 dlist_t *iter = NULL; 2118 2119 for (iter = devices; iter != NULL; iter = iter->next) { 2120 2121 dm_descriptor_t device = ((uintptr_t)iter->obj); 2122 char *name = NULL; 2123 char *ctd = NULL; 2124 dlist_t *aliases = NULL; 2125 2126 (void) get_display_name(device, &ctd); 2127 (void) get_name(device, &name); 2128 oprintf(OUTPUT_VERBOSE, 2129 " %-25s %s\n", (ctd != NULL ? ctd : ""), name); 2130 2131 (void) get_aliases(device, &aliases); 2132 for (; aliases != NULL; aliases = aliases->next) { 2133 oprintf(OUTPUT_VERBOSE, 2134 gettext(" (alias: %s)\n"), 2135 (char *)aliases->obj); 2136 } 2137 2138 dlist_free_items(aliases, free); 2139 } 2140 } 2141 2142 /* 2143 * FUNCTION: print_unusable_devices( 2144 * dlist_t *bad_slices, dlist_t *bad_disks, 2145 * dlist_t *used_classes) 2146 * 2147 * INPUT: used_classes - a list of slice_class_t structs 2148 * 2149 * PURPOSE: Print out the devices determined to be unavailable for 2150 * use by layout. 2151 * 2152 * Iterates the input list of slice classifications and prints 2153 * out a description of the class and the slices so classified. 2154 * 2155 * Also iterates the lists of bad slices and disks (those that 2156 * libdiskmgt returned descriptors for but cannot be accessed) 2157 * and notes them as unusable. 2158 */ 2159 static void 2160 print_unusable_devices( 2161 dlist_t *bad_slices, 2162 dlist_t *bad_disks, 2163 dlist_t *used_classes) 2164 { 2165 dlist_t *iter = NULL; 2166 dlist_t *slices = NULL; 2167 char *preamble; 2168 2169 struct { 2170 char *msg; 2171 dlist_t *list; 2172 } devs[2]; 2173 2174 /* report bad disks and slices */ 2175 devs[0].msg = gettext("disks"); 2176 devs[0].list = bad_disks; 2177 devs[1].msg = gettext("slices"); 2178 devs[1].list = bad_slices; 2179 2180 if (bad_disks != NULL) { 2181 oprintf(OUTPUT_VERBOSE, 2182 #if defined(sparc) 2183 gettext("\n These disks are not usable, they may " 2184 "may be offline or cannot be accessed:\n\n")); 2185 #elif defined(i386) 2186 gettext("\n These disks are not usable, they may " 2187 "may be offline,\n missing a Solaris FDISK " 2188 "partition or cannot be accessed:\n\n")); 2189 #endif 2190 print_device_list(bad_disks); 2191 } 2192 2193 if (bad_slices != NULL) { 2194 oprintf(OUTPUT_VERBOSE, gettext( 2195 "\n These slices, and subsequently the disks on which they\n" 2196 "reside, are not usable, they cannot be accessed:\n\n")); 2197 print_device_list(bad_slices); 2198 } 2199 2200 /* report used slices and usages */ 2201 preamble = gettext("\n These slices are not usable, %s:\n\n"); 2202 for (iter = used_classes; iter != NULL; iter = iter->next) { 2203 slice_class_t *class = (slice_class_t *)iter->obj; 2204 2205 if (class->sliceinfo != NULL) { 2206 2207 oprintf(OUTPUT_VERBOSE, preamble, 2208 get_slice_usage_msg(class->usage)); 2209 2210 slices = class->sliceinfo; 2211 for (; slices != NULL; slices = slices->next) { 2212 oprintf(OUTPUT_VERBOSE, " %s\n", (char *)slices->obj); 2213 } 2214 } 2215 } 2216 2217 } 2218 2219 /* 2220 * FUNCTION: char * get_slice_usage_msg(char *usage) 2221 * 2222 * INPUT: usage - char * string representing a slice usage classification 2223 * 2224 * OUTPUT: char * "friendly" usage message 2225 * 2226 * PURPOSE: the input usage string comes from libdiskmgt and is very terse. 2227 * 2228 * Convert it into a friendlier usage description suitable for user 2229 * consumption. 2230 */ 2231 static char * 2232 get_slice_usage_msg( 2233 char *usage) 2234 { 2235 char *str = NULL; 2236 2237 if (string_case_compare(usage, DM_USE_MOUNT) == 0) { 2238 str = gettext("they have mounted filesystems"); 2239 } else if (string_case_compare(usage, DM_USE_FS) == 0) { 2240 str = gettext("they appear to have unmounted filesystems"); 2241 } else if (string_case_compare(usage, DM_USE_SVM) == 0) { 2242 str = gettext("they are utilized by SVM"); 2243 } else if (string_case_compare(usage, DM_USE_VXVM) == 0) { 2244 str = gettext("they are utilized by VxVm"); 2245 } else if (string_case_compare(usage, DM_USE_LU) == 0) { 2246 str = gettext("they are utilized by LiveUpgrade"); 2247 } else if (string_case_compare(usage, DM_USE_DUMP) == 0) { 2248 str = gettext("they are reserved as dump devices"); 2249 } else if (string_case_compare(usage, USE_DISKSET) == 0) { 2250 str = gettext("they have disk set issues"); 2251 } else { 2252 /* libdiskmgt has detected a usage unknown to layout */ 2253 str = usage; 2254 } 2255 2256 return (str); 2257 } 2258 2259 /* 2260 * FUNCTION: set_alias(dm_descriptor_t desc, char *alias) 2261 * 2262 * INPUT: desc - a dm_descriptor_t handle. 2263 * alias - a char * alias for the device represented 2264 * by the descriptor. 2265 * 2266 * RETURNS: int - 0 on success 2267 * !0 otherwise 2268 * 2269 * PURPOSE: Adds the specified alias to the known aliases for the 2270 * device associated with the input descriptor. 2271 */ 2272 int 2273 set_alias( 2274 dm_descriptor_t desc, 2275 char *alias) 2276 { 2277 nvlist_t *attrs = NULL; 2278 char **old_aliases = NULL; 2279 char **new_aliases = NULL; 2280 uint_t nelem = 0; 2281 int error = 0; 2282 int i = 0; 2283 2284 if ((error = get_cached_attributes(desc, &attrs)) != 0) { 2285 return (error); 2286 } 2287 2288 if ((error = get_string_array( 2289 attrs, ATTR_DEVICE_ALIASES, &old_aliases, &nelem)) != 0) { 2290 if (error != ENOENT) { 2291 return (error); 2292 } 2293 /* no aliases yet */ 2294 error = 0; 2295 } 2296 2297 /* add new alias */ 2298 new_aliases = (char **)calloc(MAX_ALIASES, sizeof (char *)); 2299 if (new_aliases != NULL) { 2300 2301 for (i = 0; i < nelem && i < MAX_ALIASES; i++) { 2302 char *dup = strdup(old_aliases[i]); 2303 if (dup != NULL) { 2304 new_aliases[i] = dup; 2305 } else { 2306 error = ENOMEM; 2307 } 2308 } 2309 2310 if (error == 0) { 2311 if (i == MAX_ALIASES) { 2312 volume_set_error( 2313 gettext("Maximum number of device aliases " 2314 "(8) reached\n"), 2315 MAX_ALIASES); 2316 error = -1; 2317 2318 } else { 2319 new_aliases[i] = alias; 2320 error = set_string_array(attrs, ATTR_DEVICE_ALIASES, 2321 new_aliases, i + 1); 2322 } 2323 } 2324 2325 free(new_aliases); 2326 } 2327 2328 if (error == 0) { 2329 /* cache descriptor under this alias */ 2330 error = add_cached_descriptor(alias, desc); 2331 } 2332 2333 return (error); 2334 } 2335 2336 /* 2337 * FUNCTION: get_aliases(dm_descriptor_t desc, dlist_t **list) 2338 * 2339 * INPUT: desc - a dm_descriptor_t handle. 2340 * 2341 * OUTPUT: list - a dlist_t list pointing to the list of 2342 * aliases associated with the device 2343 * represented by the descriptor. 2344 * 2345 * RETURNS: int - 0 on success 2346 * !0 otherwise 2347 * 2348 * PURPOSE: Retrieves aliases for the input descriptor and 2349 * appends them to the input list. 2350 * 2351 * The list of returned items must be freed by calling 2352 * dlist_free_items(list, free) 2353 */ 2354 int 2355 get_aliases( 2356 dm_descriptor_t desc, 2357 dlist_t **list) 2358 { 2359 nvlist_t *attrs = NULL; 2360 char **aliases = NULL; 2361 uint_t nelem = 0; 2362 int error = 0; 2363 int i; 2364 2365 if ((error = get_cached_attributes(desc, &attrs)) != 0) { 2366 return (error); 2367 } 2368 2369 if ((error = get_string_array( 2370 attrs, ATTR_DEVICE_ALIASES, &aliases, &nelem)) != 0) { 2371 if (error == ENOENT) { 2372 /* no aliases */ 2373 return (0); 2374 } 2375 } 2376 2377 for (i = 0; i < nelem; i++) { 2378 dlist_t *item; 2379 char *dup; 2380 2381 if ((dup = strdup(aliases[i])) == NULL) { 2382 error = ENOMEM; 2383 break; 2384 } 2385 2386 if ((item = dlist_new_item(dup)) == NULL) { 2387 free(dup); 2388 error = ENOMEM; 2389 break; 2390 } 2391 2392 *list = dlist_append(item, *list, AT_TAIL); 2393 } 2394 2395 return (error); 2396 } 2397 2398 /* 2399 * FUNCTION: compare_start_blocks( 2400 * void *obj1, void *obj2) 2401 * 2402 * INPUT: desc1 - opaque pointer to a dm_descriptor_t 2403 * desc2 - opaque pointer to a dm_descriptor_t 2404 * 2405 * RETURNS: int - <0 - if desc1.stblk < desc2.stblk 2406 * 0 - if desc1.stblk == desc2.stblk 2407 * >0 - if desc1.stblk > desc.stblk 2408 * 2409 * PURPOSE: dlist_t helper which compares the start blocks of 2410 * the two input dm_descriptor_t slice handles. 2411 */ 2412 static int 2413 compare_start_blocks( 2414 void *desc1, 2415 void *desc2) 2416 { 2417 uint64_t stblk1 = 0; 2418 uint64_t stblk2 = 0; 2419 2420 assert(desc1 != (dm_descriptor_t)0); 2421 assert(desc2 != (dm_descriptor_t)0); 2422 2423 (void) slice_get_start_block((uintptr_t)desc1, &stblk1); 2424 (void) slice_get_start_block((uintptr_t)desc2, &stblk2); 2425 2426 return (stblk1 - stblk2); 2427 } 2428 2429 /* 2430 * FUNCTION: compare_desc_display_names( 2431 * void *desc1, void *desc2) 2432 * 2433 * INPUT: desc1 - opaque pointer to a dm_descriptor_t 2434 * desc2 - opaque pointer to a dm_descriptor_t 2435 * 2436 * RETURNS: int - <0 - if desc1.name < desc2.name 2437 * 0 - if desc1.name == desc2.name 2438 * >0 - if desc1.name > desc.name 2439 * 2440 * PURPOSE: dlist_t helper which compares the CTD names of the 2441 * two input dm_descriptor_t objects. 2442 */ 2443 static int 2444 compare_desc_display_names( 2445 void *desc1, 2446 void *desc2) 2447 { 2448 char *name1 = NULL; 2449 char *name2 = NULL; 2450 2451 assert(desc1 != (dm_descriptor_t)0); 2452 assert(desc2 != (dm_descriptor_t)0); 2453 2454 (void) get_display_name((uintptr_t)desc1, &name1); 2455 (void) get_display_name((uintptr_t)desc2, &name2); 2456 2457 return (string_case_compare(name1, name2)); 2458 } 2459