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