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
layout_concat(devconfig_t * request,uint64_t nbytes,dlist_t ** results)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
compose_concat_within_hba(devconfig_t * request,dlist_t * hbas,uint64_t nbytes,devconfig_t ** concat)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
populate_concat(devconfig_t * request,uint64_t nbytes,dlist_t * disks,dlist_t * othervols,devconfig_t ** concat)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
populate_explicit_concat(devconfig_t * request,dlist_t ** results)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
assemble_concat(devconfig_t * request,dlist_t * comps,devconfig_t ** concat)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