xref: /titanic_41/usr/src/cmd/lvm/metassist/layout/layout_request.c (revision b6c8bd52ccb0f3491c2bd1f5867985cef630564a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <assert.h>
30 #include <string.h>
31 #include <libintl.h>
32 
33 #include "volume_error.h"
34 #include "volume_defaults.h"
35 #include "volume_dlist.h"
36 #include "volume_output.h"
37 #include "volume_request.h"
38 
39 #include "layout_device_cache.h"
40 #include "layout_discovery.h"
41 #include "layout_dlist_util.h"
42 #include "layout_request.h"
43 #include "layout_slice.h"
44 #include "layout_validate.h"
45 
46 #define	_LAYOUT_REQUEST_C
47 
48 static char *_request_diskset = NULL;
49 static devconfig_t *_toplevel_request = NULL;
50 static defaults_t *_defaults = NULL;
51 
52 /*
53  * This file contains code which handles various aspects of the
54  * request and defaults devconfig_t structs passed to the layout
55  * module.
56  *
57  * Functions are provided which determine what devices are available
58  * for use by the various volume layout mechanisms. These are based
59  * on the user specified available/unavailable devices included in
60  * a request or in the defaults associated with the destination diskset.
61  */
62 
63 /*
64  * A struct to hold device "specifications" extracted from a user
65  * specified device name.  This struct is used to compare the user's
66  * available and unavailable device specifications against physical
67  * devices attached to the system.
68  *
69  * The spec struct holds one of two different specifications: if the
70  * user supplied device name is parsable as a CTD name, it is parsed
71  * into the component ids.  Otherwise, it is stored as is.
72  *
73  * The CTD name space implies a device hierarchy and metassist
74  * supports an implied wildcarding scheme for the CTD name space.
75  * A CTD specification from the user is of the form cX, cXdX,
76  * cXdXsX, cXtX, cXtXdX, or cXtXdXsX, so it may or may nor
77  * correspond to an individual physical device depending on
78  * the context.
79  *
80  * For example, "c1" can mean the controller/HBA with the
81  * name "c1" or it can mean all devices attached to the
82  * controller named "c1".
83  *
84  * The ctd specs make matching physical devices against a
85  * user specification easier since the matching is based on
86  * the numeric values extracted from the cXtXdXsX string
87  * and not on the strings themselves.  The strings are
88  * troublesome because of situations like "c1" being
89  * compared to "c11t1d0s0" and getting false matches.
90  *
91  * The ID_UNSPECIFIED value is used to flag components
92  * that were not in the CTD name:
93  *
94  * "c3" -> { ctrl=3, target=ID_UNSPECIFIED,
95  *		lun=ID_UNSPECIFIED, slice=ID_UNSPECIFIED }
96  *
97  * "c3t2" -> { ctrl=3, target=2,
98  *		lun=ID_UNSPECIFIED, slice=ID_UNSPECIFIED }
99  */
100 
101 #define	ID_UNSPECIFIED	-1
102 typedef struct {
103 	int	ctrl;
104 	int	target;
105 	int	lun;
106 	int	slice;
107 	boolean_t is_ide;
108 } ctd_spec_t;
109 
110 typedef enum {
111 	SPEC_TYPE_CTD = 0,
112 	SPEC_TYPE_RAW,
113 	SPEC_TYPE_OTHER
114 } spec_type_t;
115 
116 typedef struct {
117 	spec_type_t type;
118 	union {
119 		ctd_spec_t *ctd;
120 		char	*raw;
121 	} data;
122 } device_spec_t;
123 
124 static int get_spec_for_name(
125 	char	*name,
126 	device_spec_t **id);
127 
128 static int create_device_spec(
129 	char	*name,
130 	device_spec_t **spec);
131 
132 static int create_device_ctd_spec(
133 	char	*name,
134 	device_spec_t **spec);
135 
136 static int create_device_raw_spec(
137 	char	*name,
138 	device_spec_t **spec);
139 
140 static void destroy_device_spec(
141 	device_spec_t *spec);
142 
143 static boolean_t ctd_spec_includes_device(
144 	device_spec_t *spec,
145 	device_spec_t *device);
146 
147 static boolean_t raw_spec_includes_device(
148 	device_spec_t *spec,
149 	device_spec_t *device);
150 
151 /*
152  * get_spec_for_name builds up a cached mapping of device
153  * names to the corresponding device_spec_t structs.
154  *
155  * This saves repeatedly converting the device names, which
156  * could get expensive since devices are checked against the
157  * user specified available/unavailable devices a lot.
158  *
159  * The cache is implemented as a list of these structs:
160  */
161 typedef struct {
162 
163 	char		*name;
164 	device_spec_t	*device_spec;
165 
166 } spec_cache_t;
167 
168 static dlist_t	*_spec_cache = NULL;
169 
170 static int destroy_spec_cache();
171 static int compare_name_to_spec_cache_name(
172 	void *name, void *list_item);
173 
174 /*
175  * The user specified available/unavailable devices are
176  * accessed frequently during layout. To make this more
177  * efficient, the char *arrays of available/unavailable
178  * specifications for a request or defaults devconfig_t
179  * object are converted to device_spec_ts the first time
180  * they're accessed and then cached using this struct:
181  */
182 typedef struct {
183 
184 	devconfig_t	*request;
185 
186 	/*
187 	 * avail_specs_list is a list of device spec_t
188 	 * corresponding to available devices specified
189 	 * in the request object
190 	 */
191 	dlist_t *avail_specs_list;
192 
193 	/*
194 	 * unavail_specs_list is a list of device spec_t
195 	 * corresponding to unavailable devices specified
196 	 * in the request object
197 	 */
198 	dlist_t *unavail_specs_list;
199 
200 } request_spec_list_t;
201 
202 dlist_t *_request_spec_list_cache = NULL;
203 
204 static int destroy_request_spec_list_cache();
205 static void destroy_request_spec_list_entry(void *obj);
206 
207 static int compare_request_to_request_spec_list_request(
208 	void *object,
209 	void *list_item);
210 
211 static int convert_usernames_to_specs(
212 	char **specs,
213 	dlist_t **list);
214 
215 /* other private functions */
216 static int is_device_avail(
217 	dm_descriptor_t	desc,
218 	devconfig_t	*request,
219 	boolean_t	*avail);
220 
221 static int is_named_device_avail(
222 	devconfig_t	*request,
223 	char		*device_name,
224 	boolean_t	check_aliases,
225 	boolean_t	*avail);
226 
227 static int avail_list_includes_device_name(
228 	dlist_t		*list,
229 	char		*device_name,
230 	boolean_t	check_aliases,
231 	boolean_t	*includes);
232 
233 static int unavail_list_includes_device_name(
234 	dlist_t		*list,
235 	char		*device_name,
236 	boolean_t	check_aliases,
237 	boolean_t	*includes);
238 
239 static int spec_includes_device_name(
240 	device_spec_t *spec,
241 	char		 *device_name,
242 	boolean_t	check_aliases,
243 	boolean_t	*includes);
244 
245 static boolean_t spec_includes_device(
246 	device_spec_t *spec,
247 	device_spec_t *device);
248 
249 static int disk_get_avail_space(
250 	devconfig_t	*request,
251 	dm_descriptor_t disk,
252 	uint64_t	*avail);
253 
254 static int compare_hba_n_avail_disks(
255 	void		*obj1,
256 	void		*obj2);
257 
258 /*
259  * FUNCTION:	release_request_caches()
260  *
261  * RETURNS:	0
262  *
263  * PURPOSE:	cleanup the module private caches.
264  */
265 int
release_request_caches()266 release_request_caches()
267 {
268 	(void) destroy_request_spec_list_cache();
269 	(void) destroy_spec_cache();
270 
271 	return (0);
272 }
273 /*
274  * FUNCTION:	int set_request_diskset(char *)
275  *
276  * INPUT:	char * - pointer to the diskset name
277  * OUTPUT:	0 - success
278  *		!0 - validation failure
279  * RETURNS:
280  *
281  * PURPOSE:	set the module global diskset name.
282  */
283 int
set_request_diskset(char * dsname)284 set_request_diskset(
285 	char	*dsname)
286 {
287 	_request_diskset = dsname;
288 
289 	if (dsname == NULL || dsname[0] == '\0') {
290 	    volume_set_error(
291 		    gettext("No disk set specified in request\n"));
292 	    return (-1);
293 	}
294 
295 	return (0);
296 }
297 
298 /*
299  * FUNCTION:	char *get_request_diskset()
300  *
301  * INPUT:	none   -
302  * OUTPUT:	none   -
303  * RETURNS:	char * - pointer to the currently set diskset name
304  *
305  * PURPOSE:	get the global name of the current diskset.
306  */
307 char *
get_request_diskset()308 get_request_diskset()
309 {
310 	return (_request_diskset);
311 }
312 
313 /*
314  * FUNCTION:	void unset_request_diskset()
315  *
316  * PURPOSE:	unset the module global diskset name.
317  */
318 void
unset_request_diskset(char * dsname)319 unset_request_diskset(
320 	char	*dsname)
321 {
322 	_request_diskset = NULL;
323 }
324 
325 /*
326  * FUNCTION:	int set_toplevel_request(devconfig_t *)
327  *
328  * INPUT:	devconfig_t * - pointer to the diskset request
329  * OUTPUT:	0 - success
330  *		!0 - validation failure
331  * RETURNS:
332  *
333  * PURPOSE:	set the module global toplevel request struct.
334  *		this will be set within the only public entry
335  *		point to the module -- get_layout()
336  *
337  * SIDEEFFECT:	The devconfig_t's list of available and unavailable
338  * 		devices will be validated.
339  */
340 int
set_toplevel_request(devconfig_t * req)341 set_toplevel_request(
342 	devconfig_t	*req)
343 {
344 	_toplevel_request = req;
345 
346 	return (validate_request_avail_unavail(req));
347 }
348 
349 /*
350  * FUNCTION:	void unset_toplevel_request()
351  *
352  * PURPOSE:	unset the layout module global toplevel request struct.
353  *
354  */
355 void
unset_toplevel_request()356 unset_toplevel_request()
357 {
358 	_toplevel_request = NULL;
359 }
360 
361 /*
362  * FUNCTION:	int set_defaults(devconfig_t *)
363  *
364  * INPUT:	devconfig_t * - pointer to the global defaults devconfig_t
365  * OUTPUT:	0 - success
366  *		!0 - validation failure
367  * RETURNS:
368  *
369  * PURPOSE:	set the module global defaults struct.
370  *		this will be set within the only public entry
371  *		point to the module -- get_layout()
372  *
373  * SIDEEFFECT:	The devconfig_t's list of available and unavailable
374  * 		devices will be validated.
375  */
376 int
set_request_defaults(defaults_t * defaults)377 set_request_defaults(
378 	defaults_t *defaults)
379 {
380 	int	error = 0;
381 	devconfig_t *diskset = NULL;
382 
383 	_defaults = defaults;
384 
385 	if ((error = defaults_get_diskset_by_name(
386 	    _defaults, get_request_diskset(), &diskset)) == 0) {
387 
388 	    error = validate_request_avail_unavail(diskset);
389 
390 	} else if (error == ENOENT) {
391 	    /* no defaults to verify */
392 	    error = 0;
393 	}
394 
395 	return (error);
396 }
397 
398 /*
399  * FUNCTION:	void unset_request_defaults()
400  *
401  * PURPOSE:	unset the layout module global defaults struct.
402  *
403  */
404 void
unset_request_defaults()405 unset_request_defaults()
406 {
407 	_defaults = NULL;
408 }
409 
410 /*
411  * FUNCTION:	get_stripe_min_comp(devconfig_t *req, uint16_t *val)
412  * INPUT:	req	- a devconfig_t pointer to the current request
413  *		val	- pointer to a uint64_t to hold the result
414  *
415  * RETURNS:	int	-  0 - on success
416  *			  !0 - otherwise
417  *
418  * PURPOSE:	helper which determines the minimum of components
419  *		for striped volumes satisfying the input request.
420  *
421  *		The value to use is taken from the input request, the
422  *		toplevel diskset request, the diskset defaults or the
423  *		global defaults.
424  */
425 int
get_stripe_min_comp(devconfig_t * req,uint16_t * val)426 get_stripe_min_comp(
427 	devconfig_t	*req,
428 	uint16_t	*val)
429 {
430 	int		error = 0;
431 
432 	*val = 0;
433 
434 	if ((error = devconfig_get_stripe_mincomp(req, val)) != 0) {
435 	    if (error != ERR_ATTR_UNSET) {
436 		return (error);
437 	    }
438 	}
439 
440 	if (*val == 0) {
441 	    if ((error = defaults_get_stripe_mincomp(
442 		_defaults, get_request_diskset(), val)) != 0) {
443 		if (error != ERR_ATTR_UNSET) {
444 		    return (error);
445 		}
446 	    }
447 	}
448 
449 	return (error);
450 }
451 
452 /*
453  * FUNCTION:	get_stripe_max_comp(devconfig_t *req, uint16_t *val)
454  * INPUT:	req	- a devconfig_t pointer to the current request
455  *		val	- pointer to a uint64_t to hold the result
456  *
457  * RETURNS:	int	-  0 - on success
458  *			  !0 - otherwise
459  *
460  * PURPOSE:	helper which determines the maximum number of components
461  *		for striped volumes satisfying the input request.
462  *
463  *		The value to use is taken from the input request, the
464  *		toplevel diskset request, the diskset defaults or the
465  *		global defaults.
466  */
467 int
get_stripe_max_comp(devconfig_t * req,uint16_t * val)468 get_stripe_max_comp(
469 	devconfig_t	*req,
470 	uint16_t	*val)
471 {
472 	int		error = 0;
473 
474 	*val = 0;
475 
476 	if ((error = devconfig_get_stripe_maxcomp(req, val)) != 0) {
477 	    if (error != ERR_ATTR_UNSET) {
478 		return (error);
479 	    }
480 	}
481 
482 	if (*val == 0) {
483 	    if ((error = defaults_get_stripe_maxcomp(
484 		_defaults, get_request_diskset(), val)) != 0) {
485 		if (error != ERR_ATTR_UNSET) {
486 		    return (error);
487 		}
488 	    }
489 	}
490 
491 	return (error);
492 }
493 
494 /*
495  * FUNCTION:	get_stripe_interlace(devconfig_t *req, uint64_t *val)
496  * INPUT:	req	- a devconfig_t pointer to the current request
497  *		val	- pointer to a uint64_t to hold the result
498  *
499  * RETURNS:	int	-  0 - on success
500  *			  !0 - otherwise
501  *
502  * PURPOSE:	helper which determines the interlace value for striped
503  *		volumes satisfying the input request.
504  *
505  *		The value to use is taken from the input request, the
506  *		toplevel diskset request, the diskset defaults or the
507  *		global defaults.
508  *
509  *		If no value is explictly specified, ERR_ATTR_UNSET is
510  *		returned.
511  */
512 int
get_stripe_interlace(devconfig_t * req,uint64_t * val)513 get_stripe_interlace(
514 	devconfig_t	*req,
515 	uint64_t	*val)
516 {
517 	int		error = 0;
518 
519 	*val = 0;
520 
521 	if ((error = devconfig_get_stripe_interlace(req, val)) != 0) {
522 	    if (error != ERR_ATTR_UNSET) {
523 		return (error);
524 	    }
525 	    error = 0;
526 	}
527 
528 	if (*val == 0) {
529 	    if ((error = defaults_get_stripe_interlace(
530 		_defaults, get_request_diskset(), val)) != 0) {
531 		if (error != ERR_ATTR_UNSET) {
532 		    return (error);
533 		}
534 	    }
535 	}
536 
537 	return (error);
538 }
539 
540 /*
541  * FUNCTION:	get_mirror_read_strategy(devconfig_t *req,
542  *			mirror_read_strategy_t *val)
543  * INPUT:	req	- a devconfig_t pointer to the current request
544  *		val	- pointer to a mirror_read_strategy_t to hold the result
545  *
546  * RETURNS:	int	-  0 - on success
547  *			  !0 - otherwise
548  *
549  * PURPOSE:	helper which determines the write strategy mirrored volumes
550  *		should have for volumes satisfying the input request.
551  *
552  *		The value to use is taken from the input request, the
553  *		toplevel diskset request, the diskset defaults or the
554  *		global defaults.
555  *
556  *		If no value is explictly specified, ERR_ATTR_UNSET is
557  *		returned.
558  */
559 int
get_mirror_read_strategy(devconfig_t * req,mirror_read_strategy_t * val)560 get_mirror_read_strategy(
561 	devconfig_t	*req,
562 	mirror_read_strategy_t	*val)
563 {
564 	int		error = 0;
565 
566 	*val = 0;
567 
568 	if ((error = devconfig_get_mirror_read(req, val)) != 0) {
569 	    if (error != ERR_ATTR_UNSET) {
570 		return (error);
571 	    }
572 	}
573 
574 	if (*val == 0) {
575 	    if ((error = defaults_get_mirror_read(
576 		_defaults, get_request_diskset(), val)) != 0) {
577 		if (error != ERR_ATTR_UNSET) {
578 		    return (error);
579 		}
580 	    }
581 	}
582 
583 	return (error);
584 }
585 
586 /*
587  * FUNCTION:	get_mirror_write_strategy(devconfig_t *req,
588  *			mirror_write_strategy_t *val)
589  * INPUT:	req	- a devconfig_t pointer to the current request
590  *		val	- pointer to a mirror_write_strategy_t to hold result
591  *
592  * RETURNS:	int	-  0 - on success
593  *			  !0 - otherwise
594  *
595  * PURPOSE:	helper which determines the write strategy mirrored volumes
596  *		should have for volumes satisfying the input request.
597  *
598  *		The value to use is taken from the input request, the
599  *		toplevel diskset request, the diskset defaults or the
600  *		global defaults.
601  *
602  *		If no value is explictly specified, ERR_ATTR_UNSET is
603  *		returned.
604  */
605 int
get_mirror_write_strategy(devconfig_t * req,mirror_write_strategy_t * val)606 get_mirror_write_strategy(
607 	devconfig_t	*req,
608 	mirror_write_strategy_t	*val)
609 {
610 	int		error = 0;
611 
612 	*val = 0;
613 
614 	if ((error = devconfig_get_mirror_write(req, val)) != 0) {
615 	    if (error != ERR_ATTR_UNSET) {
616 		return (error);
617 	    }
618 	}
619 
620 	if (*val == 0) {
621 	    if ((error = defaults_get_mirror_write(
622 		_defaults, get_request_diskset(), val)) != 0) {
623 		if (error != ERR_ATTR_UNSET) {
624 		    return (error);
625 		}
626 	    }
627 	}
628 
629 	return (error);
630 }
631 
632 /*
633  * FUNCTION:	get_mirror_pass(devconfig_t *req, uint16_t *val)
634  * INPUT:	req	- a devconfig_t pointer to the current request
635  *		val	- pointer to a uint16_t to hold the result
636  *
637  * RETURNS:	int	-  0 - on success
638  *			  !0 - otherwise
639  *
640  * PURPOSE:	helper which determines the resync pass mirrored volumes
641  *		should have for volumes satisfying the input request.
642  *
643  *		The value to use is taken from the input request, the
644  *		toplevel diskset request, the diskset defaults or the
645  *		global defaults.
646  *
647  *		If no value is explictly specified, ERR_ATTR_UNSET is
648  *		returned.
649  */
650 int
get_mirror_pass(devconfig_t * req,uint16_t * val)651 get_mirror_pass(
652 	devconfig_t	*req,
653 	uint16_t	*val)
654 {
655 	int		error = 0;
656 
657 	*val = 0;
658 
659 	if ((error = devconfig_get_mirror_pass(req, val)) != 0) {
660 	    if (error != ERR_ATTR_UNSET) {
661 		return (error);
662 	    }
663 	}
664 
665 	if (*val == 0) {
666 	    if ((error = defaults_get_mirror_pass(
667 		_defaults, get_request_diskset(), val)) != 0) {
668 		if (error != ERR_ATTR_UNSET) {
669 		    return (error);
670 		}
671 	    }
672 	}
673 
674 	return (error);
675 }
676 
677 /*
678  * FUNCTION:	get_mirror_nsubs(devconfig_t *req, uint16_t *val)
679  * INPUT:	req	- a devconfig_t pointer to the current request
680  *		val	- pointer to a uint16_t to hold the result
681  *
682  * RETURNS:	int	-  0 - on success
683  *			  !0 - otherwise
684  *
685  * PURPOSE:	helper which determines how many submirrors mirrored
686  *		volumes should have for volumes satisfying the input
687  *		request.
688  *
689  *		The value to use is taken from the input request, the
690  *		toplevel diskset request, the diskset defaults or the
691  *		global defaults.
692  */
693 int
get_mirror_nsubs(devconfig_t * req,uint16_t * val)694 get_mirror_nsubs(
695 	devconfig_t	*req,
696 	uint16_t	*val)
697 {
698 	int		error = 0;
699 
700 	*val = 0;
701 
702 	if ((error = devconfig_get_mirror_nsubs(req, val)) != 0) {
703 	    if (error != ERR_ATTR_UNSET) {
704 		return (error);
705 	    }
706 	}
707 
708 	if (*val == 0) {
709 	    if ((error = defaults_get_mirror_nsubs(
710 		_defaults, get_request_diskset(), val)) != 0) {
711 		if (error != ERR_ATTR_UNSET) {
712 		    return (error);
713 		}
714 	    }
715 	}
716 
717 	return (error);
718 }
719 
720 /*
721  * FUNCTION:	get_volume_faultrecov(devconfig_t *req, boolean_t *val)
722  * INPUT:	req	- a devconfig_t pointer to the current request
723  *		val	- pointer to a boolean_t to hold the result
724  *
725  * RETURNS:	int	-  0 - on success
726  *			  !0 - otherwise
727  *
728  * PURPOSE:	helper which determines whether data redundant volumes
729  *		should also have fault recovery (e.g., HSPs) for volumes
730  *		satisfying the input request.
731  *
732  *		The value to use is taken from the input request, the
733  *		toplevel diskset request, the diskset defaults or the
734  *		global defaults.
735  */
736 int
get_volume_faultrecov(devconfig_t * req,boolean_t * val)737 get_volume_faultrecov(
738 	devconfig_t	*req,
739 	boolean_t	*val)
740 {
741 	int		error = 0;
742 
743 	*val = B_FALSE;
744 
745 	if ((error = devconfig_get_volume_usehsp(req, val)) != 0) {
746 	    if (error == ERR_ATTR_UNSET) {
747 		component_type_t	type = TYPE_UNKNOWN;
748 		(void) devconfig_get_type(req, &type);
749 
750 		switch (type) {
751 		case TYPE_MIRROR:
752 		    error = defaults_get_mirror_usehsp(
753 			    _defaults, get_request_diskset(), val);
754 		    break;
755 
756 		case TYPE_STRIPE:
757 		    error = defaults_get_stripe_usehsp(
758 			    _defaults, get_request_diskset(), val);
759 		    break;
760 
761 		case TYPE_CONCAT:
762 		    error = defaults_get_concat_usehsp(
763 			    _defaults, get_request_diskset(), val);
764 		    break;
765 
766 		case TYPE_VOLUME:
767 		    error = defaults_get_volume_usehsp(
768 			    _defaults, get_request_diskset(), val);
769 		    break;
770 		}
771 	    }
772 	}
773 
774 	return (error);
775 }
776 
777 /*
778  * FUNCTION:	get_volume_redundancy_level(devconfig_t *req, uint16_t val)
779  * INPUT:	req	- a devconfig_t pointer to the current request
780  *		val	- pointer to a uint16-t to hold the result
781  *
782  * RETURNS:	int	-  0 - on success
783  *			  !0 - otherwise
784  *
785  * PURPOSE:	helper which determines the appropriate level of data
786  *		redundancy a volume should have for volumes satisfying
787  *		the input request.
788  *
789  *		The value to use is taken from the input request, the
790  *		toplevel diskset request, the diskset defaults or the
791  *		global defaults.
792  */
793 int
get_volume_redundancy_level(devconfig_t * req,uint16_t * val)794 get_volume_redundancy_level(
795 	devconfig_t	*req,
796 	uint16_t	*val)
797 {
798 	int		error = 0;
799 
800 	*val = 0;
801 
802 	if ((error = devconfig_get_volume_redundancy_level(req, val)) != 0) {
803 	    if (error != ERR_ATTR_UNSET) {
804 		return (error);
805 	    }
806 	}
807 
808 	if (*val == 0) {
809 	    if ((error = defaults_get_volume_redundancy_level(
810 		_defaults, get_request_diskset(), val)) != 0) {
811 		if (error != ERR_ATTR_UNSET) {
812 		    return (error);
813 		}
814 	    }
815 	}
816 
817 	return (error);
818 }
819 
820 /*
821  * FUNCTION:	get_volume_npaths(devconfig_t *req, uint16_t val)
822  * INPUT:	req	- a devconfig_t pointer to the current request
823  *		val	- pointer to a uint16-t to hold the result
824  *
825  * RETURNS:	int	-  0 - on success
826  *			  !0 - otherwise
827  *
828  * PURPOSE:	helper which determines the appropriate level of datapath
829  *		redundancy a slice component should have for volumes
830  *		satisfying the input request.
831  *
832  *		The value to use is taken from the input request, the
833  *		toplevel diskset request, the diskset defaults or the
834  *		global defaults.
835  */
836 int
get_volume_npaths(devconfig_t * req,uint16_t * val)837 get_volume_npaths(
838 	devconfig_t	*req,
839 	uint16_t	*val)
840 {
841 	int		error = 0;
842 
843 	*val = 0;
844 
845 	if ((error = devconfig_get_volume_npaths(req, val)) != 0) {
846 	    if (error != ERR_ATTR_UNSET) {
847 		return (error);
848 	    }
849 	}
850 
851 	if (*val == 0) {
852 	    if ((error = defaults_get_volume_npaths(
853 		_defaults, get_request_diskset(), val)) != 0) {
854 		if (error != ERR_ATTR_UNSET) {
855 		    return (error);
856 		}
857 	    }
858 	}
859 
860 	return (error);
861 }
862 
863 /*
864  * FUNCTION:	get_default_hsp_name(devconfig_t *req, char **hspname)
865  * INPUT:	req	- a devconfig_t pointer to the current request
866  *		hspname	- pointer to a char * to hold the result, if any
867  *
868  * RETURNS:	int	-  0 - on success
869  *			  !0 - otherwise
870  *
871  * PURPOSE:	helper which determines the default HSP name for the
872  *		input request.
873  *
874  *		The value to use is taken from the input request, the
875  *		toplevel diskset request, the diskset defaults or the
876  *		global defaults.
877  */
878 int
get_default_hsp_name(devconfig_t * req,char ** name)879 get_default_hsp_name(
880 	devconfig_t	*req,
881 	char		**name)
882 {
883 	int		error = 0;
884 
885 	*name = NULL;
886 
887 	if ((error = defaults_get_hsp_name(_defaults,
888 	    get_request_diskset(), name)) != 0) {
889 	    if (error != ENOENT) {
890 		return (error);
891 	    }
892 	    error = 0;
893 	}
894 
895 	return (error);
896 }
897 
898 /*
899  * FUNCTION:	slice_is_available(char *sname, devconfig_t *request,
900  *			boolean_t bool)
901  * INPUT:	sname	- a slice name
902  *		request	- pointer to a devconfig_t struct representing
903  *				the current layout request being processed
904  * 		bool	- pointer to a boolean to hold the result
905  *
906  * RETURNS:	int	-  0 - on success
907  *			  !0 - otherwise
908  *
909  * PURPOSE:	Validation helper which determines if the named slice can
910  *		be used as a volume component when satisfying the input
911  *		request.
912  *
913  *		Check if the slice appears in the known slice list,
914  *		then check the request's available and unavailable
915  *		device specifications.
916  */
917 int
slice_is_available(char * sname,devconfig_t * request,boolean_t * bool)918 slice_is_available(
919 	char		*sname,
920 	devconfig_t	*request,
921 	boolean_t	*bool)
922 {
923 	dm_descriptor_t	slice = (dm_descriptor_t)0;
924 	int		error = 0;
925 
926 	*bool = B_FALSE;
927 
928 	if ((error = slice_get_by_name(sname, &slice)) != 0) {
929 	    return (error);
930 	}
931 
932 	if (slice == (dm_descriptor_t)0) {
933 	    /* no slice found */
934 	    return (ENODEV);
935 	}
936 
937 	if (error == 0) {
938 	    error = is_named_device_avail(request, sname, B_TRUE, bool);
939 	}
940 
941 	return (error);
942 }
943 
944 /*
945  * FUNCTION:	get_disks_for_target(char *name, dlist_t **disks)
946  *
947  * INPUT:	name	- a char* device CTD name
948  *
949  * OUTPUT:	disks	- disks matching the input target name
950  *
951  * RETURNS:	int	- 0 on success
952  *			 !0 otherwise
953  *
954  * PURPOSE:	Validation helper function which finds all disks "on" the
955  *		input target.
956  *
957  *		The input name is assumed to be a target name, cXtX, and
958  *		the list of known disks is searched to find any disk that
959  *		looks to be "on" that target.
960  *
961  *		"On" is determined by comparing a disk's name and
962  *		aliases to the target to see if they match.
963  */
964 int
get_disks_for_target(char * name,dlist_t ** disks)965 get_disks_for_target(
966 	char *name,
967 	dlist_t **disks)
968 {
969 	int error = 0;
970 	device_spec_t *targetid = NULL;
971 
972 	error = get_spec_for_name(name, &targetid);
973 	if (error == 0) {
974 	    dlist_t *known_disks = NULL;
975 	    dlist_t *iter = NULL;
976 
977 	    get_known_disks(&known_disks);
978 	    for (iter = known_disks;
979 		(iter != NULL) && (error == 0);
980 		iter = iter->next) {
981 
982 		dm_descriptor_t disk = (uintptr_t)iter->obj;
983 		device_spec_t *diskid = NULL;
984 		char	*diskname = NULL;
985 		dlist_t *diskaliases = NULL;
986 		dlist_t *item;
987 
988 		((error = get_display_name(disk, &diskname)) != 0) ||
989 		(error = get_aliases(disk, &diskaliases)) ||
990 		(error = get_spec_for_name(diskname, &diskid));
991 
992 		if (error == 0) {
993 		    if (spec_includes_device(targetid, diskid) == B_TRUE) {
994 			/* add disk */
995 			if ((item = dlist_new_item((void *)(uintptr_t)disk)) ==
996 			    NULL) {
997 			    error = ENOMEM;
998 			} else {
999 			    *disks = dlist_append(item, *disks, AT_HEAD);
1000 			}
1001 		    } else {
1002 			/* check disk's aliases */
1003 			dlist_t *iter2;
1004 			for (iter2 = diskaliases;
1005 			    (iter2 != NULL) && (error == 0);
1006 			    iter2 = iter2->next) {
1007 
1008 			    char *aliasname = NULL;
1009 			    device_spec_t *aliasid = NULL;
1010 			    error = get_display_name(disk, &aliasname);
1011 			    error = get_spec_for_name(aliasname, &aliasid);
1012 
1013 			    if (spec_includes_device(
1014 					targetid, aliasid) == B_TRUE) {
1015 
1016 				/* alias matched, add disk */
1017 				item = dlist_new_item((void *)(uintptr_t)disk);
1018 				if (item == NULL) {
1019 				    error = ENOMEM;
1020 				} else {
1021 				    *disks =
1022 					dlist_append(item, *disks, AT_HEAD);
1023 				}
1024 			    }
1025 			}
1026 		    }
1027 		}
1028 	    }
1029 	}
1030 
1031 	return (error);
1032 }
1033 
1034 /*
1035  * FUNCTION:	select_hbas_with_n_disks(devconfig_t *request,
1036  *			dlist_t	*hbas, int mindisks, dlist_t **selhbas,
1037  *			dlist_t **seldisks)
1038  *
1039  * INPUT:	request	- pointer to a devconfig_t struct representing
1040  *				the current layout request being processed
1041  * 		hbas	- pointer to a list of HBAs
1042  *		mindisks - minimum number of disks required on the HBAs
1043  *
1044  * OUTPUT:	selhbas	- pointer to a list containing the HBAs with at
1045  *				least mindisks available disks.
1046  *		seldisks - pointer to a list containing the available disks
1047  *				for the HBAs in selhbas
1048  *
1049  * RETURNS:	int	-  0 - on success
1050  *			  !0 - otherwise
1051  *
1052  * PURPOSE:	helper which counts the number of available disks associated
1053  *		with each of the input HBAs and adds those that have at
1054  *		least mindisks to the output list.
1055  *
1056  *		Only available disks that have available space are counted.
1057  *
1058  *		Disks connected thru multiple HBAs are only counted for
1059  *		the first HBA they're accessed through.
1060  *
1061  *		The list of HBAs returned will be in descending order,
1062  *		i.e., HBAs with more disks come before those with fewer.
1063  *
1064  *		The returned lists of HBAs and disks must be passed to
1065  *		dlist_free_items() to recover the space allocated to hold
1066  *		each list item.
1067  *
1068  *		for (each HBA) {
1069  *
1070  *		    select HBA
1071  *		    get available disks on HBA
1072  *
1073  *		    for (each disk) {
1074  *			if (disk is not in selected disk list)
1075  *			    add it to the list
1076  *			else
1077  *			    count it as a distinct disk on this HBA
1078  *		    }
1079  *
1080  *		    if (this HBA has >= mindisks distinct disks)
1081  *			add this HBA to the list of returned HBAs
1082  *
1083  *		}
1084  */
1085 int
select_hbas_with_n_disks(devconfig_t * request,dlist_t * hbas,int mindisks,dlist_t ** selhbas,dlist_t ** seldisks)1086 select_hbas_with_n_disks(
1087 	devconfig_t	*request,
1088 	dlist_t		*hbas,
1089 	int		mindisks,
1090 	dlist_t		**selhbas,
1091 	dlist_t		**seldisks)
1092 {
1093 	dlist_t		*iter = NULL;
1094 	int		error = 0;
1095 
1096 	*selhbas = NULL;
1097 	*seldisks = NULL;
1098 
1099 	/* for each input HBA */
1100 	for (iter = hbas; (error == 0) && (iter != NULL); iter = iter->next) {
1101 
1102 	    dm_descriptor_t hba = (uintptr_t)iter->obj;
1103 	    dlist_t *iter2 = NULL;
1104 	    dlist_t *disks = NULL;
1105 	    uint64_t space = 0;
1106 	    uint16_t ndistinct = 0;
1107 
1108 	    error = hba_get_avail_disks_and_space(request, hba, &disks, &space);
1109 
1110 	    /* for each of this HBA's disks */
1111 	    for (iter2 = disks;
1112 		(iter2 != NULL) && (error == 0);
1113 		iter2 = iter2->next) {
1114 
1115 		dm_descriptor_t disk = (uintptr_t)iter2->obj;
1116 
1117 		/* unique disk? has it been seen thru some other HBA? */
1118 		if (dlist_contains(*seldisks, (void *)(uintptr_t)disk,
1119 		    compare_descriptor_names) != B_TRUE) {
1120 
1121 		    /* distinct, add to list of all_distinct */
1122 		    dlist_t *item = dlist_new_item((void *)(uintptr_t)disk);
1123 		    if (item == NULL) {
1124 			error = ENOMEM;
1125 		    } else {
1126 
1127 			*seldisks =
1128 			    dlist_append(item, *seldisks, AT_HEAD);
1129 
1130 			/* increment this HBA's distinct disk count */
1131 			++ndistinct;
1132 		    }
1133 		}
1134 	    }
1135 
1136 	    if (ndistinct >= mindisks) {
1137 
1138 		/* this HBA has minimum # of disks, add to output list */
1139 		dlist_t	*item = dlist_new_item((void *)(uintptr_t)hba);
1140 		if (item == NULL) {
1141 		    error = ENOMEM;
1142 		} else {
1143 		    *selhbas =
1144 			dlist_insert_ordered(
1145 				item, *selhbas, DESCENDING,
1146 				compare_hba_n_avail_disks);
1147 
1148 		    /* save # of disks for ordering the list */
1149 		    hba_set_n_avail_disks(hba, ndistinct);
1150 		}
1151 	    }
1152 
1153 	    dlist_free_items(disks, NULL);
1154 	}
1155 
1156 	if (error != 0) {
1157 	    oprintf(OUTPUT_TERSE,
1158 		    gettext("failed selecting HBAs with n disks: %d\n"),
1159 		    error);
1160 
1161 	    dlist_free_items(*selhbas, NULL);
1162 	    *selhbas = NULL;
1163 	    dlist_free_items(*seldisks, NULL);
1164 	    *seldisks = NULL;
1165 	}
1166 
1167 	return (error);
1168 }
1169 
1170 /*
1171  * FUNCTION:	hba_get_avail_disks_and_space(devconfig_t *request,
1172  *			dm_descriptor_t hba, dlist_t **disks, uint64_t *space)
1173  *
1174  * INPUT:	request	- pointer to a devconfig_t struct representing
1175  *				the current layout request being processed
1176  * 		hba	- dm_descriptor_t handle for an HBA
1177  *
1178  * OUTPUT:	disks	- pointer to a list to hold the computed available
1179  *				disks
1180  * 		avail	- pointer to a uint64_t to hold the aggregate
1181  *				available space on the available disks
1182  *
1183  * RETURNS:	int	-  0 - on success
1184  *			  !0 - otherwise
1185  *
1186  * PURPOSE:	helper which examines the disks associated with the
1187  *		input HBA and assembles a list of those that are available.
1188  *
1189  *		Available is defined as being in the usable list, having
1190  *		unused space and not specifically excluded by the request's
1191  *		list of unavailable devices.
1192  *
1193  *		The returned list must be passed to dlist_free_items()
1194  *		to recover the memory allocated to hold each list item.
1195  */
1196 int
hba_get_avail_disks_and_space(devconfig_t * request,dm_descriptor_t hba,dlist_t ** disks,uint64_t * space)1197 hba_get_avail_disks_and_space(
1198 	devconfig_t	*request,
1199 	dm_descriptor_t	hba,
1200 	dlist_t		**disks,
1201 	uint64_t	*space)
1202 {
1203 	dlist_t		*usable_disks = NULL;
1204 	dlist_t		*iter = NULL;
1205 	int		error = 0;
1206 
1207 	*disks = NULL;
1208 
1209 	/* for each usable disk */
1210 	error = get_usable_disks(&usable_disks);
1211 	for (iter = usable_disks;
1212 	    (error == 0) && (iter != NULL);
1213 	    iter = iter->next) {
1214 
1215 	    dm_descriptor_t disk = (uintptr_t)iter->obj;
1216 	    boolean_t	avail = B_FALSE;
1217 	    dlist_t	*hbas = NULL;
1218 
1219 	    /* is disk attached to HBA in question? */
1220 	    error = disk_get_hbas(disk, &hbas);
1221 	    if (error != 0) {
1222 		continue;
1223 	    }
1224 
1225 	    if (dlist_contains(hbas, (void *)(uintptr_t)hba,
1226 			compare_descriptor_names) == B_TRUE) {
1227 
1228 		/* is disk available? */
1229 		error = is_device_avail(disk, request, &avail);
1230 		if ((error == 0) && (avail == B_TRUE)) {
1231 		    uint64_t disk_space = 0;
1232 
1233 		    /* does disk have available space? */
1234 		    error = disk_get_avail_space(request, disk, &disk_space);
1235 		    if ((error == 0) && (disk_space > 0)) {
1236 
1237 			dlist_t *item = dlist_new_item((void *)(uintptr_t)disk);
1238 			if (item == NULL) {
1239 			    error = ENOMEM;
1240 			} else {
1241 			    *disks = dlist_append(item, *disks, AT_HEAD);
1242 			}
1243 
1244 			*space += disk_space;
1245 		    }
1246 		}
1247 	    }
1248 
1249 	    dlist_free_items(hbas, NULL);
1250 	}
1251 
1252 	if (error != 0) {
1253 	    dlist_free_items(*disks, NULL);
1254 	    *disks = NULL;
1255 	}
1256 
1257 	return (error);
1258 }
1259 
1260 /*
1261  * FUNCTION:	disk_get_avail_space(devconfig_t *request,
1262  *			dlist_t *disks, uint64_t space)
1263  *
1264  * INPUT:	request	- pointer to a devconfig_t struct representing
1265  *				the current layout request being processed
1266  * 		disks	- pointer to a list of disks
1267  * 		space	- pointer to a uint64_t to hold the computed available
1268  *				space
1269  *
1270  * RETURNS:	int	-  0 - on success
1271  *			  !0 - otherwise
1272  *
1273  * PURPOSE:	helper which iterates the input list of disks and determines
1274  *		the aggregate amount of available space they represent.
1275  *
1276  *		Only disk slices that are in the usable slice list and not
1277  *		specifically excluded by the request's list of unavailable
1278  *		devices	will contribute to the aggregate space computation.
1279  */
1280 static int
disk_get_avail_space(devconfig_t * request,dm_descriptor_t disk,uint64_t * space)1281 disk_get_avail_space(
1282 	devconfig_t	*request,
1283 	dm_descriptor_t	disk,
1284 	uint64_t	*space)
1285 {
1286 	dlist_t		*usable_slices = NULL;
1287 	dlist_t		*iter = NULL;
1288 	int		error = 0;
1289 
1290 	*space = 0;
1291 
1292 	/* for each usable slice */
1293 	error = get_usable_slices(&usable_slices);
1294 	for (iter = usable_slices;
1295 	    (error == 0) && (iter != NULL);
1296 	    iter = iter->next) {
1297 
1298 	    dm_descriptor_t slice = (uintptr_t)iter->obj;
1299 	    dm_descriptor_t slice_disk;
1300 	    boolean_t	avail = B_FALSE;
1301 	    boolean_t	reserved = B_FALSE;
1302 	    boolean_t	used = B_FALSE;
1303 
1304 	    /* is slice on disk in question? */
1305 	    if (((error = slice_get_disk(slice, &slice_disk)) != 0) ||
1306 		(compare_descriptor_names((void *)(uintptr_t)slice_disk,
1307 			(void *)(uintptr_t)disk) != 0)) {
1308 		continue;
1309 	    }
1310 
1311 	    /* is slice reserved by an explicit layout request? */
1312 	    if (((error = is_reserved_slice(slice, &reserved)) != 0) ||
1313 		(reserved == B_TRUE)) {
1314 		continue;
1315 	    }
1316 
1317 	    /* is slice used by a pending layout request? */
1318 	    if (((error = is_used_slice(slice, &used)) != 0) ||
1319 		(used == B_TRUE)) {
1320 		continue;
1321 	    }
1322 
1323 	    /* is slice available? */
1324 	    if (((error = is_device_avail(slice, request, &avail)) == 0) &&
1325 		(avail == B_TRUE)) {
1326 
1327 		/* does slice have usable space? */
1328 		uint64_t size = 0;
1329 		if ((error = slice_get_size(slice, &size)) == 0) {
1330 		    *space += size;
1331 		}
1332 	    }
1333 	}
1334 
1335 	return (error);
1336 }
1337 
1338 /*
1339  * FUNCTION:	disks_get_avail_slices(devconfig_t *request,
1340  *			dlist_t *disks, dlist_t **slices)
1341  *
1342  * INPUT:	request	- pointer to a devconfig_t struct representing
1343  *				the current layout request being processed
1344  * 		disks	- pointer to a list of disks
1345  * 		slices	- pointer to an output list of disks
1346  *
1347  * RETURNS:	int	-  0 - on success
1348  *			  !0 - otherwise
1349  *
1350  * PURPOSE:	helper which iterates the input list of disks and builds a
1351  *		new list which contains disks that are determined to be
1352  * 		available for satisfying the input request.
1353  *
1354  *		A disk must contain at least one slice in the available
1355  * 		slice list as well as have available space in order
1356  *		to be available.
1357  */
1358 int
disks_get_avail_slices(devconfig_t * request,dlist_t * disks,dlist_t ** slices)1359 disks_get_avail_slices(
1360 	devconfig_t	*request,
1361 	dlist_t		*disks,
1362 	dlist_t		**slices)
1363 {
1364 	dlist_t		*usable_slices = NULL;
1365 	dlist_t		*iter = NULL;
1366 	int		error = 0;
1367 
1368 	*slices = NULL;
1369 
1370 	/* for each usable slice */
1371 	error = get_usable_slices(&usable_slices);
1372 	for (iter = usable_slices;
1373 	    (error == 0) && (iter != NULL);
1374 	    iter = iter->next) {
1375 
1376 	    dm_descriptor_t slice = (uintptr_t)iter->obj;
1377 	    dm_descriptor_t disk = (dm_descriptor_t)0;
1378 	    boolean_t	avail = B_FALSE;
1379 	    boolean_t	reserved = B_FALSE;
1380 	    boolean_t	used = B_FALSE;
1381 
1382 	    /* is slice on a disk in the input list? */
1383 	    if (((error = slice_get_disk(slice, &disk)) != 0) ||
1384 		(dlist_contains(disks, (void *)(uintptr_t)disk,
1385 			compare_descriptor_names) != B_TRUE)) {
1386 		continue;
1387 	    }
1388 
1389 	    /* is slice reserved by an explicit layout request? */
1390 	    if (((error = is_reserved_slice(slice, &reserved)) != 0) ||
1391 		(reserved == B_TRUE)) {
1392 		continue;
1393 	    }
1394 
1395 	    /* is slice used by a pending layout request? */
1396 	    if (((error = is_used_slice(slice, &used)) != 0) ||
1397 		(used == B_TRUE)) {
1398 		continue;
1399 	    }
1400 
1401 	    /* is slice available? */
1402 	    if (((error = is_device_avail(slice, request, &avail)) == 0) &&
1403 		(avail == B_TRUE)) {
1404 
1405 		/* does slice have available space? */
1406 		uint64_t size = 0;
1407 		error = slice_get_size(slice, &size);
1408 		if ((error == 0) && (size > 0)) {
1409 		    dlist_t *item = dlist_new_item((void *)(uintptr_t)slice);
1410 		    if (item == NULL) {
1411 			error = ENOMEM;
1412 		    } else {
1413 			*slices = dlist_append(item, *slices, AT_TAIL);
1414 		    }
1415 		}
1416 	    }
1417 	}
1418 
1419 	if (error != 0) {
1420 	    dlist_free_items(*slices, NULL);
1421 	    *slices = NULL;
1422 	}
1423 
1424 	return (error);
1425 }
1426 
1427 
1428 /*
1429  * FUNCTION:	get_hbas_and_disks_used_by_volumes(dlist_t *volumes,
1430  *			dlist_t **hbas,	dlist_t **disks)
1431  *
1432  * INPUT:	volumes	- pointer to a list of devconfig_t volumes
1433  *
1434  * OUTPUT:	hbas - a list of HBAs utilized by the input volumes
1435  *		disks - a list of disks utilized by the input volumes
1436  *
1437  * RETURNS:	int	- 0 on success
1438  *			 !0 otherwise
1439  *
1440  * PURPOSE:	An aggregate list of HBAs and disks used by the input volumes
1441  *		is built up by iterating the list of volumes and calling
1442  *		get_hbas_disks_used_by_volume() to determine the HBAs and disk
1443  *		used by each volume.
1444  *
1445  *		The returned lists of HBAs and disks may contain duplicates.
1446  */
1447 int
get_hbas_and_disks_used_by_volumes(dlist_t * volumes,dlist_t ** hbas,dlist_t ** disks)1448 get_hbas_and_disks_used_by_volumes(
1449 	dlist_t		*volumes,
1450 	dlist_t		**hbas,
1451 	dlist_t		**disks)
1452 {
1453 	dlist_t		*iter = NULL;
1454 	int		error = 0;
1455 
1456 	for (iter = volumes;
1457 	    (iter != NULL) && (error == 0);
1458 	    iter = iter->next) {
1459 	    error = get_hbas_and_disks_used_by_volume(
1460 		    (devconfig_t *)iter->obj, hbas, disks);
1461 	}
1462 
1463 	return (error);
1464 }
1465 
1466 /*
1467  * FUNCTION:	get_hbas_and_disks_used_by_volume(devconfig_t *volume,
1468  *			dlist_t **hbas, dlist_t **disks)
1469  *
1470  * INPUT:	volume	- pointer to a devconfig_t volume
1471  *
1472  * OUTPUT:	hbas - a list of HBAs updated to include those utilized
1473  *			by the input volume
1474  *		disks - a list of disks updated to inlclude those utilized
1475  *			by the input volume
1476  *
1477  * RETURNS:	int	- 0 on success
1478  *			 !0 otherwise
1479  *
1480  * PURPOSE:	The volume's components are iterated and the disks and HBAs
1481  *		for each component are determined and appended to the input
1482  *		lists of HBAs and disks.
1483  *
1484  *		The returned lists of HBAs and disks may contain duplicates.
1485  */
1486 int
get_hbas_and_disks_used_by_volume(devconfig_t * volume,dlist_t ** hbas,dlist_t ** disks)1487 get_hbas_and_disks_used_by_volume(
1488 	devconfig_t	*volume,
1489 	dlist_t		**hbas,
1490 	dlist_t		**disks)
1491 {
1492 	dlist_t		*iter = NULL;
1493 	int		error = 0;
1494 
1495 	for (iter = devconfig_get_components(volume);
1496 	    (iter != NULL) && (error == 0);
1497 	    iter = iter->next) {
1498 
1499 	    devconfig_t	*dev = (devconfig_t *)iter->obj;
1500 	    if (devconfig_isA(dev, TYPE_SLICE)) {
1501 
1502 		dm_descriptor_t	disk = NULL;
1503 		char		*name = NULL;
1504 
1505 		/* get disk for component slice */
1506 		((error = devconfig_get_name(dev, &name)) != 0) ||
1507 		(error = get_disk_for_named_slice(name, &disk));
1508 		if (error == 0) {
1509 		    dlist_t *item = dlist_new_item((void *)(uintptr_t)disk);
1510 		    if (item == NULL) {
1511 			error = ENOMEM;
1512 		    } else {
1513 			*disks = dlist_append(item, *disks, AT_HEAD);
1514 		    }
1515 		}
1516 
1517 		/* get HBAs for disk */
1518 		if (error == 0) {
1519 		    dlist_t *disk_hbas = NULL;
1520 		    if ((error = disk_get_hbas(disk, &disk_hbas)) == 0) {
1521 			/* the hba list may contain dups, but that's ok */
1522 			*hbas = dlist_append(disk_hbas, *hbas, AT_HEAD);
1523 		    }
1524 		}
1525 
1526 	    } else if (devconfig_isA(dev, TYPE_MIRROR)) {
1527 
1528 		/* collect info for submirrors */
1529 		dlist_t *iter1;
1530 		for (iter1 = devconfig_get_components(dev);
1531 		    (iter1 != NULL) && (error == 0);
1532 		    iter1 = iter1->next) {
1533 		    error = get_hbas_and_disks_used_by_volume(
1534 			    (devconfig_t *)iter1->obj, hbas, disks);
1535 		}
1536 
1537 	    }
1538 	}
1539 
1540 	return (error);
1541 }
1542 
1543 /*
1544  * FUNCTION:	compare_hba_n_avail_disks(void *obj1, void *obj2)
1545  *
1546  * INPUT:	obj1	- opaque pointer
1547  * 		obj2	- opaque pointer
1548  *
1549  * RETURNS:	int	- <0 - if obj1 has fewer available disks than obj2
1550  *			   0 - if obj1 has the same # of available disks as obj2
1551  *			  >0 - if obj1 has more available disks than obj2
1552  *
1553  * PURPOSE:	dlist_t helper which compares the number of available disks
1554  *		for two HBAs represented as dm_descriptor_t handles.
1555  *
1556  *		Both input objects are assumed to be dm_descriptor_t handles.
1557  *
1558  *		The number of available disks associated with the HBAs was
1559  *		computed and saved in select_hbas_with_n_disks(), this
1560  *		function just checks the saved values.
1561  */
1562 static int
compare_hba_n_avail_disks(void * obj1,void * obj2)1563 compare_hba_n_avail_disks(
1564 	void		*obj1,
1565 	void		*obj2)
1566 {
1567 	uint16_t	n1 = 0;
1568 	uint16_t	n2 = 0;
1569 
1570 	assert(obj1 != NULL);
1571 	assert(obj2 != NULL);
1572 
1573 	(void) hba_get_n_avail_disks((uintptr_t)obj1, &n1);
1574 	(void) hba_get_n_avail_disks((uintptr_t)obj2, &n2);
1575 
1576 	return ((int)n1 - n2);
1577 }
1578 
1579 /*
1580  * FUNCTION:	is_device_avail(dm_descriptor_t desc,
1581  *			devconfig_t *request, boolean_t *avail)
1582  *
1583  * INPUT:	desc	- a dm_descriptor_t device handle
1584  *		request	- pointer to a devconfig_t struct representing
1585  *				the current layout request being processed
1586  * 		avail	- pointer to a boolean to hold the result
1587  *
1588  * RETURNS:	int	-  0 - on success
1589  *			  !0 - otherwise
1590  *
1591  * PURPOSE:	Internal helper which determines if the input device can
1592  *		be used as a volume component when satisfying the input
1593  *		request.
1594  *
1595  *		The device is assumed to be a known valid device.
1596  *
1597  *		The function checks if the device passes the request's
1598  *		available and unavailable device specifications.
1599  *
1600  *		The input device name may be either a DID name or a CTD
1601  *		name.  All name comparisons are done using the CTD name.
1602  */
1603 static int
is_device_avail(dm_descriptor_t desc,devconfig_t * request,boolean_t * avail)1604 is_device_avail(
1605 	dm_descriptor_t	desc,
1606 	devconfig_t	*request,
1607 	boolean_t	*avail)
1608 {
1609 	char		*name = NULL;
1610 	int		error = 0;
1611 
1612 	*avail = B_FALSE;
1613 
1614 	if ((error = get_display_name(desc, &name)) == 0) {
1615 	    error = is_named_device_avail(request, name, B_TRUE, avail);
1616 	}
1617 
1618 	return (error);
1619 }
1620 
1621 /*
1622  * FUNCTION:	compare_request_to_request_spec_list_request(
1623  *			void *request, void *list_item)
1624  *
1625  * INPUT:	request	- opaque pointer to a devconfig_t
1626  * 		list_item - opaque pointer to a request_spec_list_t
1627  *
1628  * RETURNS:	int	- 0 - if request is the same as list_item->request
1629  *			  !0 - otherwise
1630  *
1631  * PURPOSE:	dlist_t helper which compares the input request pointer
1632  *		to the list_item's request pointer for equality.
1633  *
1634  *		This function is the lookup mechanism for the lists of
1635  *		cached device_spec_ts representing available/unavailable
1636  *		devices for a given defaults_t request/defaults struct.
1637  *
1638  *		The defaults_t struct pointer is the lookup key.
1639  */
1640 static int
compare_request_to_request_spec_list_request(void * request,void * list_item)1641 compare_request_to_request_spec_list_request(
1642 	void *request,
1643 	void *list_item)
1644 {
1645 	request_spec_list_t *entry =
1646 	    (request_spec_list_t *)list_item;
1647 
1648 	assert(request != NULL);
1649 	assert(entry != NULL);
1650 
1651 	/* compare two devconfig_t pointers, if identical, return 0 */
1652 	return ((devconfig_t *)request != entry->request);
1653 }
1654 
1655 /*
1656  * FUNCTION:	compare_device_spec_specificity(void *spec1, void *spec2)
1657  *
1658  * INPUT:	spec1	- opaque pointer to a device_spec_t
1659  * 		spec2	- opaque pointer to a device_spec_t
1660  *
1661  * RETURNS:	int	- <0 - if spec1 is less specific than spec2
1662  *			   0 - if spec1 is as specific than spec2
1663  *			  >0 - if spec1 is more specific than spec2
1664  *
1665  * PURPOSE:	dlist_t helper which compares the level of specificity
1666  *		in the two input device_spec_t structs.  The one
1667  *		which specifies more "components" of a cXtXdXsX device
1668  *		name is considered more specific.
1669  */
1670 static int
compare_device_spec_specificity(void * spec1,void * spec2)1671 compare_device_spec_specificity(
1672 	void	*spec1,
1673 	void	*spec2)
1674 {
1675 	if (spec1 == NULL || spec2 == NULL) {
1676 	    return (-1);
1677 	}
1678 
1679 	if ((((device_spec_t *)spec1)->data.ctd->slice != ID_UNSPECIFIED) &&
1680 	    (((device_spec_t *)spec2)->data.ctd->slice == ID_UNSPECIFIED)) {
1681 	    /* spec1 has slice, spec2 does not, spec1 more specific */
1682 	    return (1);
1683 	}
1684 
1685 	if ((((device_spec_t *)spec2)->data.ctd->slice != ID_UNSPECIFIED) &&
1686 	    (((device_spec_t *)spec1)->data.ctd->slice == ID_UNSPECIFIED)) {
1687 	    /* spec2 has slice, spec1 does not, spec2 more specific */
1688 	    return (-1);
1689 	}
1690 
1691 	if ((((device_spec_t *)spec2)->data.ctd->slice != ID_UNSPECIFIED) &&
1692 	    (((device_spec_t *)spec1)->data.ctd->slice != ID_UNSPECIFIED)) {
1693 	    /* both spec1 and spec2 have slice */
1694 	    return (0);
1695 	}
1696 
1697 	if ((((device_spec_t *)spec1)->data.ctd->lun != ID_UNSPECIFIED) &&
1698 	    (((device_spec_t *)spec2)->data.ctd->lun == ID_UNSPECIFIED)) {
1699 	    /* spec1 has lun, spec2 does not, spec1 more specific */
1700 	    return (1);
1701 	}
1702 
1703 	if ((((device_spec_t *)spec2)->data.ctd->lun != ID_UNSPECIFIED) &&
1704 	    (((device_spec_t *)spec1)->data.ctd->lun == ID_UNSPECIFIED)) {
1705 	    /* spec2 has lun, spec1 does not, spec2 more specific */
1706 	    return (-1);
1707 	}
1708 
1709 	if ((((device_spec_t *)spec2)->data.ctd->lun != ID_UNSPECIFIED) &&
1710 	    (((device_spec_t *)spec1)->data.ctd->lun != ID_UNSPECIFIED)) {
1711 	    /* both spec1 and spec2 have lun */
1712 	    return (0);
1713 	}
1714 
1715 	if ((((device_spec_t *)spec1)->data.ctd->target != ID_UNSPECIFIED) &&
1716 	    (((device_spec_t *)spec2)->data.ctd->target == ID_UNSPECIFIED)) {
1717 	    /* spec1 has target, spec2 does not, spec1 more specific */
1718 	    return (1);
1719 	}
1720 
1721 	if ((((device_spec_t *)spec2)->data.ctd->target != ID_UNSPECIFIED) &&
1722 	    (((device_spec_t *)spec1)->data.ctd->target == ID_UNSPECIFIED)) {
1723 	    /* spec2 has target, spec1 does not, spec2 more specific */
1724 	    return (-1);
1725 	}
1726 
1727 	if ((((device_spec_t *)spec2)->data.ctd->target != ID_UNSPECIFIED) &&
1728 	    (((device_spec_t *)spec1)->data.ctd->target != ID_UNSPECIFIED)) {
1729 	    /* both spec1 and spec2 have target */
1730 	    return (0);
1731 	}
1732 
1733 	/* both specify just ctrl */
1734 	return (0);
1735 }
1736 
1737 /*
1738  * FUNCTION:	find_request_spec_list_entry(devconfig_t *request)
1739  *
1740  * INPUT:	request	- pointer to a devconfig_t struct
1741  *
1742  * RETURNS:	request_spec_list_entry - pointer to a
1743  *			request_spec_list_entry struct
1744  *
1745  * PURPOSE:	Lookup function which encapsulates the details of locating
1746  *		the device_spec_list_t cache entry for the input request.
1747  */
1748 static request_spec_list_t *
find_request_spec_list_entry(devconfig_t * request)1749 find_request_spec_list_entry(
1750 	devconfig_t *request)
1751 {
1752 	dlist_t *list_item = NULL;
1753 	request_spec_list_t *entry = NULL;
1754 
1755 	list_item = dlist_find(
1756 		_request_spec_list_cache,
1757 		(void *)request,
1758 		compare_request_to_request_spec_list_request);
1759 
1760 	if (list_item != NULL) {
1761 	    entry = (request_spec_list_t *)list_item->obj;
1762 	}
1763 
1764 	return (entry);
1765 }
1766 
1767 /*
1768  * FUNCTION:	add_request_spec_list_entry(devconfig_t *request,
1769  *			char **avail_device_specs, char **unavail_device_specs,
1770  *			request_spec_list_entry_t **entry)
1771  *
1772  * INPUT:	entry - pointer to the request_spec_list_entry struct to be
1773  *			added to the cache.
1774  *
1775  * RETURNS:	int	- 0 on success
1776  *			 !0 otherwise.
1777  *
1778  * PURPOSE:	Function which encapsulates the details of adding a
1779  *		device_spec_list_t cache entry.
1780  */
1781 static int
add_request_spec_list_entry(request_spec_list_t * entry)1782 add_request_spec_list_entry(
1783 	request_spec_list_t *entry)
1784 {
1785 	dlist_t *list_item = dlist_new_item((void *)entry);
1786 
1787 	if (list_item == NULL) {
1788 	    return (ENOMEM);
1789 	}
1790 
1791 	_request_spec_list_cache = dlist_append(list_item,
1792 		_request_spec_list_cache, AT_HEAD);
1793 
1794 	return (0);
1795 }
1796 
1797 /*
1798  * FUNCTION:	make_request_spec_list_entry(devconfig_t *request,
1799  *			char **avail_device_specs, char **unavail_device_specs,
1800  *			request_spec_list_entry_t **entry)
1801  *
1802  * INPUT:	request	- pointer to a devconfig_t struct
1803  *		avail_device_specs - char * array of user specified available
1804  *			devices associated with the input request
1805  *		unavail_device_specs - char * array of user specified
1806  *			unavailable devices associated with the input
1807  *			request
1808  *
1809  * RETURNS:	int	- 0 on success
1810  *			 !0 otherwise.
1811  *
1812  * PURPOSE:	Function which encapsulates the details of generating a new
1813  *		the device_spec_list_t cache entry for the input request
1814  *		and its lists of avail/unavail devices.
1815  *
1816  *		Converts the input arrays of (un)available device names into
1817  *		equivalent lists of device_spec_t structs.
1818  *
1819  *		Creates a new cache entry, populates it and adds it to the
1820  *		cache.
1821  */
1822 static int
make_request_spec_list_entry(devconfig_t * request,char ** avail_device_specs,char ** unavail_device_specs,request_spec_list_t ** entry)1823 make_request_spec_list_entry(
1824 	devconfig_t *request,
1825 	char	**avail_device_specs,
1826 	char	**unavail_device_specs,
1827 	request_spec_list_t **entry)
1828 {
1829 	int error = 0;
1830 	dlist_t *list = NULL;
1831 
1832 	*entry = calloc(1, sizeof (request_spec_list_t));
1833 	if (*entry == NULL) {
1834 	    return (ENOMEM);
1835 	}
1836 
1837 	(*entry)->request = request;
1838 
1839 	/*
1840 	 * map the avail_device_name array into a list of device_spec_t
1841 	 * and save the list as the entry's available list
1842 	 */
1843 	error = convert_usernames_to_specs(
1844 		avail_device_specs, &list);
1845 
1846 	if (error == 0) {
1847 	    (*entry)->avail_specs_list = list;
1848 	}
1849 
1850 	/*
1851 	 * map the unavail_device_name array into a list of device_spec_t
1852 	 * and save the list as the entry's unavailable list
1853 	 */
1854 	list = NULL;
1855 	error = convert_usernames_to_specs(
1856 		unavail_device_specs, &list);
1857 
1858 	if (error == 0) {
1859 	    (*entry)->unavail_specs_list = list;
1860 	}
1861 
1862 	if (error != 0) {
1863 	    /* delete the partial entry */
1864 	    destroy_request_spec_list_entry((void *)*entry);
1865 	    *entry = NULL;
1866 	}
1867 
1868 	return (error);
1869 }
1870 
1871 /*
1872  * FUNCTION:	convert_usernames_to_specs(char **specs, dlist_t **list)
1873  *
1874  * INPUT:	specs	- char * array of device CTD names
1875  *
1876  * OUTPUT:	list	- pointer to a list of device_spec_t corresponding
1877  *				to each name in the input array
1878  *
1879  * RETURNS:	int	- 0 on success
1880  *			 !0 otherwise.
1881  *
1882  * PURPOSE:	Function which converts the input CTD device names to the
1883  *		equivalent device_spec_t structs.
1884  *
1885  *		Iterates the input array and converts each CTD name to a
1886  *		device_spec_t using get_spec_for_name().
1887  */
1888 static int
convert_usernames_to_specs(char ** specs,dlist_t ** list)1889 convert_usernames_to_specs(
1890 	char	**specs,
1891 	dlist_t **list)
1892 {
1893 	int i = 0;
1894 	int error = 0;
1895 
1896 	/*
1897 	 * For each spec in the array, get the corresponding
1898 	 * device_spec_t and add it to the list.
1899 	 *
1900 	 * Any spec in the array that looks to be a DID name
1901 	 * is first converted to its equivalent CTD name.
1902 	 */
1903 	for (i = 0;
1904 	    (specs != NULL) && (specs[i] != NULL) && (error == 0);
1905 	    i++) {
1906 
1907 	    device_spec_t *spec = NULL;
1908 	    char *userspec = specs[i];
1909 
1910 	    error = get_spec_for_name(userspec, &spec);
1911 	    if ((error == 0) && (spec != NULL)) {
1912 		dlist_t *list_item = dlist_new_item((void *)spec);
1913 		if (spec == NULL) {
1914 		    error = ENOMEM;
1915 		} else {
1916 		    *list = dlist_insert_ordered
1917 			(list_item, *list, DESCENDING,
1918 				compare_device_spec_specificity);
1919 		}
1920 	    }
1921 	}
1922 
1923 	if (error != 0) {
1924 	    /* the device_spec_t in the list items are maintained */
1925 	    /* in a cache elsewhere, so don't free them here. */
1926 	    dlist_free_items(*list, NULL);
1927 	    *list = NULL;
1928 	}
1929 
1930 	return (error);
1931 }
1932 
1933 /*
1934  * FUNCTION:	destroy_request_spec_list_entry(void *entry)
1935  *
1936  * INPUT:	entry	- opaque pointer to a request_spec_list_t
1937  *
1938  * RETURNS:	nothing
1939  *
1940  * PURPOSE:	Function which reclaims memory allocated to a
1941  *		request_spec_list_t.
1942  *
1943  *		Frees memory allocated to the avail_spec_list and
1944  *		unavail_spec_list.  Entries in the list are not freed,
1945  *		since they are owned by the device_spec cache.
1946  */
1947 static void
destroy_request_spec_list_entry(void * obj)1948 destroy_request_spec_list_entry(
1949 	void *obj)
1950 {
1951 	request_spec_list_t *entry = (request_spec_list_t *)obj;
1952 
1953 	if (entry != NULL) {
1954 	    /* items in the list are in the spec_cache and will */
1955 	    /* be cleaned up when it is destroyed. */
1956 	    dlist_free_items(entry->avail_specs_list, NULL);
1957 	    dlist_free_items(entry->unavail_specs_list, NULL);
1958 	    free(entry);
1959 	}
1960 }
1961 
1962 /*
1963  * FUNCTION:	destroy_request_spec_list_cache()
1964  *
1965  * RETURNS:	int	- 0 on success
1966  *			 !0 otherwise.
1967  *
1968  * PURPOSE:	Function which destroys all entries in the request_spec_list
1969  *		cache.
1970  */
1971 static int
destroy_request_spec_list_cache()1972 destroy_request_spec_list_cache()
1973 {
1974 	dlist_free_items(_request_spec_list_cache,
1975 		destroy_request_spec_list_entry);
1976 	_request_spec_list_cache = NULL;
1977 
1978 	return (0);
1979 }
1980 
1981 /*
1982  * FUNCTION:	get_request_avail_spec_list(devconfig_t *request,
1983  *			dlist_t **list)
1984  *
1985  * INPUT:	request	- a pointer to a devconfig_t
1986  *
1987  * OUTPUT:	list	- pointer to a list of device_spec_t corresponding
1988  *				to the devices specified as available by the
1989  *				input request.
1990  *
1991  * RETURNS:	int	- 0 on success
1992  *			 !0 otherwise.
1993  *
1994  * PURPOSE:	Function which locates or builds the list of device_spec_t
1995  *		for the available devices specified in the input request.
1996  *
1997  *		Looks up the input request in the request_spec_list cache.
1998  *		If there is currently no entry in the cache for the request,
1999  *		an entry is built and added.
2000  *
2001  *		The entry's list of available device_spec_t is returned.
2002  */
2003 static int
get_request_avail_spec_list(devconfig_t * request,dlist_t ** list)2004 get_request_avail_spec_list(
2005 	devconfig_t *request,
2006 	dlist_t	    **list)
2007 {
2008 	request_spec_list_t *entry = NULL;
2009 	int error = 0;
2010 
2011 	if ((entry = find_request_spec_list_entry(request)) == NULL) {
2012 
2013 	    /* create cache entry for this request */
2014 	    error = make_request_spec_list_entry(
2015 		    request,
2016 		    devconfig_get_available(request),
2017 		    devconfig_get_unavailable(request),
2018 		    &entry);
2019 
2020 	    if ((error == 0) && (entry != NULL)) {
2021 		if ((error = add_request_spec_list_entry(entry)) != 0) {
2022 		    destroy_request_spec_list_entry(entry);
2023 		    entry = NULL;
2024 		}
2025 	    }
2026 	}
2027 
2028 	if ((error == 0) && (entry != NULL)) {
2029 	    *list = entry->avail_specs_list;
2030 	}
2031 
2032 	return (error);
2033 }
2034 
2035 /*
2036  * FUNCTION:	get_request_unavail_spec_list(devconfig_t *request,
2037  *			dlist_t **list)
2038  *
2039  * INPUT:	request	- a pointer to a devconfig_t
2040  *
2041  * OUTPUT:	list	- pointer to a list of device_spec_t corresponding
2042  *				to the devices specified as unavailable by the
2043  *				input request.
2044  *
2045  * RETURNS:	int	- 0 on success
2046  *			 !0 otherwise.
2047  *
2048  * PURPOSE:	Function which locates or builds the list of device_spec_t
2049  *		for the unavailable devices specified in the input request.
2050  *
2051  *		Looks up the input request in the request_spec_list cache.
2052  *		If there is currently no entry in the cache for the request,
2053  *		an entry is built and added.
2054  *
2055  *		The entry's list of unavailable device_spec_t is returned.
2056  */
2057 static int
get_request_unavail_spec_list(devconfig_t * request,dlist_t ** list)2058 get_request_unavail_spec_list(
2059 	devconfig_t *request,
2060 	dlist_t	    **list)
2061 {
2062 	request_spec_list_t *entry = NULL;
2063 	int error = 0;
2064 
2065 	if ((entry = find_request_spec_list_entry(request)) == NULL) {
2066 
2067 	    /* create new entry for this request */
2068 	    error = make_request_spec_list_entry(
2069 		    request,
2070 		    devconfig_get_available(request),
2071 		    devconfig_get_unavailable(request),
2072 		    &entry);
2073 
2074 	    if ((error == 0) && (entry != NULL)) {
2075 		if ((error = add_request_spec_list_entry(entry)) != 0) {
2076 		    destroy_request_spec_list_entry(entry);
2077 		    entry = NULL;
2078 		}
2079 	    }
2080 	}
2081 
2082 	if ((error == 0) && (entry != NULL)) {
2083 	    *list = entry->unavail_specs_list;
2084 	}
2085 
2086 	return (error);
2087 }
2088 
2089 /*
2090  * FUNCTION:	get_default_avail_spec_list(defaults_t *defaults,
2091  *			char *dsname, dlist_t **list)
2092  *
2093  * INPUT:	defaults - a pointer to a defaults_t struct
2094  *		dsname	- the name of the diskset whose defaults should be used
2095  *
2096  * OUTPUT:	list	- pointer to a list of device_spec_t corresponding
2097  *				to the devices specified as available by the
2098  *				defaults for the named diskset, or the global
2099  *				defaults for all disksets.
2100  *
2101  * RETURNS:	int	- 0 on success
2102  *			 !0 otherwise.
2103  *
2104  * PURPOSE:	Function which locates or builds the list of device_spec_t
2105  *		for the available devices for the named diskset.
2106  *
2107  *		Locates the defaults for the named diskset, if there are none,
2108  *		locates the global defaults for all disksets.
2109  *
2110  *		The defaults devconfig_t struct is then used to look up the
2111  *		the corresponding entry in the request_spec_list cache.
2112  *
2113  *		If there is currently no entry in the cache for the defaults,
2114  *		an entry is built and added.
2115  *
2116  *		The entry's list of available device_spec_t is returned.
2117  */
2118 static int
get_default_avail_spec_list(defaults_t * alldefaults,char * dsname,dlist_t ** list)2119 get_default_avail_spec_list(
2120 	defaults_t *alldefaults,
2121 	char	*dsname,
2122 	dlist_t	    **list)
2123 {
2124 	request_spec_list_t *entry = NULL;
2125 	devconfig_t *defaults = NULL;
2126 	int error = 0;
2127 
2128 	/* Get diskset defaults, or global if none for diskset */
2129 	error = defaults_get_diskset_by_name(
2130 		alldefaults, dsname, &defaults);
2131 
2132 	if (error != 0) {
2133 	    if (error == ENOENT) {
2134 		/* to get global defaults, pass a NULL diskset name */
2135 		error = defaults_get_diskset_by_name(
2136 			alldefaults, NULL, &defaults);
2137 	    }
2138 
2139 	    if (error != 0) {
2140 		if (error != ENOENT) {
2141 		    oprintf(OUTPUT_DEBUG,
2142 			    gettext("get defaults for %s returned %d\n"),
2143 			    dsname, error);
2144 		} else {
2145 		    error = 0;
2146 		}
2147 	    }
2148 	}
2149 
2150 	if ((entry = find_request_spec_list_entry(defaults)) == NULL) {
2151 
2152 	    /* create new entry for these defaults */
2153 	    error = make_request_spec_list_entry(
2154 		    defaults,
2155 		    devconfig_get_available(defaults),
2156 		    devconfig_get_unavailable(defaults),
2157 		    &entry);
2158 
2159 	    if ((error == 0) && (entry != NULL)) {
2160 		if ((error = add_request_spec_list_entry(entry)) != 0) {
2161 		    destroy_request_spec_list_entry(entry);
2162 		    entry = NULL;
2163 		}
2164 	    }
2165 	}
2166 
2167 	if ((error == 0) && (entry != NULL)) {
2168 	    *list = entry->avail_specs_list;
2169 	}
2170 
2171 	return (error);
2172 }
2173 
2174 /*
2175  * FUNCTION:	get_default_unavail_spec_list(defaults_t *defaults,
2176  *			char *dsname, dlist_t **list)
2177  *
2178  * INPUT:	defaults - a pointer to a defaults_t struct
2179  *		dsname	- the name of the diskset whose defaults should be used
2180  *
2181  * OUTPUT:	list	- pointer to a list of device_spec_t corresponding
2182  *				to the devices specified as unavailable by the
2183  *				defaults for the named diskset, or the global
2184  *				defaults for all disksets.
2185  *
2186  * RETURNS:	int	- 0 on success
2187  *			 !0 otherwise.
2188  *
2189  * PURPOSE:	Function which locates or builds the list of device_spec_t
2190  *		for the unavailable devices for the named diskset.
2191  *
2192  *		Locates the defaults for the named diskset, if there are none,
2193  *		locates the global defaults for all disksets.
2194  *
2195  *		The defaults devconfig_t struct is then used to look up the
2196  *		the corresponding entry in the request_spec_list cache.
2197  *
2198  *		If there is currently no entry in the cache for the defaults,
2199  *		an entry is built and added.
2200  *
2201  *		The entry's list of unavailable device_spec_t is returned.
2202  */
2203 static int
get_default_unavail_spec_list(defaults_t * alldefaults,char * dsname,dlist_t ** list)2204 get_default_unavail_spec_list(
2205 	defaults_t *alldefaults,
2206 	char	*dsname,
2207 	dlist_t	    **list)
2208 {
2209 	request_spec_list_t *entry = NULL;
2210 	devconfig_t *defaults = NULL;
2211 	int error = 0;
2212 
2213 	/* Get diskset defaults, or global if none for diskset */
2214 	error = defaults_get_diskset_by_name(
2215 		alldefaults, dsname, &defaults);
2216 
2217 	if (error != 0) {
2218 
2219 	    if (error == ENOENT) {
2220 		/* to get global defaults, pass a NULL diskset name */
2221 		error = defaults_get_diskset_by_name(
2222 			alldefaults, NULL, &defaults);
2223 	    }
2224 
2225 	    if (error != 0) {
2226 		if (error != ENOENT) {
2227 		    oprintf(OUTPUT_DEBUG,
2228 			    gettext("get defaults for %s returned %d\n"),
2229 			    dsname, error);
2230 		} else {
2231 		    error = 0;
2232 		}
2233 	    }
2234 	}
2235 
2236 	if ((entry = find_request_spec_list_entry(defaults)) == NULL) {
2237 
2238 	    /* create new entry for these defaults */
2239 	    error = make_request_spec_list_entry(
2240 		    defaults,
2241 		    devconfig_get_available(defaults),
2242 		    devconfig_get_unavailable(defaults),
2243 		    &entry);
2244 
2245 	    if ((error == 0) && (entry != NULL)) {
2246 		if ((error = add_request_spec_list_entry(entry)) != 0) {
2247 		    destroy_request_spec_list_entry(entry);
2248 		    entry = NULL;
2249 		}
2250 	    }
2251 	}
2252 
2253 	if ((error == 0) && (entry != NULL)) {
2254 	    *list = entry->unavail_specs_list;
2255 	}
2256 
2257 	return (error);
2258 }
2259 
2260 /*
2261  * FUNCTION:	is_named_device_avail(devconfig_t *request, char *device_name,
2262  *			boolean_t check_aliases, boolean_t *avail)
2263  *
2264  * INPUT:	request - the current request devconfig_t
2265  *		device_name - char * device name
2266  *		check_aliases - boolean_t which indicates whether the device's
2267  *			aliases should be considered by the availability checks.
2268  *
2269  * OUTPUT:	avail	- a boolean_t * to hold the result
2270  *
2271  * RETURNS:	int	- !0 on error
2272  *
2273  *		avail is set to B_TRUE if the named device is available for
2274  * 		the input request, B_FALSE otherwise.
2275  *
2276  * PURPOSE:	Determine if the named device can be used to satisfy the
2277  *		input request.
2278  *
2279  *		There are several levels at which device availabiity or
2280  *		unavailability may be specifed:
2281  *
2282  *		1. the volume subrequest,
2283  *		2. the toplevel (diskset) request,
2284  *		3. the diskset-specific defaults
2285  *		4. the global defaults
2286  *
2287  *		If the diskset-specific defaults exist, only they are checked.
2288  *
2289  *		The precedence ordering that is enforced:
2290  *
2291  *		1. if request has an avail list, the name must be in it
2292  * 			and not in the request's unavail list.
2293  *		2. if request has an unavail list, the name must not be in it.
2294  *		3. if toplevel request has an avail list, the name must be
2295  *			in it and not in the toplevel request's unavailable
2296  *			list.
2297  *		4. if toplevel request has an unavail list, the name must
2298  *			not be in it.
2299  *		5. if defaults have an avail list, the name must be in it
2300  *			and not in the defaults unavailable list.
2301  *		6. if defaults have an unavail list, the name must not be
2302  *			in it.
2303  */
2304 static int
is_named_device_avail(devconfig_t * request,char * device_name,boolean_t check_aliases,boolean_t * avail)2305 is_named_device_avail(
2306 	devconfig_t	*request,
2307 	char		*device_name,
2308 	boolean_t	check_aliases,
2309 	boolean_t	*avail)
2310 {
2311 	typedef enum check_types {
2312 		DEVICE_REQUEST = 0,
2313 		DISKSET_REQUEST,
2314 		DEFAULTS,
2315 		N_CHECKS
2316 	} check_type_t;
2317 
2318 	check_type_t	check_type;
2319 
2320 	typedef enum list_types {
2321 		AVAIL = 0,
2322 		UNAVAIL,
2323 		N_LISTS
2324 	} list_type_t;
2325 
2326 	dlist_t		*lists[N_CHECKS][N_LISTS];
2327 	boolean_t	includes;
2328 	int		error = 0;
2329 
2330 	memset(lists, 0, (N_CHECKS * N_LISTS) * sizeof (dlist_t *));
2331 
2332 	if (request != NULL) {
2333 	    /* get avail/unavail specs for request */
2334 	    ((error = get_request_avail_spec_list(
2335 		    request, &lists[DEVICE_REQUEST][AVAIL])) != 0) ||
2336 	    (error = get_request_unavail_spec_list(
2337 		    request, &lists[DEVICE_REQUEST][UNAVAIL]));
2338 	}
2339 
2340 	if ((error == 0) && (_toplevel_request != NULL)) {
2341 	    /* diskset request */
2342 	    ((error = get_request_avail_spec_list(
2343 		    _toplevel_request, &lists[DISKSET_REQUEST][AVAIL])) != 0) ||
2344 	    (error = get_request_unavail_spec_list(
2345 		    _toplevel_request, &lists[DISKSET_REQUEST][UNAVAIL]));
2346 	}
2347 
2348 	if ((error == 0) && (_defaults != NULL)) {
2349 	    /* and diskset/global defaults */
2350 	    ((error = get_default_avail_spec_list(_defaults,
2351 		    get_request_diskset(), &lists[DEFAULTS][AVAIL])) != 0) ||
2352 	    (error = get_default_unavail_spec_list(_defaults,
2353 		    get_request_diskset(), &lists[DEFAULTS][UNAVAIL]));
2354 	}
2355 
2356 	if (error != 0) {
2357 	    return (error);
2358 	}
2359 
2360 	*avail = B_TRUE;
2361 
2362 	for (check_type = DEVICE_REQUEST;
2363 	    (check_type < N_CHECKS) && (error == 0);
2364 	    check_type++) {
2365 
2366 	    if (lists[check_type][AVAIL] != NULL) {
2367 
2368 		/* does avail spec list include named device? */
2369 		if ((error = avail_list_includes_device_name(
2370 		    lists[check_type][AVAIL], device_name, check_aliases,
2371 		    &includes)) == 0) {
2372 
2373 		    if (includes != B_TRUE) {
2374 			*avail = B_FALSE;
2375 		    }
2376 
2377 		    if ((includes == B_TRUE) &&
2378 			(lists[check_type][UNAVAIL] != NULL)) {
2379 
2380 			/* device is available, is it in the unavail list? */
2381 			if ((error = unavail_list_includes_device_name(
2382 			    lists[check_type][UNAVAIL], device_name,
2383 			    check_aliases, &includes)) == 0) {
2384 
2385 			    if (includes == B_TRUE) {
2386 				*avail = B_FALSE;
2387 			    }
2388 			}
2389 		    }
2390 		}
2391 
2392 		/* lists at this level checked, skip remainder */
2393 		break;
2394 
2395 	    } else if (lists[check_type][UNAVAIL] != NULL) {
2396 
2397 		/* does unavail spec list include named device? */
2398 		if ((error = unavail_list_includes_device_name(
2399 		    lists[check_type][UNAVAIL], device_name,
2400 		    check_aliases, &includes)) == 0) {
2401 
2402 		    if (includes == B_TRUE) {
2403 			*avail = B_FALSE;
2404 		    }
2405 		}
2406 
2407 		/* list at this level checked, skip remainder */
2408 		break;
2409 	    }
2410 	}
2411 
2412 	return (error);
2413 }
2414 
2415 /*
2416  * FUNCTION:	avail_list_includes_device_name(dlist_t *list,
2417  *			char *device_name, boolean_t check_aliases,
2418  *			boolean_t *includes)
2419  *
2420  * INPUT:	list	- a dlist_t list of available device_spec_t
2421  *		device_name  - a char * device CTD name
2422  *		check_aliases - boolean_t which indicates if the device's
2423  *			aliases	should be considered in the availability
2424  *			checking.
2425  *
2426  * OUTPUT:	includes - B_TRUE - if named device is "included" by any
2427  *				specification in the input list
2428  *			   B_FALSE - otherwise
2429  *
2430  * RETURNS:	int	- 0 on success
2431  *			- !0 otherwise
2432  *
2433  * PURPOSE:	Helper used by is_named_device_avail that determines
2434  *		if the input list of device specifications "includes"
2435  *		a specific device.
2436  *
2437  *		Iterates the elements of the input array and searches
2438  *		for a match using spec_includes_device_name().
2439  */
2440 static int
avail_list_includes_device_name(dlist_t * list,char * device_name,boolean_t check_aliases,boolean_t * includes)2441 avail_list_includes_device_name(
2442 	dlist_t	*list,
2443 	char	*device_name,
2444 	boolean_t check_aliases,
2445 	boolean_t *includes)
2446 {
2447 	dlist_t *iter = NULL;
2448 	int	error = 0;
2449 
2450 	*includes = B_FALSE;
2451 
2452 	for (iter = list;
2453 	    (*includes == B_FALSE) && (iter != NULL) && (error == 0);
2454 	    iter = iter->next) {
2455 
2456 	    device_spec_t *spec = (device_spec_t *)iter->obj;
2457 	    error = spec_includes_device_name(spec, device_name,
2458 		    check_aliases, includes);
2459 	}
2460 
2461 	return (0);
2462 }
2463 
2464 /*
2465  * FUNCTION:	unavail_list_includes_device_name(dlist_t *list,
2466  *			char *device_name, boolean_t check_aliases,
2467  *			boolean_t *includes)
2468  *
2469  * INPUT:	list	- a dlist_t list of unavailable device_spec_t
2470  *		device_name  - a char * device CTD name
2471  *		check_aliases - boolean_t which indicates if the device's
2472  *			aliases	should be considered in the availability
2473  *			checking.
2474  *
2475  * OUTPUT:	includes - B_TRUE - if named device is "included" by any
2476  *				specification in the input list
2477  *			   B_FALSE - otherwise
2478  *
2479  * RETURNS:	int	- 0 on success
2480  *			- !0 otherwise
2481  *
2482  * PURPOSE:	Helper used by is_named_device_avail that determines
2483  *		if the input list of device specifications "includes"
2484  *		a specific device.
2485  *
2486  *		Iterates the elements of the input array and searches
2487  *		for a match using spec_includes_device_name_or_alias().
2488  */
2489 static int
unavail_list_includes_device_name(dlist_t * list,char * device_name,boolean_t check_aliases,boolean_t * includes)2490 unavail_list_includes_device_name(
2491 	dlist_t	*list,
2492 	char	*device_name,
2493 	boolean_t check_aliases,
2494 	boolean_t *includes)
2495 {
2496 	dlist_t *iter = NULL;
2497 	int	error = 0;
2498 	device_spec_t *unavail_spec;
2499 	boolean_t	check_for_alternate_hba = B_FALSE;
2500 
2501 	*includes = B_FALSE;
2502 
2503 	/*
2504 	 * the specs in the list are in descending order of specificity.
2505 	 * so a more exact spec will rule the device out before a less
2506 	 * exact spec.
2507 	 *
2508 	 * Meaning: if the list has { "c3t0d0", ..., "c3", ... } and the
2509 	 * input device name is "c3t0d0s0", it will match "c3t0d0"
2510 	 * before "c3".
2511 	 *
2512 	 * This is important for the multi-path alias checking below.
2513 	 * If the input device name is ruled out by a non-controller
2514 	 * specification, it is really unavailable.
2515 	 */
2516 	for (iter = list;
2517 	    (*includes == B_FALSE) && (iter != NULL);
2518 	    iter = iter->next) {
2519 
2520 	    unavail_spec = (device_spec_t *)iter->obj;
2521 	    error = spec_includes_device_name(
2522 		    unavail_spec, device_name, check_aliases, includes);
2523 
2524 	}
2525 
2526 	if ((error == 0) && (*includes == B_TRUE)) {
2527 
2528 	    /* matched an unavailable spec, was it a controller/HBA? */
2529 	    oprintf(OUTPUT_DEBUG,
2530 		    "device \"%s\" is unavailable, "
2531 		    "it matched \"c(%d)t(%d)d(%d)s(%d)\"\n",
2532 		    device_name,
2533 		    unavail_spec->data.ctd->ctrl,
2534 		    unavail_spec->data.ctd->target,
2535 		    unavail_spec->data.ctd->lun,
2536 		    unavail_spec->data.ctd->slice);
2537 
2538 	    if ((unavail_spec->data.ctd->ctrl != ID_UNSPECIFIED) &&
2539 		(unavail_spec->data.ctd->target == ID_UNSPECIFIED) &&
2540 		(unavail_spec->data.ctd->lun == ID_UNSPECIFIED) &&
2541 		(unavail_spec->data.ctd->slice == ID_UNSPECIFIED)) {
2542 
2543 		/*
2544 		 * Need to see if the named device is a disk or slice,
2545 		 * and if so check to see if the it is multipathed
2546 		 * and possibly accessible thru another controller/HBA.
2547 		 */
2548 		check_for_alternate_hba = B_TRUE;
2549 	    }
2550 	}
2551 
2552 	if ((error == 0) && (check_for_alternate_hba == B_TRUE)) {
2553 
2554 	    dm_descriptor_t slice = (dm_descriptor_t)0;
2555 	    dm_descriptor_t disk = (dm_descriptor_t)0;
2556 
2557 	    ((error = slice_get_by_name(device_name, &slice)) != 0) ||
2558 	    (error = disk_get_by_name(device_name, &disk));
2559 	    if (error != 0) {
2560 		return (error);
2561 	    }
2562 
2563 	    /* if it is a slice, get its disk */
2564 	    if ((error == 0) && (slice != (dm_descriptor_t)0)) {
2565 		error = slice_get_disk(slice, &disk);
2566 	    }
2567 
2568 	    if ((error == 0) && (disk != (dm_descriptor_t)0)) {
2569 
2570 		/* see if all the disk's HBAs are unavailable */
2571 		dlist_t *hbas = NULL;
2572 		dlist_t *iter = NULL;
2573 
2574 		error = disk_get_hbas(disk, &hbas);
2575 
2576 		if (hbas != NULL) {
2577 		    oprintf(OUTPUT_DEBUG,
2578 			    gettext("    checking alternate paths for %s\n"),
2579 			    device_name);
2580 		} else {
2581 		    oprintf(OUTPUT_DEBUG,
2582 			    gettext("    no alternate paths for %s\n"),
2583 			    device_name);
2584 		}
2585 
2586 		/* for each of the disk's HBAs */
2587 		for (iter = hbas;
2588 		    (iter != NULL) && (*includes == B_TRUE) && (error == 0);
2589 		    iter = iter->next) {
2590 
2591 		    dm_descriptor_t hba = (uintptr_t)iter->obj;
2592 		    device_spec_t *hbaspec;
2593 		    char *hbaname = NULL;
2594 		    dlist_t *iter2 = NULL;
2595 
2596 		    *includes = B_FALSE;
2597 
2598 		    ((error = get_display_name(hba, &hbaname)) != 0) ||
2599 		    (error = get_spec_for_name(hbaname, &hbaspec));
2600 
2601 		    /* is HBA unavailable? */
2602 		    for (iter2 = list;
2603 			(iter2 != NULL) && (error == 0) &&
2604 				(*includes == B_FALSE);
2605 			iter2 = iter2->next) {
2606 
2607 			device_spec_t *spec =
2608 			    (device_spec_t *)iter2->obj;
2609 
2610 			*includes = spec_includes_device(spec, hbaspec);
2611 		    }
2612 		}
2613 		dlist_free_items(hbas, NULL);
2614 
2615 		/* if *includes==B_TRUE here, all HBAs are unavailable */
2616 	    }
2617 	}
2618 
2619 	return (error);
2620 }
2621 
2622 /*
2623  * FUNCTION:	spec_includes_device_name(device_spec_t *spec,
2624  *			char *device_name, boolean_t check_aliases,
2625  *			boolean_t *includes)
2626  *
2627  * INPUT:	spec	- a device_spec_t CTD specification.
2628  *		device_name  - a char * device CTD name
2629  *		check_aliases - boolean_t which indicates if the device's
2630  *			aliases	should be considered in the checking.
2631  *
2632  * OUTPUT:	includes - B_TRUE - if device is "included" by the input
2633  *				specification
2634  *			    B_FALSE - otherwise
2635  *
2636  * RETURNS:	int	- 0 on success
2637  *			- !0 otherwise
2638  *
2639  * PURPOSE:	Helper used by (un)avail_specs_includes_device_name() that
2640  *		determines if the input device specification "includes"
2641  *		the named device.
2642  *
2643  *		If check_aliases is true and the named device is a slice or
2644  *		a disk drive, its multi-pathed aliases are also checked
2645  *		against the spec.
2646  */
2647 static int
spec_includes_device_name(device_spec_t * spec,char * device_name,boolean_t check_aliases,boolean_t * includes)2648 spec_includes_device_name(
2649 	device_spec_t *spec,
2650 	char		 *device_name,
2651 	boolean_t	check_aliases,
2652 	boolean_t	*includes)
2653 {
2654 	device_spec_t *device_spec;
2655 	int error = 0;
2656 
2657 	error = get_spec_for_name(device_name, &device_spec);
2658 	if (error == 0) {
2659 
2660 	    *includes = spec_includes_device(spec, device_spec);
2661 
2662 	    if ((*includes == B_FALSE) && (check_aliases == B_TRUE)) {
2663 
2664 		/* spec doesn't include name, check aliases */
2665 
2666 		dm_descriptor_t device = (dm_descriptor_t)0;
2667 		dlist_t *aliases = NULL;
2668 
2669 		/* only slices and disks have aliases */
2670 		error = slice_get_by_name(device_name, &device);
2671 		if (device != (dm_descriptor_t)0) {
2672 		    error = get_aliases(device, &aliases);
2673 		} else if (error == 0) {
2674 		    error = disk_get_by_name(device_name, &device);
2675 		    if (device != (dm_descriptor_t)0) {
2676 			error = get_aliases(device, &aliases);
2677 		    }
2678 		}
2679 
2680 		if ((error == 0) && (aliases != NULL)) {
2681 
2682 		    dlist_t *iter;
2683 		    for (iter = aliases;
2684 			(iter != NULL) && (*includes == B_FALSE) &&
2685 				(error == 0);
2686 			iter = iter->next) {
2687 
2688 			char *alias = (char *)iter->obj;
2689 			device_spec_t *alias_spec;
2690 
2691 			error = get_spec_for_name(alias, &alias_spec);
2692 			if (error == 0) {
2693 			    /* does spec include alias? */
2694 			    *includes =	spec_includes_device(spec, alias_spec);
2695 			}
2696 		    }
2697 		}
2698 		dlist_free_items(aliases, free);
2699 	    }
2700 	}
2701 
2702 	return (error);
2703 }
2704 
2705 /*
2706  * FUNCTION:	destroy_device_spec(device_spec_t *spec)
2707  *
2708  * INPUT:	spec	- pointer to a device_spec_t
2709  *
2710  * RETURNS:	nothing
2711  *
2712  * PURPOSE:	Function which reclaims memory allocated to a device_spec_t.
2713  *
2714  *		Frees memory allocated to hold the specific data in the spec.
2715  */
2716 static void
destroy_device_spec(device_spec_t * spec)2717 destroy_device_spec(
2718 	device_spec_t *spec)
2719 {
2720 	if (spec != NULL) {
2721 	    if (spec->type == SPEC_TYPE_CTD) {
2722 		free(spec->data.ctd);
2723 	    } else if (spec->type == SPEC_TYPE_RAW) {
2724 		free(spec->data.raw);
2725 	    }
2726 	    free(spec);
2727 	}
2728 }
2729 
2730 /*
2731  * FUNCTION:	create_device_spec(char *name, device_spec_t **spec);
2732  *
2733  * INPUT:	name	- pointer to a char* device name
2734  *
2735  * OUTPUT:	spec	- pointer to a device_spec_t to hold the result
2736  *
2737  * RETURNS:	int	- 0 on success
2738  *			 !0 otherwise
2739  *
2740  * PURPOSE:	Function which creates a device_spec_t for the input
2741  *		device name.
2742  *
2743  */
2744 static int
create_device_spec(char * name,device_spec_t ** spec)2745 create_device_spec(
2746 	char	*name,
2747 	device_spec_t **spec)
2748 {
2749 	int error = 0;
2750 
2751 	/* allocate the device spec and try various parsing schemes */
2752 	*spec = (device_spec_t *)calloc(1, sizeof (device_spec_t));
2753 	if (*spec == NULL) {
2754 	    error = ENOMEM;
2755 	} else {
2756 	    if (((error = create_device_ctd_spec(name, spec)) != 0) &&
2757 		    (error != ENOMEM)) {
2758 		/* CTD failed, try other parsing schemes */
2759 		error = create_device_raw_spec(name, spec);
2760 	    }
2761 	}
2762 
2763 	return (error);
2764 }
2765 
2766 /*
2767  * FUNCTION:	create_device_ctd_spec(char *name, device_spec_t **spec);
2768  *
2769  * INPUT:	name	- pointer to a char* device name
2770  *
2771  * OUTPUT:	spec	- pointer to a device_spec_t updated with the parsed
2772  *				CTD spec, if successful
2773  *
2774  * RETURNS:	int	- 0 on success
2775  *			 !0 otherwise
2776  *
2777  * PURPOSE:	Function which atttempts to parse the input device name into
2778  *		cXtXdXsX component ids. The ids are the integer values of each
2779  *		specified segment of the input name.
2780  *
2781  *		If the name doesn't contain a segment, the id is set to
2782  *		ID_UNSPECIFIED.
2783  *
2784  *		The input name must be well-formed.
2785  *
2786  *		These are the acceptable forms:
2787  *
2788  *		cXtXdXsX
2789  *		cXtXdX
2790  *		cXtX
2791  *		cXdXsX
2792  *		cXdX
2793  *		cX
2794  */
2795 static int
create_device_ctd_spec(char * name,device_spec_t ** spec)2796 create_device_ctd_spec(
2797 	char	*name,
2798 	device_spec_t **spec)
2799 {
2800 	uint_t	ctrl;
2801 	uint_t	target;
2802 	uint_t	lun;
2803 	uint_t	slice;
2804 
2805 	uint_t	nscan;
2806 	uint_t	nchars;
2807 
2808 	char	*device_str;
2809 	char	*target_str;
2810 	char	*ctd_str;
2811 	char	*t_ptr;
2812 	char	*d_ptr;
2813 	char	*s_ptr;
2814 
2815 	boolean_t is_ide = B_FALSE;
2816 	boolean_t got_slice = B_FALSE;
2817 	boolean_t got_lun = B_FALSE;
2818 	boolean_t got_target = B_FALSE;
2819 	boolean_t got_ctrl = B_FALSE;
2820 
2821 	int 	error = 0;
2822 
2823 	ctd_str = strdup(name);
2824 	if (ctd_str == NULL) {
2825 	    return (ENOMEM);
2826 	}
2827 
2828 	/* trim any leading path (/dev/dsk/cXtXdXsX) */
2829 	if ((device_str = strrchr(ctd_str, '/')) != NULL) {
2830 	    ++device_str;
2831 	} else {
2832 	    device_str = ctd_str;
2833 	}
2834 
2835 	/* find each segment start position */
2836 	t_ptr = strrchr(device_str, 't');
2837 	d_ptr = strrchr(device_str, 'd');
2838 	s_ptr = strrchr(device_str, 's');
2839 
2840 	/*
2841 	 * scan ids from each existing segment working backwards
2842 	 * so as to leave the device_str in the correct state
2843 	 * for the next expected segment
2844 	 */
2845 	if (s_ptr != NULL) {
2846 
2847 	    /* found 's', try to get slice */
2848 	    nchars = strlen(s_ptr);
2849 	    if ((sscanf(s_ptr, "s%u%n", &slice, &nscan) != 1) ||
2850 		(nscan != nchars)) {
2851 
2852 		error = -1;
2853 		oprintf(OUTPUT_DEBUG,
2854 			gettext("no slice component in device "
2855 				"name \"%s\".\n"),
2856 			name);
2857 
2858 	    } else {
2859 		got_slice = B_TRUE;
2860 		*s_ptr = '\0';
2861 	    }
2862 	}
2863 
2864 	if ((error == 0) && (d_ptr != NULL)) {
2865 
2866 	    /* found 'd', try to get disk/lun */
2867 	    nchars = strlen(d_ptr);
2868 	    if ((sscanf(d_ptr, "d%u%n", &lun, &nscan) != 1) ||
2869 		(nscan != nchars)) {
2870 
2871 		error = -1;
2872 		oprintf(OUTPUT_DEBUG,
2873 			gettext("no disk/lun component "
2874 				"in device name \"%s\".\n"),
2875 			name);
2876 
2877 	    } else {
2878 		got_lun = B_TRUE;
2879 		*d_ptr = '\0';
2880 	    }
2881 	}
2882 
2883 	if ((error == 0) && (t_ptr != NULL)) {
2884 
2885 	    /* found 't', try to get target, it may be a hex WWN id */
2886 
2887 	    /* skip leading 't' and add two for the 'OX' */
2888 	    nchars = strlen(t_ptr + 1) + 2;
2889 	    if ((target_str = (char *)malloc(nchars+1)) == NULL) {
2890 
2891 		error = ENOMEM;
2892 
2893 	    } else {
2894 
2895 		strcpy(target_str, "0X");
2896 		strcpy(target_str+2, t_ptr + 1);
2897 		target_str[nchars] = '\0';
2898 
2899 		if ((sscanf(target_str, "%x%n", &target, &nscan) != 1) ||
2900 		    (nscan != nchars)) {
2901 
2902 		    error = -1;
2903 		    oprintf(OUTPUT_DEBUG,
2904 			    gettext("no target/WWN component "
2905 				    "in device name \"%s\".\n"),
2906 			    name);
2907 
2908 		} else {
2909 		    got_target = B_TRUE;
2910 		    *t_ptr = '\0';
2911 		}
2912 
2913 		free(target_str);
2914 	    }
2915 
2916 	} else {
2917 	    is_ide = B_TRUE;
2918 	}
2919 
2920 	if ((error == 0) && (device_str != NULL)) {
2921 
2922 	    /* get controller/hba/channel */
2923 	    nchars = strlen(device_str);
2924 	    if ((sscanf(device_str, "c%u%n", &ctrl, &nscan) != 1) ||
2925 		    (nscan != nchars)) {
2926 
2927 		error = -1;
2928 		oprintf(OUTPUT_DEBUG,
2929 			gettext("no channel/HBA component "
2930 				"in device name \"%s\".\n"),
2931 			name);
2932 
2933 	    } else {
2934 		got_ctrl = B_TRUE;
2935 	    }
2936 	}
2937 
2938 	free(ctd_str);
2939 
2940 	if (error == 0) {
2941 
2942 	    /* allocate the ctd_spec_t struct and store the ids */
2943 	    (*spec)->type = SPEC_TYPE_CTD;
2944 	    (*spec)->data.ctd = (ctd_spec_t *)calloc(1, sizeof (ctd_spec_t));
2945 
2946 	    if ((*spec)->data.ctd == NULL) {
2947 		error = ENOMEM;
2948 	    }
2949 
2950 	    (*spec)->data.ctd->slice = ID_UNSPECIFIED;
2951 	    (*spec)->data.ctd->lun = ID_UNSPECIFIED;
2952 	    (*spec)->data.ctd->target = ID_UNSPECIFIED;
2953 	    (*spec)->data.ctd->ctrl = ID_UNSPECIFIED;
2954 
2955 	    if (got_slice == B_TRUE) {
2956 		(*spec)->data.ctd->slice = slice;
2957 	    }
2958 
2959 	    if (got_lun == B_TRUE) {
2960 		(*spec)->data.ctd->lun = lun;
2961 	    }
2962 
2963 	    if (got_target == B_TRUE) {
2964 		(*spec)->data.ctd->target = target;
2965 	    }
2966 
2967 	    if (got_ctrl == B_TRUE) {
2968 		(*spec)->data.ctd->ctrl = ctrl;
2969 	    }
2970 
2971 	    (*spec)->data.ctd->is_ide = is_ide;
2972 	}
2973 
2974 	return (error);
2975 }
2976 
2977 /*
2978  * FUNCTION:	create_device_raw_spec(char *name, device_spec_t **spec);
2979  *
2980  * INPUT:	name	- pointer to a char* device name
2981  *
2982  * OUTPUT:	spec	- pointer to a device_spec_t updated with the raw spec
2983  *
2984  * RETURNS:	int	- 0 on success
2985  *			 !0 otherwise
2986  *
2987  * PURPOSE:	Function which creates a "raw" spec for the input name.
2988  *
2989  *		This is a last resort if all other spec parsing schemes failed,
2990  *		the "raw" spec is just the input device name.
2991  */
2992 static int
create_device_raw_spec(char * name,device_spec_t ** spec)2993 create_device_raw_spec(
2994 	char	*name,
2995 	device_spec_t **spec)
2996 {
2997 	int 	error = 0;
2998 	char	*ctd_str = strdup(name);
2999 
3000 	if (ctd_str == NULL) {
3001 	    return (ENOMEM);
3002 	}
3003 
3004 	(*spec)->type = SPEC_TYPE_RAW;
3005 	(*spec)->data.raw = ctd_str;
3006 
3007 	oprintf(OUTPUT_DEBUG,
3008 		gettext("made raw device spec for \"%s\"\n"), ctd_str);
3009 
3010 	return (error);
3011 }
3012 
3013 /*
3014  * FUNCTION:	get_spec_for_name(char *name, device_spec_t **id);
3015  *
3016  * INPUT:	name	- pointer to a char* device name
3017  *
3018  * OUTPUT:	id	- pointer to a device_spec_t to hold the result
3019  *
3020  * RETURNS:	int	- 0 on success
3021  *			 !0 otherwise
3022  *
3023  * PURPOSE:	Function which finds the device_spec_t that already
3024  *		exists for the input name or creates it.
3025  *
3026  *		The returned struct should not be freed, it is maintained
3027  *		in a cache that will be purged when the layout process
3028  *		is complete.
3029  */
3030 int
get_spec_for_name(char * name,device_spec_t ** id)3031 get_spec_for_name(
3032 	char	*name,
3033 	device_spec_t **id)
3034 {
3035 	dlist_t *item;
3036 	int	error = 0;
3037 
3038 	item = dlist_find(_spec_cache, (void *)name,
3039 		compare_name_to_spec_cache_name);
3040 
3041 	if (item == NULL) {
3042 	    if ((error = create_device_spec(name, id)) == 0) {
3043 
3044 		spec_cache_t *entry = (spec_cache_t *)
3045 		    calloc(1, sizeof (spec_cache_t));
3046 
3047 		if (entry == NULL) {
3048 		    destroy_device_spec(*id);
3049 		    error = ENOMEM;
3050 		} else {
3051 		    char *dup = strdup(name);
3052 		    if (dup == NULL) {
3053 			free(entry);
3054 			destroy_device_spec(*id);
3055 			*id = NULL;
3056 			error = ENOMEM;
3057 		    } else {
3058 			entry->name = dup;
3059 			entry->device_spec = *id;
3060 		    }
3061 
3062 		    if (error == 0) {
3063 			dlist_t *item = dlist_new_item((void *)entry);
3064 			if (item == NULL) {
3065 			    free(entry);
3066 			    destroy_device_spec(*id);
3067 			    *id = NULL;
3068 			    error = ENOMEM;
3069 			} else {
3070 			    _spec_cache =
3071 				dlist_append(item, _spec_cache, AT_HEAD);
3072 			}
3073 		    }
3074 		}
3075 	    }
3076 	} else {
3077 	    *id = ((spec_cache_t *)item->obj)->device_spec;
3078 	}
3079 
3080 	return (error);
3081 }
3082 
3083 /*
3084  * FUNCTION:	spec_includes_device(device_spec_t *spec,
3085  *			device_spec_t *device)
3086  *
3087  * INPUT:	spec	- pointer to a device_spec struct
3088  *		device	- pointer to a device_spec struct
3089  *
3090  * RETURNS:	boolean_t - B_TRUE if the device is included in the spec
3091  *			 B_FALSE otherwise
3092  *
3093  * PURPOSE:	Function which determines if the input device matches the
3094  *		input spec.
3095  *
3096  *		If both specs are of the same type, the appropriate
3097  *		comparison function is called.
3098  *
3099  *		If the two specs are of different types, no comparison
3100  *		is done and B_FALSE is returned.
3101  */
3102 boolean_t
spec_includes_device(device_spec_t * spec,device_spec_t * device)3103 spec_includes_device(
3104 	device_spec_t *spec,
3105 	device_spec_t *device)
3106 {
3107 	if ((spec->type == SPEC_TYPE_CTD) && (device->type == SPEC_TYPE_CTD)) {
3108 	    return (ctd_spec_includes_device(spec, device));
3109 	} else if ((spec->type == SPEC_TYPE_RAW) &&
3110 	    (device->type == SPEC_TYPE_RAW)) {
3111 	    return (raw_spec_includes_device(spec, device));
3112 	}
3113 
3114 	return (B_FALSE);
3115 }
3116 
3117 /*
3118  * FUNCTION:	ctd_spec_includes_device(device_spec_t *spec,
3119  *			device_spec_t *device)
3120  *
3121  * INPUT:	spec	- pointer to a device_spec struct
3122  *		device	- pointer to a device_spec struct
3123  *
3124  * RETURNS:	boolean_t - B_TRUE if the device is included in the spec
3125  *			 B_FALSE otherwise
3126  *
3127  * PURPOSE:	Function which determines if the input CTD device spec
3128  *		matches the input CTD spec.
3129  *
3130  *		The device_spec_t structs contain component "ids" for
3131  *		both the specification and the device.
3132  *
3133  *		The device must match each of the ids in the spec that
3134  *		are specified.
3135  *
3136  *		spec		devices matched
3137  *		--------------------------------------------------------
3138  *		cX		cX, cXtX, cXtXdX, cXtXdXsX, cXdX, cXdXsX
3139  *		cXtX		cXtX, cXtXdX, cXtXdXsX
3140  *		cXtXdX		cXtXdX, cXtXdXsX
3141  *		cXtXdXsX	cXtXdXsX
3142  *		cXdX		cXdX, cXdXsX
3143  *		cXdXsX		cXdXsX
3144  */
3145 static boolean_t
ctd_spec_includes_device(device_spec_t * spec,device_spec_t * device)3146 ctd_spec_includes_device(
3147 	device_spec_t *spec,
3148 	device_spec_t *device)
3149 {
3150 	boolean_t match = B_FALSE;
3151 
3152 	if (spec->data.ctd->is_ide) {
3153 
3154 	    /* valid IDE names are cX, cXdX, cXdXsX, no target */
3155 
3156 	    if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) &&
3157 		(spec->data.ctd->lun != ID_UNSPECIFIED) &&
3158 		(spec->data.ctd->slice != ID_UNSPECIFIED)) {
3159 
3160 		match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) &&
3161 		    (spec->data.ctd->lun == device->data.ctd->lun) &&
3162 		    (spec->data.ctd->slice == device->data.ctd->slice);
3163 
3164 	    } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) &&
3165 		(spec->data.ctd->lun != ID_UNSPECIFIED)) {
3166 
3167 		match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) &&
3168 		    (spec->data.ctd->lun == device->data.ctd->lun);
3169 
3170 	    } else if (spec->data.ctd->ctrl != ID_UNSPECIFIED) {
3171 
3172 		match = (spec->data.ctd->ctrl == device->data.ctd->ctrl);
3173 
3174 	    }
3175 
3176 	} else {
3177 
3178 	    /* valid names are cX, cXtX, cXtXdX, cXtXdXsX */
3179 
3180 	    if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) &&
3181 		(spec->data.ctd->target != ID_UNSPECIFIED) &&
3182 		(spec->data.ctd->lun != ID_UNSPECIFIED) &&
3183 		(spec->data.ctd->slice != ID_UNSPECIFIED)) {
3184 
3185 		match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) &&
3186 		    (spec->data.ctd->target == device->data.ctd->target) &&
3187 		    (spec->data.ctd->lun == device->data.ctd->lun) &&
3188 		    (spec->data.ctd->slice == device->data.ctd->slice);
3189 
3190 	    } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) &&
3191 		(spec->data.ctd->target != ID_UNSPECIFIED) &&
3192 		(spec->data.ctd->lun != ID_UNSPECIFIED)) {
3193 
3194 		match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) &&
3195 		    (spec->data.ctd->target == device->data.ctd->target) &&
3196 		    (spec->data.ctd->lun == device->data.ctd->lun);
3197 
3198 	    } else if ((spec->data.ctd->ctrl != ID_UNSPECIFIED) &&
3199 		(spec->data.ctd->target != ID_UNSPECIFIED)) {
3200 
3201 		match = (spec->data.ctd->ctrl == device->data.ctd->ctrl) &&
3202 		    (spec->data.ctd->target == device->data.ctd->target);
3203 
3204 	    } else if (spec->data.ctd->ctrl != ID_UNSPECIFIED) {
3205 
3206 		match = (spec->data.ctd->ctrl == device->data.ctd->ctrl);
3207 
3208 	    }
3209 	}
3210 
3211 	oprintf(OUTPUT_DEBUG,
3212 		gettext("spec: c(%d) t(%d) d(%d) s(%d) "
3213 			"%s: c(%d) t(%d) d(%d) s(%d)\n"),
3214 		spec->data.ctd->ctrl, spec->data.ctd->target,
3215 		spec->data.ctd->lun, spec->data.ctd->slice,
3216 		(match ? gettext("includes") : gettext("does not include")),
3217 		device->data.ctd->ctrl, device->data.ctd->target,
3218 		device->data.ctd->lun, device->data.ctd->slice);
3219 
3220 	return (match);
3221 }
3222 
3223 /*
3224  * FUNCTION:	raw_spec_includes_device(device_spec_t *spec,
3225  *			device_spec_t *device)
3226  *
3227  * INPUT:	spec	- pointer to a device_spec struct
3228  *		device	- pointer to a device_spec struct
3229  *
3230  * RETURNS:	boolean_t - B_TRUE if the device is included in the spec
3231  *			 B_FALSE otherwise
3232  *
3233  * PURPOSE:	Function which determines if the input raw device spec
3234  *		matches the input spec.
3235  *
3236  *		The device_spec_t raw elements are checked.
3237  *
3238  *		If the spec's raw device name is exactly contained at the
3239  *		beginning of the device spec's raw name, then the function
3240  *		evaluates to true.
3241  */
3242 static boolean_t
raw_spec_includes_device(device_spec_t * spec,device_spec_t * device)3243 raw_spec_includes_device(
3244 	device_spec_t *spec,
3245 	device_spec_t *device)
3246 {
3247 	return (strncasecmp(spec->data.raw,
3248 			device->data.raw, strlen(spec->data.raw)) == 0);
3249 }
3250 
3251 /*
3252  * FUNCTION:	compare_name_to_spec_cache_name(void *name, void *list_item)
3253  *
3254  * INPUT:	name	- opaque pointer to a char * device name
3255  * 		list_item - opaque pointer to a spec_cache_t entry
3256  *
3257  * RETURNS:	int	- 0 - if request is the same as list_item->request
3258  *			  !0 - otherwise
3259  *
3260  * PURPOSE:	dlist_t helper which compares the input device name
3261  *		to the list_item's device name for equality.
3262  *
3263  *		This function is the lookup mechanism for the device_spec
3264  *		associated with the name.
3265  */
3266 static int
compare_name_to_spec_cache_name(void * name,void * list_item)3267 compare_name_to_spec_cache_name(
3268 	void *name,
3269 	void *list_item)
3270 {
3271 	spec_cache_t *entry = (spec_cache_t *)list_item;
3272 
3273 	assert(name != NULL);
3274 	assert(entry != NULL);
3275 
3276 	return (string_case_compare((char *)name, entry->name));
3277 }
3278 
3279 /*
3280  * FUNCTION:	destroy_spec_cache_entry(void *entry)
3281  *
3282  * INPUT:	entry	- opaque pointer to a spec_cache_t
3283  *
3284  * RETURNS:	nothing
3285  *
3286  * PURPOSE:	Function which reclaims memory allocated to a
3287  *		spec_cache_t entry.
3288  *
3289  *		Frees memory allocated to hold the CTD name and the
3290  *		corresponding device_spec_t.
3291  */
3292 static void
destroy_spec_cache_entry(void * obj)3293 destroy_spec_cache_entry(
3294 	void *obj)
3295 {
3296 	spec_cache_t *entry = (spec_cache_t *)obj;
3297 
3298 	if (entry != NULL) {
3299 	    free(entry->name);
3300 	    destroy_device_spec(entry->device_spec);
3301 	    free(entry);
3302 	}
3303 }
3304 
3305 /*
3306  * FUNCTION:	destroy_spec_cache()
3307  *
3308  * RETURNS:	int	- 0 on success
3309  *			 !0 otherwise.
3310  *
3311  * PURPOSE:	Function which destroys all entries in the device_spec
3312  *		cache.
3313  */
3314 static int
destroy_spec_cache()3315 destroy_spec_cache()
3316 {
3317 	dlist_free_items(_spec_cache, destroy_spec_cache_entry);
3318 	_spec_cache = NULL;
3319 
3320 	return (0);
3321 }
3322 
3323 /*
3324  * FUNCTION:	get_device_access_name(devconfig_t *request,
3325  *			dm_descriptor_t desc, char **name)
3326  *
3327  * INPUT:	request	- a devconfig_t request
3328  *		desc	- a dm_descriptor_t device handle
3329  *
3330  * OUTPUT:	name	- a char * pointer to hold the preferred name
3331  *
3332  * RETURNS:	int	- 0 - if request is the same as list_item->request
3333  *			  !0 - otherwise
3334  *
3335  * PURPOSE:	Utility function to determine which of the possible device
3336  *		names should be used to access a known available device.
3337  *
3338  *		Devices handled are slices and disks.
3339  *
3340  *		If the input device is a multipathed disk or slice, it
3341  *		can have several possible names.  Determine which of the
3342  *		names should be used based on the input request's available
3343  *		or unavailable device specifications.
3344  *
3345  */
3346 int
get_device_access_name(devconfig_t * request,dm_descriptor_t desc,char ** name)3347 get_device_access_name(
3348 	devconfig_t	*request,
3349 	dm_descriptor_t desc,
3350 	char		**name)
3351 {
3352 	int		error = 0;
3353 	boolean_t	avail = B_FALSE;
3354 	dlist_t		*aliases = NULL;
3355 
3356 	assert(desc != (dm_descriptor_t)0);
3357 
3358 	*name = NULL;
3359 
3360 	if ((error = get_display_name(desc, name)) != 0) {
3361 	    return (error);
3362 	}
3363 
3364 	if (is_did_name(*name) == B_TRUE) {
3365 	    oprintf(OUTPUT_DEBUG,
3366 		    gettext("device DID name %s is preferred\n"),
3367 		    *name);
3368 	    return (0);
3369 	}
3370 
3371 	error = is_named_device_avail(request, *name, B_FALSE, &avail);
3372 	if (error != 0) {
3373 	    return (error);
3374 	}
3375 
3376 	if (avail == B_TRUE) {
3377 	    oprintf(OUTPUT_DEBUG,
3378 		    gettext("device name %s is accessible\n"),
3379 		    *name);
3380 	    return (0);
3381 	}
3382 
3383 	/* search aliases for an 'available' name, prefer DID names */
3384 	if ((error = get_aliases(desc, &aliases)) == 0) {
3385 
3386 	    dlist_t *iter = aliases;
3387 	    char *availname = NULL;
3388 	    char *didname = NULL;
3389 
3390 	    for (; (iter != NULL) && (error == 0); iter = iter->next) {
3391 
3392 		char *alias = (char *)iter->obj;
3393 		error = is_named_device_avail(request, alias, B_FALSE, &avail);
3394 
3395 		if ((error == 0) && (avail == B_TRUE)) {
3396 		    oprintf(OUTPUT_DEBUG,
3397 			    gettext("device alias %s is accessible for %s\n"),
3398 			    alias, *name);
3399 
3400 		    availname = alias;
3401 
3402 		    if (is_did_name(availname) == B_TRUE) {
3403 			didname = alias;
3404 			break;
3405 		    }
3406 		}
3407 	    }
3408 
3409 	    if (error == 0) {
3410 		if (didname != NULL) {
3411 		    *name = didname;
3412 		} else if (availname != NULL) {
3413 		    *name = availname;
3414 		}
3415 	    }
3416 	}
3417 
3418 	return (error);
3419 }
3420