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 <string.h>
30#include <stdlib.h>
31#include <stdio.h>
32#include <sys/types.h>
33#include <sys/vtoc.h>
34#include <sys/dktp/fdisk.h>
35#include <errno.h>
36#include <meta.h>
37
38#include <libdiskmgt.h>
39#include "meta_repartition.h"
40
41#define	_LAYOUT_DEVICE_UTIL_C
42
43#include "volume_dlist.h"
44#include "volume_error.h"
45#include "volume_output.h"
46#include "volume_nvpair.h"
47
48#include "layout_device_cache.h"
49#include "layout_device_util.h"
50#include "layout_discovery.h"
51#include "layout_dlist_util.h"
52#include "layout_slice.h"
53
54/*
55 *	Macros to produce a quoted string containing the value of a
56 *	preprocessor macro. For example, if SIZE is defined to be 256,
57 *	VAL2STR(SIZE) is "256". This is used to construct format
58 *	strings for scanf-family functions below.
59 */
60#define	QUOTE(x)	#x
61#define	VAL2STR(x)	QUOTE(x)
62
63/* private utilities for disks */
64static int disk_get_uint64_attribute(
65	dm_descriptor_t disk,
66	char		*attr,
67	uint64_t 	*val);
68
69static int disk_get_boolean_attribute(
70	dm_descriptor_t	disk,
71	char		*attr,
72	boolean_t	*bool);
73
74static int disk_get_rpm(
75	dm_descriptor_t disk,
76	uint32_t 	*val);
77
78static int disk_get_sync_speed(
79	dm_descriptor_t disk,
80	uint32_t 	*val);
81
82static int disk_has_virtual_slices(
83	dm_descriptor_t disk,
84	boolean_t 	*bool);
85
86static int disk_get_virtual_slices(
87	dm_descriptor_t disk,
88	dlist_t 	**list);
89
90static int disk_get_reserved_indexes(
91	dm_descriptor_t disk,
92	uint16_t 	**array);
93
94static int disk_get_associated_desc(
95	dm_descriptor_t	disk,
96	dm_desc_type_t	assoc_type,
97	char		*assoc_type_str,
98	dlist_t		**list);
99
100/* utilities for slices */
101static int slice_get_uint64_attribute(
102	dm_descriptor_t slice,
103	char		*attr,
104	uint64_t 	*val);
105
106static int slice_set_attribute(
107	dm_descriptor_t slice,
108	char		*attr,
109	uint64_t 	val);
110
111/*
112 * Virtual slices are created to represent slices that will be
113 * on the system after disks have been added to the destination
114 * diskset.  For the purposes of layout, these slices must
115 * look & function just as real slices that are currently on
116 * the system.
117 */
118static dlist_t	*_virtual_slices = NULL;
119
120/* temporary implementation */
121static int virtual_repartition_drive(
122	dm_descriptor_t disk,
123	mdvtoc_t	*vtocp);
124
125static int disk_add_virtual_slice(
126	dm_descriptor_t disk,
127	dm_descriptor_t slice);
128
129static int virtual_slice_get_disk(
130	dm_descriptor_t slice,
131	dm_descriptor_t *diskp);
132
133/*
134 * attribute names for layout private information stored in
135 * device nvpair attribute lists.
136 */
137static char *ATTR_RESERVED_INDEX = "vdu_reserved_index";
138static char *ATTR_VIRTUAL_SLICES = "vdu_virtual_slices";
139static char *ATTR_DISK_FOR_SLICE = "vdu_disk_for_slice";
140static char *ATTR_DEV_CTD_NAME = "vdu_device_ctd_name";
141static char *ATTR_HBA_N_DISKS = "vdu_hba_n_usable_disks";
142
143/*
144 * FUNCTION:	is_ctd_like_slice_name(char *name)
145 * INPUT:	name	- a char *
146 *
147 * RETURNS:	boolean_t - B_TRUE - if name follows an alternate slice
148 *				naming scheme similar to CTD
149 *			    B_FALSE - otherwise
150 *
151 * PURPOSE:	Determines if the input name is of the form XXXsNNN
152 *		(e.g., whizzy0s1)
153 */
154boolean_t
155is_ctd_like_slice_name(
156	char *name)
157{
158	uint_t		s = 0;
159	uint_t		d = 0;
160	int		l = 0;
161	boolean_t	is = B_FALSE;
162
163	/* The format strings below match and discard the non-numeric part. */
164	if ((sscanf(name, "/dev/dsk/%*[^0-9/]%us%u%n", &d, &s, &l) == 2 ||
165	    sscanf(name, "/dev/rdsk/%*[^0-9/]%us%u%n", &d, &s, &l) == 2 ||
166	    sscanf(name, "%*[^0-9/]%us%u%n", &d, &s, &l) == 2) &&
167		(l == strlen(name))) {
168	    is = B_TRUE;
169	}
170
171	return (is);
172}
173
174/*
175 * FUNCTION:	is_bsd_like_slice_name(char *name)
176 * INPUT:	name	- a char *
177 *
178 * RETURNS:	boolean_t - B_TRUE - if name follows an alternate slice
179 *				BSD-like naming scheme
180 *			    B_FALSE - otherwise
181 *
182 * PURPOSE:	Determines if the input name is of the form XXXNNN[a-h]
183 *			(e.g., whizzy0a)
184 */
185boolean_t
186is_bsd_like_slice_name(
187	char *name)
188{
189	uint_t		d = 0;
190	int		l = 0;
191	boolean_t	is = B_FALSE;
192
193	/* The format strings below match and discard the non-numeric part. */
194	if ((sscanf(name, "/dev/dsk/%*[^0-9/]%u%*[a-h]%n", &d, &l) == 1 ||
195	    sscanf(name, "/dev/rdsk/%*[^0-9/]%u%*[a-h]%n", &d, &l) == 1 ||
196	    sscanf(name, "%*[^0-9/]%u%*[a-h]%n", &d, &l) == 1) &&
197		(l == strlen(name))) {
198	    is = B_TRUE;
199	}
200
201	return (is);
202}
203
204/*
205 * FUNCTION:	is_did_name(char *name)
206 * INPUT:	name	- a char *
207 *
208 * RETURNS:	boolean_t - B_TRUE - if name is from the DID namespace
209 *			    B_FALSE - otherwise
210 *
211 * PURPOSE:	Determines if the input name is from the DID namespace.
212 */
213boolean_t
214is_did_name(
215	char *name)
216{
217	return (is_did_slice_name(name) || is_did_disk_name(name));
218}
219
220/*
221 * FUNCTION:	is_did_slice_name(char *name)
222 * INPUT:	name	- a char *
223 *
224 * RETURNS:	boolean_t - B_TRUE - if name represents a slice from the DID
225 *					namespace
226 *			    B_FALSE - otherwise
227 *
228 * PURPOSE:	Determines if the input name is a slice from the DID namespace.
229 */
230boolean_t
231is_did_slice_name(
232	char *name)
233{
234	uint_t		d = 0, s = 0;
235	int		l = 0;
236	boolean_t	is = B_FALSE;
237
238	if ((sscanf(name, "/dev/did/rdsk/d%us%u%n", &d, &s, &l) == 2 ||
239		sscanf(name, "/dev/did/dsk/d%us%u%n", &d, &s, &l) == 2 ||
240		sscanf(name, "d%us%u%n", &d, &s, &l) == 2) ||
241		(l == strlen(name))) {
242	    is = B_TRUE;
243	}
244
245	return (is);
246}
247
248/*
249 * FUNCTION:	is_did_disk_name(char *name)
250 * INPUT:	name	- a char *
251 *
252 * RETURNS:	boolean_t - B_TRUE - if name represents a disk from the DID
253 *					namespace
254 *			    B_FALSE - otherwise
255 *
256 * PURPOSE:	Determines if the input name is a disk from the DID namespace.
257 */
258boolean_t
259is_did_disk_name(
260	char *name)
261{
262	uint_t		d = 0;
263	int		l = 0;
264	boolean_t	is = B_FALSE;
265
266	if ((sscanf(name, "/dev/did/rdsk/d%u%n", &d, &l) == 1 ||
267		sscanf(name, "/dev/did/dsk/d%u%n", &d, &l) == 1 ||
268		sscanf(name, "d%u%n", &d, &l) == 1) &&
269		(l == strlen(name))) {
270	    is = B_TRUE;
271	}
272
273	return (is);
274}
275
276/*
277 * FUNCTION:	is_ctd_name(char *name)
278 * INPUT:	name	- a char *
279 *
280 * RETURNS:	boolean_t - B_TRUE - if name is from the CTD namespace
281 *			    B_FALSE - otherwise
282 *
283 * PURPOSE:	Determines if the input name is from the CTD namespace.
284 *
285 *		{/dev/dsk/, /dev/rdsk/}cXtXdXsX
286 *		{/dev/dsk/, /dev/rdsk/}cXtXdX
287 *		{/dev/dsk/, /dev/rdsk/}cXdXsX
288 *		{/dev/dsk/, /dev/rdsk/}cXdX
289 */
290boolean_t
291is_ctd_name(
292	char *name)
293{
294	return (is_ctd_slice_name(name) || is_ctd_disk_name(name) ||
295		is_ctd_target_name(name) || is_ctd_ctrl_name(name));
296}
297
298/*
299 * FUNCTION:	is_ctd_slice_name(char *name)
300 * INPUT:	name	- a char *
301 *
302 * RETURNS:	boolean_t - B_TRUE - if name represents a slice from the CTD
303 *				namespace
304 *			    B_FALSE - otherwise
305 *
306 * PURPOSE:	Determines if the input name is a slice name from the
307 *		CTD namespace.
308 *
309 *		{/dev/dsk/, /dev/rdsk/}cXt<WWN>dXsX
310 *		{/dev/dsk/, /dev/rdsk/}cXtXdXsX
311 *		{/dev/dsk/, /dev/rdsk/}cXdXsX
312 */
313boolean_t
314is_ctd_slice_name(
315    char *name)
316{
317	uint_t		c = 0, t = 0, d = 0, s = 0;
318	char		buf[MAXNAMELEN+1];
319	int		l = 0;
320	boolean_t	is = B_FALSE;
321
322	if ((sscanf(name, "/dev/dsk/c%ut%ud%us%u%n", &c, &t, &d, &s, &l) == 4 ||
323	    sscanf(name, "/dev/rdsk/c%ut%ud%us%u%n", &c, &t, &d, &s, &l) == 4 ||
324	    sscanf(name, "c%ut%ud%us%u%n", &c, &t, &d, &s, &l) == 4 ||
325	    sscanf(name, "/dev/dsk/c%ud%us%u%n", &c, &d, &s, &l) == 3 ||
326	    sscanf(name, "/dev/rdsk/c%ud%us%u%n", &c, &d, &s, &l) == 3 ||
327	    sscanf(name, "c%ud%us%u%n", &c, &d, &s, &l) == 3 ||
328	    sscanf(name, "c%ud%us%u%n", &c, &d, &s, &l) == 2) &&
329		(l == strlen(name))) {
330	    is = B_TRUE;
331	} else if (
332	    (sscanf(name, "/dev/dsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n",
333		&c, buf, &l) == 2 ||
334	    sscanf(name, "/dev/rdsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n",
335		&c, buf, &l) == 2 ||
336	    sscanf(name, "c%ut%" VAL2STR(MAXNAMELEN) "s%n",
337		&c, buf, &l) == 2) && (l == strlen(name))) {
338	    char *dev_pos;
339
340	    /* see if buf ends with "dXsX" */
341	    if (((dev_pos = strrchr(buf, 'd')) != NULL) &&
342		(sscanf(dev_pos, "d%us%u%n", &d, &s, &l) == 2) &&
343		(l == strlen(dev_pos))) {
344
345		char wwn[MAXNAMELEN+2];
346
347		/* buf ends with "dXsX", truncate at the 'd' */
348		*dev_pos = '\0';
349
350		/* prepend "0X" to remainder and try to scan as a hex WWN */
351		(void) snprintf(wwn, sizeof (wwn), "%s%s", "0X", buf);
352		if ((sscanf(wwn, "%x%n", &t, &l) == 1) && (l == strlen(wwn))) {
353		    is = B_TRUE;
354		}
355	    }
356	}
357
358	return (is);
359}
360
361/*
362 * FUNCTION:	is_ctd_disk_name(char *name)
363 * INPUT:	name	- a char *
364 *
365 * RETURNS:	boolean_t - B_TRUE - if name represents a disk from the CTD
366 *				namespace
367 *			    B_FALSE - otherwise
368 *
369 * PURPOSE:	Determines if the input name is a disk name from the
370 *		CTD namespace.
371 *
372 *		{/dev/dsk/, /dev/rdsk/}cXt<WWN>dX
373 *		{/dev/dsk/, /dev/rdsk/}cXtXdX
374 *		{/dev/dsk/, /dev/rdsk/}cXdX
375 */
376boolean_t
377is_ctd_disk_name(
378    char *name)
379{
380	uint_t		c = 0, t = 0, d = 0;
381	int		l = 0;
382	char		buf[MAXNAMELEN+1];
383	boolean_t	is = B_FALSE;
384
385	if ((sscanf(name, "/dev/dsk/c%ut%ud%u%n", &c, &t, &d, &l) == 3 ||
386	    sscanf(name, "/dev/rdsk/c%ut%ud%u%n", &c, &t, &d, &l) == 3 ||
387	    sscanf(name, "c%ut%ud%u%n", &c, &t, &d, &l) == 3 ||
388	    sscanf(name, "/dev/dsk/c%ud%u%n", &c, &d, &l) == 2 ||
389	    sscanf(name, "/dev/rdsk/c%ud%n%n", &c, &d, &l) == 2 ||
390	    sscanf(name, "c%ud%u%n", &c, &d, &l) == 2) &&
391		(l == strlen(name))) {
392	    is = B_TRUE;
393	} else if ((sscanf(name, "/dev/dsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n",
394	    &c, buf, &l) == 2 ||
395	    sscanf(name, "/dev/rdsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n",
396		&c, buf, &l) == 2 ||
397	    sscanf(name, "c%ut%" VAL2STR(MAXNAMELEN) "s%n",
398		&c, buf, &l) == 2) && (l == strlen(name))) {
399	    char *dev_pos;
400
401	    /* see if buf ends with "dX" */
402	    if (((dev_pos = strrchr(buf, 'd')) != NULL) &&
403		(sscanf(dev_pos, "d%u%n", &d, &l) == 1) &&
404		(l == strlen(dev_pos))) {
405
406		char wwn[MAXNAMELEN+2];
407
408		/* buf ends with "dX", truncate at the 'd' */
409		*dev_pos = '\0';
410
411		/* prepend "0X" to remainder and try to scan as a hex WWN */
412		(void) snprintf(wwn, sizeof (wwn), "%s%s", "0X", buf);
413		if ((sscanf(wwn, "%x%n", &t, &l) == 1) && (l == strlen(wwn))) {
414		    is = B_TRUE;
415		}
416	    }
417	}
418
419	return (is);
420}
421
422/*
423 * FUNCTION:	is_ctd_disk_name(char *name)
424 * INPUT:	name	- a char *
425 *
426 * RETURNS:	boolean_t - B_TRUE - if name represents a target from the CTD
427 *				namespace
428 *			    B_FALSE - otherwise
429 *
430 * PURPOSE:	Determines if the input name is a target name from the
431 *		CTD namespace.
432 *
433 *		{/dev/dsk/, /dev/rdsk/}cXt<WWN>
434 *		{/dev/dsk/, /dev/rdsk/}cXtX
435 */
436boolean_t
437is_ctd_target_name(
438    char *name)
439{
440	uint_t		c = 0, t = 0;
441	int		l = 0;
442	char		buf[MAXNAMELEN+1];
443	boolean_t	is = B_FALSE;
444
445	if ((sscanf(name, "/dev/dsk/c%ut%u%n", &c, &t, &l) == 2 ||
446	    sscanf(name, "/dev/rdsk/c%ut%u%n", &c, &t, &l) == 2 ||
447	    sscanf(name, "c%ut%u%n", &c, &t, &l) == 2) &&
448		(l == strlen(name))) {
449	    is = B_TRUE;
450	} else if (
451	    (sscanf(name, "/dev/dsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n",
452		&c, buf, &l) == 2 ||
453	    sscanf(name, "/dev/rdsk/c%ut%" VAL2STR(MAXNAMELEN) "s%n",
454		&c, buf, &l) == 2 ||
455	    sscanf(name, "c%ut%" VAL2STR(MAXNAMELEN) "s%n",
456		&c, &buf, &l) == 2) && (l == strlen(name))) {
457
458	    char wwn[MAXNAMELEN+2];
459
460	    /* prepend "0X" to buf and try to scan as a hex WWN */
461	    (void) snprintf(wwn, sizeof (wwn), "%s%s", "0X", buf);
462	    if ((sscanf(wwn, "%x%n", &t, &l) == 1) && (l == strlen(wwn))) {
463		is = B_TRUE;
464	    }
465	}
466
467	return (is);
468}
469
470/*
471 * FUNCTION:	is_ctd_ctrl_name(char *name)
472 * INPUT:	name	- a char *
473 *
474 * RETURNS:	boolean_t - B_TRUE - if name represents a controller/hba
475 *				from the CTD namespace
476 *			    B_FALSE - otherwise
477 *
478 * PURPOSE:	Determines if the input name is an HBA name from the
479 *		CTD namespace.
480 *
481 *		{/dev/dsk/, /dev/rdsk/}cX
482 */
483boolean_t
484is_ctd_ctrl_name(
485	char	*name)
486{
487	uint_t		c = 0;
488	int		l = 0;
489	boolean_t	is = B_FALSE;
490
491	if ((sscanf(name, "/dev/dsk/c%u%n", &c, &l) == 1 ||
492	    sscanf(name, "/dev/rdsk/c%u%n", &c, &l) == 1 ||
493	    sscanf(name, "c%u%n", &c, &l) == 1) &&
494		(l == strlen(name))) {
495	    is = B_TRUE;
496	}
497
498	return (is);
499}
500
501/*
502 * FUNCTION:	set_display_name(dm_descriptor_t desc, char *name)
503 *		get_display_name(dm_descriptor_t desc, char **name)
504 *
505 * INPUT:	desc	- a dm_descriptor_t handle for a device
506 *		name    - a char * name
507 *
508 * OUTPUT:	**name	- a pointer to a char * to hold the display
509 *			name associated with the input descriptor.
510 *
511 * RETURNS:	int	- 0 on success
512 *			 !0 otherwise.
513 *
514 * PURPOSE:	Helpers to set/get the input descriptor's display name.
515 *
516 *		Only slices, disks and HBAs should have display names.
517 *
518 *		The attribute is only set in the cached copy of
519 *		the device's nvpair attribute list.  This function
520 *		does not affect the underlying physical device.
521 *
522 *		An entry is added in the name->descriptor cache
523 *		so the descriptor can be found by name quickly.
524 */
525int
526set_display_name(
527	dm_descriptor_t	desc,
528	char		*name)
529{
530	nvlist_t	*attrs	= NULL;
531	int		error	= 0;
532
533	((error = add_cached_descriptor(name, desc)) != 0) ||
534	(error = get_cached_attributes(desc, &attrs)) ||
535	(error = set_string(attrs, ATTR_DEV_CTD_NAME, name));
536
537	return (error);
538}
539
540int
541get_display_name(
542	dm_descriptor_t	desc,
543	char		**name)
544{
545	nvlist_t	*attrs	= NULL;
546	int		error	= 0;
547
548	((error = get_cached_attributes(desc, &attrs)) != 0) ||
549	(error = get_string(attrs, ATTR_DEV_CTD_NAME, name));
550
551	return (error);
552}
553
554/*
555 * FUNCTION:	disk_get_slices(dm_descriptor_t disk, dlist_t **list)
556 *
557 * INPUT:	disk	- a dm_descriptor_t handle for a disk
558 *
559 * OUTPUT:	*list	- a pointer to list to hold the results.
560 *
561 * RETURNS:	int	- 0 on success
562 *			 !0 otherwise.
563 *
564 * PURPOSE:	Collect all of the known slices for the input disk.
565 *
566 *		These slices may be actual slices which currently exist
567 *		on the disk, or virtual slices which will exist when the
568 *		disk is added to the destination diskset.
569 */
570int
571disk_get_slices(
572	dm_descriptor_t disk,
573	dlist_t		**list)
574{
575	dm_descriptor_t	*media = NULL;
576	boolean_t	virtual = B_FALSE;
577	int		i = 0;
578	int		error = 0;
579
580	*list = 0;
581
582	if ((error = disk_has_virtual_slices(disk, &virtual)) != 0) {
583	    return (error);
584	}
585
586	if (virtual == B_TRUE) {
587	    error = disk_get_virtual_slices(disk, list);
588	}
589
590	/* add real slices from disk's media... */
591	media = dm_get_associated_descriptors(disk, DM_MEDIA, &error);
592	(void) add_descriptors_to_free(media);
593
594	if (error == 0) {
595	    /* if there's no media, this is a removeable drive */
596	    if (media != NULL && *media != NULL) {
597
598		/* examine media's slices... */
599		dm_descriptor_t	*slices = NULL;
600		slices = dm_get_associated_descriptors(*media,
601			DM_SLICE, &error);
602		(void) add_descriptors_to_free(slices);
603
604		if (error != 0) {
605		    print_get_assoc_desc_error(disk, gettext("slice"), error);
606		} else {
607		    for (i = 0; (slices[i] != NULL) && (error == 0); i++) {
608			dlist_t *item =
609			    dlist_new_item((void *)(uintptr_t)slices[i]);
610			if (item == NULL) {
611			    error = ENOMEM;
612			} else {
613			    *list = dlist_append(item, *list, AT_TAIL);
614			}
615		    }
616		    free(slices);
617		}
618		free(media);
619	    }
620	} else {
621	    print_get_assoc_desc_error(disk, gettext("media"), error);
622	}
623
624	return (error);
625}
626
627int
628get_virtual_slices(
629	dlist_t **list)
630{
631	*list = _virtual_slices;
632
633	return (0);
634}
635
636/*
637 * FUNCTION:	virtual_repartition_drive(dm_descriptor_t disk,
638 *			mdvtoc_t *vtocp)
639 *
640 * INPUT:	disk	- the disk to be virtually repartitioned
641 *
642 * OUTPUT:	vtocp	- a poitner to a mdvtoc struct to hold the results
643 *
644 * RETURNS:	int	- 0 on success
645 *			 !0 otherwise.
646 *
647 * PURPOSE:	Helper which emulates the repartitioning that is done
648 *		when a disk is added to a diskset.
649 *
650 *		Modified version of meta_partition_drive which uses info
651 * 		from libdiskmgt to accomplish the repartitioning.
652 *
653 * 		This exists to allow the layout module to run with a
654 *		simulated hardware environment.
655 *
656 *		XXX This method absolutely does not produce the exact
657 *		same result as meta_repartition_drive: only information
658 *		required by the layout code is returned.  Basically,
659 *		a slice 7 (or 6 on EFI labelled disks) is created and
660 *		sized, the remained of the available cylinders are put
661 *		into slice 0.
662 *
663 *		XXX2 This method is required until there is resolution
664 *		on whether metassist testing will be done using the
665 *		hardware simulation mechanism libdiskmgt provides.
666 *		Doing so will also require parts of libmeta to be
667 *		simulated as well.  Some research has been done into
668 *		building an alternate libmeta.so containing
669 *		implementations of the functions used by metassist
670 *		that are compatible with the simulated hardware.
671 *		Actual work is currently on hold.
672 */
673static int
674virtual_repartition_drive(
675	dm_descriptor_t	disk,
676	mdvtoc_t	*vtocp)
677{
678	uint_t		replicaslice = 7;
679	unsigned long long cylsize;
680	unsigned long long drvsize;
681	uint_t		reservedcyl;
682	ushort_t	resflag;
683	unsigned long long ressize;
684	diskaddr_t	replica_start;
685	diskaddr_t	replica_size;
686	diskaddr_t	data_start;
687	diskaddr_t	data_size;
688
689	boolean_t	efi = B_FALSE;
690	uint64_t	ncyls = 0;
691	uint64_t	nheads = 0;
692	uint64_t	nsects = 0;
693	int		error = 0;
694
695	/*
696	 * At this point, ressize is used as a minimum value.  Later it
697	 * will be rounded up to a cylinder boundary.  ressize is in
698	 * units of disk sectors.
699	 */
700	ressize = MD_DBSIZE + VTOC_SIZE;
701	resflag = V_UNMNT;
702
703	((error = disk_get_is_efi(disk, &efi)) != 0) ||
704	(error = disk_get_ncylinders(disk, &ncyls)) ||
705	(error = disk_get_nheads(disk, &nheads)) ||
706	(error = disk_get_nsectors(disk, &nsects));
707	if (error != 0) {
708	    return (error);
709	}
710
711	if (efi) {
712	    replicaslice = 6;
713	}
714
715	/*
716	 * Both cylsize and drvsize are in units of disk sectors.
717	 *
718	 * The intended results are of type unsigned long long.  Since
719	 * each operand of the first multiplication is of type
720	 * unsigned int, we risk overflow by multiplying and then
721	 * converting the result.  Therefore we explicitly cast (at
722	 * least) one of the operands, forcing conversion BEFORE
723	 * multiplication, and avoiding overflow.  The second
724	 * assignment is OK, since one of the operands is already of
725	 * the desired type.
726	 */
727	cylsize = ((unsigned long long)nheads) * nsects;
728	drvsize = cylsize * ncyls;
729
730	/*
731	 * How many cylinders must we reserve for slice seven to
732	 * ensure that it meets the previously calculated minimum
733	 * size?
734	 */
735	reservedcyl = (ressize + cylsize - 1) / cylsize;
736
737	/*
738	 * It seems unlikely that someone would pass us too small a
739	 * disk, but it's still worth checking for...
740	 */
741	if (reservedcyl >= ncyls) {
742	    volume_set_error(
743		    gettext("disk is too small to hold a metadb replica"));
744	    return (-1);
745	}
746
747	replica_start = 0;
748	replica_size = reservedcyl * cylsize;
749	data_start = reservedcyl * cylsize;
750	data_size = drvsize - (reservedcyl * cylsize);
751
752	/*
753	 * fill in the proposed VTOC information.
754	 */
755
756	/* We need at least replicaslice partitions in the proposed vtoc */
757	vtocp->nparts = replicaslice + 1;
758	vtocp->parts[MD_SLICE0].start = data_start;
759	vtocp->parts[MD_SLICE0].size = data_size;
760	vtocp->parts[MD_SLICE0].tag = V_USR;
761	vtocp->parts[replicaslice].start = replica_start;
762	vtocp->parts[replicaslice].size = replica_size;
763	vtocp->parts[replicaslice].flag = resflag;
764	vtocp->parts[replicaslice].tag = V_USR;
765
766	return (0);
767}
768
769/*
770 * FUNCTION:	create_virtual_slices(dlist_t *disks)
771 *
772 * INPUT:	possibles - a list of dm_descriptor_t disk handles for
773 *			disks known to be available for use by layout.
774 *
775 * SIDEEFFECT:	populates the private of virtual slices.
776 *
777 * RETURNS:	int	- 0 on success
778 *			 !0 otherwise.
779 *
780 * PURPOSE:	Helper which creates virtual slices for each disk which
781 *		could be added to a diskset if necessary...
782 *
783 *		Iterate the input list of available disks and see what the
784 *		slicing would be if the disk were added to a diskset.
785 *
786 *		For the resulting slices, create virtual slice descriptors
787 *		and attributes for these slices and add them to the list of
788 *		available slices.
789 */
790int
791create_virtual_slices(
792	dlist_t 	*disks)
793{
794	int		error = 0;
795	dlist_t		*iter;
796	boolean_t	sim = B_FALSE;
797	static char	*simfile = "METASSISTSIMFILE";
798
799	sim = ((getenv(simfile) != NULL) && (strlen(getenv(simfile)) > 0));
800
801	/* see what slices each of the disks will have when added to a set */
802	for (iter = disks; error == 0 && iter != NULL; iter = iter->next) {
803
804	    dm_descriptor_t 	disk = (uintptr_t)iter->obj;
805	    dlist_t		*slices = NULL;
806	    mdvtoc_t		vtoc;
807	    char		*dname;
808	    int			i = 0;
809
810	    if ((error = get_display_name(disk, &dname)) != 0) {
811		break;
812	    }
813
814	    if (sim != B_TRUE) {
815
816		/* sim disabled: use meta_repartition_drive() */
817
818		md_error_t	mderror = mdnullerror;
819		int		opts = (MD_REPART_FORCE | MD_REPART_DONT_LABEL);
820		mdsetname_t	*sp;
821		mddrivename_t	*dnp;
822
823		/* disk is in the local set */
824		sp = metasetname(MD_LOCAL_NAME, &mderror);
825		if (!mdisok(&mderror)) {
826		    volume_set_error(mde_sperror(&mderror, NULL));
827		    mdclrerror(&mderror);
828		    error = -1;
829		    break;
830		}
831
832		dnp = metadrivename(&sp, dname, &mderror);
833		if (!mdisok(&mderror)) {
834		    volume_set_error(mde_sperror(&mderror, NULL));
835		    mdclrerror(&mderror);
836		    error = -1;
837		    break;
838		}
839
840		if (meta_repartition_drive(
841		    sp, dnp, opts, &vtoc, &mderror) != 0) {
842		    volume_set_error(
843			    gettext("failed to repartition disk %s\n"),
844			    dname);
845		    error = -1;
846		    break;
847		}
848
849	    } else {
850
851		/* sim enabled: use faked repartition code */
852		if (virtual_repartition_drive(disk, &vtoc) != 0) {
853		    volume_set_error(
854			    gettext("failed simulated repartition of %s\n"),
855			    dname);
856		    error = -1;
857		    break;
858		}
859	    }
860
861	    /* BEGIN CSTYLED */
862	    /*
863	     * get the existing slices on the disk, if the repartition
864	     * was successful, these slices need to have their size, start
865	     * blk and size in blks set to 0
866	     */
867	    /* END CSTYLED */
868	    if ((error = disk_get_slices(disk, &slices)) == 0) {
869		dlist_t *iter2 = slices;
870		for (; iter2 != NULL; iter2 = iter2->next) {
871		    dm_descriptor_t sp = (uintptr_t)iter2->obj;
872		    ((error = slice_set_start_block(sp, 0)) != 0) ||
873		    (error = slice_set_size_in_blocks(sp, 0)) ||
874		    (error = slice_set_size(sp, 0));
875		}
876		dlist_free_items(slices, NULL);
877	    }
878
879	    /* scan VTOC, find slice with the free space */
880	    for (i = 0; i < vtoc.nparts; i++) {
881
882		if (vtoc.parts[i].tag == V_USR &&
883			vtoc.parts[i].flag != V_UNMNT) {
884
885		    /* non-replica slice with free space */
886		    char buf[MAXPATHLEN];
887		    (void) snprintf(buf, MAXPATHLEN-1, "%ss%d", dname, i);
888
889		    if ((error = add_virtual_slice(buf,
890			(uint32_t)i,
891			(uint64_t)vtoc.parts[i].start,
892			(uint64_t)vtoc.parts[i].size,
893			disk)) != 0) {
894			break;
895		    }
896
897		} else if (vtoc.parts[i].tag == V_RESERVED) {
898
899		    /* skip EFI reserved slice */
900		    continue;
901
902		} else if (vtoc.parts[i].tag == V_USR &&
903			vtoc.parts[i].flag == V_UNMNT) {
904
905		    /* BEGIN CSTYLED */
906		    /*
907		     * Make the replica slice 0 sized -- this will
908		     * force the disk to be repartitioned by
909		     * metaset when it is added to the disk set.
910		     *
911		     * XXX this is a temporary workaround until
912		     * 4712873 is integrated...
913		     */
914		    /* BEGIN CSTYLED */
915		    char buf[MAXPATHLEN];
916		    (void) snprintf(buf, MAXPATHLEN-1, "%ss%d", dname, i);
917		    add_slice_to_remove(buf, i);
918
919		    /* replica slice, stop here */
920		    break;
921		}
922	    }
923	}
924
925	return (error);
926}
927
928/*
929 * FUNCTION:	add_virtual_slice(char *name, uint32_t index,
930 *			uint64_t startblk, uint64_t sizeblks,
931 *			dm_descriptor_t disk)
932 *
933 * INPUT:	name	- the name of the new virtual slice
934 *		index	- the VTOC index ...
935 *		startblk - the start block ...
936 *		sizeblks - the size in blocks ...
937 *		disk	- the parent disk ...
938 *
939 * RETURNS:	int	- 0 on success
940 *			 !0 otherwise.
941 *
942 * PURPOSE:	Helper which adds the appropriate data structures to
943 *		represent a new virtual slice.
944 *
945 *		allocates a new descriptor
946 *		adds entries to name->desc and desc->name caches
947 *		allocates an attribute nvpair list
948 *		fills in the relevant attributes for the slice
949 *		associates the slice with its parent disk
950 *		adds an entry to the list of all virtual slices
951 *		generates aliases if the associated disk has aliases.
952 */
953int
954add_virtual_slice(
955	char		*name,
956	uint32_t	index,
957	uint64_t	startblk,
958	uint64_t	sizeblks,
959	dm_descriptor_t	disk)
960{
961	dm_descriptor_t sp;
962	nvlist_t	*attrs;
963	char		*sname;
964	dlist_t		*aliases = NULL;
965	dlist_t		*item = NULL;
966	int 		error = 0;
967
968	if ((error = nvlist_alloc(&attrs, NV_UNIQUE_NAME, 0)) != 0) {
969	    return (error);
970	}
971
972	/* create descriptor */
973	((error = new_descriptor(&sp)) != 0) ||
974	/* cache name for the descriptor */
975	(error = add_cached_name(sp, name)) ||
976	/* cache descriptor for the name */
977	(error = add_cached_descriptor(name, sp)) ||
978
979	/* fill in attributes */
980	(error = set_string(attrs, ATTR_DEV_CTD_NAME, name)) ||
981	(error = set_uint32(attrs, DM_INDEX, index)) ||
982	(error = set_uint64(attrs, DM_START, startblk)) ||
983	(error = set_uint64(attrs, DM_SIZE, sizeblks)) ||
984	(error = set_uint64(attrs, ATTR_DISK_FOR_SLICE, (uint64_t)disk)) ||
985
986	/* add attributes to the cache */
987	(error = get_name(sp, &sname)) ||
988	(error = add_cached_attributes(sname, attrs)) ||
989
990	/* connect slice to disk */
991	(error = disk_add_virtual_slice(disk, sp)) ||
992	(error = get_display_name(disk, &name)) ||
993	(error = get_aliases(disk, &aliases));
994
995	if (error != 0) {
996	    return (error);
997	}
998
999	/* generate slice's aliases if the disk has aliases */
1000	if (aliases != NULL) {
1001	    char buf[MAXNAMELEN];
1002
1003	    for (; aliases != NULL; aliases = aliases->next) {
1004		(void) snprintf(buf, MAXNAMELEN-1, "%ss%d",
1005			(char *)aliases->obj, index);
1006		error = set_alias(sp, buf);
1007	    }
1008	    dlist_free_items(aliases, free);
1009	}
1010
1011	if ((item = dlist_new_item((void *)(uintptr_t)sp)) == NULL) {
1012	    return (ENOMEM);
1013	}
1014
1015	_virtual_slices = dlist_append(item, _virtual_slices, AT_HEAD);
1016
1017	oprintf(OUTPUT_DEBUG,
1018		gettext("  created virtual slice %s start: %llu, size: %llu\n"),
1019		sname, startblk, sizeblks);
1020
1021	return (error);
1022}
1023
1024/*
1025 * FUNCTION:	release_virtual_slices()
1026 *
1027 * PURPOSE:	Helper which cleans up the module private list of virtual
1028 *		slices.
1029 *
1030 *		The descriptors for the virtual slices are cleaned up
1031 *		in device_cache_util.free_cached_descriptors
1032 */
1033void
1034release_virtual_slices()
1035{
1036	dlist_free_items(_virtual_slices, NULL);
1037	_virtual_slices = NULL;
1038}
1039
1040/*
1041 * FUNCTION:	disk_add_virtual_slice(dm_descriptor_t disk,
1042 *			dm_descriptor_t slice)
1043 *
1044 * INPUT:	disk	- a dm_descriptor_t disk handle
1045 *		slice	- a dm_descriptor_t virtual slice handle
1046 *
1047 * RETURNS:	int	- 0 on success
1048 *			 !0 otherwise
1049 *
1050 * PURPOSE:	Helper which adds a virtual slice to the input disk's
1051 *		list of virtual slices.
1052 *
1053 *		The disk's virtual slice dm_descriptor_t handles are
1054 *		stored in the disk's nvpair attribute list.
1055 */
1056static int
1057disk_add_virtual_slice(
1058	dm_descriptor_t	disk,
1059	dm_descriptor_t slice)
1060{
1061	nvlist_t	*attrs = NULL;
1062	uint64_t	*old_slices = NULL;
1063	uint64_t	*new_slices = NULL;
1064	uint_t		nelem = 0;
1065	int		i = 0;
1066	int		error = 0;
1067
1068	if ((error = get_cached_attributes(disk, &attrs)) != 0) {
1069	    return (error);
1070	}
1071
1072	if ((error = get_uint64_array(
1073	    attrs, ATTR_VIRTUAL_SLICES, &old_slices, &nelem)) != 0) {
1074	    if (error != ENOENT) {
1075		return (error);
1076	    }
1077	    error = 0;
1078	}
1079
1080	/* make a new array */
1081	new_slices = (uint64_t *)calloc(nelem + 1, sizeof (uint64_t));
1082	if (new_slices != NULL) {
1083
1084	    for (i = 0; i < nelem; i++) {
1085		new_slices[i] = old_slices[i];
1086	    }
1087	    new_slices[i] = slice;
1088
1089	    error = set_uint64_array(
1090		    attrs, ATTR_VIRTUAL_SLICES, new_slices, nelem);
1091
1092	    free(new_slices);
1093
1094	} else {
1095	    error = ENOMEM;
1096	}
1097
1098	return (error);
1099}
1100
1101/*
1102 * FUNCTION:	disk_has_virtual_slices(dm_descriptor_t disk, boolean_t *bool)
1103 *
1104 * INPUT:	disk	- a dm_descriptor_t disk handle
1105 *
1106 * OUTPUT:	bool	- B_TRUE - if the disk has virtual slices
1107 *			  B_FALSE - otherwise
1108 *
1109 * RETURNS:	int	- 0 on success
1110 *			 !0 otherwise
1111 *
1112 * PURPOSE:	Helper which determines if the input disk has virtual slices.
1113 *
1114 *		If a disk has virtual slices, their dm_descriptor_t handles
1115 *		will be stored in the disk's nvpair attribute list.
1116 */
1117static int
1118disk_has_virtual_slices(
1119	dm_descriptor_t	disk,
1120	boolean_t	*bool)
1121{
1122	nvlist_t	*attrs = NULL;
1123	uint64_t	*slices = NULL;
1124	uint_t		nelem = 0;
1125	int		error = 0;
1126
1127	*bool = B_FALSE;
1128
1129	if ((error = get_cached_attributes(disk, &attrs)) != 0) {
1130	    return (error);
1131	}
1132
1133	if ((error = get_uint64_array(
1134	    attrs, ATTR_VIRTUAL_SLICES, &slices, &nelem)) != 0) {
1135	    if (error == ENOENT) {
1136		error = 0;
1137		nelem = 0;
1138	    } else {
1139		/* count actual number of elements */
1140		int i = 0;
1141		while (i < nelem) {
1142		    if (slices[i] != -1) {
1143			++i;
1144		    }
1145		}
1146		nelem = i;
1147	    }
1148	}
1149
1150	*bool = (nelem != 0);
1151
1152	return (error);
1153}
1154
1155/*
1156 * FUNCTION:	disk_get_virtual_slices(dm_descriptor_t disk, boolean_t *bool)
1157 *
1158 * INPUT:	disk	- a dm_descriptor_t disk handle
1159 *
1160 * OUTPUT:	list	- a dlist_t list of dm_descriptor_t handles for the
1161 *				disk's virtual slices.
1162 *
1163 * RETURNS:	int	- 0 on success
1164 *			 !0 otherwise
1165 *
1166 * PURPOSE:	Helper which retrieves a list of the input disk's virtual
1167 *		slices.
1168 *
1169 *		If a disk has virtual slices, their dm_descriptor_t handles
1170 *		will be stored in the disk's nvpair attribute list.
1171 */
1172static int
1173disk_get_virtual_slices(
1174	dm_descriptor_t	disk,
1175	dlist_t		**list)
1176{
1177	nvlist_t	*attrs = NULL;
1178	uint64_t	*slices = NULL;
1179	uint_t		nelem = 0;
1180	int		error = 0;
1181	int		i = 0;
1182
1183	if ((error = get_cached_attributes(disk, &attrs)) != 0) {
1184	    return (error);
1185	}
1186
1187	if ((error = get_uint64_array(
1188	    attrs, ATTR_VIRTUAL_SLICES, &slices, &nelem)) != 0) {
1189	    if (error != ENOENT) {
1190		return (error);
1191	    }
1192
1193	    return (0);
1194	}
1195
1196	for (i = 0; i < nelem && slices[i] != -1; i++) {
1197	    dlist_t *item = NULL;
1198
1199	    if ((item = dlist_new_item((void*)(uintptr_t)slices[i])) == NULL) {
1200		error = ENOMEM;
1201		break;
1202	    }
1203
1204	    *list = dlist_append(item, *list, AT_TAIL);
1205	}
1206
1207	return (error);
1208}
1209
1210/*
1211 * FUNCTION:	is_virtual_slice(dm_descriptor_t desc)
1212 *
1213 * INPUT:	desc	- a dm_descriptor_t handle
1214 *
1215 * RETURNS:	boolean_t - B_TRUE if the input descriptor is for
1216 *				a virtual slice.
1217 * 			B_FALSE otherwise
1218 *
1219 * PURPOSE:	Helper which determines whether the input descriptor
1220 *		corresponds to a virtual slice.
1221 *
1222 *		All virtual slices are stored in a module private list.
1223 *		This list is iterated to see if it contains the input
1224 *		descriptor.
1225 */
1226boolean_t
1227is_virtual_slice(
1228	dm_descriptor_t desc)
1229{
1230        return (dlist_contains(_virtual_slices,
1231			(void*)(uintptr_t)desc, compare_descriptors));
1232}
1233
1234/*
1235 * FUNCTION:	disk_get_available_slice_index(dm_descriptor_t disk,
1236 *			uint32_t *newindex)
1237 *
1238 * INPUT:	disk	- a dm_descriptor_t handle for a disk
1239 *
1240 * OUTPUT:	*newindex - a pointer to a uint32_t to hold the available
1241 *			index.  If no index is available, the value pointed
1242 *			to is not modified.
1243 *
1244 * RETURNS:	int	- 0 on success
1245 *			 !0 otherwise.
1246 *
1247 * PURPOSE:	examine the input disk's list of slices and find an unused
1248 *		slice index.  The replica slice (index 7 or 6) is always
1249 *		off-limits -- it shows up as in use.  Slice 0 should only
1250 *		be used as a last resort.
1251 *
1252 *		If an available index is found, it is stored into newindex.
1253 *		Otherwise, newindex is unchanged.  This allows the caller to
1254 *		pass in an index and check if it has been modified on return.
1255 *
1256 *		V_NUMPAR is used as the number of available slices,
1257 *		SPARC systems have V_NUMPAR == 8, X86 have V_NUMPAR == 16.
1258 *
1259 *		EFI disks have only 7.
1260 */
1261int
1262disk_get_available_slice_index(
1263	dm_descriptor_t	disk,
1264	uint32_t	*newindex)
1265{
1266	dlist_t		*iter	= NULL;
1267	dlist_t		*slices = NULL;
1268	uint32_t	index	= 0;
1269	uint16_t	*reserved = NULL;
1270	boolean_t 	*used 	= NULL;
1271	boolean_t 	is_efi	= B_FALSE;
1272	int		error	= 0;
1273	int		i	= 0;
1274	int		nslices = V_NUMPAR;
1275
1276	if (((error = disk_get_slices(disk, &slices)) != 0) ||
1277	    (error = disk_get_is_efi(disk, &is_efi)) != 0) {
1278	    return (error);
1279	}
1280
1281	if (is_efi == B_TRUE) {
1282	    /* limit possible indexes to 7 for EFI */
1283	    nslices = 7;
1284	}
1285
1286	used = (boolean_t *)calloc(nslices, sizeof (boolean_t));
1287	if (used == NULL) {
1288	    oprintf(OUTPUT_DEBUG,
1289		    gettext("failed allocating slice index array\n"),
1290		    NULL);
1291	    return (ENOMEM);
1292	}
1293
1294	/* eliminate indexes that are reserved */
1295	if ((error = disk_get_reserved_indexes(disk, &reserved)) != 0) {
1296	    return (error);
1297	}
1298
1299	if (reserved != NULL) {
1300	    for (i = 0; i < nslices; i++) {
1301		if (reserved[i] == 1) {
1302		    used[i] = B_TRUE;
1303		}
1304	    }
1305	}
1306
1307	/* eliminate slices that are in use (have a size > 0) */
1308	/* 0 sized slices unused slices */
1309	for (iter = slices; iter != NULL; iter = iter->next) {
1310	    dm_descriptor_t sp = (uintptr_t)iter->obj;
1311	    uint64_t	size = 0;
1312
1313	    ((error = slice_get_index(sp, &index)) != 0) ||
1314	    (error = slice_get_size_in_blocks(sp, &size));
1315	    if (error != 0) {
1316		return (error);
1317	    }
1318
1319	    if (size > 0) {
1320		used[(int)index] = B_TRUE;
1321	    }
1322	}
1323	dlist_free_items(slices, NULL);
1324
1325	for (i = 0; i < nslices; i++) {
1326
1327	    /* skip the index passed in */
1328	    if (i == *newindex) {
1329		continue;
1330	    }
1331
1332	    if (used[i] != B_TRUE) {
1333		index = i;
1334		break;
1335	    }
1336	}
1337
1338	if (i != nslices) {
1339	    /* return unused slice index */
1340	    *newindex = index;
1341	}
1342
1343	free((void *)used);
1344
1345	return (0);
1346}
1347
1348/*
1349 * FUNCTION:	disk_get_media_type(dm_descriptor_t slice, uint32_t *type)
1350 *
1351 * INPUT:	slice	- a dm_descriptor_t handle for a disk
1352 *
1353 * OUTPUT:	*type	- a pointer to a uint32_t to hold the
1354 *			current type value for the media on which
1355 *			the input slice resides.
1356 *
1357 * RETURNS:	int	- 0 on success
1358 *			 !0 otherwise.
1359 *
1360 * PURPOSE:	Retrieves the media type for the disk.
1361 *
1362 *		Get the media associate with the input disk descriptor
1363 *		and determine its type.
1364 */
1365int
1366disk_get_media_type(
1367	dm_descriptor_t	disk,
1368	uint32_t	*type)
1369{
1370	int		error = 0;
1371	dm_descriptor_t	*mdp = NULL;
1372
1373	mdp = dm_get_associated_descriptors(disk, DM_MEDIA, &error);
1374	(void) add_descriptors_to_free(mdp);
1375
1376	if (error != 0) {
1377	    print_get_assoc_desc_error(disk, gettext("media"), error);
1378	} else {
1379	    /* disk should have exactly 1 media */
1380	    if ((mdp != NULL) && (*mdp != NULL)) {
1381		nvlist_t *attrs = dm_get_attributes(*mdp, &error);
1382		if ((error == 0) && (attrs != NULL)) {
1383		    error = get_uint32(attrs, DM_MTYPE, type);
1384		}
1385
1386		nvlist_free(attrs);
1387	    }
1388	    /* no media: removeable drive */
1389	}
1390
1391	if (mdp != NULL) {
1392	    free(mdp);
1393	}
1394
1395	return (error);
1396}
1397
1398/*
1399 * FUNCTION:	disk_get_rpm(dm_descriptor_t disk, uint32_t *val)
1400 *		disk_get_sync_speed(dm_descriptor_t disk, uint32_t *val)
1401 *		disk_get_size_in_blocks(dm_descriptor_t disk, uint64_t *val)
1402 *		disk_get_blocksize(dm_descriptor_t disk, uint64_t *val)
1403 *		disk_get_ncylinders(dm_descriptor_t disk, uint64_t *val)
1404 *		disk_get_nheads(dm_descriptor_t disk, uint64_t *val)
1405 *		disk_get_nsectors(dm_descriptor_t disk, uint64_t *val)
1406 *		disk_get_is_efi(dm_descriptor_t disk, boolean_t *val)
1407 *		disk_get_is_online(dm_descriptor_t disk, boolean_t *val)
1408 *		disk_get_media_type(dm_descriptor_t disk, uint32_t *type)
1409 *		disk_get_has_fdisk(dm_descriptor_t disk, boolean_t *val)
1410 *		disk_get_start_block(dm_descriptor_t disk, uint64_t *val)
1411 *
1412 * INPUT:	disk	- a dm_descriptor_t handle for a disk
1413 *
1414 * OUTPUT:	*bool	- a pointer to a variable of the appropriate
1415 *			type to hold the current value for the attribute
1416 *			of interest.
1417 *
1418 * RETURNS:	int	- 0 on success
1419 *			 !0 otherwise.
1420 *
1421 * PURPOSE:	Wrappers around disk_get_XXX_attribute that know
1422 *	        which attribute needs to be retrieved and also handle
1423 *		any necesasry type or units conversions.
1424 */
1425static int
1426disk_get_rpm(
1427	dm_descriptor_t	disk,
1428	uint32_t	*val)
1429{
1430	uint64_t	val64 = 0;
1431	int		error = 0;
1432
1433	if ((error = disk_get_uint64_attribute(
1434	    disk, DM_RPM, &val64)) != 0) {
1435	    return (error);
1436	}
1437
1438	*val = (uint32_t)val64;
1439
1440	return (error);
1441}
1442
1443int
1444disk_get_drive_type(
1445	dm_descriptor_t	disk,
1446	uint32_t	*val)
1447{
1448	uint64_t	val64 = 0;
1449	int		error = 0;
1450
1451	if ((error = disk_get_uint64_attribute(
1452	    disk, DM_DRVTYPE, &val64)) != 0) {
1453	    return (error);
1454	}
1455
1456	*val = (uint32_t)val64;
1457
1458	return (error);
1459}
1460
1461static int
1462disk_get_sync_speed(
1463	dm_descriptor_t	disk,
1464	uint32_t	*val)
1465{
1466	uint64_t	val64 = 0;
1467	int		error = 0;
1468
1469	if ((error = disk_get_uint64_attribute(
1470	    disk, DM_SYNC_SPEED, &val64)) != 0) {
1471	    return (error);
1472	}
1473
1474	*val = (uint32_t)val64;
1475
1476	return (error);
1477}
1478
1479/* returns number of usable blocks */
1480int
1481disk_get_size_in_blocks(
1482	dm_descriptor_t	disk,
1483	uint64_t	*val)
1484{
1485	return (disk_get_uint64_attribute(disk, DM_NACCESSIBLE, val));
1486}
1487
1488/* returns first usable block on disk */
1489int
1490disk_get_start_block(
1491	dm_descriptor_t	disk,
1492	uint64_t	*val)
1493{
1494	return (disk_get_uint64_attribute(disk, DM_START, val));
1495}
1496
1497int
1498disk_get_blocksize(
1499	dm_descriptor_t	disk,
1500	uint64_t	*val)
1501{
1502	return (disk_get_uint64_attribute(disk, DM_BLOCKSIZE, val));
1503}
1504
1505int
1506disk_get_ncylinders(
1507	dm_descriptor_t	disk,
1508	uint64_t	*val)
1509{
1510	return (disk_get_uint64_attribute(disk, DM_NCYLINDERS, val));
1511}
1512
1513int
1514disk_get_nheads(
1515	dm_descriptor_t	disk,
1516	uint64_t	*val)
1517{
1518	return (disk_get_uint64_attribute(disk, DM_NHEADS, val));
1519}
1520
1521int
1522disk_get_nsectors(
1523	dm_descriptor_t	disk,
1524	uint64_t	*val)
1525{
1526	return (disk_get_uint64_attribute(disk, DM_NSECTORS, val));
1527}
1528
1529/*
1530 * FUNCTION:	disk_get_is_online(dm_descriptor_t disk, boolean_t *val)
1531 *
1532 * INPUT:	disk	- a dm_descriptor_t handle for a disk
1533 *
1534 * OUTPUT:	*bool	- a pointer to a boolean_t to hold the result.
1535 *
1536 * RETURNS:	int	- 0 on success
1537 *			 !0 otherwise.
1538 *
1539 * PURPOSE:	Determine if the input disk is "online".
1540 *
1541 *		Check the status bit of the drive, if it is 1 the drive
1542 *		is online, if it is 0 the drive is offline.
1543 */
1544int
1545disk_get_is_online(
1546	dm_descriptor_t	disk,
1547	boolean_t	*val)
1548{
1549	uint64_t	status = 0;
1550	int		error = 0;
1551
1552	*val = B_FALSE;
1553
1554	error = disk_get_uint64_attribute(disk, DM_STATUS, &status);
1555	if (error == 0) {
1556	    *val = (status == 1) ? B_TRUE : B_FALSE;
1557	}
1558
1559	return (error);
1560}
1561
1562/*
1563 * FUNCTION:	disk_get_is_efi(dm_descriptor_t disk, boolean_t *bool)
1564 *
1565 * INPUT:	disk	- a dm_descriptor_t handle for a disk
1566 *
1567 * OUTPUT:	*bool	- a pointer to a boolean_t to hold the result.
1568 *
1569 * RETURNS:	int	- 0 on success
1570 *			 !0 otherwise.
1571 *
1572 * PURPOSE:	Determine if the input disk is labeled with an EFI label.
1573 *
1574 *		The label type is actually a property of the media
1575 *		associated with the disk, so retrieve the media and
1576 *		check if it is EFI labeled.
1577 */
1578int
1579disk_get_is_efi(
1580	dm_descriptor_t	disk,
1581	boolean_t	*bool)
1582{
1583	return (disk_get_boolean_attribute(disk, DM_EFI, bool));
1584}
1585
1586/*
1587 * FUNCTION:	disk_get_has_fdisk(dm_descriptor_t disk, boolean_t *bool)
1588 *
1589 * INPUT:	disk	- a dm_descriptor_t handle for a disk
1590 *
1591 * OUTPUT:	*bool	- a pointer to a boolean_t to hold the result.
1592 *
1593 * RETURNS:	int	- 0 on success
1594 *			 !0 otherwise.
1595 *
1596 * PURPOSE:	Determine if the input disk has an FDISK partition.
1597 */
1598int
1599disk_get_has_fdisk(
1600	dm_descriptor_t	disk,
1601	boolean_t	*bool)
1602{
1603	return (disk_get_boolean_attribute(disk, DM_FDISK, bool));
1604}
1605
1606/*
1607 * FUNCTION:	disk_get_has_solaris_partition(dm_descriptor_t disk, boolean_t *bool)
1608 *
1609 * INPUT:	disk	- a dm_descriptor_t handle for a disk
1610 *
1611 * OUTPUT:	*bool	- a pointer to a boolean_t to hold the result.
1612 *
1613 * RETURNS:	int	- 0 on success
1614 *			 !0 otherwise.
1615 *
1616 * PURPOSE:	Determine if the input disk has a Solaris FDISK partition.
1617 */
1618int
1619disk_get_has_solaris_partition(
1620	dm_descriptor_t	disk,
1621	boolean_t	*bool)
1622{
1623	boolean_t	has_fdisk = B_FALSE;
1624	int		error = 0;
1625
1626	if ((error = disk_get_has_fdisk(disk, &has_fdisk)) != 0) {
1627	    return (error);
1628	}
1629
1630	*bool = B_FALSE;
1631
1632	if (has_fdisk == B_TRUE) {
1633	    /* get disk's media */
1634	    dm_descriptor_t *media;
1635	    media = dm_get_associated_descriptors(disk, DM_MEDIA, &error);
1636	    (void) add_descriptors_to_free(media);
1637	    if (error != 0) {
1638		print_get_assoc_desc_error(disk, gettext("media"), error);
1639	    } else if ((media != NULL) && (*media != NULL)) {
1640		/* get media's partitions */
1641		dm_descriptor_t *parts;
1642		parts = dm_get_associated_descriptors(
1643			media[0], DM_PARTITION, &error);
1644		(void) add_descriptors_to_free(parts);
1645		if (error != 0) {
1646		    print_get_assoc_desc_error(media[0],
1647			    gettext("partitions"), error);
1648		} else {
1649		    /* search partitions for one with type Solaris */
1650		    int i = 0;
1651		    for (; (parts != NULL) && (parts[i] != NULL) &&
1652			(error == 0) && (*bool == B_FALSE); i++) {
1653			nvlist_t *attrs = dm_get_attributes(parts[i], &error);
1654			uint32_t ptype = 0;
1655			if ((error == 0) && (attrs != NULL)) {
1656			    error = get_uint32(attrs, DM_PTYPE, &ptype);
1657			    if ((error == 0) &&
1658			        (ptype == SUNIXOS || ptype == SUNIXOS2)) {
1659				    *bool = B_TRUE;
1660			    }
1661			}
1662			nvlist_free(attrs);
1663		    }
1664		}
1665
1666		free(parts);
1667		free(media);
1668	    }
1669
1670	    /* if there was no media, it was a removeable drive */
1671	}
1672
1673	return (error);
1674}
1675
1676static int
1677disk_get_boolean_attribute(
1678	dm_descriptor_t	disk,
1679	char		*attr,
1680	boolean_t	*bool)
1681{
1682	nvlist_t	*attrs	= NULL;
1683	int		error	= 0;
1684
1685	*bool = B_FALSE;
1686
1687	if ((strcmp(attr, DM_EFI) == 0) ||
1688	    (strcmp(attr, DM_FDISK) == 0)) {
1689
1690	    /*
1691	     * these attributes are actually on the media,
1692	     * not the disk... so get the media descriptor
1693	     * for this disk
1694	     */
1695	    dm_descriptor_t *media;
1696
1697	    media = dm_get_associated_descriptors(disk, DM_MEDIA, &error);
1698	    (void) add_descriptors_to_free(media);
1699
1700	    if (error != 0) {
1701		print_get_assoc_desc_error(disk, gettext("media"), error);
1702	    } else if ((media != NULL) && (*media != NULL)) {
1703		/* if there's no media, it is a removeable drive */
1704		error = get_cached_attributes(media[0], &attrs);
1705	    }
1706	    free(media);
1707
1708	} else {
1709	    error = get_cached_attributes(disk, &attrs);
1710	    if (error != 0) {
1711		print_get_desc_attr_error(disk, gettext("drive"), attr, error);
1712	    }
1713	}
1714
1715	if (error != 0) {
1716	    return (error);
1717	}
1718
1719	if (nvlist_lookup_boolean(attrs, attr) == 0) {
1720	    *bool = B_TRUE;
1721	}
1722
1723	return (error);
1724}
1725
1726static int
1727disk_get_uint64_attribute(
1728	dm_descriptor_t	disk,
1729	char		*attr,
1730	uint64_t	*val)
1731{
1732	nvlist_t	*attrs	= NULL;
1733	uint32_t	ui32	= 0;
1734	int		error	= 0;
1735
1736	/*
1737	 * these attributes are actually on the media,
1738	 * not the disk... so get the media descriptor
1739	 * for this disk
1740	 */
1741	if ((strcmp(attr, DM_SIZE) == 0) ||
1742	    (strcmp(attr, DM_START) == 0) ||
1743	    (strcmp(attr, DM_NACCESSIBLE) == 0) ||
1744	    (strcmp(attr, DM_BLOCKSIZE) == 0) ||
1745	    (strcmp(attr, DM_NCYLINDERS) == 0) ||
1746	    (strcmp(attr, DM_NHEADS) == 0) ||
1747	    (strcmp(attr, DM_NSECTORS) == 0)) {
1748
1749	    dm_descriptor_t *media;
1750
1751	    media = dm_get_associated_descriptors(disk, DM_MEDIA, &error);
1752	    (void) add_descriptors_to_free(media);
1753
1754	    if (error != 0) {
1755		print_get_assoc_desc_error(disk, gettext("media"), error);
1756	    } else if ((media == NULL) || (*media == NULL)) {
1757		print_get_assoc_desc_error(disk, gettext("media"), error);
1758		error = -1;
1759	    } else {
1760		error = get_cached_attributes(media[0], &attrs);
1761		free(media);
1762	    }
1763
1764	} else {
1765	    error = get_cached_attributes(disk, &attrs);
1766	    if (error != 0) {
1767		print_get_desc_attr_error(disk, gettext("drive"), attr, error);
1768	    }
1769	}
1770
1771	if (error != 0) {
1772	    return (error);
1773	}
1774
1775	if (strcmp(attr, DM_SIZE) == 0 ||
1776	    strcmp(attr, DM_NACCESSIBLE) == 0 ||
1777	    strcmp(attr, DM_START) == 0) {
1778	    error = get_uint64(attrs, attr, val);
1779	} else if (strcmp(attr, DM_BLOCKSIZE) == 0 ||
1780	    strcmp(attr, DM_NCYLINDERS) == 0 ||
1781	    strcmp(attr, DM_NHEADS) == 0 ||
1782	    strcmp(attr, DM_NSECTORS) == 0 ||
1783	    strcmp(attr, DM_RPM) == 0 ||
1784	    strcmp(attr, DM_DRVTYPE) == 0 ||
1785	    strcmp(attr, DM_SYNC_SPEED) == 0 ||
1786	    strcmp(attr, DM_STATUS) == 0) {
1787	    error = get_uint32(attrs, attr, &ui32);
1788	    *val = (uint64_t)ui32;
1789	}
1790
1791	return (error);
1792}
1793
1794/*
1795 * FUNCTION:	group_similar_hbas(dlist_t *hbas, dlist_t **list)
1796 *
1797 * INPUT:	hbas	- a list of HBA dm_descriptor_t handles.
1798 *
1799 * OUTPUT:	**list	- a pointer to a list to hold the lists of HBAs
1800 *			grouped by characteristics.
1801 *
1802 * RETURNS:	int	- 0 on success
1803 *			 !0 otherwise.
1804 *
1805 * PURPOSE:	Examine the input HBAs and collate them into separate
1806 *		lists, grouped by their type and the protocols they
1807 *		support.
1808 *
1809 *		The returned list of list is arranged in decreasing order
1810 *		of preference, "better" HBAs come first.
1811 *
1812 *		find all MPXIO controllers
1813 *		find all similar FC HBAs
1814 *		find all similar SCSI HBAs
1815 *		    fast{wide}80
1816 *		    fast{wide}40
1817 *		    fast{wide}20
1818 *		    clock         uint32  ??
1819 *		find all similar ATA/IDE HBAs
1820 *		find all similar USB HBAs
1821 */
1822int
1823group_similar_hbas(
1824	dlist_t	*hbas,
1825	dlist_t **list)
1826{
1827	/* preference order of HBAs */
1828	enum {
1829		HBA_FIBRE_MPXIO = 0,
1830		HBA_SCSI_MPXIO,
1831		HBA_FIBRE,
1832		HBA_SCSI_FW80,
1833		HBA_SCSI_FW40,
1834		HBA_SCSI_FW20,
1835		HBA_SCSI_F80,
1836		HBA_SCSI_F40,
1837		HBA_SCSI_F20,
1838		HBA_SCSI,
1839		HBA_ATA,
1840		HBA_USB,
1841		HBA_LAST
1842	};
1843
1844	dlist_t		*groups	= NULL;
1845	dlist_t		*iter = NULL;
1846	dlist_t		*item = NULL;
1847	dlist_t		*lists[HBA_LAST];
1848
1849	int		error = 0;
1850	int		i = 0;
1851
1852	(void) memset(lists, '\0', HBA_LAST * sizeof (dlist_t *));
1853
1854	for (iter = hbas;
1855	    (iter != NULL) && (error == 0);
1856	    iter = iter->next) {
1857
1858	    dm_descriptor_t hba = (uintptr_t)iter->obj;
1859	    char	*type = NULL;
1860
1861	    /* if item doesn't go into a list it must be freed */
1862	    if ((item = dlist_new_item((void *)(uintptr_t)hba)) == NULL) {
1863		error = ENOMEM;
1864		continue;
1865	    }
1866
1867	    if ((error = hba_get_type(hba, &type)) != 0) {
1868		free(item);
1869		continue;
1870	    }
1871
1872	    if (strcmp(type, DM_CTYPE_FIBRE) == 0) {
1873
1874		boolean_t	ismpxio = B_FALSE;
1875
1876		if ((error = hba_is_multiplex(hba, &ismpxio)) == 0) {
1877		    if (ismpxio) {
1878			lists[HBA_FIBRE_MPXIO] =
1879			    dlist_append(item,
1880				    lists[HBA_FIBRE_MPXIO], AT_TAIL);
1881		    } else {
1882			lists[HBA_FIBRE] =
1883			    dlist_append(item,
1884				    lists[HBA_FIBRE], AT_TAIL);
1885		    }
1886		} else {
1887		    free(item);
1888		}
1889
1890	    } else if (strcmp(type, DM_CTYPE_SCSI) == 0) {
1891
1892		/* determine subtype */
1893		boolean_t	iswide = B_FALSE;
1894		boolean_t	ismpxio = B_FALSE;
1895		boolean_t	is80 = B_FALSE;
1896		boolean_t	is40 = B_FALSE;
1897		boolean_t	is20 = B_FALSE;
1898
1899		((error = hba_supports_wide(hba, &iswide)) != 0) ||
1900		(error = hba_is_multiplex(hba, &ismpxio)) ||
1901		(error = hba_is_fast_80(hba, &is80)) ||
1902		(error = hba_is_fast_40(hba, &is40)) ||
1903		(error = hba_is_fast_20(hba, &is20));
1904
1905		if (error == 0) {
1906
1907		    if (ismpxio) {
1908
1909			lists[HBA_SCSI_MPXIO] =
1910			    dlist_append(item,
1911				    lists[HBA_SCSI_MPXIO], AT_TAIL);
1912
1913		    } else if (is80) {
1914
1915			if (iswide) {
1916			    lists[HBA_SCSI_FW80] =
1917				dlist_append(item,
1918					lists[HBA_SCSI_FW80], AT_TAIL);
1919			} else {
1920			    lists[HBA_SCSI_F80] =
1921				dlist_append(item,
1922					lists[HBA_SCSI_F80], AT_TAIL);
1923			}
1924
1925		    } else if (is40) {
1926
1927			if (iswide) {
1928			    lists[HBA_SCSI_FW40] =
1929				dlist_append(item,
1930					lists[HBA_SCSI_FW40], AT_TAIL);
1931			} else {
1932			    lists[HBA_SCSI_F40] =
1933				dlist_append(item,
1934					lists[HBA_SCSI_F40], AT_TAIL);
1935			}
1936
1937		    } else if (is20) {
1938
1939			if (iswide) {
1940			    lists[HBA_SCSI_FW20] =
1941				dlist_append(item,
1942					lists[HBA_SCSI_FW20], AT_TAIL);
1943			} else {
1944			    lists[HBA_SCSI_F20] =
1945				dlist_append(item,
1946					lists[HBA_SCSI_F20], AT_TAIL);
1947			}
1948
1949		    } else {
1950			lists[HBA_SCSI] =
1951			    dlist_append(item, lists[HBA_SCSI], AT_TAIL);
1952		    }
1953
1954		} else {
1955		    free(item);
1956		}
1957
1958	    } else if (strcmp(type, DM_CTYPE_ATA) == 0) {
1959		lists[HBA_ATA] =
1960		    dlist_append(item, lists[HBA_ATA], AT_TAIL);
1961	    } else if (strcmp(type, DM_CTYPE_USB) == 0) {
1962		lists[HBA_USB] =
1963		    dlist_append(item, lists[HBA_USB], AT_TAIL);
1964	    } else if (strcmp(type, DM_CTYPE_UNKNOWN) == 0) {
1965		oprintf(OUTPUT_DEBUG,
1966			gettext("found an HBA with unknown type\n"));
1967		free(item);
1968	    }
1969	}
1970
1971	if (error == 0) {
1972	    /* collect individual lists into a list of lists */
1973	    for (i = 0; (i < HBA_LAST) && (error == 0); i++) {
1974		if (lists[i] != NULL) {
1975		    if ((item = dlist_new_item(lists[i])) == NULL) {
1976			error = ENOMEM;
1977		    } else {
1978			groups = dlist_append(item, groups, AT_TAIL);
1979		    }
1980		}
1981	    }
1982	}
1983
1984	if (error != 0) {
1985	    for (i = 0; i < HBA_LAST; i++) {
1986		dlist_free_items(lists[i], NULL);
1987		lists[i] = NULL;
1988	    }
1989
1990	    if (groups != NULL) {
1991		dlist_free_items(groups, NULL);
1992	    }
1993	}
1994
1995	*list = groups;
1996
1997	return (error);
1998}
1999
2000/*
2001 * FUNCTION:	hba_group_usable_disks(dm_descriptor_t hba, dlist_t **list)
2002 *
2003 * INPUT:	hba	- a dm_descriptor_t handle for a slice
2004 *
2005 * OUTPUT:	**list	- a pointer to a list to hold the lists of disks
2006 *			grouped by characteristics.
2007 *
2008 * RETURNS:	int	- 0 on success
2009 *			 !0 otherwise.
2010 *
2011 * PURPOSE:	Examine the disks assocated with the HBA and collates them
2012 *		into separate lists, grouped by similar characteristics.
2013 *
2014 *		get disks on HBA
2015 *		check disks against _usable_disks list
2016 *		group disks by similarities:
2017 *			sync-speed    uint32
2018 *			wide          boolean
2019 *			rpm           uint32
2020 *
2021 *		XXX this function is currently unused.  At some point,
2022 *		it may be useful to group disks by performance
2023 *		characteristics and use "better" disks before others.
2024 */
2025int
2026hba_group_usable_disks(
2027	dm_descriptor_t	hba,
2028	dlist_t		**list)
2029{
2030	dm_descriptor_t *disk = NULL;
2031	char 		*name = NULL;
2032	int		i = 0;
2033	int		error = 0;
2034
2035	disk = dm_get_associated_descriptors(hba, DM_DRIVE, &error);
2036	(void) add_descriptors_to_free(disk);
2037
2038	if (error != 0) {
2039	    print_get_assoc_desc_error(hba, gettext("drive"), error);
2040	    return (error);
2041	} else if ((disk == NULL) || (*disk == NULL)) {
2042	    print_get_assoc_desc_error(hba, gettext("drive"), error);
2043	    error = -1;
2044	}
2045
2046	for (i = 0; (disk[i] != NULL) && (error == 0); i++) {
2047
2048	    uint32_t dtype = DM_DT_UNKNOWN;
2049	    dlist_t *usable = NULL;
2050
2051	    /* ignore non fixed media drives */
2052	    if (((error = disk_get_drive_type(disk[i], &dtype)) != 0) ||
2053		(dtype != DM_DT_FIXED)) {
2054		continue;
2055	    }
2056
2057	    if (dlist_contains(usable, &disk[i],
2058		compare_descriptor_names) == B_TRUE) {
2059
2060		uint64_t bsize	= 0;
2061		uint64_t ncyls	= 0;
2062		uint64_t nsects	= 0;
2063		uint64_t nheads	= 0;
2064		uint32_t rpm	= 0;
2065		uint32_t sync	= 0;
2066
2067		name = NULL;
2068		((error = get_display_name(disk[i], &name)) != 0) ||
2069		(error = disk_get_blocksize(disk[i], &bsize)) ||
2070		(error = disk_get_nheads(disk[i], &nheads)) ||
2071		(error = disk_get_nsectors(disk[i], &nsects)) ||
2072		(error = disk_get_ncylinders(disk[i], &ncyls)) ||
2073		(error = disk_get_rpm(disk[i], &rpm)) ||
2074		(error = disk_get_sync_speed(disk[i], &sync));
2075		if (error != 0) {
2076		    continue;
2077		}
2078
2079		oprintf(OUTPUT_VERBOSE,
2080			gettext("found an available disk: %s\n\t"
2081			"sync_speed = %u, rpm = %u, "
2082			"nsect = %llu, blksiz = %llu\n"),
2083			name, sync, rpm, nsects, bsize);
2084
2085		/* add to the appropriate list */
2086	    }
2087	}
2088
2089	if (disk != NULL) {
2090	    free(disk);
2091	}
2092
2093	return (error);
2094}
2095
2096/*
2097 * FUNCTION:	hba_get_n_avail_disks(dm_descriptor_t hba, uint16_t *val)
2098 *		hba_set_n_avail_disks(dm_descriptor_t hba, uint16_t val)
2099 *
2100 * INPUT:	hba	- a dm_descriptor_t handle for a slice
2101 *
2102 * OUTPUT:	*val	- a pointer to a uint16_t to hold the current number
2103 *				of available disks for the input HBA.
2104 *
2105 * RETURNS:	int	- 0 on success
2106 *			 !0 otherwise.
2107 */
2108int
2109hba_set_n_avail_disks(
2110	dm_descriptor_t	hba,
2111	uint16_t	val)
2112{
2113	nvlist_t	*attrs;
2114	int		error = 0;
2115
2116	((error = get_cached_attributes(hba, &attrs)) != 0) ||
2117	(error = set_uint16(attrs, ATTR_HBA_N_DISKS, val));
2118
2119	return (error);
2120}
2121
2122int
2123hba_get_n_avail_disks(
2124	dm_descriptor_t	hba,
2125	uint16_t	*val)
2126{
2127	nvlist_t	*attrs;
2128	int		error = 0;
2129
2130	*val = 0;
2131
2132	((error = get_cached_attributes(hba, &attrs)) != 0) ||
2133	(error = get_uint16(attrs, ATTR_HBA_N_DISKS, val));
2134
2135	return (error);
2136}
2137
2138/*
2139 * FUNCTION:	hba_get_type(dm_descriptor_t hba, char **type)
2140 *
2141 * INPUT:	hba	- a dm_descriptor_t handle for a HBA
2142 *
2143 * OUTPUT:	**type	- a char * to hold the current type value for
2144 *			the HBA.
2145 *
2146 * RETURNS:	int	- 0 on success
2147 *			 !0 otherwise.
2148 *
2149 * PURPOSE:	Retrieves the type attribute for the HBA.
2150 */
2151int
2152hba_get_type(
2153	dm_descriptor_t	hba,
2154	char		**type)
2155{
2156	nvlist_t	*attrs;
2157	int		error = 0;
2158
2159	*type = NULL;
2160
2161	((error = get_cached_attributes(hba, &attrs)) != 0) ||
2162	(error = get_string(attrs, DM_CTYPE, type));
2163
2164	return (error);
2165}
2166
2167/*
2168 * FUNCTION:	hba_is_fast(dm_descriptor_t hba, boolean_t *bool)
2169 *		hba_is_fast20(dm_descriptor_t hba, boolean_t *bool)
2170 *		hba_is_fast40(dm_descriptor_t hba, boolean_t *bool)
2171 *		hba_is_fast80(dm_descriptor_t hba, boolean_t *bool)
2172 *		hba_is_multiplex(dm_descriptor_t hba, boolean_t *bool)
2173 *		hba_is_wide(dm_descriptor_t hba, boolean_t *bool)
2174 *
2175 * INPUT:	hba	- a dm_descriptor_t handle for a HBA
2176 *
2177 * OUTPUT:	*bool	- a pointer to a boolean_t to hold the
2178 *			boolean value of the predicate.
2179 *
2180 * RETURNS:	int	- 0 on success
2181 *			 !0 otherwise.
2182 *
2183 * PURPOSE:	Wrappers around hba_supports_protocol which determines
2184 *		if the input HBA supports the protocol of interest.
2185 */
2186int
2187hba_is_fast(
2188	dm_descriptor_t	hba,
2189	boolean_t	*bool)
2190{
2191	return (hba_supports_protocol(hba, DM_FAST, bool));
2192}
2193
2194int
2195hba_is_fast_20(
2196	dm_descriptor_t	hba,
2197	boolean_t	*bool)
2198{
2199	return (hba_supports_protocol(hba, DM_FAST20, bool));
2200}
2201
2202int
2203hba_is_fast_40(
2204	dm_descriptor_t	hba,
2205	boolean_t	*bool)
2206{
2207	return (hba_supports_protocol(hba, DM_FAST40, bool));
2208}
2209
2210int
2211hba_is_fast_80(
2212	dm_descriptor_t	hba,
2213	boolean_t	*bool)
2214{
2215	return (hba_supports_protocol(hba, DM_FAST80, bool));
2216}
2217
2218int
2219hba_is_multiplex(
2220	dm_descriptor_t	hba,
2221	boolean_t	*bool)
2222{
2223	return (hba_supports_protocol(hba, DM_MULTIPLEX, bool));
2224}
2225
2226int
2227hba_supports_wide(
2228	dm_descriptor_t	hba,
2229	boolean_t	*bool)
2230{
2231	nvlist_t	*attrs	= NULL;
2232	int		error	= 0;
2233
2234	*bool = B_FALSE;
2235
2236	if ((error = get_cached_attributes(hba, &attrs)) != 0) {
2237	    return (error);
2238	}
2239
2240	*bool = (0 == nvlist_lookup_boolean(attrs, DM_WIDE));
2241
2242	return (error);
2243}
2244
2245/*
2246 * FUNCTION:	hba_supports_protocol(dm_descriptor_t hba, char *attr,
2247 *			boolean_t *bool)
2248 *
2249 * INPUT:	hba	- a dm_descriptor_t handle for a HBA
2250 *		attr	- a protocol "name"
2251 *
2252 * OUTPUT:	*bool	- a pointer to a boolean_t to hold the
2253 *			boolean value of the predicate.
2254 *
2255 * RETURNS:	int	- 0 on success
2256 *			 !0 otherwise.
2257 *
2258 * PURPOSE:	Checks the HBAs attributes to see if it is known to
2259 *		support the protocol of interest.
2260 *
2261 *		If the protocol is supported, it will have an entry
2262 *		in the nvpair attribute list that can be retrieved.
2263 *
2264 *		If the entry cannot be retrieved, the protocol is not
2265 *		supported.
2266 */
2267int
2268hba_supports_protocol(
2269	dm_descriptor_t	hba,
2270	char		*attr,
2271	boolean_t	*bool)
2272{
2273	nvlist_t	*attrs	= NULL;
2274	int		error	= 0;
2275
2276	*bool = B_FALSE;
2277
2278	if ((error = get_cached_attributes(hba, &attrs)) != 0) {
2279	    return (error);
2280	}
2281
2282	*bool = (0 == nvlist_lookup_boolean(attrs, attr));
2283
2284	return (error);
2285}
2286
2287/*
2288 * FUNCTION:	slice_set_size(dm_descriptor_t slice, uint64_t size)
2289 *
2290 * INPUT:	slice	- a dm_descriptor_t handle for a slice
2291 *
2292 * OUTPUT:	size	- a uint64_t value representing the size of the
2293 *			slice.
2294 *
2295 * RETURNS:	int	- 0 on success
2296 *			 !0 otherwise.
2297 *
2298 * PURPOSE:	Wrapper around slice_set_uint64_attribute which converts
2299 *		the input size in bytes to blocks prior to storing it.
2300 *
2301 *		This function is used when an existing slice gets resized
2302 *		to provide space for a new slice. It is necessary to update
2303 *		the slice's size so that it is accurate.
2304 */
2305int
2306slice_set_size(
2307	dm_descriptor_t	slice,
2308	uint64_t	size)
2309{
2310	dm_descriptor_t	disk	= NULL;
2311	uint64_t	blksize	= 0;
2312	int		error	= 0;
2313
2314	((error = slice_get_disk(slice, &disk)) != 0) ||
2315	(error = disk_get_blocksize(disk, &blksize)) ||
2316	(error = slice_set_size_in_blocks(slice, (uint64_t)(size / blksize)));
2317
2318	return (error);
2319}
2320
2321/*
2322 * FUNCTION:	slice_set_size_in_blocks(dm_descriptor_t slice, uint64_t size)
2323 *
2324 * INPUT:	slice	- a dm_descriptor_t handle for a slice
2325 *
2326 * OUTPUT:	size	- a uint64_t value representing the size of the
2327 *			slice.
2328 *
2329 * RETURNS:	int	- 0 on success
2330 *			 !0 otherwise.
2331 *
2332 * PURPOSE:	Wrapper around slice_set_uint64_attribute to set the slice
2333 *		size.
2334 *
2335 *		This function is used when an existing slice gets resized
2336 *		to provide space for a new slice. It is necessary to update
2337 *		the slice's size so that it is accurate.
2338 */
2339int
2340slice_set_size_in_blocks(
2341	dm_descriptor_t	slice,
2342	uint64_t	size)
2343{
2344	return (slice_set_attribute(slice, DM_SIZE, size));
2345}
2346
2347/*
2348 * FUNCTION:	slice_set_start_block(dm_descriptor_t slice, uint64_t start)
2349 *
2350 * INPUT:	slice	- a dm_descriptor_t handle for a slice
2351 *
2352 * OUTPUT:	size	- a uint64_t value representing the start block of the
2353 *			slice.
2354 *
2355 * RETURNS:	int	- 0 on success
2356 *			 !0 otherwise.
2357 *
2358 * PURPOSE:	Wrapper around slice_set_attribute.
2359 *
2360 *		This function is used when an existing slice gets adjusted
2361 *		due to being resized or combined with another slice.
2362 */
2363int
2364slice_set_start_block(
2365	dm_descriptor_t	slice,
2366	uint64_t	start)
2367{
2368	return (slice_set_attribute(slice, DM_START, start));
2369}
2370
2371/*
2372 * FUNCTION:	slice_get_start_block(dm_descriptor_t slice, uint64_t *val)
2373 *		slice_get_size_in_blocks(dm_descriptor_t slice, uint64_t *val)
2374 *		slice_get_start(dm_descriptor_t slice, uint64_t *val)
2375 *		slice_get_size(dm_descriptor_t slice, uint64_t *val)
2376 *		slice_get_index(dm_descriptor_t slice, uint64_t *val)
2377 *
2378 * INPUT:	slice	- a dm_descriptor_t handle for a slice
2379 *
2380 * OUTPUT:	*val	- a pointer to a uint64_t to hold the
2381 *			current value of the desired attribute.
2382 *
2383 * RETURNS:	int	- 0 on success
2384 *			 !0 otherwise.
2385 *
2386 * PURPOSE:	Wrappers around slice_get_uint64_attribute which retrieve
2387 *		specific attribute values.
2388 */
2389int
2390slice_get_start_block(
2391	dm_descriptor_t	slice,
2392	uint64_t	*val)
2393{
2394	return (slice_get_uint64_attribute(slice, DM_START, val));
2395}
2396
2397int
2398slice_get_size_in_blocks(
2399	dm_descriptor_t	slice,
2400	uint64_t	*val)
2401{
2402	return (slice_get_uint64_attribute(slice, DM_SIZE, val));
2403}
2404
2405int
2406slice_get_start(
2407	dm_descriptor_t	slice,
2408	uint64_t	*val)
2409{
2410	dm_descriptor_t	disk	= NULL;
2411	uint64_t	blksize	= 0;
2412	uint64_t	nblks	= 0;
2413	int		error	= 0;
2414
2415	((error = slice_get_disk(slice, &disk)) != 0) ||
2416	(error = disk_get_blocksize(disk, &blksize)) ||
2417	(error = slice_get_start_block(slice, &nblks));
2418
2419	if (error == 0) {
2420	    *val = (blksize * nblks);
2421	}
2422
2423	return (error);
2424}
2425
2426int
2427slice_get_size(
2428	dm_descriptor_t	slice,
2429	uint64_t	*val)
2430{
2431	dm_descriptor_t	disk	= NULL;
2432	uint64_t	blksize	= 0;
2433	uint64_t	nblks	= 0;
2434	int		error	= 0;
2435
2436	*val = 0;
2437
2438	((error = slice_get_disk(slice, &disk)) != 0) ||
2439	(error = slice_get_size_in_blocks(slice, &nblks)) ||
2440	(error = disk_get_blocksize(disk, &blksize));
2441
2442	if (error == 0) {
2443	    *val = (blksize * nblks);
2444	}
2445
2446	return (error);
2447}
2448
2449int
2450slice_get_index(
2451	dm_descriptor_t	slice,
2452	uint32_t	*val)
2453{
2454	uint64_t	index = 0;
2455	int		error = 0;
2456
2457	if ((error = slice_get_uint64_attribute(
2458	    slice, DM_INDEX, &index)) != 0) {
2459	    return (error);
2460	}
2461
2462	*val = (uint32_t)index;
2463
2464	return (0);
2465}
2466
2467/*
2468 * FUNCTION:	slice_set_uint64_attribute(dm_descriptor_t slice,
2469 *			char *attr, uint64_t val)
2470 * 		slice_get_uint64_attribute(dm_descriptor_t slice,
2471 *			char *attr, uint64_t *val)
2472 *
2473 * INPUT:	slice	- a dm_descriptor_t handle for a slice
2474 *		attr    - a char * attribute name
2475 *		val	- auint64_t value
2476 *
2477 * OUTPUT:	*val	- a pointer to a uint64_t to hold the
2478 *			current value of the named attribute.
2479 *
2480 * RETURNS:	int	- 0 on success
2481 *			 !0 otherwise.
2482 *
2483 * PURPOSE:	Helpers to set/get the value for a slice's attribute.
2484 *
2485 *		Consolidate the details of getting/setting slice
2486 *		attributes.  Some attributes are actually stored as
2487 *		uint32_t or uint16_t values, these functions mask
2488 *		the type conversions.
2489 */
2490static int
2491slice_get_uint64_attribute(
2492	dm_descriptor_t	slice,
2493	char		*attr,
2494	uint64_t	*val)
2495{
2496	nvlist_t	*attrs	= NULL;
2497	uint32_t	ui32	= 0;
2498	int		error	= 0;
2499
2500	if ((error = get_cached_attributes(slice, &attrs)) != 0) {
2501	    return (error);
2502	}
2503
2504	if (strcmp(attr, DM_INDEX) == 0) {
2505	    error = get_uint32(attrs, attr, &ui32);
2506	    *val = (uint64_t)ui32;
2507	} else if (strcmp(attr, DM_START) == 0) {
2508	    error = get_uint64(attrs, attr, val);
2509	} else if (strcmp(attr, DM_SIZE) == 0) {
2510	    error = get_uint64(attrs, attr, val);
2511	} else if (strcmp(attr, ATTR_DISK_FOR_SLICE) == 0) {
2512	    error = get_uint64(attrs, attr, val);
2513	}
2514
2515	if (error != 0) {
2516	    print_get_desc_attr_error(slice, "slice", attr, error);
2517	}
2518
2519	return (error);
2520}
2521
2522/*
2523 * Set a slice attribute.  The attribute is only set in the cached
2524 * copy of the slice's nvpair attribute list.  This function does
2525 * NOT affect the underlying physical device.
2526 */
2527static int
2528slice_set_attribute(
2529	dm_descriptor_t	slice,
2530	char		*attr,
2531	uint64_t	val)
2532{
2533	nvlist_t	*attrs = NULL;
2534	int		error = 0;
2535
2536	if ((error = get_cached_attributes(slice, &attrs)) != 0) {
2537	    return (error);
2538	}
2539
2540	if (strcmp(attr, DM_INDEX) == 0) {
2541	    error = set_uint32(attrs, attr, (uint32_t)val);
2542	} else if (strcmp(attr, DM_START) == 0) {
2543	    error = set_uint64(attrs, attr, val);
2544	} else if (strcmp(attr, DM_SIZE) == 0) {
2545	    error = set_uint64(attrs, attr, val);
2546	} else if (strcmp(attr, ATTR_DISK_FOR_SLICE) == 0) {
2547	    error = set_uint64(attrs, attr, val);
2548	}
2549
2550	if (error != 0) {
2551	    print_set_desc_attr_error(slice, "slice", attr, error);
2552	}
2553
2554	return (error);
2555}
2556
2557/*
2558 * FUNCTION:	virtual_slice_get_disk(dm_descriptor_t slice,
2559 *			dm_descriptor_t *diskp)
2560 *
2561 * INPUT:	slice	- a dm_descriptor_t virtual slice handle
2562 *		diskp	- pointer to a dm_descriptor_t disk handle
2563 *				to return the slice's disk
2564 *
2565 * OUTPUT:	the disk associated with the virtual slice.
2566 *
2567 * RETURNS:	int	- 0 on success
2568 *			 !0 otherwise
2569 *
2570 * PURPOSE:	Helper which determines the disk that the input virtual
2571 *		slice "belongs" to.
2572 *
2573 *		The virtual slice's disk is stored in the slice's nvpair
2574 *		attribute list when the slice gets created.
2575 */
2576static int
2577virtual_slice_get_disk(
2578	dm_descriptor_t	slice,
2579	dm_descriptor_t	*diskp)
2580{
2581	uint64_t disk = 0;
2582	int	error = 0;
2583
2584	if ((error = slice_get_uint64_attribute(
2585	    slice, ATTR_DISK_FOR_SLICE, &disk)) != 0) {
2586	    return (error);
2587	}
2588
2589	*diskp = (dm_descriptor_t)disk;
2590
2591	if (disk == 0) {
2592	    print_get_desc_attr_error(slice, "virtual slice", "disk", error);
2593	    return (-1);
2594	}
2595
2596	return (0);
2597}
2598
2599/*
2600 * FUNCTION:	slice_get_disk(dm_descriptor_t disk, dm_descriptor_t *diskp)
2601 *
2602 * INPUT:	slice	- a dm_descriptor_t handle for a slice
2603 *
2604 * OUTPUT:	diskp	- a pointer to a dm_descriptor_t to hold the
2605 *			disk associated with the input slice
2606 *
2607 * RETURNS:	int	- 0 on success
2608 *			 !0 otherwise.
2609 *
2610 * PURPOSE:	Helper which retrieves the disk for a slice device.
2611 *
2612 *		A slice is actually connected to its disk thru an intermediate
2613 *		device known as the "media". The media concept exists to
2614 *		model drives with removeable disk media. For the purposes
2615 *		of layout, such devices aren't relevant and the intermediate
2616 *		media can mostly be ignored.
2617 */
2618int
2619slice_get_disk(
2620	dm_descriptor_t	slice,
2621	dm_descriptor_t *diskp)
2622{
2623	dm_descriptor_t	*media = NULL;
2624
2625	int	i = 0;
2626	int	error = 0;
2627
2628	*diskp = 0;
2629
2630	if (is_virtual_slice(slice)) {
2631	    return (virtual_slice_get_disk(slice, diskp));
2632	}
2633
2634	media = dm_get_associated_descriptors(slice, DM_MEDIA, &error);
2635	(void) add_descriptors_to_free(media);
2636
2637	if (error != 0) {
2638	    print_get_assoc_desc_error(slice, gettext("media"), error);
2639	} else if ((media == NULL) || (*media == NULL)) {
2640	    print_get_assoc_desc_error(slice, gettext("media"), error);
2641	    error = -1;
2642	}
2643
2644	if (error != 0) {
2645	    return (error);
2646	}
2647
2648	/* slice should have exactly 1 media */
2649	for (i = 0; (media[i] != NULL) && (*diskp == NULL); i++) {
2650	    /* get disk from media */
2651	    dm_descriptor_t *disks = NULL;
2652	    disks = dm_get_associated_descriptors(media[i], DM_DRIVE, &error);
2653	    (void) add_descriptors_to_free(disks);
2654
2655	    if ((error == 0) && (disks != NULL) && (disks[0] != NULL)) {
2656		*diskp = disks[0];
2657	    }
2658	    free(disks);
2659	}
2660
2661	if (media != NULL) {
2662	    free(media);
2663	}
2664
2665	if (*diskp == 0) {
2666	    print_get_desc_attr_error(slice,
2667		    gettext("slice"), gettext("disk"), ENODEV);
2668	    error = -1;
2669	}
2670
2671	return (error);
2672}
2673
2674/*
2675 * FUNCTION:	slice_get_hbas(dm_descriptor_t slice, dlist_t **list)
2676 *
2677 * INPUT:	slice	- a dm_descriptor_t handle for a slice
2678 *
2679 * OUTPUT:	list	- a pointer to a dlist_t list to hold the
2680 *			HBAs associated with the input slice
2681 *
2682 * RETURNS:	int	- 0 on success
2683 *			 !0 otherwise.
2684 *
2685 * PURPOSE:	Helper which retrieves the known HBAs for a slice device.
2686 *
2687 */
2688int
2689slice_get_hbas(
2690	dm_descriptor_t	slice,
2691	dlist_t		**list)
2692{
2693	dm_descriptor_t	disk	= NULL;
2694	int		error	= 0;
2695
2696	*list = NULL;
2697
2698	((error = slice_get_disk(slice, &disk)) != 0) ||
2699	(error = disk_get_hbas(disk, list));
2700
2701	if (*list == NULL) {
2702	    print_get_desc_attr_error(slice, "slice", "HBA", ENODEV);
2703	    error = -1;
2704	}
2705
2706	return (error);
2707}
2708
2709/*
2710 * FUNCTION:	disk_get_associated_desc(dm_descriptor_t disk,
2711 *			dm_desc_type_t assoc_type, char *assoc_type_str,
2712 *			dlist_t **list)
2713 *
2714 * INPUT:	disk	- a dm_descriptor_t handle for a disk
2715 *		assoc_type - the type of associated object to get
2716 *		assoc_type_str - a char * string for the associated type
2717 *
2718 * OUTPUT:	list	- a pointer to a dlist_t list to hold the
2719 *			objects associated with the input disk
2720 *
2721 * RETURNS:	int	- 0 on success
2722 *			 !0 otherwise.
2723 *
2724 * PURPOSE:	Helper which retrieves the associated objects of the
2725 *		requested type for a disk device.
2726 */
2727static int
2728disk_get_associated_desc(
2729	dm_descriptor_t	disk,
2730	dm_desc_type_t 	assoc_type,
2731	char		*assoc_type_str,
2732	dlist_t		**list)
2733{
2734	int	i = 0;
2735	int	error = 0;
2736
2737	dm_descriptor_t	*assoc =
2738	    dm_get_associated_descriptors(disk, assoc_type, &error);
2739
2740	(void) add_descriptors_to_free(assoc);
2741
2742	if (error == 0) {
2743	    for (i = 0;
2744		(assoc != NULL) && (assoc[i] != NULL) && (error == 0);
2745		i++) {
2746		dlist_t *item = dlist_new_item((void *)(uintptr_t)assoc[i]);
2747		if (item == NULL) {
2748		    error = ENOMEM;
2749		} else {
2750		    *list = dlist_append(item, *list, AT_TAIL);
2751		}
2752	    }
2753	} else {
2754	    print_get_assoc_desc_error(disk, assoc_type_str, error);
2755	}
2756
2757	if (assoc != NULL) {
2758	    free(assoc);
2759	}
2760
2761	if (error != 0) {
2762	    dlist_free_items(*list, NULL);
2763	    *list = NULL;
2764	}
2765
2766	return (error);
2767}
2768
2769/*
2770 * FUNCTION:	disk_get_hbas(dm_descriptor_t disk, dlist_t **list)
2771 *
2772 * INPUT:	disk	- a dm_descriptor_t handle for a disk
2773 *
2774 * OUTPUT:	list	- a pointer to a dlist_t list to hold the
2775 *			HBAs associated with the input disk
2776 *
2777 * RETURNS:	int	- 0 on success
2778 *			 !0 otherwise.
2779 *
2780 * PURPOSE:	Helper which retrieves the known HBAs for a disk device.
2781 *
2782 */
2783int
2784disk_get_hbas(
2785	dm_descriptor_t	disk,
2786	dlist_t		**list)
2787{
2788	return (disk_get_associated_desc(disk, DM_CONTROLLER,
2789			gettext("controller"), list));
2790}
2791
2792/*
2793 * FUNCTION:	disk_get_paths(dm_descriptor_t disk, dlist_t **list)
2794 *
2795 * INPUT:	disk	- a dm_descriptor_t handle for a disk
2796 *
2797 * OUTPUT:	list	- a pointer to a dlist_t list to hold the
2798 *			paths associated with the input disk
2799 *
2800 * RETURNS:	int	- 0 on success
2801 *			 !0 otherwise.
2802 *
2803 * PURPOSE:	Helper which retrieves the known paths for a disk device.
2804 *
2805 *		Paths are managed by the MPXIO driver, they represent hardware
2806 *		paths to the disk drive managed by the MPXIO and not visible
2807 *		externally, unlike aliases which are.
2808 */
2809int
2810disk_get_paths(
2811	dm_descriptor_t	disk,
2812	dlist_t		**list)
2813{
2814	return (disk_get_associated_desc(disk, DM_PATH,
2815			gettext("path"), list));
2816}
2817
2818/*
2819 * FUNCTION:	disk_get_aliases(dm_descriptor_t disk, dlist_t **list)
2820 *
2821 * INPUT:	disk	- a dm_descriptor_t handle for a disk
2822 *
2823 * OUTPUT:	list	- a pointer to a dlist_t list to hold the
2824 *			alias descriptors associated with the input disk
2825 *
2826 * RETURNS:	int	- 0 on success
2827 *			 !0 otherwise.
2828 *
2829 * PURPOSE:	Helper which retrieves the known aliases for a disk device.
2830 *
2831 *		Aliases are the different CTD names for the disk drive when
2832 *		MPXIO is not enabled for multipathed drives.
2833 */
2834int
2835disk_get_aliases(
2836	dm_descriptor_t	disk,
2837	dlist_t		**list)
2838{
2839	return (disk_get_associated_desc(disk, DM_ALIAS,
2840			gettext("alias"), list));
2841}
2842
2843/*
2844 * FUNCTION:	compare_string_to_desc_name_or_alias(
2845 *			void *str, void *desc)
2846 *
2847 * INPUT:	str	- opaque pointer
2848 * 		descr	- opaque pointer
2849 *
2850 * RETURNS:	int	- <0 - if str < desc.name
2851 *			   0 - if str == desc.name
2852 *			  >0 - if str > desc.name
2853 *
2854 * PURPOSE:	dlist_t helper which compares a string to the name
2855 *		and aliases associated with the input dm_descriptor_t
2856 *		handle.
2857 *
2858 *		Comparison is done via compare_device_names.
2859 */
2860static int
2861compare_string_to_desc_name_or_alias(
2862	void	*str,
2863	void	*desc)
2864{
2865	char	*dname = NULL;
2866	int	result = -1;
2867
2868	assert(str != (char *)NULL);
2869	assert(desc != (dm_descriptor_t)0);
2870
2871	(void) get_display_name((uintptr_t)desc, &dname);
2872
2873	/* try name first, then aliases */
2874	if ((result = compare_device_names(str, dname)) != 0) {
2875	    dlist_t *aliases = NULL;
2876
2877	    (void) get_aliases((uintptr_t)desc, &aliases);
2878	    if ((aliases != NULL) && (dlist_contains(aliases,
2879			str, compare_device_names) == B_TRUE)) {
2880		result = 0;
2881	    }
2882	    dlist_free_items(aliases, free);
2883	}
2884
2885	return (result);
2886}
2887
2888/*
2889 * FUNCTION:	hba_get_by_name(char *name, dm_descriptor_t *hba)
2890 *
2891 * INPUT:	name	- a char * disk name
2892 *
2893 * OUTPUT:	hba	- a pointer to a dm_descriptor_t to hold the
2894 *			HBA corresponding to the input name, if found
2895 *
2896 * RETURNS:	int	- 0 on success
2897 *			 !0 otherwise
2898 *
2899 * PURPOSE:	Helper which iterates the known HBAs, searching for
2900 *		the one matching name.
2901 *
2902 *		If no HBA matches the name, 0 is returned and the
2903 *		value of 'hba' will be (dm_descriptor_t)0;
2904 */
2905int
2906hba_get_by_name(
2907	char		*name,
2908	dm_descriptor_t *hba)
2909{
2910	int		error = 0;
2911	dlist_t		*list = NULL;
2912	dlist_t		*item = NULL;
2913
2914	*hba = (dm_descriptor_t)0;
2915
2916	if (name == NULL) {
2917	    return (0);
2918	}
2919
2920	if ((error = get_known_hbas(&list)) != 0) {
2921	    return (error);
2922	}
2923
2924	if ((item = dlist_find(list, name,
2925	    compare_string_to_desc_name_or_alias)) != NULL) {
2926	    *hba = (uintptr_t)item->obj;
2927	}
2928
2929	return (error);
2930}
2931
2932/*
2933 * FUNCTION:	disk_get_by_name(char *name, dm_descriptor_t *disk)
2934 *
2935 * INPUT:	name	- a char * disk name
2936 *
2937 * OUTPUT:	disk	- a pointer to a dm_descriptor_t to hold the
2938 *			disk corresponding to the input name, if found
2939 *
2940 * RETURNS:	int	- 0 on success
2941 *			 !0 otherwise.
2942 *
2943 * PURPOSE:	Helper which retrieves a dm_descriptor_t disk handle
2944 *		by name.
2945 *
2946 *		If no disk is found for the input name, variations of
2947 *		the name are tried.
2948 *
2949 *		If the input name is unqualified, an appropriate leading
2950 *		path is prepended.
2951 *
2952 *		If the input name is qualified, the leading path is
2953 *		removed.
2954 *
2955 *		If no disk is found for the variations, 0 is returned
2956 *		and the	value of 'disk' will be (dm_descriptor_t)0;
2957 */
2958int
2959disk_get_by_name(
2960	char		*name,
2961	dm_descriptor_t *disk)
2962{
2963	assert(name != (char *)NULL);
2964
2965	*disk = find_cached_descriptor(name);
2966	if (*disk == (dm_descriptor_t)0) {
2967	    if (name[0] == '/') {
2968		/* fully qualified, try unqualified */
2969		char *cp = strrchr(name, '/');
2970		if (cp != NULL) {
2971		    *disk = find_cached_descriptor(cp + 1);
2972		}
2973	    } else {
2974		/* unqualified, try fully qualified */
2975		char buf[MAXNAMELEN+1];
2976		if (is_ctd_disk_name(name)) {
2977		    (void) snprintf(buf, MAXNAMELEN, "/dev/dsk/%s", name);
2978		} else if (is_did_disk_name(name)) {
2979		    (void) snprintf(buf, MAXNAMELEN, "/dev/did/dsk/%s", name);
2980		}
2981		*disk = find_cached_descriptor(buf);
2982	    }
2983	}
2984
2985	/*
2986	 * since the descriptor cache includes HBAs, disks and slices,
2987	 * what gets returned may not be a disk... make sure it is
2988	 */
2989	if (*disk != (dm_descriptor_t)0) {
2990	    if (dm_get_type(*disk) != DM_DRIVE) {
2991		*disk = (dm_descriptor_t)0;
2992	    }
2993	}
2994
2995	return (0);
2996}
2997
2998/*
2999 * FUNCTION:	slice_get_by_name(char *name, dm_descriptor_t *slice)
3000 *
3001 * INPUT:	name	- a char * slice name
3002 *
3003 * OUTPUT:	slice	- a pointer to a dm_descriptor_t to hold the
3004 *			slice corresponding to the input name, if found.
3005 *
3006 * RETURNS:	int	- 0 on success
3007 *			 !0 otherwise.
3008 *
3009 * PURPOSE:	Helper which iterates the known slices, searching for
3010 *		the one matching name.
3011 *
3012 *		If no slice is found for the input name, variations of
3013 *		the name are tried.
3014 *
3015 *		If the input name is unqualified, an appropriate leading
3016 *		path is prepended.
3017 *
3018 *		If the input name is qualified, the leading path is
3019 *		removed.
3020 *
3021 *		If no slice matches the variations, 0 is returned and the
3022 *		value of 'slice' will be (dm_descriptor_t)0;
3023 */
3024int
3025slice_get_by_name(
3026	char		*name,
3027	dm_descriptor_t *slice)
3028{
3029	assert(name != (char *)NULL);
3030
3031	*slice = find_cached_descriptor(name);
3032	if (*slice == (dm_descriptor_t)0) {
3033	    if (name[0] == '/') {
3034		/* fully qualified, try unqualified */
3035		char *cp = strrchr(name, '/');
3036		if (cp != NULL) {
3037		    *slice = find_cached_descriptor(cp + 1);
3038		}
3039	    } else {
3040		/* unqualified, try fully qualified */
3041		char buf[MAXNAMELEN+1];
3042		if (is_ctd_slice_name(name) || is_ctd_like_slice_name(name) ||
3043			is_bsd_like_slice_name(name)) {
3044		    (void) snprintf(buf, MAXNAMELEN, "/dev/dsk/%s", name);
3045		} else if (is_did_slice_name(name)) {
3046		    (void) snprintf(buf, MAXNAMELEN, "/dev/did/dsk/%s", name);
3047		}
3048		*slice = find_cached_descriptor(buf);
3049	    }
3050	}
3051
3052	/*
3053	 * since the descriptor cache includes HBAs, disks and slices,
3054	 * what gets returned may not be a slice... make sure it is
3055	 */
3056	if (*slice != (dm_descriptor_t)0) {
3057	    if (dm_get_type(*slice) != DM_SLICE &&
3058		is_virtual_slice(*slice) != B_TRUE) {
3059		*slice = (dm_descriptor_t)0;
3060	    }
3061	}
3062
3063	return (0);
3064}
3065
3066/*
3067 * FUNCTION:	extract_hbaname(char *name, char **hbaname)
3068 *
3069 * INPUT:	slicename - a char * device name
3070 *
3071 * OUTPUT:	hbaname - a pointer to a char * to hold the hbaname derived
3072 *			from the input name.
3073 *
3074 * RETURNS:	int	- 0 on success
3075 *			 !0 otherwise.
3076 *
3077 * PURPOSE:	Helper which extracts the HBA name from the input name.
3078 *
3079 *		If the input name is in ctd form, extracts just the cX part,
3080 *		by truncating everything following the last 't'.
3081 *
3082 *		Of course on X86, with IDE drives, there is no 't' in the
3083 *		ctd name, so start by truncating everything following 'd'
3084 *		and then look for 't'.
3085 *
3086 * 		The returned string must be passed to free().
3087 */
3088int
3089extract_hbaname(
3090	char	*name,
3091	char	**hbaname)
3092{
3093	char	*cp;
3094
3095	if (is_ctd_name(name)) {
3096	    if ((*hbaname = strdup(name)) == NULL) {
3097		return (ENOMEM);
3098	    }
3099	    if ((cp = strrchr(*hbaname, 'd')) != NULL) {
3100		*cp = '\0';
3101	    }
3102	    if ((cp = strrchr(*hbaname, 't')) != NULL) {
3103		*cp = '\0';
3104	    }
3105	}
3106
3107	return (0);
3108}
3109
3110/*
3111 * FUNCTION:	extract_diskname(char *slicename, char **diskname)
3112 *
3113 * INPUT:	slicename - a char * slice name
3114 *
3115 * OUTPUT:	diskname - a pointer to a char * to hold the diskname derived
3116 *			from the input slicename.
3117 *
3118 * RETURNS:	int	- 0 on success
3119 *			 !0 otherwise.
3120 *
3121 * PURPOSE:	Helper which extracts the disk's name from a slice name.
3122 *
3123 *		Checks to see if the input slicename is in ctd or did form,
3124 *		and if so, truncates everything following the last 's'.
3125 *
3126 *		If the input slicename is BSD-like, truncate the last
3127 *		character (a-h).
3128 *
3129 * 		The returned string must be passed to free().
3130 */
3131int
3132extract_diskname(
3133	char	*slicename,
3134	char	**diskname)
3135{
3136	char	*cp;
3137
3138	if (is_ctd_slice_name(slicename) || is_did_slice_name(slicename) ||
3139	    is_ctd_like_slice_name(slicename)) {
3140
3141	    if ((*diskname = strdup(slicename)) == NULL) {
3142		return (ENOMEM);
3143	    }
3144	    if ((cp = strrchr(*diskname, 's')) != NULL) {
3145		*cp = '\0';
3146	    }
3147
3148	} else if (is_bsd_like_slice_name(slicename)) {
3149
3150	    if ((*diskname = strdup(slicename)) == NULL) {
3151		return (ENOMEM);
3152	    }
3153	    (*diskname)[strlen((*diskname)-1)] = '\0';
3154
3155	}
3156
3157	return (0);
3158}
3159
3160/*
3161 * FUNCTION:	get_disk_for_named_slice(char *slicename,
3162 *			dm_descriptor_t disk)
3163 *
3164 * INPUT:	slicename - a char * slice name
3165 *
3166 * OUTPUT:	disk	- a pointer to a dm_descriptor_t to hold the
3167 *			disk corresponding to the input name, if found
3168 *
3169 * RETURNS:	int	- 0 on success
3170 *			 !0 otherwise.
3171 *
3172 * PURPOSE:	Helper which locates the disk dm_descriptor_t handle for
3173 *		the input slice name.
3174 *
3175 *		If no disk matches the name, 0 is returned and the
3176 *		value of 'disk' will be (dm_descriptor_t)0;
3177 */
3178int
3179get_disk_for_named_slice(
3180	char		*slicename,
3181	dm_descriptor_t *disk)
3182{
3183	dm_descriptor_t slice = (dm_descriptor_t)0;
3184	int		error = 0;
3185
3186	assert(slicename != NULL);
3187
3188	/* find disk for slice */
3189	if ((error = slice_get_by_name(slicename, &slice)) == 0) {
3190
3191	    if (slice != (dm_descriptor_t)0) {
3192		error = slice_get_disk(slice, disk);
3193	    } else {
3194		/* named slice was created by layout: */
3195		/* need to find disk by name */
3196		char *dname;
3197
3198		error = extract_diskname(slicename, &dname);
3199		if (error == 0) {
3200		    error = disk_get_by_name(dname, disk);
3201		}
3202		free(dname);
3203	    }
3204	}
3205
3206	assert(*disk != (dm_descriptor_t)0);
3207
3208	return (error);
3209}
3210
3211/*
3212 * FUNCTION:	disk_get_reserved_indexes(dm_descriptor_t disk,
3213 *			uint16_t **array)
3214 *
3215 * INPUT:	disk	- a dm_descriptor_t disk handle
3216 *
3217 * RETURNS:	int	- 0 on success
3218 *			 !0 otherwise
3219 *
3220 * PURPOSE:	Retrieves the input disk's list of reserved slice indices.
3221 *
3222 *		The list of reserved indices is stored as an array in
3223 *		the disk's nvpair attribute list.
3224 */
3225static int
3226disk_get_reserved_indexes(
3227	dm_descriptor_t	disk,
3228	uint16_t	**array)
3229{
3230	nvlist_t	*attrs = NULL;
3231	uint_t		nelem = 0;
3232	int		error = 0;
3233
3234	if ((error = get_cached_attributes(disk, &attrs)) != 0) {
3235	    return (error);
3236	}
3237
3238	if ((error = get_uint16_array(
3239	    attrs, ATTR_RESERVED_INDEX, array, &nelem)) != 0) {
3240	    if (error == ENOENT) {
3241		/* no reserved indices yet */
3242		error = 0;
3243	    }
3244	}
3245
3246	return (error);
3247}
3248
3249/*
3250 * FUNCTION:	disk_reserve_index(dm_descriptor_t disk, uint16_t index)
3251 *
3252 * INPUT:	disk	- a disk dm_descirptor_t handle
3253 *		undex	- a VTOC slice index
3254 *
3255 * RETURNS:	int	- 0 on success
3256 *			 !0 otherwise
3257 *
3258 * PURPOSE:	Reserves the input VTOC slice index for the input disk.
3259 *
3260 *		The list of reserved indices is stored as an array in
3261 *		the disk's nvpair attribute list.
3262 */
3263int
3264disk_reserve_index(
3265	dm_descriptor_t	disk,
3266	uint16_t	index)
3267{
3268	nvlist_t	*attrs = NULL;
3269	uint16_t	*oldindexes = NULL;
3270	uint16_t	*newindexes = NULL;
3271	uint_t		nelem = 0;
3272	int		error = 0;
3273	int		i = 0;
3274
3275	if ((error = get_cached_attributes(disk, &attrs)) != 0) {
3276	    return (error);
3277	}
3278
3279	if ((error = get_uint16_array(
3280	    attrs, ATTR_RESERVED_INDEX, &oldindexes, &nelem)) != 0) {
3281	    if (error != ENOENT) {
3282		return (error);
3283	    }
3284	    /* no reserved indices yet */
3285	    error = 0;
3286	}
3287
3288	/* add new index */
3289	newindexes = (uint16_t *)calloc(VTOC_SIZE, sizeof (uint16_t));
3290	if (newindexes != NULL) {
3291	    for (i = 0; i < nelem; i++) {
3292		newindexes[i] = oldindexes[i];
3293	    }
3294	    newindexes[(int)index] = 1;
3295
3296	    error = set_uint16_array(attrs, ATTR_RESERVED_INDEX,
3297		    newindexes, VTOC_SIZE);
3298
3299	    free(newindexes);
3300	} else {
3301	    error = ENOMEM;
3302	}
3303	return (error);
3304}
3305
3306/*
3307 * FUNCTION:	disk_release_index(dm_descriptor_t disk, uint16_t index)
3308 *
3309 * INPUT:	disk	- a disk dm_descirptor_t handle
3310 *		undex	- a VTOC slice index
3311 *
3312 * RETURNS:	int	- 0 on success
3313 *			 !0 otherwise
3314 *
3315 * PURPOSE:	Releases the input VTOC slice index for the input disk.
3316 *		The index was previously reserved by disk_reserve_index()
3317 */
3318int
3319disk_release_index(
3320	dm_descriptor_t	disk,
3321	uint16_t	index)
3322{
3323	nvlist_t	*attrs = NULL;
3324	uint16_t	*oldindexes = NULL;
3325	uint16_t	*newindexes = NULL;
3326	uint_t		nelem = 0;
3327	int		error = 0;
3328	int		i = 0;
3329
3330	if ((error = get_cached_attributes(disk, &attrs)) != 0) {
3331	    return (error);
3332	}
3333
3334	if ((error = get_uint16_array(
3335	    attrs, ATTR_RESERVED_INDEX, &oldindexes, &nelem)) != 0) {
3336	    if (error != ENOENT) {
3337		return (error);
3338	    }
3339	    error = 0;
3340	}
3341
3342	newindexes = (uint16_t *)calloc(VTOC_SIZE, sizeof (uint16_t));
3343	if (newindexes != NULL) {
3344	    for (i = 0; i < nelem; i++) {
3345		newindexes[i] = oldindexes[i];
3346	    }
3347
3348	    /* release index */
3349	    newindexes[(int)index] = 0;
3350
3351	    error = set_uint16_array(attrs, ATTR_RESERVED_INDEX,
3352		    newindexes, VTOC_SIZE);
3353
3354	    free(newindexes);
3355	} else {
3356	    error = ENOMEM;
3357	}
3358
3359	return (error);
3360}
3361
3362/*
3363 * FUNCTION:	print_get_assoc_desc_error(dm_descriptor_t desc, char *which,
3364 *			int error)
3365 *
3366 * INPUT:	desc	- a dm_descriptor_t handle
3367 *		which	- a char * indicating which association
3368 *		error	- an integer error value
3369 *
3370 * PURPOSE:	Utility function to print an error message for a failed
3371 *		call to dm_get_associated_descriptors().
3372 *
3373 *		Extracts the device's CTD name and formats an error message.
3374 */
3375void
3376print_get_assoc_desc_error(
3377	dm_descriptor_t desc,
3378	char		*which,
3379	int		error)
3380{
3381	char *name = "";
3382
3383	(void) get_display_name(desc, &name);
3384	oprintf(OUTPUT_TERSE,
3385		gettext("dm_get_associated_descriptors(%s) for "
3386			"'%s' failed: %d\n"),
3387		which, name, error);
3388
3389	volume_set_error(
3390		gettext("Unexpected error getting associated "
3391			"descriptors for '%s'"),
3392			name);
3393}
3394
3395/*
3396 * FUNCTION:	print_get_desc_attr_error(dm_descriptor_t desc,
3397 *			char *devtype, char *attr, int error)
3398 *
3399 * INPUT:	desc	- a dm_descriptor_t handle
3400 *		devtype	- a char * device type that's being accessed
3401 *		attr	- a char * attribute name
3402 *		error	- an integer error value
3403 *
3404 * PURPOSE:	Shared utility function to print an error message for a failed
3405 *		call to retrieve an attribute for a descriptor.
3406 *
3407 *		Extracts the device's CTD name and formats an error message.
3408 */
3409void
3410print_get_desc_attr_error(
3411	dm_descriptor_t desc,
3412	char		*devtype,
3413	char		*attr,
3414	int		error)
3415{
3416	char *name = "";
3417
3418	(void) get_display_name(desc, &name);
3419	oprintf(OUTPUT_TERSE,
3420		gettext("'%s' get attribute (%s.%s) error: %d\n"),
3421		name, devtype, attr, error);
3422
3423	volume_set_error(
3424		gettext("Unexpected error getting attribute '%s.%s' for '%s'"),
3425			devtype, attr, name);
3426}
3427
3428/*
3429 * FUNCTION:	print_set_desc_attr_error(dm_descriptor_t desc,
3430 *			char *devtype, char *attr, int error)
3431 *
3432 * INPUT:	desc	- a dm_descriptor_t handle
3433 *		devtype	- a char * device type that's being accessed
3434 *		attr	- a char * attribute name
3435 *		error	- an integer error value
3436 *
3437 * PURPOSE:	Shared utility function to print an error message for a failed
3438 *		call to set an attribute for a descriptor.
3439 *
3440 *		Extracts the device's CTD name and formats an error message.
3441 */
3442void
3443print_set_desc_attr_error(
3444	dm_descriptor_t desc,
3445	char		*devtype,
3446	char		*attr,
3447	int		error)
3448{
3449	char *name = "";
3450
3451	(void) get_display_name(desc, &name);
3452	oprintf(OUTPUT_TERSE,
3453		gettext("'%s' set attribute (%s.%s) error: %d\n"),
3454		name, devtype, attr, error);
3455
3456	volume_set_error(
3457		gettext("Unexpected error setting attribute '%s.%s' for '%s'"),
3458			devtype, attr, name);
3459}
3460