xref: /titanic_41/usr/src/cmd/lvm/metassist/layout/layout_discovery.c (revision 6185db853e024a486ff8837e6784dd290d866112)
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 <limits.h>
30 #include <libdiskmgt.h>
31 #include <libintl.h>
32 
33 #include <meta.h>
34 
35 #define	_LAYOUT_DISCOVERY_C
36 
37 #include "volume_dlist.h"
38 #include "volume_error.h"
39 #include "volume_nvpair.h"
40 #include "volume_output.h"
41 
42 #include "layout_device_cache.h"
43 #include "layout_device_util.h"
44 #include "layout_dlist_util.h"
45 #include "layout_discovery.h"
46 #include "layout_request.h"
47 #include "layout_slice.h"
48 #include "layout_svm_util.h"
49 
50 /*
51  * lists of device dm_descriptor_t handles discovered during
52  * the initial system probe.  Lists are populated by
53  * discover_known_devices.
54  *
55  * "bad" slices are those that are known to libdiskmgt but
56  * cannot be accessed. An example would be a slice that has
57  * disappeared due to disk re-slicing: libdiskmgt may have a
58  * cached handle for it, but the slice no longer exists.
59  *
60  * "bad" disks are thoese that are known to libdiskmgt but
61  * cannot be accessed.  An example would be a disk that has
62  * failed or has gone offline: libdiskmgt may have a cached
63  * handle for it, but the disk does not respond.
64  */
65 static dlist_t	*_bad_slices = NULL;
66 static dlist_t	*_bad_disks = NULL;
67 
68 static dlist_t	*_known_slices = NULL;
69 static dlist_t	*_known_disks = NULL;
70 static dlist_t	*_known_hbas = NULL;
71 
72 /*
73  * helper functions for building known device lists, used by
74  * discover_known_devices.
75  */
76 static int generate_known_slices(dlist_t *disks, dlist_t **known,
77 	dlist_t **bad);
78 static int generate_known_disks(dlist_t **known, dlist_t **bad);
79 static int generate_known_hbas(dlist_t *disks, dlist_t **known);
80 static int generate_known_hba_name(
81 	dm_descriptor_t hba,
82 	dm_descriptor_t	alias,
83 	dm_descriptor_t disk);
84 
85 static void print_known_devices();
86 static void print_device_list(dlist_t *devices);
87 
88 /*
89  * lists of device dm_descriptor_t handles that are usable by layout.
90  * These devices must still pass the user specified available/unavailable
91  * filter before they're actually considered available.
92  *
93  * Lists are populated by discover_usable_devices.
94  */
95 static dlist_t	*_usable_slices = NULL;
96 static dlist_t	*_usable_disks = NULL;
97 static dlist_t	*_usable_hbas = NULL;
98 
99 /*
100  * private flag that remembers if any HBA is known to support MPXIO
101  */
102 static boolean_t _mpxio_enabled = B_FALSE;
103 
104 /*
105  * The slice_class struct is used to group slices by usage class.
106  */
107 typedef struct {
108 	char	*usage;		/* usage description */
109 	dlist_t	*sliceinfo;	/* list with info about each slice with usage */
110 } slice_class_t;
111 
112 #define	USE_DISKSET	"diskset"
113 
114 static int check_slice_usage(
115 	char		*dsname,
116 	dm_descriptor_t slice,
117 	dm_descriptor_t disk,
118 	boolean_t	*avail,
119 	dlist_t		**bad,
120 	dlist_t		**classes);
121 
122 static int check_svm_slice_usage(
123 	char		*dsname,
124 	dm_descriptor_t slice,
125 	dm_descriptor_t disk,
126 	boolean_t	*avail,
127 	dlist_t		**classes);
128 
129 static int save_slice_classification(
130 	char		*dsname,
131 	dm_descriptor_t slice,
132 	dm_descriptor_t disk,
133 	char		*usage,
134 	char		*usage_detail,
135 	dlist_t		**classes);
136 
137 static int generate_usable_disks_and_slices_in_local_set(
138 	dlist_t		**classes,
139 	dlist_t		**bad_disks,
140 	dlist_t		**usable_disks,
141 	dlist_t		**usable_slices);
142 
143 static int generate_usable_disks_and_slices_in_named_set(
144 	char		*dsname,
145 	dlist_t		**classes,
146 	dlist_t		**bad_slices,
147 	dlist_t		**usable_disks,
148 	dlist_t		**usable_slices);
149 
150 static int create_usable_slices(
151 	dm_descriptor_t disk,
152 	dlist_t		*used,
153 	dlist_t		*unused,
154 	dlist_t 	**usable);
155 
156 static int add_new_usable(
157 	dm_descriptor_t disk,
158 	uint64_t	stblk,
159 	uint64_t	nblks,
160 	dlist_t		**next_unused,
161 	dlist_t		**usable);
162 
163 static int update_slice_attributes(
164 	dm_descriptor_t slice,
165 	uint64_t	stblk,
166 	uint64_t	nblks,
167 	uint64_t	nbytes);
168 
169 static int generate_usable_hbas(
170 	dlist_t		*disks,
171 	dlist_t		**usable);
172 
173 static void print_usable_devices();
174 
175 static void print_unusable_devices(
176 	dlist_t		*badslices,
177 	dlist_t		*baddisks,
178 	dlist_t		*usedslices);
179 
180 static char *get_slice_usage_msg(
181 	char		*usage);
182 
183 /*
184  * virtual slices...
185  */
186 static int generate_virtual_slices(
187 	dlist_t 	*avail_disks_local_set,
188 	dlist_t		**usable);
189 
190 /*
191  * multipathed disks have aliases, as do slices on those disks.
192  * these need to be tracked since the user may specify them.
193  * A multi-pathed disk is one connected to the system thru
194  * more than one physical HBA, each connection gets a distinct
195  * name in the device tree and they're all more or less equivalent.
196  * No indication as to how many possible physical connections a
197  * disk may have, so we pick an arbitrary number of aliases to
198  * support. There is nothing significant about this number,
199  * it just controls the number of alias slots that get allocated.
200  */
201 #define	MAX_ALIASES	8
202 
203 /*
204  * attribute name for layout private information stored in
205  * device nvpair attribute lists.
206  */
207 static char *ATTR_DEVICE_ALIASES = "layout_device_aliases";
208 
209 static int compare_start_blocks(
210 	void *desc1, void *desc2);
211 
212 static int compare_desc_display_names(
213 	void *desc1, void *desc2);
214 
215 /*
216  * FUNCTION:	is_mpxio_enabled()
217  *
218  * RETURNS:	boolean_t - B_TRUE - if MPXIO appears enabled for the system
219  *			    B_FALSE - otherwise
220  *
221  * PURPOSE:	returns the value of _mpxio_enabled which is set to B_TRUE
222  *		during system configuration discovery if any of the knwon
223  *		HBAs advertises itself as a "multiplex" controller.
224  */
225 boolean_t
226 is_mpxio_enabled()
227 {
228 	return (_mpxio_enabled);
229 }
230 
231 /*
232  * FUNCTION:	discover_known_devices()
233  *
234  * SIDEEFFECT:	populates the module private lists of known devices
235  *		(_known_slices, _known_disks, _known_hbas).
236  *
237  *		All known devices will also have had their CTD
238  *		short names inferred and stored.
239  *
240  * RETURNS:	int	- 0 on success
241  *			 !0 otherwise
242  *
243  * PURPOSE:	Load physical devices discovered thru libdiskmgt.
244  */
245 int
246 discover_known_devices()
247 {
248 	int	error = 0;
249 
250 	oprintf(OUTPUT_TERSE,
251 		gettext("\nScanning system physical "
252 			"device configuration...\n"));
253 
254 	/* initialize layout_device_cache */
255 	((error = create_device_caches()) != 0) ||
256 
257 	(error = generate_known_disks(&_known_disks, &_bad_disks)) ||
258 	(error = generate_known_slices(_known_disks, &_known_slices,
259 		&_bad_slices)) ||
260 	(error = generate_known_hbas(_known_disks, &_known_hbas));
261 
262 	if (error == 0) {
263 	    print_known_devices();
264 	}
265 
266 	return (error);
267 }
268 
269 /*
270  * FUNCTION:	release_known_devices()
271  *
272  * RETURNS:	int	- 0 on success
273  *			 !0 otherwise
274  *
275  * PURPOSE:	Unloads all state currently held for known
276  *		physical devices.
277  */
278 int
279 release_known_devices(
280 	char	*diskset)
281 {
282 	/* these lists are module private */
283 	dlist_free_items(_bad_slices, NULL);
284 	dlist_free_items(_bad_disks, NULL);
285 	dlist_free_items(_known_slices, NULL);
286 	dlist_free_items(_known_disks, NULL);
287 	dlist_free_items(_known_hbas, NULL);
288 
289 	_bad_slices = NULL;
290 	_bad_disks = NULL;
291 	_known_slices = NULL;
292 	_known_disks = NULL;
293 	_known_hbas = NULL;
294 
295 	/* clean up state kept in layout_device_cache */
296 	release_device_caches();
297 
298 	return (0);
299 }
300 
301 /*
302  * FUNCTION:	discover_usable_devices(char *diskset)
303  *
304  * INPUT:	diskset	- a char * diskset name.
305  *
306  * SIDEEFFECT:	Traverses the lists of known devices and populates the
307  *		module private lists of usable devices (_usable_slices,
308  *		_usable_disks, _usable_hbas), as well as the module
309  *		private list of used slices.
310  *
311  * RETURNS:	int	- 0 on success
312  *			 !0 otherwise
313  *
314  * PURPOSE:	Process the known devices and determine which of them are
315  *		usable for generating volumes in the specified diskset.
316  *
317  *		The specified diskset's name cannot be NULL or 0 length.
318  */
319 int
320 discover_usable_devices(
321 	char	*diskset)
322 {
323 	int	error = 0;
324 
325 	dlist_t *used_classes = NULL;
326 	dlist_t *iter = NULL;
327 
328 	if (diskset == NULL || diskset[0] == '\0') {
329 	    volume_set_error(
330 		    gettext("a diskset name must be specified in "
331 			    "the request\n"));
332 	    return (-1);
333 	}
334 
335 	oprintf(OUTPUT_TERSE,
336 		gettext("\nDetermining usable physical devices "
337 			"for disk set \"%s\"...\n"),
338 		diskset);
339 
340 	error = generate_usable_disks_and_slices_in_local_set(
341 	    &used_classes, &_bad_slices, &_usable_disks, &_usable_slices);
342 	if (error == 0) {
343 
344 	    error = generate_usable_disks_and_slices_in_named_set(
345 		diskset, &used_classes, &_bad_slices, &_usable_disks,
346 		&_usable_slices);
347 	    if (error == 0) {
348 
349 		error = generate_usable_hbas(_usable_disks, &_usable_hbas);
350 		if (error == 0) {
351 
352 		    print_usable_devices();
353 		    print_unusable_devices(
354 			_bad_slices, _bad_disks, used_classes);
355 		}
356 	    }
357 	}
358 
359 	/*
360 	 * free slice classification usage and lists, items are char*
361 	 * the used_classes structure is only filled in if verbose
362 	 * output was requested.
363 	 */
364 	for (iter = used_classes; iter != NULL; iter = iter->next) {
365 	    slice_class_t *class = (slice_class_t *)iter->obj;
366 	    free(class->usage);
367 	    dlist_free_items(class->sliceinfo, free);
368 	}
369 
370 	dlist_free_items(used_classes, free);
371 	return (error);
372 }
373 
374 /*
375  * FUNCTION:	release_usable_devices()
376  *
377  * RETURNS:	int	- 0 on success
378  *			 !0 otherwise
379  *
380  * PURPOSE:	Unloads all state currently held for usable
381  *		physical devices.
382  */
383 int
384 release_usable_devices()
385 {
386 	/* list items are shared with _known_XXX lists */
387 
388 	dlist_free_items(_usable_slices, NULL);
389 	dlist_free_items(_usable_disks, NULL);
390 	dlist_free_items(_usable_hbas, NULL);
391 
392 	_usable_slices = NULL;
393 	_usable_disks = NULL;
394 	_usable_hbas = NULL;
395 
396 	/* clean up state kept in layout_device_util */
397 	release_virtual_slices();
398 
399 	return (0);
400 }
401 
402 /*
403  * FUNCTION:	get_known_slices(dlist_t **list)
404  *		get_known_disks(dlist_t **list)
405  *		get_known_hbas(dlist_t **list)
406  *
407  * OUTPUT:	list	- a dlist_t pointer to hold the returned list of
408  *			devices.
409  *
410  * RETURNS:	int	- 0 on success
411  *			 !0 otherwise
412  *
413  * PURPOSE:	Public accessors for the module private lists of
414  *		available devices.
415  */
416 int
417 get_known_slices(
418 	dlist_t **list)
419 {
420 	*list = _known_slices;
421 
422 	return (0);
423 }
424 
425 int
426 get_known_disks(
427 	dlist_t **list)
428 {
429 	*list = _known_disks;
430 
431 	return (0);
432 }
433 
434 int
435 get_known_hbas(
436 	dlist_t **list)
437 {
438 	*list = _known_hbas;
439 
440 	return (0);
441 }
442 
443 /* make fully qualified DID device name */
444 static char *
445 make_fully_qualified_did_device_name(
446 	char	*device)
447 {
448 	static char	buf[MAXPATHLEN];
449 
450 	if (device != NULL && strrchr(device, '/') == NULL) {
451 	    (void) snprintf(buf, MAXPATHLEN-1, "%s/%s",
452 		    "/dev/did/dsk", device);
453 	    return (buf);
454 	}
455 
456 	return (device);
457 }
458 
459 /*
460  * FUNCTION:	generate_known_disks(dlist_t **known,
461  *			dlist_t **bad)
462  *
463  * INPUT:	NONE
464  *
465  * OUTPUT:	known	- populated list of known disks
466  *		bad	- populated list of known bad disks
467  *
468  * RETURNS:	int	- 0 on success
469  *			 !0 otherwise
470  *
471  * PURPOSE:	Does the system configuration discovery to determine
472  *		what disks are known to be attached to the system.
473  *
474  *		Determines the CTD name for each disk and saves it.
475  */
476 static int
477 generate_known_disks(
478 	dlist_t	**known,
479 	dlist_t **bad)
480 {
481 	int	i;
482 	int	error = 0;
483 	dm_descriptor_t	*ddp;
484 
485 	ddp = dm_get_descriptors(DM_DRIVE, NULL, &error);
486 	(void) add_descriptors_to_free(ddp);
487 
488 	*known = NULL;
489 
490 	if (error != 0) {
491 	    volume_set_error(
492 		    gettext("Error discovering system hardware configuration,\n"
493 		    "unable to communicate with libdiskmgt or diskmgtd.\n"));
494 	    return (-1);
495 	}
496 
497 	if ((ddp == NULL) || (ddp[0] == NULL)) {
498 	    volume_set_error(gettext("there are no known disks\n"));
499 	    return (-1);
500 	}
501 
502 	/* iterate all returned disks and add them to the known list */
503 	for (i = 0; (ddp[i] != NULL) && (error == 0); i++) {
504 	    dm_descriptor_t disk = (dm_descriptor_t)ddp[i];
505 	    dlist_t *aliases = NULL;
506 	    uint32_t mtype = DM_MT_UNKNOWN;
507 	    uint32_t dtype = DM_DT_UNKNOWN;
508 	    boolean_t bad_disk = B_FALSE;
509 	    boolean_t online = B_TRUE;
510 
511 #if defined(i386)
512 	    /* on X86, disks must have a solaris FDISK partition */
513 	    boolean_t solpart = B_FALSE;
514 #endif	/* defined(i386) */
515 
516 	    if (((error = disk_get_is_online(disk, &online)) == 0 &&
517 		online == B_FALSE) || error == ENODEV) {
518 		/* if the disk is offline, report it as bad */
519 		bad_disk = B_TRUE;
520 		error = 0;
521 	    } else
522 
523 	    if (error == 0 &&
524 		(((error = disk_get_media_type(disk, &mtype)) != 0) ||
525 		((error = disk_get_drive_type(disk, &dtype)) != 0)) &&
526 		error == ENODEV) {
527 		/*
528 		 * if any disk attribute access fails with ENODEV
529 		 * report it as bad
530 		 */
531 		bad_disk = B_TRUE;
532 		error = 0;
533 	    } else {
534 
535 		/*
536 		 * Determine whether disk is fixed by checking its
537 		 * drive type.  If drive type is unknown, check media
538 		 * type.
539 		 */
540 		int isfixed = (dtype == DM_DT_FIXED ||
541 		    (dtype == DM_DT_UNKNOWN && mtype == DM_MT_FIXED));
542 
543 		if (!isfixed) {
544 		    continue;  /* ignore non-fixed disks */
545 		}
546 
547 #if defined(i386)
548 		if (((error = disk_get_has_solaris_partition(disk,
549 		    &solpart)) != 0) || (solpart != B_TRUE)) {
550 
551 		    /* X86 drive has no solaris partition, report as bad */
552 		    oprintf(OUTPUT_DEBUG,
553 			    gettext("%s has no solaris FDISK partition.\n"));
554 
555 		    bad_disk = B_TRUE;
556 		}
557 #endif	/* defined(i386) */
558 
559 	    }
560 
561 	    if (bad_disk) {
562 		/* remember bad disks and continue */
563 		if (dlist_contains(*bad, (void *)(uintptr_t)disk,
564 		    compare_descriptor_names) != B_TRUE) {
565 		    dlist_t *item = dlist_new_item((void *)(uintptr_t)disk);
566 		    if (item == NULL) {
567 			error = ENOMEM;
568 		    } else {
569 			*bad = dlist_append(item, *bad, AT_TAIL);
570 		    }
571 		}
572 		continue;
573 	    }
574 
575 	    /* get disk name and multipath aliases */
576 	    if ((error = disk_get_aliases(disk, &aliases)) == 0) {
577 		dlist_t *iter;
578 		boolean_t disk_name_set = B_FALSE;
579 
580 		for (iter = aliases;
581 		    (iter != NULL) && (error == 0);
582 		    iter = iter->next) {
583 
584 		    dm_descriptor_t	ap = (uintptr_t)iter->obj;
585 		    char		*alias;
586 
587 		    if ((error = get_name(ap, &alias)) == 0) {
588 			/* save first alias as display name */
589 			if (disk_name_set != B_TRUE) {
590 			    /* make sure DID disk alias is fully qualified */
591 
592 			    if (is_did_disk_name(alias) == B_TRUE) {
593 				char *qual_name =
594 				    make_fully_qualified_did_device_name(alias);
595 
596 				set_display_name(disk, qual_name);
597 				oprintf(OUTPUT_DEBUG,
598 					gettext("DID disk name: %s\n"),
599 					qual_name);
600 			    } else {
601 				set_display_name(disk, alias);
602 				oprintf(OUTPUT_DEBUG,
603 					gettext("disk name: %s\n"),
604 					alias);
605 			    }
606 			    disk_name_set = B_TRUE;
607 
608 			} else {
609 			    /* save others as aliases */
610 			    set_alias(disk, alias);
611 			    oprintf(OUTPUT_DEBUG,
612 				    gettext("  alias: %s\n"),
613 				    alias);
614 			}
615 		    }
616 		}
617 
618 		dlist_free_items(aliases, NULL);
619 	    }
620 
621 	    if (error == 0) {
622 		dlist_t *item = dlist_new_item((void *)(uintptr_t)disk);
623 		if (item == NULL) {
624 		    error = ENOMEM;
625 		} else {
626 		    *known =
627 			dlist_insert_ordered(item, *known,
628 				ASCENDING, compare_desc_display_names);
629 		}
630 	    }
631 	}
632 
633 	if (ddp != NULL) {
634 	    free(ddp);
635 	}
636 
637 	return (error);
638 }
639 
640 /*
641  * FUNCTION:	generate_known_slices(dlist_t *disks,
642  *		dlist_t **known, dlist_t **bad)
643  *
644  * OUTPUT:	disks	- a pointer to a list of known disks
645  *		known	- a pointer to a dlist_t list to hold the known slices
646  *		bad	- a pointer to a dlist_t to hold the bad slices
647  *
648  * RETURNS:	int	- 0 on success
649  *			 !0 otherwise.
650  *
651  * PURPOSE:	Examines input list of known disks and determines the slices
652  *		attached to each.
653  *
654  *		Some slices returned from libdiskmgt may not really exist,
655  *		this is detected when trying to get more information about
656  *		the slice -- ENODEV is returned.  Any such slices will be
657  *		added to the bad slice list.
658  */
659 static int
660 generate_known_slices(
661 	dlist_t		*disks,
662 	dlist_t		**known,
663 	dlist_t		**bad)
664 {
665 	dlist_t		*iter;
666 	int		error = 0;
667 
668 	/* iterate list of disks and add their slices to the known list */
669 	for (iter = disks; (iter != NULL) && (error == 0); iter = iter->next) {
670 
671 	    dm_descriptor_t disk = (uintptr_t)iter->obj;
672 	    dlist_t *slices = NULL;
673 	    dlist_t *iter1;
674 	    char *dname = NULL;
675 	    boolean_t disk_ctd_alias_derived = B_FALSE;
676 
677 	    if (((error = disk_get_slices(disk, &slices)) != 0) ||
678 		((error = get_display_name(disk, &dname)) != 0)) {
679 		continue;
680 	    }
681 
682 	    for (iter1 = slices;
683 		(iter1 != NULL) && (error == 0);
684 		iter1 = iter1->next) {
685 
686 		dm_descriptor_t slice = (uintptr_t)iter1->obj;
687 		uint32_t index = 0;
688 		nvlist_t *attrs = NULL;
689 		char *sname = NULL;
690 
691 		if (((error = get_name(slice, &sname)) != 0) ||
692 		    ((error = slice_get_index(slice, &index)) != 0) ||
693 		    ((error = get_cached_attributes(slice, &attrs)) != 0)) {
694 
695 		    if (error == ENODEV) {
696 			/* bad slice, remember it and continue */
697 			dlist_t *item =
698 			    dlist_new_item((void *)(uintptr_t)slice);
699 			if (item == NULL) {
700 			    error = ENOMEM;
701 			} else {
702 			    *bad = dlist_insert_ordered(
703 				    item, *bad,
704 				    ASCENDING, compare_descriptor_names);
705 			    error = 0;
706 			}
707 		    }
708 		    continue;
709 		}
710 
711 		if ((error == 0) && (is_did_slice_name(sname) == B_TRUE) &&
712 		    (disk_ctd_alias_derived == B_FALSE)) {
713 		    /* BEGIN CSTYLED */
714 		    /*
715 		     * If the slice name is a DID name, get the local CTD
716 		     * name for slice, extract the disk name and add it as
717 		     * an alias for the disk.
718 		     *
719 		     * This is the only way to derive the CTD alias for the
720 		     * disk when DID is enabled.
721 		     *
722 		     * The disk_ctd_alias_derived flag ensure the disk's
723 		     * CTD alias is only set once.
724 		     *
725 		     * The slice's CTD aliases are then derived from the
726 		     * disk's CTD alias in the normal, non-DID name processing
727 		     * which happens below.
728 		     */
729 		    /* END CSTYLED */
730 		    char *local = NULL;
731 		    if ((error = nvlist_lookup_string(attrs, DM_LOCALNAME,
732 				&local)) != 0) {
733 			if (error == ENOENT) {
734 			    /* no local name -> no DID */
735 			    error = 0;
736 			}
737 		    } else {
738 			char *localdisk = NULL;
739 			char *diskonly = NULL;
740 			if ((error = extract_diskname(local,
741 			    &localdisk)) == 0) {
742 			    if ((diskonly = strrchr(localdisk, '/')) != NULL) {
743 				++diskonly;
744 			    } else {
745 				diskonly = localdisk;
746 			    }
747 			    oprintf(OUTPUT_DEBUG,
748 				    gettext("  set DID disk CTD alias: %s\n"),
749 				    diskonly);
750 			    error = set_alias(disk, diskonly);
751 			    free(localdisk);
752 			    disk_ctd_alias_derived = B_TRUE;
753 			}
754 		    }
755 		}
756 
757 		/* derive slice display name from disk's display name */
758 		if (error == 0) {
759 		    if ((error = make_slicename_for_diskname_and_index(
760 			dname, index, &sname)) == 0) {
761 			error = set_display_name(slice, sname);
762 		    }
763 		}
764 
765 		/* set slice aliases using disk aliases */
766 		if (error == 0) {
767 		    dlist_t *aliases = NULL;
768 		    if ((error = get_aliases(disk, &aliases)) == 0) {
769 
770 			dlist_t *iter2 = aliases;
771 			for (; (iter2 != NULL) && (error == 0);
772 			    iter2 = iter2->next) {
773 
774 			    char *dalias = (char *)iter2->obj;
775 			    char *salias = NULL;
776 
777 			    if ((error = make_slicename_for_diskname_and_index(
778 				dalias, index, &salias)) == 0) {
779 				error = set_alias(slice, salias);
780 				free(salias);
781 			    }
782 			}
783 			dlist_free_items(aliases, free);
784 		    }
785 		}
786 
787 		if (error == 0) {
788 		    dlist_t *item = dlist_new_item((void *)(uintptr_t)slice);
789 		    if (item == NULL) {
790 			error = ENOMEM;
791 		    } else {
792 			*known =
793 			    dlist_insert_ordered(
794 				    item, *known,
795 				    ASCENDING, compare_desc_display_names);
796 		    }
797 		}
798 	    }
799 
800 	    dlist_free_items(slices, NULL);
801 	}
802 
803 	return (error);
804 }
805 
806 /*
807  * FUNCTION:	generate_known_hbas(dlist_t *disks, dlist_t **known)
808  *
809  * INPUT:	diskset	- a char * diskset name.
810  *
811  * OUTPUT:	populates the list of known HBAs.
812  *
813  * RETURNS:	int	- 0 on success
814  *			 !0 otherwise
815  *
816  * PURPOSE:	Examines known disk list and derives the list of known HBAs.
817  *
818  *		Determines the CTD name for an HBA and saves it.
819  */
820 static int
821 generate_known_hbas(
822 	dlist_t	*disks,
823 	dlist_t	**known)
824 {
825 	dlist_t	*iter;
826 	int	error = 0;
827 
828 	/*
829 	 * for each known disk follow its HBA connections and
830 	 * assemble the list of known HBAs.
831 	 */
832 	for (iter = disks;
833 	    (iter != NULL) && (error == 0);
834 	    iter = iter->next) {
835 
836 	    dm_descriptor_t	disk = (uintptr_t)iter->obj;
837 	    dlist_t 		*hbas = NULL;
838 	    dlist_t 		*iter2 = NULL;
839 	    dlist_t		*iter3 = NULL;
840 	    dlist_t		*aliases = NULL;
841 	    char		*dname = NULL;
842 
843 	    ((error = get_display_name(disk, &dname)) != 0) ||
844 	    (error = disk_get_aliases(disk, &aliases)) ||
845 	    (error = disk_get_hbas(disk, &hbas));
846 
847 	    if (error == 0) {
848 
849 		if ((hbas == NULL) || (dlist_length(hbas) == 0)) {
850 
851 		    oprintf(OUTPUT_DEBUG,
852 			    gettext("Disk %s has no HBA/Controller?!\n"),
853 			    dname);
854 		    error = -1;
855 
856 		    dlist_free_items(hbas, NULL);
857 		    dlist_free_items(aliases, NULL);
858 
859 		    continue;
860 		}
861 
862 		for (iter2 = hbas, iter3 = aliases;
863 		    iter2 != NULL && iter3 != NULL;
864 		    iter2 = iter2->next, iter3 = iter3->next) {
865 
866 		    dm_descriptor_t	hba = (uintptr_t)iter2->obj;
867 		    dm_descriptor_t	alias = (uintptr_t)iter3->obj;
868 		    dlist_t		*item = NULL;
869 
870 		    /* scan list of known HBAs and see if known */
871 		    if (dlist_contains(*known, (void*)(uintptr_t)hba,
872 			compare_descriptor_names) == B_TRUE) {
873 			/* known HBA */
874 			continue;
875 		    }
876 
877 		    /* see if HBA supports MPXIO */
878 		    if ((error == 0) && (_mpxio_enabled != B_TRUE)) {
879 			hba_is_multiplex(hba, &_mpxio_enabled);
880 		    }
881 
882 		    /* generate a CTD name for HBA */
883 		    error = generate_known_hba_name(hba, alias, disk);
884 		    if (error == 0) {
885 			/* add to known HBA list */
886 			if ((item = dlist_new_item((void *)(uintptr_t)hba)) ==
887 			    NULL) {
888 			    error = ENOMEM;
889 			} else {
890 			    *known =
891 				dlist_insert_ordered(item, *known,
892 				    ASCENDING, compare_desc_display_names);
893 			}
894 		    }
895 		}
896 	    }
897 
898 	    dlist_free_items(aliases, NULL);
899 	    dlist_free_items(hbas, NULL);
900 	}
901 
902 	return (error);
903 }
904 
905 /*
906  * FUNCTION:	generate_known_hba_name(dm_descriptor_t hba,
907  *		dm_descriptor_t alias, char *diskname)
908  *
909  * INPUT:	hba	- a dm_descriptor_t HBA handle.
910  *		alias	- a dm_descriptor_t disk alias handle.
911  *		diskname - a char * disk name
912  *
913  * RETURNS:	int	- 0 on success
914  *			 !0 otherwise
915  *
916  * PURPOSE:	Sets the CTD name for the input HBA.
917  *
918  *		The CTD name for the HBA is generated from the input
919  *		disk alias (ex: cXdXtXsX) or from the disk name if
920  *		the input alias is a DID name (ex: dX).
921  */
922 static int
923 generate_known_hba_name(
924 	dm_descriptor_t	hba,
925 	dm_descriptor_t	alias,
926 	dm_descriptor_t disk)
927 {
928 	char	*hbaname = NULL;
929 	char	*aliasname = NULL;
930 	int	error = 0;
931 
932 	((error = get_name(alias, &aliasname)) != 0) ||
933 	(error = extract_hbaname(aliasname, &hbaname));
934 	if (error != 0) {
935 	    free(hbaname);
936 	    return (error);
937 	}
938 
939 	/* see if the input alias is a DID name... */
940 	if (is_did_disk_name(aliasname) == B_TRUE) {
941 
942 	    /* look for a non-DID name in disk's aliases */
943 	    dlist_t *aliases = NULL;
944 	    error = get_aliases(disk, &aliases);
945 
946 	    for (; (error == 0) && (aliases != NULL);
947 		aliases = aliases->next) {
948 
949 		aliasname = (char *)aliases->obj;
950 		if (is_did_disk_name(aliasname) != B_TRUE) {
951 		    /* this is the "local" CTD name generated by */
952 		    /* generate_known_disks() above */
953 		    error = extract_hbaname(aliasname, &hbaname);
954 		    if ((error == 0) && (hbaname != NULL)) {
955 			set_display_name(hba, hbaname);
956 			break;
957 		    }
958 		}
959 	    }
960 	    dlist_free_items(aliases, free);
961 
962 	} else {
963 	    /* use whatever was derived from the alias name */
964 	    set_display_name(hba, hbaname);
965 	}
966 
967 	return (error);
968 }
969 
970 /*
971  * FUNCTION:	print_known_devices()
972  *
973  * PURPOSE:	Print out the known devices.
974  *
975  *		Iterates the lists of known slices, disks and HBAs
976  *		and prints out their CTD and device names.
977  */
978 static void
979 print_known_devices(
980 	char	*diskset)
981 {
982 	int i = 0;
983 	struct {
984 		char *msg;
985 		dlist_t *list;
986 	}	devs[3];
987 
988 	devs[0].msg = gettext("HBA/Controllers");
989 	devs[0].list = _known_hbas;
990 	devs[1].msg = gettext("disks");
991 	devs[1].list = _known_disks;
992 	devs[2].msg = gettext("slices");
993 	devs[2].list = _known_slices;
994 
995 	for (i = 0; i < 3; i++) {
996 
997 	    oprintf(OUTPUT_VERBOSE,
998 		    gettext("\n  These %s are known:\n\n"),
999 		    devs[i].msg);
1000 
1001 	    print_device_list(devs[i].list);
1002 	}
1003 }
1004 
1005 /*
1006  * FUNCTION:	get_usable_slices(dlist_t **list)
1007  *
1008  * OUTPUT:	list	- a dlist_t pointer to hold the returned list of
1009  *			devices.
1010  *
1011  * RETURNS:	int	- 0 on success
1012  *			 !0 otherwise
1013  *
1014  * PURPOSE:	Public accessors the the modules private lists of
1015  *		available devices.
1016  *
1017  *		The functions are keyed by diskset name in the event
1018  *		objects in different disksets are loaded concurrently.
1019  */
1020 int
1021 get_usable_slices(
1022 	dlist_t **list)
1023 {
1024 	*list = _usable_slices;
1025 
1026 	return (0);
1027 }
1028 
1029 int
1030 get_usable_disks(
1031 	dlist_t **list)
1032 {
1033 	*list = _usable_disks;
1034 
1035 	return (0);
1036 }
1037 
1038 int
1039 get_usable_hbas(
1040 	dlist_t **list)
1041 {
1042 	*list = _usable_hbas;
1043 
1044 	return (0);
1045 }
1046 
1047 /*
1048  * FUNCTION:	generate_usable_disks_and_slices_in_local_set(dlist_t **classes,
1049  *			dlist_t **bad_disks, dlist_t **usable_disks,
1050  *			dlist_t **usable_slices)
1051  *
1052  * OUTPUT:	used_classes - a pointer to a list of slice_class_t structs
1053  *			updated with known slices that have detected uses
1054  *			added to the correct class'e list of slices.
1055  *		bad_disks - a pointer to a list of bad/unusable disks updated
1056  *			with any bad disks that were detected
1057  *		useable_disks - a pointer to a list of usable disks
1058  *		useable_slices - a pointer to a list of usable slices
1059  *
1060  * RETURNS:	int	- 0 on success
1061  *			 !0 otherwise.
1062  *
1063  * PURPOSE:	Scans the disks in the local set to determine which are
1064  *		usable during layout processing.
1065  *
1066  *		Determines which are usable by layout using usages detected
1067  *		by libdiskmgt.
1068  */
1069 static int
1070 generate_usable_disks_and_slices_in_local_set(
1071 	dlist_t **classes,
1072 	dlist_t **bad_slices,
1073 	dlist_t **usable_disks,
1074 	dlist_t **usable_slices)
1075 {
1076 	char	*dsname = MD_LOCAL_NAME;
1077 	dlist_t *disks;
1078 	dlist_t *iter;
1079 	int 	error;
1080 
1081 	/* Get disks in local set */
1082 	error = get_disks_in_diskset(dsname, &disks);
1083 	if (error != 0) {
1084 	    return (error);
1085 	}
1086 
1087 	/* For each disk in this set... */
1088 	for (iter = disks; iter != NULL && error == 0; iter = iter->next) {
1089 	    dm_descriptor_t disk = (uintptr_t)iter->obj;
1090 	    dlist_t *slices;
1091 
1092 	    /* Get slices on this disk */
1093 	    error = disk_get_slices(disk, &slices);
1094 	    if (error == 0) {
1095 		dlist_t *iter2;
1096 
1097 		/*
1098 		 * Assume disk is available until a bad or unavailable
1099 		 * slice is found
1100 		 */
1101 		boolean_t avail = B_TRUE;
1102 		boolean_t bad_disk = B_FALSE;
1103 
1104 		/* For each slice on this disk... */
1105 		for (iter2 = slices;
1106 		    iter2 != NULL && error == 0 &&
1107 			avail == B_TRUE && bad_disk == B_FALSE;
1108 		    iter2 = iter2->next) {
1109 
1110 		    dm_descriptor_t slice = (uintptr_t)iter2->obj;
1111 		    dlist_t *bad_slices_on_this_disk = NULL;
1112 
1113 		    /* Is this slice available? */
1114 		    error = check_slice_usage(dsname, slice,
1115 			disk, &avail, &bad_slices_on_this_disk, classes);
1116 
1117 		    /* Is the slice bad (inaccessible)? */
1118 		    if (error != 0 && bad_slices_on_this_disk != NULL) {
1119 			bad_disk = B_TRUE;
1120 			*bad_slices = dlist_append_list(
1121 			    *bad_slices, bad_slices_on_this_disk);
1122 		    }
1123 		}
1124 
1125 		/* Is the disk available? */
1126 		if (error == 0 && bad_disk == B_FALSE && avail == B_TRUE) {
1127 		    error = dlist_append_object(
1128 			(void *)(uintptr_t)disk, usable_disks, AT_TAIL);
1129 		}
1130 
1131 		dlist_free_items(slices, NULL);
1132 	    }
1133 	}
1134 
1135 	dlist_free_items(disks, NULL);
1136 
1137 	if (error == 0) {
1138 	    /* BEGIN CSTYLED */
1139 	    /*
1140 	     * Now reslice usable disks in the local set to
1141 	     * simulate the slices they'll have when they're added
1142 	     * to the named disk set, and add these resulting
1143 	     * virtual slices to the list of available slices.
1144 	     */
1145 	    /* END CSTYLED */
1146 	    error = generate_virtual_slices(*usable_disks, usable_slices);
1147 	}
1148 
1149 	return (error);
1150 }
1151 
1152 /*
1153  * FUNCTION:	generate_virtual_slices(dlist_t *unused, dlist_t **usable)
1154  *
1155  * INPUT:	slice_classes - a list of unused slice dm_descriptor_t handles.
1156  *
1157  * OUTPUT:	usable - pointer to the list of usable slices, updated
1158  *			with any created virtual slices.
1159  *
1160  * RETURNS:	int	- 0 on success
1161  *			 !0 otherwise.
1162  *
1163  * PURPOSE:	Helper which creates virtual slices for each disk which
1164  *		could be added to a diskset if necessary...
1165  *
1166  *		Search the input list of slice classes for the entry
1167  *		containing slices known to be available for use by layout.
1168  *
1169  *		Iterate the list of unused slices and determine the set
1170  *		of unique disks.
1171  *
1172  *		For each unique disk, create virtual slice descriptors to
1173  *		represent those that will exist if/when the disk is added
1174  *		to the diskset.
1175  *
1176  *		Add theese virtual slices to the list of usable slices.
1177  */
1178 static int
1179 generate_virtual_slices(
1180 	dlist_t 	*avail_disks_local_set,
1181 	dlist_t		**usable)
1182 {
1183 	dlist_t	*iter = NULL;
1184 	int	error = 0;
1185 
1186 	/* generate virtual slices */
1187 	error = create_virtual_slices(avail_disks_local_set);
1188 	if (error == 0) {
1189 
1190 	    get_virtual_slices(&iter);
1191 	    for (; (iter != NULL) && (error == 0); iter = iter->next) {
1192 
1193 		dlist_t *item = dlist_new_item((void *) iter->obj);
1194 		if (item == NULL) {
1195 		    error = ENOMEM;
1196 		} else {
1197 		    *usable =
1198 			dlist_insert_ordered(item, *usable,
1199 				ASCENDING, compare_desc_display_names);
1200 		}
1201 	    }
1202 	}
1203 
1204 	return (error);
1205 }
1206 
1207 /*
1208  * FUNCTION:	generate_usable_disks_and_slices_in_named_set(char *dsname,
1209  *			dlist_t **classes, dlist_t **bad_slices,
1210  *			dlist_t **usable_slices, dlist_t **usable_disks)
1211  *
1212  * INPUT:	dsname	- a char * diskset name.
1213  *
1214  * OUTPUT:	classes	- pointer to a list of slice_class_t structs,
1215  *			updated to include slices in the disk set with
1216  *			known uses.
1217  * 		bad_slices - pointer to a list of bad/unusable slices,
1218  *			updated to include slices in the disk set that
1219  *			are inaccessible or no longer existent.
1220  *		usable_slices - pointer to a list of usable slices in the
1221  *			disk set.
1222  *		usable_disks - pointer to a list of usable disks in the
1223  *			disk set.
1224  *
1225  * RETURNS:	int	- 0 on success
1226  *			 !0 otherwise.
1227  *
1228  * PURPOSE:	1. determine the disks in the named disk set
1229  *		2. determine the used slices on the disks
1230  *		3. determine the unused slices on the disks
1231  *		4. look for unused space on the disks and collect it
1232  *		   into an existing unused slice, or create a new
1233  *		   virtual slice.
1234  */
1235 static int
1236 generate_usable_disks_and_slices_in_named_set(
1237 	char		*dsname,
1238 	dlist_t		**classes,
1239 	dlist_t		**bad_slices,
1240 	dlist_t		**usable_disks,
1241 	dlist_t		**usable_slices)
1242 {
1243 	dlist_t		*disks = NULL;
1244 	dlist_t		*iter = NULL;
1245 	int		error = 0;
1246 
1247 	error = get_disks_in_diskset(dsname, &disks);
1248 	if (error != 0) {
1249 	    return (error);
1250 	}
1251 
1252 	/* For each disk... */
1253 	for (iter = disks;
1254 	    iter != NULL && error == 0;
1255 	    iter = iter->next) {
1256 
1257 	    dm_descriptor_t	disk = (uintptr_t)iter->obj;
1258 	    dlist_t		*iter2;
1259 	    dlist_t		*slices = NULL;
1260 	    dlist_t		*bad_slices_on_this_disk = NULL;
1261 	    dlist_t		*used_slices_on_this_disk = NULL;
1262 	    dlist_t		*unused_slices_on_this_disk = NULL;
1263 	    boolean_t		bad_disk = B_FALSE;
1264 
1265 	    error = disk_get_slices(disk, &slices);
1266 	    if (error != 0) {
1267 		break;
1268 	    }
1269 
1270 	    /* Determine the used, unused, and bad slices on the disk */
1271 
1272 	    /* For each slice... */
1273 	    for (iter2 = slices;
1274 		iter2 != NULL && error == 0 && bad_disk == B_FALSE;
1275 		iter2 = iter2->next) {
1276 
1277 		dm_descriptor_t slice = (uintptr_t)iter2->obj;
1278 
1279 		boolean_t	rsvd = B_FALSE;
1280 		boolean_t	avail = B_FALSE;
1281 
1282 		/* Get slice usage */
1283 		if (((error = is_reserved_slice(slice, &rsvd)) == 0) &&
1284 		    ((error = check_slice_usage(dsname, slice, disk, &avail,
1285 			&bad_slices_on_this_disk, classes)) == 0)) {
1286 
1287 		    /* Is the slice bad (inaccessible)? */
1288 		    if (bad_slices_on_this_disk != NULL) {
1289 			*bad_slices = dlist_append_list(
1290 			    *bad_slices, bad_slices_on_this_disk);
1291 			/*
1292 			 * Since one slice on this disk is bad, don't
1293 			 * use any slices on this disk
1294 			 */
1295 			bad_disk = B_TRUE;
1296 		    } else {
1297 
1298 			dlist_t *item =
1299 			    dlist_new_item((void *)(uintptr_t)slice);
1300 			if (item == NULL) {
1301 			    error = ENOMEM;
1302 			} else {
1303 			    /* Add slice to used/unused list as appropriate */
1304 			    if (avail == B_TRUE && rsvd == B_FALSE) {
1305 				unused_slices_on_this_disk = dlist_append(
1306 				    item, unused_slices_on_this_disk, AT_TAIL);
1307 			    } else {
1308 				used_slices_on_this_disk =
1309 				    dlist_insert_ordered(item,
1310 					used_slices_on_this_disk,
1311 					ASCENDING, compare_start_blocks);
1312 			    }
1313 			}
1314 		    }
1315 		}
1316 	    }
1317 
1318 	    /* Done iterating slices */
1319 
1320 	    if (error == 0 && bad_disk == B_FALSE) {
1321 		/* For each unused slice... */
1322 		for (iter2 = unused_slices_on_this_disk;
1323 		    iter2 != NULL && error == 0;
1324 		    iter2 = iter2->next) {
1325 
1326 		    dm_descriptor_t slice = (uintptr_t)iter2->obj;
1327 		    error = update_slice_attributes(slice, 0, 0, 0);
1328 
1329 		    /* Only do this once */
1330 		    if (error == 0 && iter2 == unused_slices_on_this_disk) {
1331 			error = add_modified_disk(NULL, disk);
1332 		    }
1333 		}
1334 
1335 		if (error == 0) {
1336 		    /* Create usable slices from the used/unused slice lists */
1337 		    error = create_usable_slices(disk, used_slices_on_this_disk,
1338 			unused_slices_on_this_disk, usable_slices);
1339 		    if (error == 0) {
1340 			error = dlist_append_object((void *)(uintptr_t)disk,
1341 			    usable_disks, AT_TAIL);
1342 		    }
1343 		}
1344 	    }
1345 
1346 	    dlist_free_items(slices, NULL);
1347 	    dlist_free_items(used_slices_on_this_disk, NULL);
1348 	    dlist_free_items(unused_slices_on_this_disk, NULL);
1349 	}
1350 
1351 	return (error);
1352 }
1353 
1354 /*
1355  * FUNCTION:	create_usable_slices(dm_descriptor_t disk, dlist_t *used,
1356  *			dlist_t *unused, dlist_t **usable);
1357  *
1358  * INPUT:	disk	- a dm_descriptor_t disk handle
1359  *		used	- pointer to a list of pvt_t structs
1360  *			  representing existing used slices
1361  *			  on the input disk.
1362  *		unused	- pointer to a list of pvt_t structs
1363  *			  representing existing unused slices
1364  *			  on the input disk.
1365  *
1366  * OUTPUT:	usable	- pointer to a list of pvts representing slices
1367  *			which can be used for new volume layouts.
1368  *
1369  *			Slices in this list have any available space on the
1370  *			disk collected into the fewest, lowest indexed slices
1371  *			possible.
1372  *
1373  * RETURNS:	int	- 0 on success
1374  *			 !0 otherwise.
1375  *
1376  * PURPOSE:	helper for generate_usable_slices_and_disks_in_diskset() which
1377  *		turns any detected free space on the input disk into one or
1378  *		more slices.
1379  */
1380 static int
1381 create_usable_slices(
1382 	dm_descriptor_t disk,
1383 	dlist_t		*used,
1384 	dlist_t		*unused,
1385 	dlist_t		**usable)
1386 {
1387 	dlist_t		*iter;
1388 	int		error = 0;
1389 	boolean_t	first = B_TRUE;
1390 	dlist_t		*next_unused = unused;
1391 
1392 	char		*dname = NULL;
1393 	uint64_t 	disk_firstblk = 0;
1394 	uint64_t 	disk_nblks = 0;
1395 	uint64_t 	disk_endblk = 0;
1396 
1397 	oprintf(OUTPUT_DEBUG,
1398 		gettext("\n  create_usable_slices for disk\n"));
1399 
1400 	/* get necessary info about disk: */
1401 	error = get_display_name(disk, &dname);
1402 	if (error != 0) {
1403 	    return (error);
1404 	}
1405 
1406 	/* disk start block is first usable block */
1407 	error = disk_get_start_block(disk, &disk_firstblk);
1408 	if (error != 0) {
1409 	    return (error);
1410 	}
1411 
1412 	/* disk size determines last usable disk block */
1413 	error = disk_get_size_in_blocks(disk, &disk_nblks);
1414 	if (error != 0) {
1415 	    return (error);
1416 	}
1417 
1418 	disk_endblk = disk_firstblk + disk_nblks - 1;
1419 
1420 	/* search for gaps before, between and after used slices */
1421 	for (iter = used; iter != NULL && error == 0; iter = iter->next) {
1422 
1423 	    dm_descriptor_t cur = (uintptr_t)iter->obj;
1424 
1425 	    uint64_t cur_stblk = 0;
1426 	    uint64_t cur_nblks = 0;
1427 	    uint64_t cur_endblk = 0;
1428 	    uint32_t cur_index = 0;
1429 
1430 	    uint64_t new_stblk = 0;
1431 	    uint64_t new_endblk = 0;
1432 
1433 	    char *sname = NULL;
1434 	    (void) get_display_name(cur, &sname);
1435 
1436 	    if (((error = slice_get_index(cur, &cur_index)) != 0) ||
1437 		((error = slice_get_start_block(cur, &cur_stblk)) != 0) ||
1438 		((error = slice_get_size_in_blocks(cur, &cur_nblks)) != 0)) {
1439 		continue;
1440 	    }
1441 
1442 	    cur_endblk = cur_stblk + cur_nblks - 1;
1443 
1444 	    oprintf(OUTPUT_DEBUG,
1445 		    gettext("  used slice %d (%10llu to %10llu)\n"),
1446 		    cur_index, cur_stblk, cur_endblk);
1447 
1448 	    if (first == B_TRUE) {
1449 		/* first slice: make sure it starts at disk_firstblk */
1450 		first = B_FALSE;
1451 		if (cur_stblk != disk_firstblk) {
1452 		    /* close gap at beginning of disk */
1453 		    new_stblk = disk_firstblk;
1454 		    new_endblk = cur_stblk - 1;
1455 
1456 		    oprintf(OUTPUT_DEBUG,
1457 			    gettext("    unused space before first "
1458 				    "used slice\n"));
1459 		}
1460 	    }
1461 
1462 	    if (iter->next != NULL) {
1463 		/* check for gap between slices */
1464 		dm_descriptor_t next = (uintptr_t)iter->next->obj;
1465 		uint64_t next_stblk = 0;
1466 		uint32_t next_index = 0;
1467 
1468 		if (((error = slice_get_start_block(next, &next_stblk)) == 0) &&
1469 		    ((error = slice_get_index(next, &next_index)) == 0)) {
1470 		    if (cur_endblk != next_stblk - 1) {
1471 			/* close gap between slices */
1472 			new_stblk = cur_endblk + 1;
1473 			new_endblk = next_stblk - 1;
1474 
1475 			oprintf(OUTPUT_DEBUG,
1476 				gettext("    unused space between slices "
1477 					"%d and %d\n"), cur_index, next_index);
1478 		    }
1479 		}
1480 
1481 	    } else {
1482 		/* last slice: make sure it includes last block on disk */
1483 		if (cur_endblk != disk_endblk) {
1484 		    /* close gap at end of disk */
1485 		    new_stblk = cur_endblk + 1;
1486 		    new_endblk = disk_endblk;
1487 
1488 		    oprintf(OUTPUT_DEBUG,
1489 			    gettext("    unused space after last slice "
1490 				    "cur_endblk: %llu disk_endblk: %llu\n"),
1491 			    cur_endblk, disk_endblk);
1492 		}
1493 	    }
1494 
1495 	    if ((error == 0) && (new_endblk != 0)) {
1496 		error = add_new_usable(disk, new_stblk,
1497 			new_endblk - new_stblk + 1, &next_unused, usable);
1498 	    }
1499 	}
1500 
1501 	if (error != 0) {
1502 	    dlist_free_items(*usable, free);
1503 	    *usable = NULL;
1504 	}
1505 
1506 	return (error);
1507 }
1508 
1509 /*
1510  * FUNCTION:	add_new_usable(dm_descriptor_t disk, uint64_t stblk,
1511  *			uint64_t nblks, dlist_t **next_unused,
1512  *			dlist_t **usable);
1513  *
1514  * INPUT:	disk	- a dm_descriptor_t disk handle
1515  *		stblk	- start block of the usable space
1516  *		nblks	- number of usable blocks
1517  *		next_unused	- pointer to the next unused slice
1518  *
1519  * OUTPUT:	next_unused	- updated pointer to the next unused slice
1520  *		usable	- possibly updated pointer to a list of slices on
1521  *			the disk with usable space
1522  *
1523  * RETURNS:	int	- 0 on success
1524  *			 !0 otherwise.
1525  *
1526  * PURPOSE:	helper for create_usable_slices() which turns free space
1527  *		on the input disk into a usable slice.
1528  *
1529  *		If possible an existing unused slice will be recycled
1530  *		into a usable slice. If there are none, a new virtual
1531  *		slice will be created.
1532  */
1533 static int
1534 add_new_usable(
1535 	dm_descriptor_t disk,
1536 	uint64_t	stblk,
1537 	uint64_t	nblks,
1538 	dlist_t		**next_unused,
1539 	dlist_t		**usable)
1540 {
1541 	dm_descriptor_t new_usable = 0;
1542 	int		error = 0;
1543 
1544 	/* try to use an existing unused slice for the usable slice */
1545 	if (*next_unused != NULL) {
1546 	    new_usable = (uintptr_t)((*next_unused)->obj);
1547 	    *next_unused = (*next_unused)->next;
1548 
1549 	    oprintf(OUTPUT_DEBUG,
1550 		    gettext("\trecyling used slice into usable slice "
1551 			    "start: %llu, end: %llu\n"),
1552 		    stblk, stblk + nblks + 1);
1553 	}
1554 
1555 	if (new_usable == NULL) {
1556 	    /* no unused slices, try to make a new virtual slice */
1557 	    uint32_t index = UINT32_MAX;
1558 	    error = disk_get_available_slice_index(disk, &index);
1559 	    if ((error == 0) && (index != UINT32_MAX)) {
1560 
1561 		char *dname = NULL;
1562 		error = get_display_name(disk, &dname);
1563 		if (error == 0) {
1564 
1565 		    char buf[MAXNAMELEN];
1566 		    (void) snprintf(buf, MAXNAMELEN-1, "%ss%d", dname, index);
1567 		    error = add_virtual_slice(buf, index, 0, 0, disk);
1568 		    if (error == 0) {
1569 			/* retrieve the virtual slice */
1570 			error = slice_get_by_name(buf, &new_usable);
1571 		    }
1572 		}
1573 	    }
1574 	}
1575 
1576 	if ((error == 0) && (new_usable != (dm_descriptor_t)0)) {
1577 	    /* BEGIN CSTYLED */
1578 	    /*
1579 	     * have an unused slice, update its attributes to reflect
1580 	     * the usable space it represents
1581 	     */
1582 	    /* END CSTYLED */
1583 	    uint64_t disk_blksz = 0;
1584 	    error = disk_get_blocksize(disk, &disk_blksz);
1585 	    if (error == 0) {
1586 		error = update_slice_attributes(new_usable, stblk,
1587 		    nblks, nblks * disk_blksz);
1588 		if (error == 0) {
1589 		    error = dlist_append_object(
1590 			(void *)(uintptr_t)new_usable, usable, AT_TAIL);
1591 		}
1592 	    }
1593 	}
1594 
1595 	return (error);
1596 }
1597 
1598 /*
1599  * FUNCTION:	update_slice_attributes(dm_descriptor_t slice, uint64_t stblk,
1600  *			uint64_t nblks, uint64_t nbytes)
1601  *
1602  * INPUT:	slice	- a dm_descriptor_t slice handle
1603  *		stblk	- start block of the usable space
1604  *		nblks	- size of slice in blocks
1605  *		nbytes	- size of slice in bytes
1606  *
1607  * SIDEEFFECT:	adds a modification record for the slice.
1608  *
1609  * RETURNS:	int	- 0 on success
1610  *			 !0 otherwise.
1611  *
1612  * PURPOSE:	utility which updates several slice attributes in one call.
1613  */
1614 static int
1615 update_slice_attributes(
1616 	dm_descriptor_t slice,
1617 	uint64_t	stblk,
1618 	uint64_t	nblks,
1619 	uint64_t	nbytes)
1620 {
1621 	char		*sname = NULL;
1622 	uint32_t	 index = 0;
1623 	int		error = 0;
1624 
1625 	if ((error = get_display_name(slice, &sname)) == 0) {
1626 	    if ((error = slice_get_index(slice, &index)) == 0) {
1627 		if ((error = slice_set_start_block(slice, stblk)) == 0) {
1628 		    if ((error = slice_set_size_in_blocks(slice, nblks)) == 0) {
1629 			if (nblks == 0) {
1630 			    error = add_slice_to_remove(sname, index);
1631 			} else {
1632 			    error = assemble_modified_slice((dm_descriptor_t)0,
1633 				    sname, index, stblk, nblks, nbytes, NULL);
1634 			}
1635 		    }
1636 		}
1637 	    }
1638 	}
1639 
1640 	return (error);
1641 }
1642 
1643 /*
1644  * FUNCTION:	generate_usable_hbas(dlist_t *slices,
1645  *			dlist_t **usable)
1646  *
1647  * INPUT:	disks	- a list of usable disks.
1648  *
1649  * OUTPUT:	usable	- a populated list of usable HBAs.
1650  *
1651  * RETURNS:	int	- 0 on success
1652  *			 !0 otherwise
1653  *
1654  * PURPOSE:	Examines usable disk list and derives the list of usable HBAs.
1655  *
1656  */
1657 static int
1658 generate_usable_hbas(
1659 	dlist_t *disks,
1660 	dlist_t	**usable)
1661 {
1662 	dlist_t	*iter;
1663 	int	error = 0;
1664 
1665 	/*
1666 	 * for each usable disk, follow its HBA connections and
1667 	 * add them to the list of usable HBAs.
1668 	 */
1669 	for (iter = disks; (iter != NULL) && (error == 0); iter = iter->next) {
1670 
1671 	    dm_descriptor_t	dp = NULL;
1672 	    dlist_t 		*hbas = NULL;
1673 	    dlist_t		*iter2 = NULL;
1674 
1675 	    dp = (uintptr_t)iter->obj;
1676 
1677 	    error = disk_get_hbas(dp, &hbas);
1678 	    if (error == 0) {
1679 
1680 		for (iter2 = hbas;
1681 		    (iter2 != NULL) && (error == 0);
1682 		    iter2 = iter2->next) {
1683 
1684 		    dm_descriptor_t	hba = (uintptr_t)iter2->obj;
1685 		    dlist_t		*item = NULL;
1686 
1687 		    /* scan list of usable HBAs and see if known */
1688 		    if (dlist_contains(*usable, (void*)(uintptr_t)hba,
1689 			compare_descriptor_names) == B_TRUE) {
1690 			/* known HBA, continue to next HBA/alias */
1691 			continue;
1692 		    }
1693 
1694 		    /* add this HBA to the usable list */
1695 		    if ((item = dlist_new_item((void *)(uintptr_t)hba)) ==
1696 			NULL) {
1697 			error = ENOMEM;
1698 		    } else {
1699 			*usable =
1700 			    dlist_insert_ordered(item, *usable,
1701 				    ASCENDING, compare_desc_display_names);
1702 		    }
1703 		}
1704 	    }
1705 
1706 	    dlist_free_items(hbas, NULL);
1707 	}
1708 
1709 	return (error);
1710 }
1711 
1712 /*
1713  * FUNCTION:	check_slice_usage(char *dsname, dm_descriptor_t slice,
1714  *			dm_descriptor_t disk, boolean_t *avail,
1715  *			dlist_t **bad, dlist_t **classes)
1716  *
1717  * INPUT:	dsname	- a char * diskset name.
1718  *		slice	- a dm_descriptor_t handle for a known slices.
1719  *		disk	- a dm_descriptor_t handle the slice's disk.
1720  *
1721  * OUTPUT:	avail	- a boolean_t to hold the slice's availability.
1722  *		bad	- pointer to a list of bad/unusable slices,
1723  *				possibly updated if the input slice
1724  *				was determined to be inaccessible.
1725  *		classes	- pointer to a list of slice_class_t structs,
1726  *				possibly updated to include the input slice
1727  *				if it has a known use.
1728  *
1729  * RETURNS:	int	- 0 on success
1730  *			 !0 otherwise.
1731  *
1732  * PURPOSE:	Handles the details of
1733  *		determining usage and/or availability of a single slice.
1734  *
1735  *		Queries the device library for the input slice's detectable
1736  *		usage status.
1737  *
1738  *		If the slice has a detected usage, its name is added to
1739  *		the appropriate slice_class_t list in the input list of
1740  *		slice classes, this is only done if verbose output was
1741  * 		requested.
1742  */
1743 static int
1744 check_slice_usage(
1745 	char		*dsname,
1746 	dm_descriptor_t slice,
1747 	dm_descriptor_t disk,
1748 	boolean_t	*avail,
1749 	dlist_t		**bad,
1750 	dlist_t		**classes)
1751 {
1752 	boolean_t	online = B_FALSE;
1753 	boolean_t	used = B_FALSE;
1754 	nvlist_t	*stats = NULL;
1755 	char		*name = NULL;
1756 	char		*used_by = NULL;
1757 	char		*use_detail = NULL;
1758 	int		error = 0;
1759 
1760 	*avail = B_FALSE;
1761 
1762 	if (((error = get_display_name(slice, &name)) != 0) ||
1763 	    (error = disk_get_is_online(disk, &online))) {
1764 	    return (error);
1765 	}
1766 
1767 	/*
1768 	 * if the disk is known to be offline, skip getting status
1769 	 * for the slice since it will just fail and return ENODEV.
1770 	 */
1771 	if (online != B_TRUE) {
1772 	    error = ENODEV;
1773 	} else {
1774 	    stats = dm_get_stats(slice, DM_SLICE_STAT_USE, &error);
1775 	}
1776 
1777 	if (error != 0) {
1778 	    if (error == ENODEV) {
1779 		dlist_t *item = dlist_new_item((void *)(uintptr_t)slice);
1780 		oprintf(OUTPUT_TERSE,
1781 			gettext("Warning: unable to get slice information "
1782 				"for %s, it will not be used.\n"), name);
1783 
1784 		if (item == NULL) {
1785 		    error = ENOMEM;
1786 		} else {
1787 		    error = 0;
1788 		    *bad = dlist_insert_ordered(item, *bad, ASCENDING,
1789 			    compare_desc_display_names);
1790 		}
1791 	    } else {
1792 		oprintf(OUTPUT_TERSE,
1793 			gettext("check_slice_usage: dm_get_stats for "
1794 				"%s failed %d\n"),
1795 			name, error);
1796 	    }
1797 
1798 	    return (error);
1799 	}
1800 
1801 	/*
1802 	 * check if/how the slice is currently being used,
1803 	 * device library provides this info in the nvpair_t list:
1804 	 *
1805 	 *   stat_type is DM_SLICE_STAT_USE
1806 	 *	used_by:	string (mount, svm, lu, vxvm, fs)
1807 	 *	used_name:	string
1808 	 *
1809 	 */
1810 	if (stats != NULL) {
1811 	    error = get_string(stats, DM_USED_BY, &used_by);
1812 	    if (error != 0) {
1813 		if (error == ENOENT) {
1814 		    used_by = NULL;
1815 		    error = 0;
1816 		} else {
1817 		    oprintf(OUTPUT_TERSE,
1818 			    gettext("check_slice_usage: dm_get_stats.%s for "
1819 				    "%s failed %d\n"),
1820 			    DM_USED_BY, name, error);
1821 		}
1822 	    }
1823 
1824 	    if (error == 0) {
1825 		error = get_string(stats, DM_USED_NAME, &use_detail);
1826 		if (error != 0) {
1827 		    if (error == ENOENT) {
1828 			use_detail = NULL;
1829 			error = 0;
1830 		    } else {
1831 			oprintf(OUTPUT_TERSE,
1832 				gettext("check_slice_usage: "
1833 					"dm_get_stats.%s for "
1834 					"%s failed %d\n"),
1835 					DM_USED_NAME, name, error);
1836 		    }
1837 		}
1838 	    }
1839 	}
1840 
1841 	if ((error == 0) && (used_by != NULL) && (used_by[0] != '\0')) {
1842 
1843 	    /* was detected usage SVM? */
1844 	    if (string_case_compare(used_by, DM_USE_SVM) == 0) {
1845 
1846 		/* check use_detail, it is in the form diskset:name */
1847 		if (strncmp("diskset:", use_detail, 8) == 0) {
1848 
1849 		    /* check disk set name */
1850 		    char *str = strrchr(use_detail, ':');
1851 		    if ((str != NULL) &&
1852 			    (string_case_compare(str+1, dsname) == 0)) {
1853 
1854 			/* slice in the right diskset */
1855 			error = check_svm_slice_usage(
1856 				dsname, slice, disk, &used, classes);
1857 
1858 		    } else {
1859 
1860 			/* slice in other diskset */
1861 			save_slice_classification(
1862 				dsname, slice, disk, used_by, use_detail,
1863 				classes);
1864 			used = B_TRUE;
1865 		    }
1866 
1867 		} else {
1868 
1869 		    /* slice is volume component */
1870 		    save_slice_classification(
1871 			    dsname, slice, disk, used_by, use_detail,
1872 			    classes);
1873 		    used = B_TRUE;
1874 		}
1875 
1876 	    } else {
1877 
1878 		/* save usage */
1879 		save_slice_classification(
1880 			dsname, slice, disk, used_by, use_detail,
1881 			classes);
1882 		used = B_TRUE;
1883 	    }
1884 	}
1885 
1886 	nvlist_free(stats);
1887 
1888 	if (error == 0) {
1889 	    if (used == B_TRUE) {
1890 		*avail = B_FALSE;
1891 	    } else {
1892 		*avail = B_TRUE;
1893 	    }
1894 	}
1895 
1896 	return (error);
1897 }
1898 
1899 /*
1900  * FUNCTION:	check_svm_slice_usage(char *dsname, dm_descriptor_t slice,
1901  *			dm_descriptor_t disk, boolean_t *used,
1902  *			dlist_t **classes)
1903  *
1904  * INPUT:	dsname	- a char * diskset name.
1905  *		slice	- a dm_descriptor_t handle for a known slices.
1906  *		disk	- a dm_descriptor_t handle the slice's disk.
1907  *
1908  * OUTPUT:	used	- a boolean_t to hold the slice usage status.
1909  *		classes	- pointer to a list of slice_class_t possibly updated
1910  *				with the input slice's SVM specific usage
1911  *				classification.
1912  *
1913  * RETURNS:	int	- 0 on success
1914  *			 !0 otherwise.
1915  *
1916  * PURPOSE:	Handles the finer details of
1917  *		a single slice is being used in the context of SVM.
1918  *
1919  *		Currently, one thing is checked:
1920  *
1921  *		1. determine if the slice is reserved for metadb replicas.
1922  *		   The convention for disks in disksets is that a single slice
1923  *		   (index 6 or 7) is set aside for metadb replicas.
1924  *
1925  *		If this condition does not hold, the slice is considered
1926  *		available for use by layout and 'used' is set to B_FALSE.
1927  */
1928 static int
1929 check_svm_slice_usage(
1930 	char		*dsname,
1931 	dm_descriptor_t slice,
1932 	dm_descriptor_t disk,
1933 	boolean_t	*used,
1934 	dlist_t		**classes)
1935 {
1936 	boolean_t is_replica = B_FALSE;
1937 	uint32_t index = 0;
1938 	char	*diskname = NULL;
1939 	int	error = 0;
1940 
1941 	((error = slice_get_index(slice, &index)) != 0) ||
1942 	(error = get_display_name(disk, &diskname)) ||
1943 	(error = is_reserved_replica_slice_index(
1944 		dsname, diskname, index, &is_replica));
1945 
1946 	if (error == 0) {
1947 	    if (is_replica == B_TRUE) {
1948 		/* is replica slice -> used */
1949 		save_slice_classification(dsname, slice, disk, DM_USE_SVM,
1950 			gettext("reserved for metadb replicas"), classes);
1951 		*used = B_TRUE;
1952 	    } else {
1953 		*used = B_FALSE;
1954 	    }
1955 	}
1956 
1957 	return (error);
1958 }
1959 
1960 /*
1961  * FUNCTION:	save_slice_classification(char *dsname, dm_descriptor_t slice,
1962  *			dm_descriptor_t disk, char *used_by, char *usage_detail,
1963  *			dlist_t **classes)
1964  *
1965  * INPUT:	dsname	- a char * disk set name
1966  *		slice	- a dm_descriptor_t slice handle.
1967  *		disk	- a dm_descriptor_t handle for the slice's disk.
1968  *		used_by - a char * usage classification.
1969  *		usage_detail - a char * usage description for the slice.
1970  *
1971  * OUTPUT:	classes	- a list of slice_class_t updated to hold a usage
1972  *				entry for the input slicexs.
1973  *
1974  * SIDEEFFECT:	adds the input slice to the list of known, used slices.
1975  *
1976  * RETURNS:	int	- 0 on success
1977  *			 !0 otherwise.
1978  *
1979  * PURPOSE:	Adds an entry to the
1980  *		appropriate slice_class_t list of slices.  If there is
1981  *		not an appropriate slice_class_t entry in the input list
1982  *		of classes, one is added.
1983  *
1984  *		As a performance optimization the slice usage classification
1985  *		information is only saved if verbose output was requested by
1986  *		the user.
1987  */
1988 static int
1989 save_slice_classification(
1990 	char		*dsname,
1991 	dm_descriptor_t	slice,
1992 	dm_descriptor_t	disk,
1993 	char		*usage,
1994 	char		*usage_detail,
1995 	dlist_t		**classes)
1996 {
1997 	int		error = 0;
1998 
1999 	error = add_used_slice(slice);
2000 
2001 	if ((error == 0) && (get_max_verbosity() >= OUTPUT_VERBOSE)) {
2002 
2003 	    dlist_t		*iter;
2004 	    dlist_t		*item;
2005 	    slice_class_t 	*class = NULL;
2006 
2007 	    /* locate class struct matching 'usage' */
2008 	    for (iter = *classes; iter != NULL; iter = iter->next) {
2009 		class = (slice_class_t *)iter->obj;
2010 		if (string_case_compare(usage, class->usage) == 0) {
2011 		    break;
2012 		}
2013 	    }
2014 
2015 	    if (iter == NULL) {
2016 		/* add a new class to the list of classes */
2017 		class = (slice_class_t *)calloc(1, sizeof (slice_class_t));
2018 		if (class == NULL) {
2019 		    error = ENOMEM;
2020 		} else {
2021 		    class->usage = strdup(usage);
2022 		    if (class->usage == NULL) {
2023 			free(class);
2024 			class = NULL;
2025 			error = ENOMEM;
2026 		    } else {
2027 			item = dlist_new_item((void *)class);
2028 			if (item == NULL) {
2029 			    free(class->usage);
2030 			    free(class);
2031 			    class = NULL;
2032 			    error = ENOMEM;
2033 			} else {
2034 			    *classes = dlist_append(item, *classes, AT_TAIL);
2035 			}
2036 		    }
2037 		}
2038 	    }
2039 
2040 	    if ((error == 0) && (class != NULL)) {
2041 
2042 		char buf[BUFSIZ];
2043 		char *dup = NULL;
2044 		char *slicename = NULL;
2045 
2046 		(void) get_display_name(slice, &slicename);
2047 		(void) snprintf(buf, BUFSIZ-1, "  %s: %s",
2048 			slicename, usage_detail);
2049 		if ((dup = strdup(buf)) == NULL) {
2050 		    error = ENOMEM;
2051 		} else {
2052 		    if ((item = dlist_new_item((void *)dup)) == NULL) {
2053 			free(dup);
2054 			error = ENOMEM;
2055 		    } else {
2056 			class->sliceinfo =
2057 			    dlist_insert_ordered(
2058 				    item, class->sliceinfo,
2059 				    ASCENDING, compare_strings);
2060 		    }
2061 		}
2062 	    }
2063 	}
2064 
2065 	return (error);
2066 }
2067 
2068 /*
2069  * FUNCTION:	print_usable_devices()
2070  *
2071  * PURPOSE:	Print out the devices determined to be available for
2072  *		use by layout.
2073  *
2074  *		Iterates the lists of usable slices, disks and HBAs
2075  *		and prints out their CTD and device names.
2076  */
2077 static void
2078 print_usable_devices()
2079 {
2080 	int	i = 0;
2081 
2082 	struct {
2083 		char *msg;
2084 		dlist_t *list;
2085 	}	devs[3];
2086 
2087 	devs[0].msg = gettext("HBA/Controllers");
2088 	devs[0].list = _usable_hbas;
2089 	devs[1].msg = gettext("disks");
2090 	devs[1].list = _usable_disks;
2091 	devs[2].msg = gettext("slices");
2092 	devs[2].list = _usable_slices;
2093 
2094 	for (i = 0; i < 3; i++) {
2095 
2096 	    oprintf(OUTPUT_VERBOSE,
2097 		    gettext("\n  These %s are usable:\n\n"),
2098 		    devs[i].msg);
2099 
2100 	    print_device_list(devs[i].list);
2101 	}
2102 }
2103 
2104 /*
2105  * FUNCTION:	print_device_list(dlist_t *devices)
2106  *
2107  * INPUT:	devices	- a list of device descriptor handles
2108  *
2109  * PURPOSE:	A helper for the print_XXX_devices() routines which iterates
2110  *		the input list and prints out each device name, CTD name and
2111  *		alias(es).
2112  */
2113 static void
2114 print_device_list(
2115 	dlist_t *devices)
2116 {
2117 	dlist_t *iter = NULL;
2118 
2119 	for (iter = devices; iter != NULL; iter = iter->next) {
2120 
2121 	    dm_descriptor_t device = ((uintptr_t)iter->obj);
2122 	    char	*name = NULL;
2123 	    char	*ctd = NULL;
2124 	    dlist_t	*aliases = NULL;
2125 
2126 	    (void) get_display_name(device, &ctd);
2127 	    (void) get_name(device, &name);
2128 	    oprintf(OUTPUT_VERBOSE,
2129 		    "    %-25s %s\n", (ctd != NULL ? ctd : ""), name);
2130 
2131 	    (void) get_aliases(device, &aliases);
2132 	    for (; aliases != NULL; aliases = aliases->next) {
2133 		oprintf(OUTPUT_VERBOSE,
2134 			gettext("      (alias: %s)\n"),
2135 			(char *)aliases->obj);
2136 	    }
2137 
2138 	    dlist_free_items(aliases, free);
2139 	}
2140 }
2141 
2142 /*
2143  * FUNCTION:	print_unusable_devices(
2144  *			dlist_t *bad_slices, dlist_t *bad_disks,
2145  *			dlist_t	*used_classes)
2146  *
2147  * INPUT:	used_classes - a list of slice_class_t structs
2148  *
2149  * PURPOSE:	Print out the devices determined to be unavailable for
2150  *		use by layout.
2151  *
2152  *		Iterates the input list of slice classifications and prints
2153  *		out a description of the class and the slices so classified.
2154  *
2155  *		Also iterates the lists of bad slices and disks (those that
2156  *		libdiskmgt returned descriptors for but cannot be accessed)
2157  *		and notes them as unusable.
2158  */
2159 static void
2160 print_unusable_devices(
2161 	dlist_t	*bad_slices,
2162 	dlist_t	*bad_disks,
2163 	dlist_t	*used_classes)
2164 {
2165 	dlist_t	*iter = NULL;
2166 	dlist_t	*slices = NULL;
2167 	char	*preamble;
2168 
2169 	struct {
2170 		char *msg;
2171 		dlist_t *list;
2172 	}	devs[2];
2173 
2174 	/* report bad disks and slices */
2175 	devs[0].msg = gettext("disks");
2176 	devs[0].list = bad_disks;
2177 	devs[1].msg = gettext("slices");
2178 	devs[1].list = bad_slices;
2179 
2180 	if (bad_disks != NULL) {
2181 	    oprintf(OUTPUT_VERBOSE,
2182 #if defined(sparc)
2183 		    gettext("\n  These disks are not usable, they may "
2184 			    "may be offline or cannot be accessed:\n\n"));
2185 #elif defined(i386)
2186 		    gettext("\n  These disks are not usable, they may "
2187 			    "may be offline,\n  missing a Solaris FDISK "
2188 			    "partition or cannot be accessed:\n\n"));
2189 #endif
2190 	    print_device_list(bad_disks);
2191 	}
2192 
2193 	if (bad_slices != NULL) {
2194 	    oprintf(OUTPUT_VERBOSE, gettext(
2195 		"\n  These slices, and subsequently the disks on which they\n"
2196 		"reside, are not usable, they cannot be accessed:\n\n"));
2197 	    print_device_list(bad_slices);
2198 	}
2199 
2200 	/* report used slices and usages */
2201 	preamble = gettext("\n  These slices are not usable, %s:\n\n");
2202 	for (iter = used_classes; iter != NULL; iter = iter->next) {
2203 	    slice_class_t *class = (slice_class_t *)iter->obj;
2204 
2205 	    if (class->sliceinfo != NULL) {
2206 
2207 		oprintf(OUTPUT_VERBOSE, preamble,
2208 			get_slice_usage_msg(class->usage));
2209 
2210 		slices = class->sliceinfo;
2211 		for (; slices != NULL; slices = slices->next) {
2212 		    oprintf(OUTPUT_VERBOSE, "  %s\n", (char *)slices->obj);
2213 		}
2214 	    }
2215 	}
2216 
2217 }
2218 
2219 /*
2220  * FUNCTION:	char * get_slice_usage_msg(char *usage)
2221  *
2222  * INPUT:	usage - char * string representing a slice usage classification
2223  *
2224  * OUTPUT:	char * "friendly" usage message
2225  *
2226  * PURPOSE:	the input usage string comes from libdiskmgt and is very terse.
2227  *
2228  *		Convert it into a friendlier usage description suitable for user
2229  *		consumption.
2230  */
2231 static char *
2232 get_slice_usage_msg(
2233 	char *usage)
2234 {
2235 	char *str = NULL;
2236 
2237 	if (string_case_compare(usage, DM_USE_MOUNT) == 0) {
2238 	    str = gettext("they have mounted filesystems");
2239 	} else if (string_case_compare(usage, DM_USE_FS) == 0) {
2240 	    str = gettext("they appear to have unmounted filesystems");
2241 	} else if (string_case_compare(usage, DM_USE_SVM) == 0) {
2242 	    str = gettext("they are utilized by SVM");
2243 	} else if (string_case_compare(usage, DM_USE_VXVM) == 0) {
2244 	    str = gettext("they are utilized by VxVm");
2245 	} else if (string_case_compare(usage, DM_USE_LU) == 0) {
2246 	    str = gettext("they are utilized by LiveUpgrade");
2247 	} else if (string_case_compare(usage, DM_USE_DUMP) == 0) {
2248 	    str = gettext("they are reserved as dump devices");
2249 	} else if (string_case_compare(usage, USE_DISKSET) == 0) {
2250 	    str = gettext("they have disk set issues");
2251 	} else {
2252 	    /* libdiskmgt has detected a usage unknown to layout */
2253 	    str = usage;
2254 	}
2255 
2256 	return (str);
2257 }
2258 
2259 /*
2260  * FUNCTION:	set_alias(dm_descriptor_t desc, char *alias)
2261  *
2262  * INPUT:	desc	- a dm_descriptor_t handle.
2263  *		alias	- a char * alias for the device represented
2264  *				by the descriptor.
2265  *
2266  * RETURNS:	int	- 0 on success
2267  *			 !0 otherwise
2268  *
2269  * PURPOSE:	Adds the specified alias to the known aliases for the
2270  *		device associated with the input descriptor.
2271  */
2272 int
2273 set_alias(
2274 	dm_descriptor_t desc,
2275 	char	*alias)
2276 {
2277 	nvlist_t	*attrs = NULL;
2278 	char		**old_aliases = NULL;
2279 	char		**new_aliases = NULL;
2280 	uint_t		nelem = 0;
2281 	int		error = 0;
2282 	int		i = 0;
2283 
2284 	if ((error = get_cached_attributes(desc, &attrs)) != 0) {
2285 	    return (error);
2286 	}
2287 
2288 	if ((error = get_string_array(
2289 	    attrs, ATTR_DEVICE_ALIASES, &old_aliases, &nelem)) != 0) {
2290 	    if (error != ENOENT) {
2291 		return (error);
2292 	    }
2293 	    /* no aliases yet */
2294 	    error = 0;
2295 	}
2296 
2297 	/* add new alias */
2298 	new_aliases = (char **)calloc(MAX_ALIASES, sizeof (char *));
2299 	if (new_aliases != NULL) {
2300 
2301 	    for (i = 0; i < nelem && i < MAX_ALIASES; i++) {
2302 		char *dup = strdup(old_aliases[i]);
2303 		if (dup != NULL) {
2304 		    new_aliases[i] = dup;
2305 		} else {
2306 		    error = ENOMEM;
2307 		}
2308 	    }
2309 
2310 	    if (error == 0) {
2311 		if (i == MAX_ALIASES) {
2312 		    volume_set_error(
2313 			    gettext("Maximum number of device aliases "
2314 				    "(8) reached\n"),
2315 			    MAX_ALIASES);
2316 		    error = -1;
2317 
2318 		} else {
2319 		    new_aliases[i] = alias;
2320 		    error = set_string_array(attrs, ATTR_DEVICE_ALIASES,
2321 			    new_aliases, i + 1);
2322 		}
2323 	    }
2324 
2325 	    free(new_aliases);
2326 	}
2327 
2328 	if (error == 0) {
2329 	    /* cache descriptor under this alias */
2330 	    error = add_cached_descriptor(alias, desc);
2331 	}
2332 
2333 	return (error);
2334 }
2335 
2336 /*
2337  * FUNCTION:	get_aliases(dm_descriptor_t desc, dlist_t **list)
2338  *
2339  * INPUT:	desc	- a dm_descriptor_t handle.
2340  *
2341  * OUTPUT:	list	- a dlist_t list pointing to the list of
2342  *				aliases associated with the device
2343  *				represented by the descriptor.
2344  *
2345  * RETURNS:	int	- 0 on success
2346  *			 !0 otherwise
2347  *
2348  * PURPOSE:	Retrieves aliases for the input descriptor and
2349  *		appends them to the input list.
2350  *
2351  *		The list of returned items must be freed by calling
2352  *		dlist_free_items(list, free)
2353  */
2354 int
2355 get_aliases(
2356 	dm_descriptor_t desc,
2357 	dlist_t		**list)
2358 {
2359 	nvlist_t	*attrs = NULL;
2360 	char		**aliases = NULL;
2361 	uint_t		nelem = 0;
2362 	int		error = 0;
2363 	int		i;
2364 
2365 	if ((error = get_cached_attributes(desc, &attrs)) != 0) {
2366 	    return (error);
2367 	}
2368 
2369 	if ((error = get_string_array(
2370 	    attrs, ATTR_DEVICE_ALIASES, &aliases, &nelem)) != 0) {
2371 	    if (error == ENOENT) {
2372 		/* no aliases */
2373 		return (0);
2374 	    }
2375 	}
2376 
2377 	for (i = 0; i < nelem; i++) {
2378 	    dlist_t *item;
2379 	    char *dup;
2380 
2381 	    if ((dup = strdup(aliases[i])) == NULL) {
2382 		error = ENOMEM;
2383 		break;
2384 	    }
2385 
2386 	    if ((item = dlist_new_item(dup)) == NULL) {
2387 		free(dup);
2388 		error = ENOMEM;
2389 		break;
2390 	    }
2391 
2392 	    *list = dlist_append(item, *list, AT_TAIL);
2393 	}
2394 
2395 	return (error);
2396 }
2397 
2398 /*
2399  * FUNCTION:	compare_start_blocks(
2400  *			void *obj1, void *obj2)
2401  *
2402  * INPUT:	desc1	- opaque pointer to a dm_descriptor_t
2403  * 		desc2	- opaque pointer to a dm_descriptor_t
2404  *
2405  * RETURNS:	int	- <0 - if desc1.stblk < desc2.stblk
2406  *			   0 - if desc1.stblk == desc2.stblk
2407  *			  >0 - if desc1.stblk > desc.stblk
2408  *
2409  * PURPOSE:	dlist_t helper which compares the start blocks of
2410  *		the two input dm_descriptor_t slice handles.
2411  */
2412 static int
2413 compare_start_blocks(
2414 	void	*desc1,
2415 	void	*desc2)
2416 {
2417 	uint64_t stblk1 = 0;
2418 	uint64_t stblk2 = 0;
2419 
2420 	assert(desc1 != (dm_descriptor_t)0);
2421 	assert(desc2 != (dm_descriptor_t)0);
2422 
2423 	(void) slice_get_start_block((uintptr_t)desc1, &stblk1);
2424 	(void) slice_get_start_block((uintptr_t)desc2, &stblk2);
2425 
2426 	return (stblk1 - stblk2);
2427 }
2428 
2429 /*
2430  * FUNCTION:	compare_desc_display_names(
2431  *			void *desc1, void *desc2)
2432  *
2433  * INPUT:	desc1	- opaque pointer to a dm_descriptor_t
2434  * 		desc2	- opaque pointer to a dm_descriptor_t
2435  *
2436  * RETURNS:	int	- <0 - if desc1.name < desc2.name
2437  *			   0 - if desc1.name == desc2.name
2438  *			  >0 - if desc1.name > desc.name
2439  *
2440  * PURPOSE:	dlist_t helper which compares the CTD names of the
2441  *		two input dm_descriptor_t objects.
2442  */
2443 static int
2444 compare_desc_display_names(
2445 	void	*desc1,
2446 	void	*desc2)
2447 {
2448 	char	*name1 = NULL;
2449 	char	*name2 = NULL;
2450 
2451 	assert(desc1 != (dm_descriptor_t)0);
2452 	assert(desc2 != (dm_descriptor_t)0);
2453 
2454 	(void) get_display_name((uintptr_t)desc1, &name1);
2455 	(void) get_display_name((uintptr_t)desc2, &name2);
2456 
2457 	return (string_case_compare(name1, name2));
2458 }
2459