xref: /titanic_41/usr/src/cmd/lvm/metassist/layout/layout_concat.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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