1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <string.h> 30 #include <libintl.h> 31 32 #include "libdiskmgt.h" 33 34 #include "volume_error.h" 35 #include "volume_defaults.h" 36 #include "volume_devconfig.h" 37 #include "volume_dlist.h" 38 #include "volume_output.h" 39 #include "volume_request.h" 40 41 #include "layout_concat.h" 42 #include "layout_device_cache.h" 43 #include "layout_device_util.h" 44 #include "layout_discovery.h" 45 #include "layout_dlist_util.h" 46 #include "layout_messages.h" 47 #include "layout_request.h" 48 #include "layout_slice.h" 49 #include "layout_svm_util.h" 50 51 #define _LAYOUT_CONCAT_C 52 53 static int 54 compose_concat_within_hba( 55 devconfig_t *request, 56 dlist_t *hbas, 57 uint64_t nbytes, 58 devconfig_t **concat); 59 60 static int 61 assemble_concat( 62 devconfig_t *request, 63 dlist_t *comps, 64 devconfig_t **concat); 65 66 /* 67 * FUNCTION: layout_concat(devconfig_t *request, uint64_t nbytes, 68 * dlist_t **results) 69 * 70 * INPUT: request - pointer to a devconfig_t of the current request 71 * nbytes - the desired capacity of the concat 72 * 73 * OUPUT: results - pointer to a list of composed volumes 74 * 75 * RETURNS: int - 0 on success 76 * !0 otherwise. 77 * 78 * PURPOSE: Main layout driver for composing concat volumes. 79 * 80 * Attempts to construct a concat of size nbytes. 81 * 82 * Several different layout strategies are tried in order 83 * of preference until one succeeds or there are none left. 84 * 85 * 1 - concat within an HBA 86 * . requires sufficient space available on the HBA 87 * 88 * 2 - concat across all available similar HBAs 89 * 90 * 3 - concat across all available HBAs 91 * 92 * get available HBAs 93 * 94 * group HBAs by characteristics 95 * for (each HBA grouping) and (concat not composed) { 96 * select next HBA group 97 * for (strategy[1,2]) and (concat not composed) { 98 * compose concat using HBAs in group 99 * } 100 * } 101 * 102 * if (concat not composed) { 103 * for (strategy[3]) and (concat not composed) { 104 * compose concat using all HBAs 105 * } 106 * } 107 * 108 * if (concat composed) { 109 * append composed concat to results 110 * } 111 */ 112 int 113 layout_concat( 114 devconfig_t *request, 115 uint64_t nbytes, 116 dlist_t **results) 117 { 118 /* 119 * these enums define the # of strategies and the preference order 120 * in which they are tried 121 */ 122 typedef enum { 123 CONCAT_WITHIN_SIMILAR_HBA = 0, 124 CONCAT_ACROSS_SIMILAR_HBAS, 125 N_SIMILAR_HBA_STRATEGIES 126 } similar_hba_strategy_order_t; 127 128 typedef enum { 129 CONCAT_ACROSS_ANY_HBAS = 0, 130 N_ANY_HBA_STRATEGIES 131 } any_hba_strategy_order_t; 132 133 dlist_t *usable_hbas = NULL; 134 dlist_t *similar_hba_groups = NULL; 135 dlist_t *iter = NULL; 136 devconfig_t *concat = NULL; 137 138 int error = 0; 139 140 (error = get_usable_hbas(&usable_hbas)); 141 if (error != 0) { 142 volume_set_error(gettext("There are no usable HBAs.")); 143 return (error); 144 } 145 146 print_layout_volume_msg(devconfig_type_to_str(TYPE_CONCAT), nbytes); 147 148 if (dlist_length(usable_hbas) == 0) { 149 print_no_hbas_msg(); 150 return (-1); 151 } 152 153 error = group_similar_hbas(usable_hbas, &similar_hba_groups); 154 if (error != 0) { 155 return (error); 156 } 157 158 for (iter = similar_hba_groups; 159 (error == 0) && (concat == NULL) && (iter != NULL); 160 iter = iter->next) { 161 162 dlist_t *hbas = (dlist_t *)iter->obj; 163 164 similar_hba_strategy_order_t order; 165 166 for (order = CONCAT_WITHIN_SIMILAR_HBA; 167 (order < N_SIMILAR_HBA_STRATEGIES) && 168 (concat == NULL) && (error == 0); 169 order++) { 170 171 dlist_t *selhbas = NULL; 172 dlist_t *disks = NULL; 173 174 switch (order) { 175 176 case CONCAT_WITHIN_SIMILAR_HBA: 177 178 error = select_hbas_with_n_disks( 179 request, hbas, 1, &selhbas, &disks); 180 181 if (error == 0) { 182 183 /* BEGIN CSTYLED */ 184 oprintf(OUTPUT_TERSE, 185 gettext(" -->Strategy 1: use disks from a single HBA - concat within HBA\n")); 186 /* END CSTYLED */ 187 188 error = compose_concat_within_hba( 189 request, selhbas, nbytes, &concat); 190 } 191 192 break; 193 194 case CONCAT_ACROSS_SIMILAR_HBAS: 195 196 error = select_hbas_with_n_disks( 197 request, hbas, 1, &selhbas, &disks); 198 199 if (error == 0) { 200 201 /* BEGIN CSTYLED */ 202 oprintf(OUTPUT_TERSE, 203 gettext(" -->Strategy 2: use disks from all similar HBAs - concat across HBAs\n")); 204 /* END CSTYLED */ 205 206 error = populate_concat( 207 request, nbytes, disks, 208 NULL, &concat); 209 } 210 211 break; 212 213 default: 214 break; 215 } 216 217 dlist_free_items(disks, NULL); 218 dlist_free_items(selhbas, NULL); 219 } 220 } 221 222 for (iter = similar_hba_groups; iter != NULL; iter = iter->next) { 223 dlist_free_items((dlist_t *)iter->obj, NULL); 224 } 225 dlist_free_items(similar_hba_groups, NULL); 226 227 /* try all HBAs */ 228 if (concat == NULL && error == 0) { 229 230 any_hba_strategy_order_t order; 231 232 for (order = CONCAT_ACROSS_ANY_HBAS; 233 (order < N_ANY_HBA_STRATEGIES) && 234 (concat == NULL) && (error == 0); 235 order++) { 236 237 dlist_t *selhbas = NULL; 238 dlist_t *disks = NULL; 239 240 switch (order) { 241 242 case CONCAT_ACROSS_ANY_HBAS: 243 244 error = select_hbas_with_n_disks( 245 request, usable_hbas, 1, &selhbas, &disks); 246 247 if (error == 0) { 248 249 /* BEGIN CSTYLED */ 250 oprintf(OUTPUT_VERBOSE, 251 gettext(" -->Strategy 3: use disks from all available HBAs - concat across HBAs\n")); 252 /* END CSTYLED */ 253 254 error = populate_concat( 255 request, nbytes, disks, 256 NULL, &concat); 257 } 258 259 break; 260 261 default: 262 break; 263 } 264 265 dlist_free_items(disks, NULL); 266 dlist_free_items(selhbas, NULL); 267 } 268 } 269 270 if (concat != NULL) { 271 272 dlist_t *item = dlist_new_item(concat); 273 if (item == NULL) { 274 error = ENOMEM; 275 } else { 276 277 *results = dlist_append(item, *results, AT_TAIL); 278 279 print_layout_success_msg(); 280 } 281 282 } else if (error != 0) { 283 284 print_debug_failure_msg( 285 devconfig_type_to_str(TYPE_CONCAT), 286 get_error_string(error)); 287 288 } else { 289 290 print_insufficient_resources_msg( 291 devconfig_type_to_str(TYPE_CONCAT)); 292 error = -1; 293 } 294 295 return (error); 296 } 297 298 static int 299 compose_concat_within_hba( 300 devconfig_t *request, 301 dlist_t *hbas, 302 uint64_t nbytes, 303 devconfig_t **concat) 304 { 305 int error = 0; 306 307 dlist_t *iter = NULL; 308 309 for (iter = hbas; 310 (iter != NULL) && (*concat == NULL) && (error == 0); 311 iter = iter->next) { 312 313 dm_descriptor_t hba = (uintptr_t)iter->obj; 314 dlist_t *disks = NULL; 315 uint64_t space = 0; 316 char *name; 317 318 /* check for sufficient space on the HBA */ 319 ((error = get_display_name(hba, &name)) != 0) || 320 (error = hba_get_avail_disks_and_space(request, 321 hba, &disks, &space)); 322 323 if (error == 0) { 324 if (space >= nbytes) { 325 error = populate_concat(request, nbytes, disks, 326 NULL, concat); 327 } else { 328 print_hba_insufficient_space_msg(name, space); 329 } 330 } 331 332 dlist_free_items(disks, NULL); 333 } 334 335 return (error); 336 } 337 338 /* 339 * FUNCTION: populate_concat(devconfig_t *request, uint64_t nbytes, 340 * dlist_t *disks, dlist_t *othervols, 341 * devconfig_t **concat) 342 * 343 * INPUT: request - pointer to a request devconfig_t 344 * nbytes - desired concat size 345 * disks - pointer to a list of availalb disks 346 * othervols - pointer to a list of other volumes whose 347 * composition may affect this concat 348 * (e.g., submirrors of the same mirror) 349 * 350 * OUTPUT: concat - pointer to a devconfig_t to hold resulting concat 351 * 352 * RETURNS: int - 0 on success 353 * !0 otherwise. 354 * 355 * PURPOSE: Helper to populate a concat with the specified aggregate 356 * capacity using slices on disks in the input list. 357 * 358 * If the othervols list is not empty, the slice components 359 * chosen for the concat must not on the same disks as any 360 * of the other volumes. 361 * 362 * If sufficient slice components can be found, the concat 363 * is assembled and returned. 364 */ 365 int 366 populate_concat( 367 devconfig_t *request, 368 uint64_t nbytes, 369 dlist_t *disks, 370 dlist_t *othervols, 371 devconfig_t **concat) 372 { 373 dlist_t *other_hbas = NULL; 374 dlist_t *other_disks = NULL; 375 376 dlist_t *slices = NULL; 377 dlist_t *comps = NULL; 378 379 uint16_t npaths = 0; 380 uint64_t capacity = 0; 381 int error = 0; 382 383 *concat = NULL; 384 385 ((error = disks_get_avail_slices(request, disks, &slices)) != 0) || 386 (error = get_volume_npaths(request, &npaths)); 387 if (error != 0) { 388 dlist_free_items(slices, NULL); 389 return (error); 390 } 391 392 print_populate_volume_msg(devconfig_type_to_str(TYPE_CONCAT), nbytes); 393 394 if (slices == NULL) { 395 print_populate_no_slices_msg(); 396 return (0); 397 } 398 399 /* determine HBAs and disks used by othervols */ 400 error = get_hbas_and_disks_used_by_volumes(othervols, 401 &other_hbas, &other_disks); 402 if (error != 0) { 403 dlist_free_items(other_hbas, NULL); 404 dlist_free_items(other_disks, NULL); 405 return (error); 406 } 407 408 print_populate_choose_slices_msg(); 409 410 while (capacity < nbytes) { 411 412 devconfig_t *comp = NULL; 413 dlist_t *item = NULL; 414 dlist_t *rmvd = NULL; 415 char *cname = NULL; 416 uint64_t csize = 0; 417 418 /* BEGIN CSTYLED */ 419 /* 420 * 1st B_TRUE: require a different disk than those used by 421 * comps and othervols 422 * 1st B_FALSE: slice with size less that requested is acceptable 423 * 2nd B_FALSE: do not add an extra cylinder when resizing slice, 424 * this is only necessary for Stripe components whose sizes 425 * get rounded down to an interlace multiple and then down 426 * to a cylinder boundary. 427 * 428 */ 429 /* END CSTYLED */ 430 error = choose_slice((nbytes-capacity), npaths, slices, comps, 431 other_hbas, other_disks, B_TRUE, B_FALSE, B_FALSE, &comp); 432 433 if ((error == 0) && (comp != NULL)) { 434 435 item = dlist_new_item(comp); 436 if (item == NULL) { 437 error = ENOMEM; 438 } else { 439 440 /* add selected component to comp list */ 441 comps = dlist_append(item, comps, AT_HEAD); 442 443 /* remove it from the available list */ 444 slices = dlist_remove_equivalent_item(slices, (void *) comp, 445 compare_devconfig_and_descriptor_names, &rmvd); 446 447 if (rmvd != NULL) { 448 free(rmvd); 449 } 450 451 /* add the component slice to the used list */ 452 if ((error = devconfig_get_name(comp, &cname)) == 0) { 453 error = add_used_slice_by_name(cname); 454 } 455 456 /* increment concat's capacity */ 457 if ((error == 0) && 458 (error = devconfig_get_size(comp, &csize)) == 0) { 459 capacity += csize; 460 } 461 } 462 463 } else { 464 /* no possible slice */ 465 break; 466 } 467 } 468 469 dlist_free_items(slices, NULL); 470 dlist_free_items(other_hbas, NULL); 471 dlist_free_items(other_disks, NULL); 472 473 if (capacity >= nbytes) { 474 475 error = assemble_concat(request, comps, concat); 476 477 if (error == 0) { 478 print_populate_success_msg(); 479 } else { 480 /* undo any slicing done for the concat */ 481 dlist_free_items(comps, free_devconfig_object); 482 } 483 484 } else if (error == 0) { 485 486 if (capacity > 0) { 487 dlist_free_items(comps, free_devconfig_object); 488 print_insufficient_capacity_msg(capacity); 489 } else { 490 print_populate_no_slices_msg(); 491 } 492 493 } 494 495 return (error); 496 } 497 498 /* 499 * FUNCTION: populate_explicit_concat(devconfig_t *request, 500 * dlist_t **results) 501 * 502 * INPUT: request - pointer to a request devconfig_t 503 * 504 * OUTPUT: results - pointer to a list of volume devconfig_t results 505 * 506 * RETURNS: int - 0 on success 507 * !0 otherwise. 508 * 509 * PURPOSE: Processes the input concat request that specifies explicit 510 * slice components. 511 * 512 * The components have already been validated and reserved, 513 * all that is required is to create devconfig_t structs 514 * for each requested slice. 515 * 516 * The net size of the concat is determined by the slice 517 * components. 518 * 519 * The concat devconfig_t is assembled and appended to the 520 * results list. 521 * 522 * This function is also called from 523 * layout_mirror.populate_explicit_mirror() 524 */ 525 int 526 populate_explicit_concat( 527 devconfig_t *request, 528 dlist_t **results) 529 { 530 int error = 0; 531 532 dlist_t *comps = NULL; 533 dlist_t *iter = NULL; 534 dlist_t *item = NULL; 535 536 devconfig_t *concat = NULL; 537 538 print_layout_explicit_msg(devconfig_type_to_str(TYPE_CONCAT)); 539 540 /* assemble components */ 541 iter = devconfig_get_components(request); 542 for (; (iter != NULL) && (error == 0); iter = iter->next) { 543 544 devconfig_t *rqst = (devconfig_t *)iter->obj; 545 dm_descriptor_t rqst_slice = NULL; 546 char *rqst_name = NULL; 547 devconfig_t *comp = NULL; 548 549 /* slice components have been validated */ 550 /* turn each into a devconfig_t */ 551 ((error = devconfig_get_name(rqst, &rqst_name)) != 0) || 552 (error = slice_get_by_name(rqst_name, &rqst_slice)) || 553 (error = create_devconfig_for_slice(rqst_slice, &comp)); 554 555 if (error == 0) { 556 557 print_layout_explicit_added_msg(rqst_name); 558 559 item = dlist_new_item((void *)comp); 560 if (item == NULL) { 561 error = ENOMEM; 562 } else { 563 comps = dlist_append(item, comps, AT_TAIL); 564 } 565 } 566 } 567 568 if (error == 0) { 569 error = assemble_concat(request, comps, &concat); 570 } 571 572 if (error == 0) { 573 if ((item = dlist_new_item(concat)) == NULL) { 574 error = ENOMEM; 575 } else { 576 *results = dlist_append(item, *results, AT_TAIL); 577 print_populate_success_msg(); 578 } 579 } else { 580 dlist_free_items(comps, free_devconfig); 581 } 582 583 return (error); 584 } 585 586 /* 587 * FUNCTION: assemble_concat(devconfig_t *request, dlist_t *comps, 588 * devconfig_t **concat) 589 * 590 * INPUT: request - pointer to a devconfig_t of the current request 591 * comps - pointer to a list of slice components 592 * 593 * OUPUT: concat - pointer to a devconfig_t to hold final concat 594 * 595 * RETURNS: int - 0 on success 596 * !0 otherwise. 597 * 598 * PURPOSE: Helper which creates and populates a concat devconfig_t 599 * struct using information from the input request and the 600 * list of slice components. 601 * 602 * Determines the name of the concat either from the request 603 * or from the default naming scheme. 604 * 605 * Attaches the input list of components to the devconfig. 606 */ 607 static int 608 assemble_concat( 609 devconfig_t *request, 610 dlist_t *comps, 611 devconfig_t **concat) 612 { 613 char *name = NULL; 614 int error = 0; 615 616 if ((error = new_devconfig(concat, TYPE_CONCAT)) == 0) { 617 /* set concat name, use requested name if specified */ 618 if ((error = devconfig_get_name(request, &name)) != 0) { 619 if (error != ERR_ATTR_UNSET) { 620 volume_set_error(gettext("error getting requested name\n")); 621 } else { 622 error = 0; 623 } 624 } 625 626 if (error == 0) { 627 if (name == NULL) { 628 if ((error = get_next_volume_name(&name, 629 TYPE_CONCAT)) == 0) { 630 error = devconfig_set_name(*concat, name); 631 free(name); 632 } 633 } else { 634 error = devconfig_set_name(*concat, name); 635 } 636 } 637 } 638 639 if (error == 0) { 640 641 /* compute and save true size of concat */ 642 if (error == 0) { 643 uint64_t nblks = 0; 644 dlist_t *iter; 645 646 for (iter = comps; 647 (error == 0) && (iter != NULL); 648 iter = iter->next) { 649 650 devconfig_t *comp = (devconfig_t *)iter->obj; 651 uint64_t comp_nblks = 0; 652 653 if ((error = devconfig_get_size_in_blocks(comp, 654 &comp_nblks)) == 0) { 655 nblks += comp_nblks; 656 } 657 } 658 659 if (error == 0) { 660 error = devconfig_set_size_in_blocks(*concat, nblks); 661 } 662 } 663 } 664 665 if (error == 0) { 666 devconfig_set_components(*concat, comps); 667 } else { 668 free_devconfig(*concat); 669 *concat = NULL; 670 } 671 672 return (error); 673 } 674