1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <assert.h> 30 #include <string.h> 31 #include <libintl.h> 32 33 #include "volume_error.h" 34 #include "volume_defaults.h" 35 #include "volume_dlist.h" 36 #include "volume_output.h" 37 #include "volume_request.h" 38 39 #include "layout.h" 40 #include "layout_request.h" 41 42 #include "layout_concat.h" 43 #include "layout_discovery.h" 44 #include "layout_device_cache.h" 45 #include "layout_device_util.h" 46 #include "layout_dlist_util.h" 47 #include "layout_hsp.h" 48 #include "layout_mirror.h" 49 #include "layout_slice.h" 50 #include "layout_stripe.h" 51 #include "layout_svm_util.h" 52 #include "layout_validate.h" 53 54 #define _LAYOUT_C 55 56 static int layout_init(devconfig_t *request, defaults_t *defaults); 57 static int layout_diskset(request_t *request, dlist_t *results); 58 59 static int process_request(devconfig_t *request, dlist_t **results); 60 static int process_qos_request(devconfig_t *request, dlist_t **results); 61 static int process_hsp_request(devconfig_t *request, dlist_t **results); 62 63 /* 64 * stuff for making/updating the HSP to service devices 65 * created by the toplevel request 66 */ 67 static devconfig_t *_hsp_request = NULL; 68 static dlist_t *_hsp_devices = NULL; 69 static void set_hsp_request(devconfig_t *request); 70 static void unset_hsp_request(); 71 72 /* 73 * struct to track which disks have been explicitly modified 74 * during the layout process... 75 * 76 * disk is the dm_descriptor_t of the modified disk 77 * accessname is the name to access the disk thru 78 * slices is the list of modified slices on the disk 79 */ 80 typedef struct { 81 dm_descriptor_t disk; 82 char *accessname; 83 dlist_t *slices; 84 } moddisk_t; 85 86 /* 87 * modified_disks is a list of moddisk_t structs 88 * tracking disks have been modified during layout. 89 */ 90 static dlist_t *_modified_disks = NULL; 91 92 static int collect_modified_disks(devconfig_t *request, dlist_t *results); 93 static int add_modified_disks_to_diskset( 94 dlist_t *devices, 95 devconfig_t *diskset); 96 static int release_modified_disks(); 97 static int get_removed_slices_for_disks( 98 dlist_t *mod_disks); 99 static int get_modified_slices_for_disks( 100 dlist_t *moddisks); 101 static int compare_disk_to_moddisk_disk( 102 void *disk, 103 void *moddisk); 104 105 static int convert_device_names(devconfig_t *request, dlist_t *devs); 106 107 /* 108 * FUNCTION: get_layout(devconfig_t *request, defaults_t *defaults) 109 * 110 * INPUT: request - a devconfig_t pointer to the toplevel request 111 * defaults - a results_t pointer to the defaults 112 * 113 * RETURNS: int - 0 - on success 114 * !0 - otherwise 115 * 116 * PURPOSE: Public entry point to layout module. 117 */ 118 int 119 get_layout( 120 request_t *request, 121 defaults_t *defaults) 122 { 123 devconfig_t *diskset_req = NULL; 124 dlist_t *iter = NULL; 125 dlist_t *results = NULL; 126 int error = 0; 127 128 if ((diskset_req = request_get_diskset_req(request)) != NULL) { 129 130 /* initialize using the the top-level disk set request... */ 131 if ((error = layout_init(diskset_req, defaults)) != 0) { 132 return (error); 133 } 134 135 oprintf(OUTPUT_TERSE, 136 gettext("\nProcessing volume request...\n")); 137 138 iter = devconfig_get_components(diskset_req); 139 for (; (iter != NULL) && (error == 0); iter = iter->next) { 140 141 /* process each volume request, stop on any error */ 142 devconfig_t *subreq = (devconfig_t *)iter->obj; 143 dlist_t *subres = NULL; 144 145 ((error = process_request(subreq, &subres)) != 0) || 146 (error = collect_modified_disks(subreq, subres)) || 147 (error = convert_device_names(subreq, subres)); 148 if (error == 0) { 149 results = dlist_append(subres, results, AT_TAIL); 150 } 151 } 152 153 if (error == 0) { 154 /* process HSP request */ 155 dlist_t *subres = NULL; 156 error = process_hsp_request(diskset_req, &subres); 157 if (error == 0) { 158 results = dlist_append(subres, results, AT_TAIL); 159 } 160 } 161 162 if (error == 0) { 163 oprintf(OUTPUT_TERSE, 164 gettext("\nAssembling volume specification...\n")); 165 /* determine required diskset modifications */ 166 error = layout_diskset(request, results); 167 } 168 169 layout_clean_up(); 170 171 if (error == 0) { 172 oprintf(OUTPUT_TERSE, 173 gettext("\nVolume request completed successfully.\n")); 174 } 175 176 } else { 177 volume_set_error( 178 gettext("Malformed request, missing top level " 179 "disk set request.")); 180 } 181 182 return (error); 183 } 184 185 /* 186 * FUNCTION: layout_clean_up() 187 * 188 * PURPOSE: function which handles the details of cleaning up cached 189 * data and any other memory allocated during the layout 190 * process. 191 * 192 * release physical device data structs 193 * release SVM logical device data structs 194 * release validation data structs 195 * release modified device data structs 196 * release request processing data structs 197 * 198 * This function is also exported as part of the public 199 * interface to the layout module, clients of layout 200 * are required to call this function if get_layout() 201 * was called and was not allowed to return. For example, 202 * if SIGINT was received while a layout request was in 203 * process. 204 */ 205 void 206 layout_clean_up() 207 { 208 (void) release_request_caches(); 209 (void) release_validation_caches(); 210 211 (void) release_slices_to_remove(); 212 (void) release_modified_slices(); 213 (void) release_modified_disks(); 214 215 (void) release_reserved_slices(); 216 (void) release_used_slices(); 217 218 (void) release_usable_devices(); 219 (void) release_svm_names(get_request_diskset()); 220 (void) release_known_devices(); 221 222 (void) unset_hsp_request(NULL); 223 (void) unset_request_defaults(NULL); 224 (void) unset_request_diskset(NULL); 225 (void) unset_toplevel_request(NULL); 226 } 227 228 /* 229 * FUNCTION: layout_init(devconfig_t *diskset, defaults_t *defaults) 230 * 231 * INPUT: diskset - a devconfig_t pointer to the toplevel request 232 * defaults - a results_t pointer to the defaults 233 * 234 * RETURNS: int - 0 - on success 235 * !0 - otherwise 236 * 237 * PURPOSE: function which handles the details of initializing the layout 238 * module prior to processing a request. 239 * 240 * Determines the requested disk set and validates it. 241 * 242 * Scans the physical device configuration. 243 * Scans the SVM logical device configuration. 244 * 245 * Initializes layout private global data structures and does 246 * semantic validation of the request. 247 */ 248 static int 249 layout_init( 250 devconfig_t *diskset, 251 defaults_t *defaults) 252 { 253 dlist_t *iter = NULL; 254 int error = 0; 255 char *dsname = NULL; 256 257 ((error = validate_basic_svm_config()) != 0) || 258 259 /* determine & validate requested disk set name */ 260 (error = devconfig_get_name(diskset, &dsname)) || 261 (error = set_request_diskset(dsname)) || 262 263 /* discover known physical and logical devices */ 264 (error = discover_known_devices()) || 265 (error = scan_svm_names(dsname)) || 266 267 /* validate and remember toplevel request */ 268 (error = set_toplevel_request(diskset)) || 269 270 /* validate and remember defaults for this request */ 271 (error = set_request_defaults(defaults)); 272 273 if (error != 0) { 274 return (error); 275 } 276 277 oprintf(OUTPUT_TERSE, 278 gettext("\nValidating volume request...\n")); 279 280 iter = devconfig_get_components(diskset); 281 for (; (iter != NULL) && (error == 0); iter = iter->next) { 282 devconfig_t *subreq = (devconfig_t *)iter->obj; 283 error = validate_request(subreq); 284 } 285 286 if (error == 0) { 287 error = discover_usable_devices(dsname); 288 } 289 290 if (error == 0) { 291 /* final validation on explicitly requested components */ 292 error = validate_reserved_slices(); 293 } 294 295 if (error == 0) { 296 /* final validation on request sizes vs. actual avail space */ 297 error = validate_request_sizes(diskset); 298 } 299 300 return (error); 301 } 302 303 /* 304 * FUNCTION: process_request(devconfig_t *req, dlist_t **results) 305 * 306 * INPUT: req - a devconfig_t pointer to the current request 307 * results - pointer to a list of resulting volumes 308 * 309 * RETURNS: int - 0 - on success 310 * !0 - otherwise 311 * 312 * PURPOSE: function which handles the details of an explicit 313 * volume request. 314 * 315 * Determines the requested volume type, whether the 316 * request contains specific subcomponents and dispatches 317 * to the appropriate layout function for that type. 318 * 319 * Resulting volumes are appended to the results list. 320 * 321 * Note that an HSP request is held until all the volumes 322 * in the request have been successfully composed. This 323 * ensures that HSP spare sizing can be appropriate to 324 * those volumes. 325 */ 326 static int 327 process_request( 328 devconfig_t *req, 329 dlist_t **results) 330 { 331 component_type_t type = TYPE_UNKNOWN; 332 uint64_t nbytes = 0; /* requested volume size */ 333 dlist_t *comps = NULL; 334 int ncomps = 0; 335 int error = 0; 336 337 (void) devconfig_get_type(req, &type); 338 (void) devconfig_get_size(req, &nbytes); 339 comps = devconfig_get_components(req); 340 341 if (type == TYPE_HSP) { 342 /* HSP processing needs to happen after all other volumes. */ 343 /* set the HSP request aside until all other requests have */ 344 /* been completed successfully */ 345 set_hsp_request(req); 346 return (0); 347 } 348 349 oprintf(OUTPUT_TERSE, "\n"); 350 oprintf(OUTPUT_VERBOSE, "******************\n"); 351 352 ncomps = dlist_length(comps); 353 354 if (type == TYPE_STRIPE) { 355 if (ncomps > 0) { 356 return (populate_explicit_stripe(req, results)); 357 } else { 358 return (layout_stripe(req, nbytes, results)); 359 } 360 } 361 362 if (type == TYPE_CONCAT) { 363 if (ncomps > 0) { 364 return (populate_explicit_concat(req, results)); 365 } else { 366 return (layout_concat(req, nbytes, results)); 367 } 368 } 369 370 if (type == TYPE_MIRROR) { 371 if (ncomps > 0) { 372 return (populate_explicit_mirror(req, results)); 373 } else { 374 uint16_t nsubs = 0; 375 if ((error = get_mirror_nsubs(req, &nsubs)) != 0) { 376 return (error); 377 } else { 378 return (layout_mirror(req, nsubs, nbytes, results)); 379 } 380 } 381 } 382 383 if (type == TYPE_VOLUME) { 384 error = process_qos_request(req, results); 385 } 386 387 return (error); 388 } 389 390 /* 391 * FUNCTION: process_qos_request(devconfig_t *req, dlist_t **results) 392 * 393 * INPUT: req - a devconfig_t pointer to the current request 394 * results - pointer to a list of resulting volumes 395 * 396 * RETURNS: int - 0 - on success 397 * !0 - otherwise 398 * 399 * PURPOSE: function which handles the details of mapping an implicit 400 * volume request of QoS attributes into a volume type. 401 * 402 * Resulting volumes are appended to the results list. 403 */ 404 static int 405 process_qos_request( 406 devconfig_t *req, 407 dlist_t **results) 408 { 409 int error = 0; 410 411 uint64_t nbytes = 0; 412 uint16_t rlevel = 0; 413 414 /* get QoS attributes */ 415 (void) devconfig_get_size(req, &nbytes); 416 417 if ((error = get_volume_redundancy_level(req, &rlevel)) != 0) { 418 if (error == ERR_ATTR_UNSET) { 419 error = 0; 420 rlevel = 0; 421 } 422 } 423 424 if (error == 0) { 425 if (rlevel == 0) { 426 error = layout_stripe(req, nbytes, results); 427 } else { 428 error = layout_mirror(req, rlevel, nbytes, results); 429 } 430 } 431 432 return (error); 433 } 434 435 /* 436 * FUNCTION: layout_diskset(request_t *req, dlist_t **results) 437 * 438 * INPUT: req - a request_t pointer to the toplevel request 439 * results - pointer to the list of composed result volumes 440 * 441 * RETURNS: int - 0 - on success 442 * !0 - otherwise 443 * 444 * PURPOSE: function which handles the details of completing an layout 445 * request. 446 * 447 * Determines if the disk set specified in the request currently 448 * exists and sets it up for creation if it doesn't. 449 * 450 * Adds new disks required by the result volumes to the disk set. 451 * 452 * Attaches the result volumes to the disk set result. 453 * 454 * Convert slice and disk names to preferred names. 455 * 456 * Attaches the disk set result to the toplevel request. 457 */ 458 static int 459 layout_diskset( 460 request_t *request, 461 dlist_t *results) 462 { 463 int error = 0; 464 devconfig_t *diskset = NULL; 465 dlist_t *comps = NULL; 466 467 ((error = new_devconfig(&diskset, TYPE_DISKSET)) != 0) || 468 (error = devconfig_set_name(diskset, get_request_diskset())) || 469 (error = add_modified_disks_to_diskset(results, diskset)); 470 if (error != 0) { 471 free_devconfig(diskset); 472 return (error); 473 } 474 475 /* add resulting volumes */ 476 if (results != NULL) { 477 comps = devconfig_get_components(diskset); 478 comps = dlist_append(results, comps, AT_TAIL); 479 devconfig_set_components(diskset, comps); 480 } 481 482 request_set_diskset_config(request, diskset); 483 484 return (error); 485 } 486 487 /* 488 * FUNCTION: convert_device_names(devconfig_t request, dlist_t *devices) 489 * 490 * INPUT: request - a devconfig_t request pointer 491 * devices - a list of devconfig_t devices 492 * 493 * RETURNS: int - 0 - on success 494 * !0 - on any error 495 * 496 * PURPOSE: Utility function to convert any slice or disk drive 497 * names in a result devconfig_t to the preferred name 498 * which should be used to access the device. 499 * 500 * This convert the temporary names used by layout to 501 * the proper DID or /dev/dsk alias. 502 */ 503 static int 504 convert_device_names( 505 devconfig_t *request, 506 dlist_t *devices) 507 { 508 int error = 0; 509 dlist_t *iter; 510 511 for (iter = devices; 512 (iter != NULL) && (error == 0); 513 iter = iter->next) { 514 515 devconfig_t *dev = (devconfig_t *)iter->obj; 516 component_type_t type = TYPE_UNKNOWN; 517 dm_descriptor_t disk = (dm_descriptor_t)0; 518 char *devname = NULL; 519 char *diskname = NULL; 520 char *slicename = NULL; 521 uint16_t index; 522 523 if ((error = devconfig_get_type(dev, &type)) == 0) { 524 switch (type) { 525 526 case TYPE_MIRROR: 527 case TYPE_STRIPE: 528 case TYPE_CONCAT: 529 case TYPE_HSP: 530 531 error = convert_device_names(request, 532 devconfig_get_components(dev)); 533 534 break; 535 536 case TYPE_SLICE: 537 538 ((error = devconfig_get_name(dev, &devname)) != 0) || 539 (error = devconfig_get_slice_index(dev, &index)) || 540 (error = get_disk_for_named_slice(devname, &disk)) || 541 (error = get_device_access_name(request, disk, 542 &diskname)) || 543 (error = make_slicename_for_diskname_and_index( 544 diskname, index, &slicename)); 545 546 if ((error == 0) && (slicename != NULL)) { 547 error = devconfig_set_name(dev, slicename); 548 free(slicename); 549 } 550 551 break; 552 } 553 } 554 } 555 556 return (error); 557 } 558 559 /* 560 * FUNCTION: add_modified_disk(devconfig_t request, dm_descriptor_t disk); 561 * 562 * INPUT: request - a pointr to a devconfig_t request 563 * disk - dm_descriptor_t handle for a disk that has been modified 564 * 565 * SIDEEFFECTS: adds an entry to the _modified_disks list which tracks 566 * the disks that have been explicitly modified by 567 * the layout code. 568 * 569 * RETURNS: int - 0 on success 570 * !0 otherwise 571 * 572 * PURPOSE: Adds the input disk to the list of those that have been 573 * modified. 574 * 575 * Disks are modified during layout for two reasons: 576 * 577 * 1. any disk that is to be added to the disk set gets 578 * an explicitly updated label. 579 * 580 * 2. once a disk is in the disk set, existing slices 581 * may be resized or new slices can be added. 582 */ 583 int 584 add_modified_disk( 585 devconfig_t *request, 586 dm_descriptor_t disk) 587 { 588 dlist_t *iter = NULL; 589 moddisk_t *moddisk = NULL; 590 dlist_t *item = NULL; 591 int error = 0; 592 593 for (iter = _modified_disks; iter != NULL; iter = iter->next) { 594 moddisk = (moddisk_t *)iter->obj; 595 if (compare_descriptor_names( 596 (void *)(uintptr_t)moddisk->disk, 597 (void *)(uintptr_t)disk) == 0) { 598 /* already in list */ 599 return (0); 600 } 601 } 602 603 moddisk = (moddisk_t *)calloc(1, sizeof (moddisk_t)); 604 if (moddisk == NULL) { 605 error = ENOMEM; 606 } else { 607 char *aname = NULL; 608 error = get_device_access_name(request, disk, &aname); 609 if (error == 0) { 610 611 /* add to list of modified disks */ 612 moddisk->disk = disk; 613 moddisk->accessname = aname; 614 moddisk->slices = NULL; 615 616 if ((item = dlist_new_item((void *)moddisk)) == NULL) { 617 free(moddisk); 618 error = ENOMEM; 619 } else { 620 _modified_disks = 621 dlist_append(item, _modified_disks, AT_HEAD); 622 } 623 } 624 } 625 626 return (error); 627 } 628 629 /* 630 * FUNCTION: collect_modified_disks(devconfig_t *request, dlist_t* devs) 631 * 632 * INPUT: devs - pointer to a list of composed volumes 633 * OUTPUT: none - 634 * SIDEEFFECT: updates the module global list _modified_disks 635 * 636 * RETURNS: int - 0 - success 637 * !0 - failure 638 * 639 * PURPOSE: Helper to maintain the list of disks to be added to the 640 * disk set. 641 * 642 * Iterates the input list of devices and determines which 643 * disks they use. If a disk is not in the _modified_disks 644 * list, it is added. 645 */ 646 static int 647 collect_modified_disks( 648 devconfig_t *request, 649 dlist_t *devs) 650 { 651 int error = 0; 652 653 char *sname = NULL; 654 dm_descriptor_t disk = (dm_descriptor_t)0; 655 656 for (; (devs != NULL) && (error == 0); devs = devs->next) { 657 658 devconfig_t *dev = (devconfig_t *)devs->obj; 659 component_type_t type = TYPE_UNKNOWN; 660 661 if ((error = devconfig_get_type(dev, &type)) == 0) { 662 663 switch (type) { 664 case TYPE_MIRROR: 665 case TYPE_STRIPE: 666 case TYPE_CONCAT: 667 case TYPE_HSP: 668 669 error = collect_modified_disks(request, 670 devconfig_get_components(dev)); 671 break; 672 673 case TYPE_SLICE: 674 675 ((error = devconfig_get_name(dev, &sname)) != 0) || 676 (error = get_disk_for_named_slice(sname, &disk)) || 677 (error = add_modified_disk(request, disk)); 678 679 break; 680 } 681 } 682 } 683 684 return (error); 685 } 686 687 /* 688 * FUNCTION: add_modified_disks_to_diskset(dlist_t *devices, 689 * devconfig_t *diskset) 690 * 691 * INPUT: devices - pointer to a list of devices 692 * 693 * OUTPUT: diskset - pointer to a devconfig_t representing the disk set, 694 * updated to include modified disks and slices as 695 * components. 696 * 697 * RETURNS: int - 0 - success 698 * !0 - failure 699 * 700 * PURPOSE: Helper to add devconfig_t structs for disks and slices 701 * to the disk set. 702 * 703 * Updates the list of _modified_disks by examining the input 704 * list of composed devices. 705 * 706 * Iterates _modified_disks and creates a devconfig_t component 707 * for each disk in the list, the list of disks is then attached 708 * to the input disk set. 709 * 710 * Modified slices for disks in the disk set are added as well. 711 */ 712 static int 713 add_modified_disks_to_diskset( 714 dlist_t *results, 715 devconfig_t *diskset) 716 { 717 int error = 0; 718 719 dlist_t *iter; 720 dlist_t *list = NULL; 721 char *dsname = get_request_diskset(); 722 723 /* add modified disks to disk set's component list */ 724 list = devconfig_get_components(diskset); 725 726 oprintf(OUTPUT_TERSE, 727 gettext(" Collecting modified disks...\n")); 728 729 /* collect removed slices for modified disks */ 730 error = get_removed_slices_for_disks(_modified_disks); 731 732 /* collect modified slices for modified disks */ 733 error = get_modified_slices_for_disks(_modified_disks); 734 735 for (iter = _modified_disks; 736 (iter != NULL) && (error == 0); 737 iter = iter->next) { 738 739 moddisk_t *moddisk = (moddisk_t *)iter->obj; 740 dm_descriptor_t disk = moddisk->disk; 741 devconfig_t *newdisk = NULL; 742 boolean_t in_set = B_FALSE; 743 744 oprintf(OUTPUT_VERBOSE, " %s\n", moddisk->accessname); 745 746 error = is_disk_in_diskset(disk, dsname, &in_set); 747 if ((error == 0) && (in_set != B_TRUE)) { 748 /* New disk, add it to the disk set */ 749 ((error = new_devconfig(&newdisk, TYPE_DRIVE)) != 0) || 750 (error = devconfig_set_name(newdisk, moddisk->accessname)); 751 if (error == 0) { 752 dlist_t *item = dlist_new_item(newdisk); 753 if (item == NULL) { 754 error = ENOMEM; 755 } else { 756 list = dlist_append(item, list, AT_TAIL); 757 oprintf(OUTPUT_DEBUG, 758 gettext(" must add %s to disk set \"%s\"\n"), 759 moddisk->accessname, dsname); 760 } 761 } else { 762 free_devconfig(newdisk); 763 } 764 } 765 766 if ((error == 0) && (moddisk->slices != NULL)) { 767 /* move moddisk's slice list to disk set comp list */ 768 list = dlist_append(moddisk->slices, list, AT_TAIL); 769 moddisk->slices = NULL; 770 } 771 } 772 773 if (error == 0) { 774 devconfig_set_components(diskset, list); 775 } else { 776 dlist_free_items(list, NULL); 777 } 778 779 return (error); 780 } 781 782 /* 783 * FUNCTIONS: void release_modified_disks() 784 * 785 * INPUT: none - 786 * OUTPUT: none - 787 * 788 * PURPOSE: cleanup the module global list of disks that need 789 * to be added to the disk set to satisfy the request. 790 */ 791 static int 792 release_modified_disks() 793 { 794 dlist_t *iter = _modified_disks; 795 796 for (; iter != NULL; iter = iter->next) { 797 moddisk_t *moddisk = (moddisk_t *)iter->obj; 798 if (moddisk->slices != NULL) { 799 dlist_free_items(moddisk->slices, free_devconfig); 800 moddisk->slices = NULL; 801 } 802 free(moddisk); 803 iter->obj = NULL; 804 } 805 806 dlist_free_items(_modified_disks, NULL); 807 _modified_disks = NULL; 808 809 return (0); 810 } 811 812 /* 813 * FUNCTION: get_removed_slices_for_disks(dlist_t *mod_disks) 814 * 815 * INPUT: mod_disks - a list of moddisk_t structs 816 * 817 * OUTPUT: mod_disks - the list of moddisk_t structs updated with 818 * the slices to be removed for each disk 819 * 820 * RETURNS: int - 0 - success 821 * !0 - failure 822 * 823 * PURPOSE: Helper to create a list of devconfig_t structs 824 * for slices on the input disks which need to be 825 * removed from the system. 826 * 827 * Iterates the list of slices to be removed and 828 * creates a devconfig_t component for each slice 829 * in the list that is on any of the input modified 830 * disks. 831 * 832 * Slice names are constructed using the modified disk's 833 * access name to ensure that the correct alias is 834 * used to get to the slice. 835 */ 836 static int 837 get_removed_slices_for_disks( 838 dlist_t *mod_disks) 839 { 840 int error = 0; 841 dlist_t *iter = NULL; 842 843 /* collect slices to be removed for the modified disks */ 844 for (iter = get_slices_to_remove(); 845 (iter != NULL) && (error == 0); 846 iter = iter->next) { 847 848 rmvdslice_t *rmvd = (rmvdslice_t *)iter->obj; 849 dm_descriptor_t disk = (dm_descriptor_t)0; 850 moddisk_t *moddisk = NULL; 851 char *sname = NULL; 852 devconfig_t *newslice = NULL; 853 dlist_t *item = NULL; 854 855 (void) get_disk_for_named_slice(rmvd->slice_name, &disk); 856 857 if ((item = dlist_find(mod_disks, (void *)(uintptr_t)disk, 858 compare_disk_to_moddisk_disk)) == NULL) { 859 /* slice on disk that we don't care about */ 860 continue; 861 } 862 863 moddisk = (moddisk_t *)item->obj; 864 865 /* create output slice struct for the removed slice */ 866 ((error = make_slicename_for_diskname_and_index( 867 moddisk->accessname, rmvd->slice_index, &sname)) != 0) || 868 (error = new_devconfig(&newslice, TYPE_SLICE)) || 869 (error = devconfig_set_name(newslice, sname)) || 870 (error = devconfig_set_size_in_blocks(newslice, 0)); 871 872 /* add to the moddisk's list of slices */ 873 if (error == 0) { 874 if ((item = dlist_new_item(newslice)) == NULL) { 875 free_devconfig(newslice); 876 error = ENOMEM; 877 } else { 878 moddisk->slices = 879 dlist_append(item, moddisk->slices, AT_TAIL); 880 } 881 } else { 882 free_devconfig(newslice); 883 } 884 } 885 886 return (error); 887 } 888 889 /* 890 * FUNCTION: get_modified_slices_for_disks(dlist_t *mod_disks) 891 * 892 * INPUT: mod_disks - a list of moddisk_t structs 893 * 894 * OUTPUT: mod_disks - the list of moddisk_t structs updated with 895 * the modified slices for each disk 896 * 897 * RETURNS: int - 0 - success 898 * !0 - failure 899 * 900 * PURPOSE: Helper to create a list of devconfig_t structs 901 * for slices on the input disks which have been 902 * modified for use by layout. 903 * 904 * Iterates the list of modified slices and creates a 905 * devconfig_t component for each slice in the list 906 * that is on any of the input modified disks. 907 * 908 * Slice names are constructed using the modified disk's 909 * access name to ensure that the correct alias is 910 * used to get to the slice. 911 */ 912 int 913 get_modified_slices_for_disks( 914 dlist_t *mod_disks) 915 { 916 int error = 0; 917 dlist_t *iter = NULL; 918 919 for (iter = get_modified_slices(); 920 (iter != NULL) && (error == 0); 921 iter = iter->next) { 922 923 modslice_t *mods = (modslice_t *)iter->obj; 924 devconfig_t *slice = mods->slice_devcfg; 925 devconfig_t *newslice = NULL; 926 dm_descriptor_t disk; 927 moddisk_t *moddisk; 928 dlist_t *item; 929 char *sname = NULL; 930 uint64_t stblk = 0; 931 uint64_t nblks = 0; 932 uint16_t index; 933 934 /* only add modified slices that were sources */ 935 if ((mods->times_modified == 0) || 936 (mods->src_slice_desc != (dm_descriptor_t)0)) { 937 continue; 938 } 939 940 (void) devconfig_get_name(slice, &sname); 941 (void) get_disk_for_named_slice(sname, &disk); 942 943 if ((item = dlist_find(mod_disks, (void *)(uintptr_t)disk, 944 compare_disk_to_moddisk_disk)) == NULL) { 945 /* slice on disk that we don't care about */ 946 continue; 947 } 948 949 moddisk = (moddisk_t *)item->obj; 950 951 /* create output slice struct for the modified slice */ 952 ((error = devconfig_get_slice_start_block(slice, 953 &stblk)) != 0) || 954 (error = devconfig_get_size_in_blocks(slice, &nblks)) || 955 (error = devconfig_get_slice_index(slice, &index)) || 956 (error = make_slicename_for_diskname_and_index( 957 moddisk->accessname, index, &sname)) || 958 (error = new_devconfig(&newslice, TYPE_SLICE)) || 959 (error = devconfig_set_name(newslice, sname)) || 960 (error = devconfig_set_slice_start_block(newslice, stblk)) || 961 (error = devconfig_set_size_in_blocks(newslice, nblks)); 962 963 /* add to the moddisk's list of slices */ 964 if (error == 0) { 965 if ((item = dlist_new_item(newslice)) == NULL) { 966 free_devconfig(newslice); 967 error = ENOMEM; 968 } else { 969 moddisk->slices = 970 dlist_append(item, moddisk->slices, AT_TAIL); 971 } 972 } else { 973 free_devconfig(newslice); 974 } 975 } 976 977 return (error); 978 } 979 980 /* 981 * FUNCTION: compare_disk_to_moddisk_disk(void *disk, void *moddisk) 982 * 983 * INPUT: disk - opaque pointer to a dm_descriptor_t 984 * moddisk - opaque moddisk_t pointer 985 * 986 * RETURNS: int - 0 - if disk == moddisk->disk 987 * !0 - otherwise 988 * 989 * PURPOSE: dlist_t helper which compares the input disk dm_descriptor_t 990 * handle to the disk dm_descriptor_t handle in the input 991 * moddisk_t struct. 992 * 993 * Comparison is done via compare_descriptor_names. 994 */ 995 static int 996 compare_disk_to_moddisk_disk( 997 void *disk, 998 void *moddisk) 999 { 1000 assert(disk != (dm_descriptor_t)0); 1001 assert(moddisk != NULL); 1002 1003 return (compare_descriptor_names((void *)disk, 1004 (void *)(uintptr_t)((moddisk_t *)moddisk)->disk)); 1005 } 1006 1007 /* 1008 * FUNCTIONS: void set_hsp_request() 1009 * 1010 * INPUT: none - 1011 * OUTPUT: none - 1012 * 1013 * PURPOSE: set the module global HSP request struct. 1014 */ 1015 static void 1016 set_hsp_request( 1017 devconfig_t *req) 1018 { 1019 _hsp_request = req; 1020 } 1021 1022 /* 1023 * FUNCTIONS: void unset_hsp_request() 1024 * 1025 * INPUT: none - 1026 * OUTPUT: none - 1027 * 1028 * PURPOSE: unset the module global HSP request struct. 1029 */ 1030 static void 1031 unset_hsp_request() 1032 { 1033 _hsp_request = NULL; 1034 } 1035 1036 /* 1037 * FUNCTION: process_hsp_request(devconfig_t *req, dlist_t **results) 1038 * INPUT: req - pointer to the toplevel disk set devconfig_t request 1039 * results - pointer to a list of composed results 1040 * 1041 * RETURNS: int - 0 - success 1042 * !0 - failure 1043 * 1044 * PURPOSE: Helper which determines HSP processing for the 1045 * composed volumes which need HSP spares. 1046 */ 1047 static int 1048 process_hsp_request( 1049 devconfig_t *req, 1050 dlist_t **results) 1051 { 1052 int error = 0; 1053 1054 if (_hsp_request != NULL) { 1055 oprintf(OUTPUT_TERSE, 1056 gettext("\nProcessing HSP...\n")); 1057 } 1058 1059 if (_hsp_devices == NULL) { 1060 /* no devices -> no HSP */ 1061 oprintf(OUTPUT_VERBOSE, 1062 gettext(" No devices require hot spares...\n")); 1063 } else { 1064 1065 oprintf(OUTPUT_TERSE, "\n"); 1066 1067 ((error = layout_hsp(req, _hsp_request, _hsp_devices, 1068 results)) != 0) || 1069 (error = collect_modified_disks(_hsp_request, *results)) || 1070 (error = convert_device_names(_hsp_request, *results)); 1071 } 1072 1073 return (error); 1074 } 1075 1076 /* 1077 * FUNCTION: add_to_hsp_list(dlist_t* list) 1078 * INPUT: devs - pointer to a list of composed volumes 1079 * OUTPUT: none - 1080 * SIDEEFFECT: updates the module global list _hsp_devices 1081 * 1082 * RETURNS: int - 0 - success 1083 * !0 - failure 1084 * 1085 * PURPOSE: Helper to update the list of devices which need HSP spares. 1086 * 1087 * Iterates the input list of devices and adds them them to the 1088 * module provate list of devices needing spares. 1089 */ 1090 int 1091 add_to_hsp_list( 1092 dlist_t *list) 1093 { 1094 dlist_t *iter = NULL; 1095 int error = 0; 1096 1097 for (iter = list; iter != NULL; iter = iter->next) { 1098 dlist_t *item = NULL; 1099 1100 if ((item = dlist_new_item(iter->obj)) == NULL) { 1101 error = ENOMEM; 1102 break; 1103 } 1104 _hsp_devices = dlist_append(item, _hsp_devices, AT_HEAD); 1105 } 1106 1107 return (error); 1108 } 1109 1110 /* 1111 * FUNCTION: string_case_compare( 1112 * char *str1, char *str2) 1113 * 1114 * INPUT: str1 - char * 1115 * str2 - char * 1116 * 1117 * RETURNS: int - <0 - if str1 < str2 1118 * 0 - if str1 == str2 1119 * >0 - if str1 > str2 1120 * 1121 * PURPOSE: More robust case independent string comparison function. 1122 * 1123 * Assumes str1 and str2 are both char * 1124 * 1125 * Compares the lengths of each and if equivalent compares 1126 * the strings using strcasecmp. 1127 */ 1128 int 1129 string_case_compare( 1130 char *str1, 1131 char *str2) 1132 { 1133 int result = 0; 1134 1135 assert(str1 != NULL); 1136 assert(str2 != NULL); 1137 1138 if ((result = (strlen(str1) - strlen(str2))) == 0) { 1139 result = strcasecmp(str1, str2); 1140 } 1141 1142 return (result); 1143 } 1144