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
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.
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]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
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"
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"
46 #define _LAYOUT_REQUEST_C
48 static char *_request_diskset
= NULL
;
49 static devconfig_t
*_toplevel_request
= NULL
;
50 static defaults_t
*_defaults
= NULL
;
53 * This file contains code which handles various aspects of the
54 * request and defaults devconfig_t structs passed to the layout
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.
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.
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.
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
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".
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.
91 * The ID_UNSPECIFIED value is used to flag components
92 * that were not in the CTD name:
94 * "c3" -> { ctrl=3, target=ID_UNSPECIFIED,
95 * lun=ID_UNSPECIFIED, slice=ID_UNSPECIFIED }
97 * "c3t2" -> { ctrl=3, target=2,
98 * lun=ID_UNSPECIFIED, slice=ID_UNSPECIFIED }
101 #define ID_UNSPECIFIED -1
124 static int get_spec_for_name(
128 static int create_device_spec(
130 device_spec_t
**spec
);
132 static int create_device_ctd_spec(
134 device_spec_t
**spec
);
136 static int create_device_raw_spec(
138 device_spec_t
**spec
);
140 static void destroy_device_spec(
141 device_spec_t
*spec
);
143 static boolean_t
ctd_spec_includes_device(
145 device_spec_t
*device
);
147 static boolean_t
raw_spec_includes_device(
149 device_spec_t
*device
);
152 * get_spec_for_name builds up a cached mapping of device
153 * names to the corresponding device_spec_t structs.
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.
159 * The cache is implemented as a list of these structs:
164 device_spec_t
*device_spec
;
168 static dlist_t
*_spec_cache
= NULL
;
170 static int destroy_spec_cache();
171 static int compare_name_to_spec_cache_name(
172 void *name
, void *list_item
);
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:
184 devconfig_t
*request
;
187 * avail_specs_list is a list of device spec_t
188 * corresponding to available devices specified
189 * in the request object
191 dlist_t
*avail_specs_list
;
194 * unavail_specs_list is a list of device spec_t
195 * corresponding to unavailable devices specified
196 * in the request object
198 dlist_t
*unavail_specs_list
;
200 } request_spec_list_t
;
202 dlist_t
*_request_spec_list_cache
= NULL
;
204 static int destroy_request_spec_list_cache();
205 static void destroy_request_spec_list_entry(void *obj
);
207 static int compare_request_to_request_spec_list_request(
211 static int convert_usernames_to_specs(
215 /* other private functions */
216 static int is_device_avail(
217 dm_descriptor_t desc
,
218 devconfig_t
*request
,
221 static int is_named_device_avail(
222 devconfig_t
*request
,
224 boolean_t check_aliases
,
227 static int avail_list_includes_device_name(
230 boolean_t check_aliases
,
231 boolean_t
*includes
);
233 static int unavail_list_includes_device_name(
236 boolean_t check_aliases
,
237 boolean_t
*includes
);
239 static int spec_includes_device_name(
242 boolean_t check_aliases
,
243 boolean_t
*includes
);
245 static boolean_t
spec_includes_device(
247 device_spec_t
*device
);
249 static int disk_get_avail_space(
250 devconfig_t
*request
,
251 dm_descriptor_t disk
,
254 static int compare_hba_n_avail_disks(
259 * FUNCTION: release_request_caches()
263 * PURPOSE: cleanup the module private caches.
266 release_request_caches()
268 (void) destroy_request_spec_list_cache();
269 (void) destroy_spec_cache();
274 * FUNCTION: int set_request_diskset(char *)
276 * INPUT: char * - pointer to the diskset name
277 * OUTPUT: 0 - success
278 * !0 - validation failure
281 * PURPOSE: set the module global diskset name.
287 _request_diskset
= dsname
;
289 if (dsname
== NULL
|| dsname
[0] == '\0') {
291 gettext("No disk set specified in request\n"));
299 * FUNCTION: char *get_request_diskset()
303 * RETURNS: char * - pointer to the currently set diskset name
305 * PURPOSE: get the global name of the current diskset.
308 get_request_diskset()
310 return (_request_diskset
);
314 * FUNCTION: void unset_request_diskset()
316 * PURPOSE: unset the module global diskset name.
319 unset_request_diskset(
322 _request_diskset
= NULL
;
326 * FUNCTION: int set_toplevel_request(devconfig_t *)
328 * INPUT: devconfig_t * - pointer to the diskset request
329 * OUTPUT: 0 - success
330 * !0 - validation failure
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()
337 * SIDEEFFECT: The devconfig_t's list of available and unavailable
338 * devices will be validated.
341 set_toplevel_request(
344 _toplevel_request
= req
;
346 return (validate_request_avail_unavail(req
));
350 * FUNCTION: void unset_toplevel_request()
352 * PURPOSE: unset the layout module global toplevel request struct.
356 unset_toplevel_request()
358 _toplevel_request
= NULL
;
362 * FUNCTION: int set_defaults(devconfig_t *)
364 * INPUT: devconfig_t * - pointer to the global defaults devconfig_t
365 * OUTPUT: 0 - success
366 * !0 - validation failure
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()
373 * SIDEEFFECT: The devconfig_t's list of available and unavailable
374 * devices will be validated.
377 set_request_defaults(
378 defaults_t
*defaults
)
381 devconfig_t
*diskset
= NULL
;
383 _defaults
= defaults
;
385 if ((error
= defaults_get_diskset_by_name(
386 _defaults
, get_request_diskset(), &diskset
)) == 0) {
388 error
= validate_request_avail_unavail(diskset
);
390 } else if (error
== ENOENT
) {
391 /* no defaults to verify */
399 * FUNCTION: void unset_request_defaults()
401 * PURPOSE: unset the layout module global defaults struct.
405 unset_request_defaults()
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
415 * RETURNS: int - 0 - on success
418 * PURPOSE: helper which determines the minimum of components
419 * for striped volumes satisfying the input request.
421 * The value to use is taken from the input request, the
422 * toplevel diskset request, the diskset defaults or the
434 if ((error
= devconfig_get_stripe_mincomp(req
, val
)) != 0) {
435 if (error
!= ERR_ATTR_UNSET
) {
441 if ((error
= defaults_get_stripe_mincomp(
442 _defaults
, get_request_diskset(), val
)) != 0) {
443 if (error
!= ERR_ATTR_UNSET
) {
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
457 * RETURNS: int - 0 - on success
460 * PURPOSE: helper which determines the maximum number of components
461 * for striped volumes satisfying the input request.
463 * The value to use is taken from the input request, the
464 * toplevel diskset request, the diskset defaults or the
476 if ((error
= devconfig_get_stripe_maxcomp(req
, val
)) != 0) {
477 if (error
!= ERR_ATTR_UNSET
) {
483 if ((error
= defaults_get_stripe_maxcomp(
484 _defaults
, get_request_diskset(), val
)) != 0) {
485 if (error
!= ERR_ATTR_UNSET
) {
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
499 * RETURNS: int - 0 - on success
502 * PURPOSE: helper which determines the interlace value for striped
503 * volumes satisfying the input request.
505 * The value to use is taken from the input request, the
506 * toplevel diskset request, the diskset defaults or the
509 * If no value is explictly specified, ERR_ATTR_UNSET is
513 get_stripe_interlace(
521 if ((error
= devconfig_get_stripe_interlace(req
, val
)) != 0) {
522 if (error
!= ERR_ATTR_UNSET
) {
529 if ((error
= defaults_get_stripe_interlace(
530 _defaults
, get_request_diskset(), val
)) != 0) {
531 if (error
!= ERR_ATTR_UNSET
) {
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
546 * RETURNS: int - 0 - on success
549 * PURPOSE: helper which determines the write strategy mirrored volumes
550 * should have for volumes satisfying the input request.
552 * The value to use is taken from the input request, the
553 * toplevel diskset request, the diskset defaults or the
556 * If no value is explictly specified, ERR_ATTR_UNSET is
560 get_mirror_read_strategy(
562 mirror_read_strategy_t
*val
)
568 if ((error
= devconfig_get_mirror_read(req
, val
)) != 0) {
569 if (error
!= ERR_ATTR_UNSET
) {
575 if ((error
= defaults_get_mirror_read(
576 _defaults
, get_request_diskset(), val
)) != 0) {
577 if (error
!= ERR_ATTR_UNSET
) {
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
592 * RETURNS: int - 0 - on success
595 * PURPOSE: helper which determines the write strategy mirrored volumes
596 * should have for volumes satisfying the input request.
598 * The value to use is taken from the input request, the
599 * toplevel diskset request, the diskset defaults or the
602 * If no value is explictly specified, ERR_ATTR_UNSET is
606 get_mirror_write_strategy(
608 mirror_write_strategy_t
*val
)
614 if ((error
= devconfig_get_mirror_write(req
, val
)) != 0) {
615 if (error
!= ERR_ATTR_UNSET
) {
621 if ((error
= defaults_get_mirror_write(
622 _defaults
, get_request_diskset(), val
)) != 0) {
623 if (error
!= ERR_ATTR_UNSET
) {
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
637 * RETURNS: int - 0 - on success
640 * PURPOSE: helper which determines the resync pass mirrored volumes
641 * should have for volumes satisfying the input request.
643 * The value to use is taken from the input request, the
644 * toplevel diskset request, the diskset defaults or the
647 * If no value is explictly specified, ERR_ATTR_UNSET is
659 if ((error
= devconfig_get_mirror_pass(req
, val
)) != 0) {
660 if (error
!= ERR_ATTR_UNSET
) {
666 if ((error
= defaults_get_mirror_pass(
667 _defaults
, get_request_diskset(), val
)) != 0) {
668 if (error
!= ERR_ATTR_UNSET
) {
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
682 * RETURNS: int - 0 - on success
685 * PURPOSE: helper which determines how many submirrors mirrored
686 * volumes should have for volumes satisfying the input
689 * The value to use is taken from the input request, the
690 * toplevel diskset request, the diskset defaults or the
702 if ((error
= devconfig_get_mirror_nsubs(req
, val
)) != 0) {
703 if (error
!= ERR_ATTR_UNSET
) {
709 if ((error
= defaults_get_mirror_nsubs(
710 _defaults
, get_request_diskset(), val
)) != 0) {
711 if (error
!= ERR_ATTR_UNSET
) {
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
725 * RETURNS: int - 0 - on success
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.
732 * The value to use is taken from the input request, the
733 * toplevel diskset request, the diskset defaults or the
737 get_volume_faultrecov(
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
);
752 error
= defaults_get_mirror_usehsp(
753 _defaults
, get_request_diskset(), val
);
757 error
= defaults_get_stripe_usehsp(
758 _defaults
, get_request_diskset(), val
);
762 error
= defaults_get_concat_usehsp(
763 _defaults
, get_request_diskset(), val
);
767 error
= defaults_get_volume_usehsp(
768 _defaults
, get_request_diskset(), val
);
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
782 * RETURNS: int - 0 - on success
785 * PURPOSE: helper which determines the appropriate level of data
786 * redundancy a volume should have for volumes satisfying
789 * The value to use is taken from the input request, the
790 * toplevel diskset request, the diskset defaults or the
794 get_volume_redundancy_level(
802 if ((error
= devconfig_get_volume_redundancy_level(req
, val
)) != 0) {
803 if (error
!= ERR_ATTR_UNSET
) {
809 if ((error
= defaults_get_volume_redundancy_level(
810 _defaults
, get_request_diskset(), val
)) != 0) {
811 if (error
!= ERR_ATTR_UNSET
) {
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
825 * RETURNS: int - 0 - on success
828 * PURPOSE: helper which determines the appropriate level of datapath
829 * redundancy a slice component should have for volumes
830 * satisfying the input request.
832 * The value to use is taken from the input request, the
833 * toplevel diskset request, the diskset defaults or the
845 if ((error
= devconfig_get_volume_npaths(req
, val
)) != 0) {
846 if (error
!= ERR_ATTR_UNSET
) {
852 if ((error
= defaults_get_volume_npaths(
853 _defaults
, get_request_diskset(), val
)) != 0) {
854 if (error
!= ERR_ATTR_UNSET
) {
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
868 * RETURNS: int - 0 - on success
871 * PURPOSE: helper which determines the default HSP name for the
874 * The value to use is taken from the input request, the
875 * toplevel diskset request, the diskset defaults or the
879 get_default_hsp_name(
887 if ((error
= defaults_get_hsp_name(_defaults
,
888 get_request_diskset(), name
)) != 0) {
889 if (error
!= ENOENT
) {
899 * FUNCTION: slice_is_available(char *sname, devconfig_t *request,
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
906 * RETURNS: int - 0 - on success
909 * PURPOSE: Validation helper which determines if the named slice can
910 * be used as a volume component when satisfying the input
913 * Check if the slice appears in the known slice list,
914 * then check the request's available and unavailable
915 * device specifications.
920 devconfig_t
*request
,
923 dm_descriptor_t slice
= (dm_descriptor_t
)0;
928 if ((error
= slice_get_by_name(sname
, &slice
)) != 0) {
932 if (slice
== (dm_descriptor_t
)0) {
938 error
= is_named_device_avail(request
, sname
, B_TRUE
, bool);
945 * FUNCTION: get_disks_for_target(char *name, dlist_t **disks)
947 * INPUT: name - a char* device CTD name
949 * OUTPUT: disks - disks matching the input target name
951 * RETURNS: int - 0 on success
954 * PURPOSE: Validation helper function which finds all disks "on" the
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.
961 * "On" is determined by comparing a disk's name and
962 * aliases to the target to see if they match.
965 get_disks_for_target(
970 device_spec_t
*targetid
= NULL
;
972 error
= get_spec_for_name(name
, &targetid
);
974 dlist_t
*known_disks
= NULL
;
975 dlist_t
*iter
= NULL
;
977 get_known_disks(&known_disks
);
978 for (iter
= known_disks
;
979 (iter
!= NULL
) && (error
== 0);
982 dm_descriptor_t disk
= (uintptr_t)iter
->obj
;
983 device_spec_t
*diskid
= NULL
;
984 char *diskname
= NULL
;
985 dlist_t
*diskaliases
= NULL
;
988 ((error
= get_display_name(disk
, &diskname
)) != 0) ||
989 (error
= get_aliases(disk
, &diskaliases
)) ||
990 (error
= get_spec_for_name(diskname
, &diskid
));
993 if (spec_includes_device(targetid
, diskid
) == B_TRUE
) {
995 if ((item
= dlist_new_item((void *)(uintptr_t)disk
)) ==
999 *disks
= dlist_append(item
, *disks
, AT_HEAD
);
1002 /* check disk's aliases */
1004 for (iter2
= diskaliases
;
1005 (iter2
!= NULL
) && (error
== 0);
1006 iter2
= iter2
->next
) {
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
);
1013 if (spec_includes_device(
1014 targetid
, aliasid
) == B_TRUE
) {
1016 /* alias matched, add disk */
1017 item
= dlist_new_item((void *)(uintptr_t)disk
);
1022 dlist_append(item
, *disks
, AT_HEAD
);
1035 * FUNCTION: select_hbas_with_n_disks(devconfig_t *request,
1036 * dlist_t *hbas, int mindisks, dlist_t **selhbas,
1037 * dlist_t **seldisks)
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
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
1049 * RETURNS: int - 0 - on success
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.
1056 * Only available disks that have available space are counted.
1058 * Disks connected thru multiple HBAs are only counted for
1059 * the first HBA they're accessed through.
1061 * The list of HBAs returned will be in descending order,
1062 * i.e., HBAs with more disks come before those with fewer.
1064 * The returned lists of HBAs and disks must be passed to
1065 * dlist_free_items() to recover the space allocated to hold
1071 * get available disks on HBA
1074 * if (disk is not in selected disk list)
1075 * add it to the list
1077 * count it as a distinct disk on this HBA
1080 * if (this HBA has >= mindisks distinct disks)
1081 * add this HBA to the list of returned HBAs
1086 select_hbas_with_n_disks(
1087 devconfig_t
*request
,
1093 dlist_t
*iter
= NULL
;
1099 /* for each input HBA */
1100 for (iter
= hbas
; (error
== 0) && (iter
!= NULL
); iter
= iter
->next
) {
1102 dm_descriptor_t hba
= (uintptr_t)iter
->obj
;
1103 dlist_t
*iter2
= NULL
;
1104 dlist_t
*disks
= NULL
;
1106 uint16_t ndistinct
= 0;
1108 error
= hba_get_avail_disks_and_space(request
, hba
, &disks
, &space
);
1110 /* for each of this HBA's disks */
1112 (iter2
!= NULL
) && (error
== 0);
1113 iter2
= iter2
->next
) {
1115 dm_descriptor_t disk
= (uintptr_t)iter2
->obj
;
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
) {
1121 /* distinct, add to list of all_distinct */
1122 dlist_t
*item
= dlist_new_item((void *)(uintptr_t)disk
);
1128 dlist_append(item
, *seldisks
, AT_HEAD
);
1130 /* increment this HBA's distinct disk count */
1136 if (ndistinct
>= mindisks
) {
1138 /* this HBA has minimum # of disks, add to output list */
1139 dlist_t
*item
= dlist_new_item((void *)(uintptr_t)hba
);
1144 dlist_insert_ordered(
1145 item
, *selhbas
, DESCENDING
,
1146 compare_hba_n_avail_disks
);
1148 /* save # of disks for ordering the list */
1149 hba_set_n_avail_disks(hba
, ndistinct
);
1153 dlist_free_items(disks
, NULL
);
1157 oprintf(OUTPUT_TERSE
,
1158 gettext("failed selecting HBAs with n disks: %d\n"),
1161 dlist_free_items(*selhbas
, NULL
);
1163 dlist_free_items(*seldisks
, NULL
);
1171 * FUNCTION: hba_get_avail_disks_and_space(devconfig_t *request,
1172 * dm_descriptor_t hba, dlist_t **disks, uint64_t *space)
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
1178 * OUTPUT: disks - pointer to a list to hold the computed available
1180 * avail - pointer to a uint64_t to hold the aggregate
1181 * available space on the available disks
1183 * RETURNS: int - 0 - on success
1186 * PURPOSE: helper which examines the disks associated with the
1187 * input HBA and assembles a list of those that are available.
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.
1193 * The returned list must be passed to dlist_free_items()
1194 * to recover the memory allocated to hold each list item.
1197 hba_get_avail_disks_and_space(
1198 devconfig_t
*request
,
1199 dm_descriptor_t hba
,
1203 dlist_t
*usable_disks
= NULL
;
1204 dlist_t
*iter
= NULL
;
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
) {
1215 dm_descriptor_t disk
= (uintptr_t)iter
->obj
;
1216 boolean_t avail
= B_FALSE
;
1217 dlist_t
*hbas
= NULL
;
1219 /* is disk attached to HBA in question? */
1220 error
= disk_get_hbas(disk
, &hbas
);
1225 if (dlist_contains(hbas
, (void *)(uintptr_t)hba
,
1226 compare_descriptor_names
) == B_TRUE
) {
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;
1233 /* does disk have available space? */
1234 error
= disk_get_avail_space(request
, disk
, &disk_space
);
1235 if ((error
== 0) && (disk_space
> 0)) {
1237 dlist_t
*item
= dlist_new_item((void *)(uintptr_t)disk
);
1241 *disks
= dlist_append(item
, *disks
, AT_HEAD
);
1244 *space
+= disk_space
;
1249 dlist_free_items(hbas
, NULL
);
1253 dlist_free_items(*disks
, NULL
);
1261 * FUNCTION: disk_get_avail_space(devconfig_t *request,
1262 * dlist_t *disks, uint64_t space)
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
1270 * RETURNS: int - 0 - on success
1273 * PURPOSE: helper which iterates the input list of disks and determines
1274 * the aggregate amount of available space they represent.
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.
1281 disk_get_avail_space(
1282 devconfig_t
*request
,
1283 dm_descriptor_t disk
,
1286 dlist_t
*usable_slices
= NULL
;
1287 dlist_t
*iter
= NULL
;
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
) {
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
;
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)) {
1311 /* is slice reserved by an explicit layout request? */
1312 if (((error
= is_reserved_slice(slice
, &reserved
)) != 0) ||
1313 (reserved
== B_TRUE
)) {
1317 /* is slice used by a pending layout request? */
1318 if (((error
= is_used_slice(slice
, &used
)) != 0) ||
1323 /* is slice available? */
1324 if (((error
= is_device_avail(slice
, request
, &avail
)) == 0) &&
1325 (avail
== B_TRUE
)) {
1327 /* does slice have usable space? */
1329 if ((error
= slice_get_size(slice
, &size
)) == 0) {
1339 * FUNCTION: disks_get_avail_slices(devconfig_t *request,
1340 * dlist_t *disks, dlist_t **slices)
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
1347 * RETURNS: int - 0 - on success
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.
1354 * A disk must contain at least one slice in the available
1355 * slice list as well as have available space in order
1359 disks_get_avail_slices(
1360 devconfig_t
*request
,
1364 dlist_t
*usable_slices
= NULL
;
1365 dlist_t
*iter
= NULL
;
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
) {
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
;
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
)) {
1389 /* is slice reserved by an explicit layout request? */
1390 if (((error
= is_reserved_slice(slice
, &reserved
)) != 0) ||
1391 (reserved
== B_TRUE
)) {
1395 /* is slice used by a pending layout request? */
1396 if (((error
= is_used_slice(slice
, &used
)) != 0) ||
1401 /* is slice available? */
1402 if (((error
= is_device_avail(slice
, request
, &avail
)) == 0) &&
1403 (avail
== B_TRUE
)) {
1405 /* does slice have available space? */
1407 error
= slice_get_size(slice
, &size
);
1408 if ((error
== 0) && (size
> 0)) {
1409 dlist_t
*item
= dlist_new_item((void *)(uintptr_t)slice
);
1413 *slices
= dlist_append(item
, *slices
, AT_TAIL
);
1420 dlist_free_items(*slices
, NULL
);
1429 * FUNCTION: get_hbas_and_disks_used_by_volumes(dlist_t *volumes,
1430 * dlist_t **hbas, dlist_t **disks)
1432 * INPUT: volumes - pointer to a list of devconfig_t volumes
1434 * OUTPUT: hbas - a list of HBAs utilized by the input volumes
1435 * disks - a list of disks utilized by the input volumes
1437 * RETURNS: int - 0 on success
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.
1445 * The returned lists of HBAs and disks may contain duplicates.
1448 get_hbas_and_disks_used_by_volumes(
1453 dlist_t
*iter
= NULL
;
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
);
1467 * FUNCTION: get_hbas_and_disks_used_by_volume(devconfig_t *volume,
1468 * dlist_t **hbas, dlist_t **disks)
1470 * INPUT: volume - pointer to a devconfig_t volume
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
1477 * RETURNS: int - 0 on success
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.
1484 * The returned lists of HBAs and disks may contain duplicates.
1487 get_hbas_and_disks_used_by_volume(
1488 devconfig_t
*volume
,
1492 dlist_t
*iter
= NULL
;
1495 for (iter
= devconfig_get_components(volume
);
1496 (iter
!= NULL
) && (error
== 0);
1497 iter
= iter
->next
) {
1499 devconfig_t
*dev
= (devconfig_t
*)iter
->obj
;
1500 if (devconfig_isA(dev
, TYPE_SLICE
)) {
1502 dm_descriptor_t disk
= NULL
;
1505 /* get disk for component slice */
1506 ((error
= devconfig_get_name(dev
, &name
)) != 0) ||
1507 (error
= get_disk_for_named_slice(name
, &disk
));
1509 dlist_t
*item
= dlist_new_item((void *)(uintptr_t)disk
);
1513 *disks
= dlist_append(item
, *disks
, AT_HEAD
);
1517 /* get HBAs for disk */
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
);
1526 } else if (devconfig_isA(dev
, TYPE_MIRROR
)) {
1528 /* collect info for submirrors */
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
);
1544 * FUNCTION: compare_hba_n_avail_disks(void *obj1, void *obj2)
1546 * INPUT: obj1 - opaque pointer
1547 * obj2 - opaque pointer
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
1553 * PURPOSE: dlist_t helper which compares the number of available disks
1554 * for two HBAs represented as dm_descriptor_t handles.
1556 * Both input objects are assumed to be dm_descriptor_t handles.
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.
1563 compare_hba_n_avail_disks(
1570 assert(obj1
!= NULL
);
1571 assert(obj2
!= NULL
);
1573 (void) hba_get_n_avail_disks((uintptr_t)obj1
, &n1
);
1574 (void) hba_get_n_avail_disks((uintptr_t)obj2
, &n2
);
1576 return ((int)n1
- n2
);
1580 * FUNCTION: is_device_avail(dm_descriptor_t desc,
1581 * devconfig_t *request, boolean_t *avail)
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
1588 * RETURNS: int - 0 - on success
1591 * PURPOSE: Internal helper which determines if the input device can
1592 * be used as a volume component when satisfying the input
1595 * The device is assumed to be a known valid device.
1597 * The function checks if the device passes the request's
1598 * available and unavailable device specifications.
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.
1605 dm_descriptor_t desc
,
1606 devconfig_t
*request
,
1614 if ((error
= get_display_name(desc
, &name
)) == 0) {
1615 error
= is_named_device_avail(request
, name
, B_TRUE
, avail
);
1622 * FUNCTION: compare_request_to_request_spec_list_request(
1623 * void *request, void *list_item)
1625 * INPUT: request - opaque pointer to a devconfig_t
1626 * list_item - opaque pointer to a request_spec_list_t
1628 * RETURNS: int - 0 - if request is the same as list_item->request
1631 * PURPOSE: dlist_t helper which compares the input request pointer
1632 * to the list_item's request pointer for equality.
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.
1638 * The defaults_t struct pointer is the lookup key.
1641 compare_request_to_request_spec_list_request(
1645 request_spec_list_t
*entry
=
1646 (request_spec_list_t
*)list_item
;
1648 assert(request
!= NULL
);
1649 assert(entry
!= NULL
);
1651 /* compare two devconfig_t pointers, if identical, return 0 */
1652 return ((devconfig_t
*)request
!= entry
->request
);
1656 * FUNCTION: compare_device_spec_specificity(void *spec1, void *spec2)
1658 * INPUT: spec1 - opaque pointer to a device_spec_t
1659 * spec2 - opaque pointer to a device_spec_t
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
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.
1671 compare_device_spec_specificity(
1675 if (spec1
== NULL
|| spec2
== NULL
) {
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
1733 /* both specify just ctrl */
1738 * FUNCTION: find_request_spec_list_entry(devconfig_t *request)
1740 * INPUT: request - pointer to a devconfig_t struct
1742 * RETURNS: request_spec_list_entry - pointer to a
1743 * request_spec_list_entry struct
1745 * PURPOSE: Lookup function which encapsulates the details of locating
1746 * the device_spec_list_t cache entry for the input request.
1748 static request_spec_list_t
*
1749 find_request_spec_list_entry(
1750 devconfig_t
*request
)
1752 dlist_t
*list_item
= NULL
;
1753 request_spec_list_t
*entry
= NULL
;
1755 list_item
= dlist_find(
1756 _request_spec_list_cache
,
1758 compare_request_to_request_spec_list_request
);
1760 if (list_item
!= NULL
) {
1761 entry
= (request_spec_list_t
*)list_item
->obj
;
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)
1772 * INPUT: entry - pointer to the request_spec_list_entry struct to be
1773 * added to the cache.
1775 * RETURNS: int - 0 on success
1778 * PURPOSE: Function which encapsulates the details of adding a
1779 * device_spec_list_t cache entry.
1782 add_request_spec_list_entry(
1783 request_spec_list_t
*entry
)
1785 dlist_t
*list_item
= dlist_new_item((void *)entry
);
1787 if (list_item
== NULL
) {
1791 _request_spec_list_cache
= dlist_append(list_item
,
1792 _request_spec_list_cache
, AT_HEAD
);
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)
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
1809 * RETURNS: int - 0 on success
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.
1816 * Converts the input arrays of (un)available device names into
1817 * equivalent lists of device_spec_t structs.
1819 * Creates a new cache entry, populates it and adds it to the
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
)
1830 dlist_t
*list
= NULL
;
1832 *entry
= calloc(1, sizeof (request_spec_list_t
));
1833 if (*entry
== NULL
) {
1837 (*entry
)->request
= request
;
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
1843 error
= convert_usernames_to_specs(
1844 avail_device_specs
, &list
);
1847 (*entry
)->avail_specs_list
= list
;
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
1855 error
= convert_usernames_to_specs(
1856 unavail_device_specs
, &list
);
1859 (*entry
)->unavail_specs_list
= list
;
1863 /* delete the partial entry */
1864 destroy_request_spec_list_entry((void *)*entry
);
1872 * FUNCTION: convert_usernames_to_specs(char **specs, dlist_t **list)
1874 * INPUT: specs - char * array of device CTD names
1876 * OUTPUT: list - pointer to a list of device_spec_t corresponding
1877 * to each name in the input array
1879 * RETURNS: int - 0 on success
1882 * PURPOSE: Function which converts the input CTD device names to the
1883 * equivalent device_spec_t structs.
1885 * Iterates the input array and converts each CTD name to a
1886 * device_spec_t using get_spec_for_name().
1889 convert_usernames_to_specs(
1897 * For each spec in the array, get the corresponding
1898 * device_spec_t and add it to the list.
1900 * Any spec in the array that looks to be a DID name
1901 * is first converted to its equivalent CTD name.
1904 (specs
!= NULL
) && (specs
[i
] != NULL
) && (error
== 0);
1907 device_spec_t
*spec
= NULL
;
1908 char *userspec
= specs
[i
];
1910 error
= get_spec_for_name(userspec
, &spec
);
1911 if ((error
== 0) && (spec
!= NULL
)) {
1912 dlist_t
*list_item
= dlist_new_item((void *)spec
);
1916 *list
= dlist_insert_ordered
1917 (list_item
, *list
, DESCENDING
,
1918 compare_device_spec_specificity
);
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
);
1934 * FUNCTION: destroy_request_spec_list_entry(void *entry)
1936 * INPUT: entry - opaque pointer to a request_spec_list_t
1940 * PURPOSE: Function which reclaims memory allocated to a
1941 * request_spec_list_t.
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.
1948 destroy_request_spec_list_entry(
1951 request_spec_list_t
*entry
= (request_spec_list_t
*)obj
;
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
);
1963 * FUNCTION: destroy_request_spec_list_cache()
1965 * RETURNS: int - 0 on success
1968 * PURPOSE: Function which destroys all entries in the request_spec_list
1972 destroy_request_spec_list_cache()
1974 dlist_free_items(_request_spec_list_cache
,
1975 destroy_request_spec_list_entry
);
1976 _request_spec_list_cache
= NULL
;
1982 * FUNCTION: get_request_avail_spec_list(devconfig_t *request,
1985 * INPUT: request - a pointer to a devconfig_t
1987 * OUTPUT: list - pointer to a list of device_spec_t corresponding
1988 * to the devices specified as available by the
1991 * RETURNS: int - 0 on success
1994 * PURPOSE: Function which locates or builds the list of device_spec_t
1995 * for the available devices specified in the input request.
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.
2001 * The entry's list of available device_spec_t is returned.
2004 get_request_avail_spec_list(
2005 devconfig_t
*request
,
2008 request_spec_list_t
*entry
= NULL
;
2011 if ((entry
= find_request_spec_list_entry(request
)) == NULL
) {
2013 /* create cache entry for this request */
2014 error
= make_request_spec_list_entry(
2016 devconfig_get_available(request
),
2017 devconfig_get_unavailable(request
),
2020 if ((error
== 0) && (entry
!= NULL
)) {
2021 if ((error
= add_request_spec_list_entry(entry
)) != 0) {
2022 destroy_request_spec_list_entry(entry
);
2028 if ((error
== 0) && (entry
!= NULL
)) {
2029 *list
= entry
->avail_specs_list
;
2036 * FUNCTION: get_request_unavail_spec_list(devconfig_t *request,
2039 * INPUT: request - a pointer to a devconfig_t
2041 * OUTPUT: list - pointer to a list of device_spec_t corresponding
2042 * to the devices specified as unavailable by the
2045 * RETURNS: int - 0 on success
2048 * PURPOSE: Function which locates or builds the list of device_spec_t
2049 * for the unavailable devices specified in the input request.
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.
2055 * The entry's list of unavailable device_spec_t is returned.
2058 get_request_unavail_spec_list(
2059 devconfig_t
*request
,
2062 request_spec_list_t
*entry
= NULL
;
2065 if ((entry
= find_request_spec_list_entry(request
)) == NULL
) {
2067 /* create new entry for this request */
2068 error
= make_request_spec_list_entry(
2070 devconfig_get_available(request
),
2071 devconfig_get_unavailable(request
),
2074 if ((error
== 0) && (entry
!= NULL
)) {
2075 if ((error
= add_request_spec_list_entry(entry
)) != 0) {
2076 destroy_request_spec_list_entry(entry
);
2082 if ((error
== 0) && (entry
!= NULL
)) {
2083 *list
= entry
->unavail_specs_list
;
2090 * FUNCTION: get_default_avail_spec_list(defaults_t *defaults,
2091 * char *dsname, dlist_t **list)
2093 * INPUT: defaults - a pointer to a defaults_t struct
2094 * dsname - the name of the diskset whose defaults should be used
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.
2101 * RETURNS: int - 0 on success
2104 * PURPOSE: Function which locates or builds the list of device_spec_t
2105 * for the available devices for the named diskset.
2107 * Locates the defaults for the named diskset, if there are none,
2108 * locates the global defaults for all disksets.
2110 * The defaults devconfig_t struct is then used to look up the
2111 * the corresponding entry in the request_spec_list cache.
2113 * If there is currently no entry in the cache for the defaults,
2114 * an entry is built and added.
2116 * The entry's list of available device_spec_t is returned.
2119 get_default_avail_spec_list(
2120 defaults_t
*alldefaults
,
2124 request_spec_list_t
*entry
= NULL
;
2125 devconfig_t
*defaults
= NULL
;
2128 /* Get diskset defaults, or global if none for diskset */
2129 error
= defaults_get_diskset_by_name(
2130 alldefaults
, dsname
, &defaults
);
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
);
2140 if (error
!= ENOENT
) {
2141 oprintf(OUTPUT_DEBUG
,
2142 gettext("get defaults for %s returned %d\n"),
2150 if ((entry
= find_request_spec_list_entry(defaults
)) == NULL
) {
2152 /* create new entry for these defaults */
2153 error
= make_request_spec_list_entry(
2155 devconfig_get_available(defaults
),
2156 devconfig_get_unavailable(defaults
),
2159 if ((error
== 0) && (entry
!= NULL
)) {
2160 if ((error
= add_request_spec_list_entry(entry
)) != 0) {
2161 destroy_request_spec_list_entry(entry
);
2167 if ((error
== 0) && (entry
!= NULL
)) {
2168 *list
= entry
->avail_specs_list
;
2175 * FUNCTION: get_default_unavail_spec_list(defaults_t *defaults,
2176 * char *dsname, dlist_t **list)
2178 * INPUT: defaults - a pointer to a defaults_t struct
2179 * dsname - the name of the diskset whose defaults should be used
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.
2186 * RETURNS: int - 0 on success
2189 * PURPOSE: Function which locates or builds the list of device_spec_t
2190 * for the unavailable devices for the named diskset.
2192 * Locates the defaults for the named diskset, if there are none,
2193 * locates the global defaults for all disksets.
2195 * The defaults devconfig_t struct is then used to look up the
2196 * the corresponding entry in the request_spec_list cache.
2198 * If there is currently no entry in the cache for the defaults,
2199 * an entry is built and added.
2201 * The entry's list of unavailable device_spec_t is returned.
2204 get_default_unavail_spec_list(
2205 defaults_t
*alldefaults
,
2209 request_spec_list_t
*entry
= NULL
;
2210 devconfig_t
*defaults
= NULL
;
2213 /* Get diskset defaults, or global if none for diskset */
2214 error
= defaults_get_diskset_by_name(
2215 alldefaults
, dsname
, &defaults
);
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
);
2226 if (error
!= ENOENT
) {
2227 oprintf(OUTPUT_DEBUG
,
2228 gettext("get defaults for %s returned %d\n"),
2236 if ((entry
= find_request_spec_list_entry(defaults
)) == NULL
) {
2238 /* create new entry for these defaults */
2239 error
= make_request_spec_list_entry(
2241 devconfig_get_available(defaults
),
2242 devconfig_get_unavailable(defaults
),
2245 if ((error
== 0) && (entry
!= NULL
)) {
2246 if ((error
= add_request_spec_list_entry(entry
)) != 0) {
2247 destroy_request_spec_list_entry(entry
);
2253 if ((error
== 0) && (entry
!= NULL
)) {
2254 *list
= entry
->unavail_specs_list
;
2261 * FUNCTION: is_named_device_avail(devconfig_t *request, char *device_name,
2262 * boolean_t check_aliases, boolean_t *avail)
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.
2269 * OUTPUT: avail - a boolean_t * to hold the result
2271 * RETURNS: int - !0 on error
2273 * avail is set to B_TRUE if the named device is available for
2274 * the input request, B_FALSE otherwise.
2276 * PURPOSE: Determine if the named device can be used to satisfy the
2279 * There are several levels at which device availabiity or
2280 * unavailability may be specifed:
2282 * 1. the volume subrequest,
2283 * 2. the toplevel (diskset) request,
2284 * 3. the diskset-specific defaults
2285 * 4. the global defaults
2287 * If the diskset-specific defaults exist, only they are checked.
2289 * The precedence ordering that is enforced:
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
2297 * 4. if toplevel request has an unavail list, the name must
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
2305 is_named_device_avail(
2306 devconfig_t
*request
,
2308 boolean_t check_aliases
,
2311 typedef enum check_types
{
2318 check_type_t check_type
;
2320 typedef enum list_types
{
2326 dlist_t
*lists
[N_CHECKS
][N_LISTS
];
2330 memset(lists
, 0, (N_CHECKS
* N_LISTS
) * sizeof (dlist_t
*));
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
]));
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
]));
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
]));
2362 for (check_type
= DEVICE_REQUEST
;
2363 (check_type
< N_CHECKS
) && (error
== 0);
2366 if (lists
[check_type
][AVAIL
] != NULL
) {
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
,
2373 if (includes
!= B_TRUE
) {
2377 if ((includes
== B_TRUE
) &&
2378 (lists
[check_type
][UNAVAIL
] != NULL
)) {
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) {
2385 if (includes
== B_TRUE
) {
2392 /* lists at this level checked, skip remainder */
2395 } else if (lists
[check_type
][UNAVAIL
] != NULL
) {
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) {
2402 if (includes
== B_TRUE
) {
2407 /* list at this level checked, skip remainder */
2416 * FUNCTION: avail_list_includes_device_name(dlist_t *list,
2417 * char *device_name, boolean_t check_aliases,
2418 * boolean_t *includes)
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
2426 * OUTPUT: includes - B_TRUE - if named device is "included" by any
2427 * specification in the input list
2428 * B_FALSE - otherwise
2430 * RETURNS: int - 0 on success
2433 * PURPOSE: Helper used by is_named_device_avail that determines
2434 * if the input list of device specifications "includes"
2435 * a specific device.
2437 * Iterates the elements of the input array and searches
2438 * for a match using spec_includes_device_name().
2441 avail_list_includes_device_name(
2444 boolean_t check_aliases
,
2445 boolean_t
*includes
)
2447 dlist_t
*iter
= NULL
;
2450 *includes
= B_FALSE
;
2453 (*includes
== B_FALSE
) && (iter
!= NULL
) && (error
== 0);
2454 iter
= iter
->next
) {
2456 device_spec_t
*spec
= (device_spec_t
*)iter
->obj
;
2457 error
= spec_includes_device_name(spec
, device_name
,
2458 check_aliases
, includes
);
2465 * FUNCTION: unavail_list_includes_device_name(dlist_t *list,
2466 * char *device_name, boolean_t check_aliases,
2467 * boolean_t *includes)
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
2475 * OUTPUT: includes - B_TRUE - if named device is "included" by any
2476 * specification in the input list
2477 * B_FALSE - otherwise
2479 * RETURNS: int - 0 on success
2482 * PURPOSE: Helper used by is_named_device_avail that determines
2483 * if the input list of device specifications "includes"
2484 * a specific device.
2486 * Iterates the elements of the input array and searches
2487 * for a match using spec_includes_device_name_or_alias().
2490 unavail_list_includes_device_name(
2493 boolean_t check_aliases
,
2494 boolean_t
*includes
)
2496 dlist_t
*iter
= NULL
;
2498 device_spec_t
*unavail_spec
;
2499 boolean_t check_for_alternate_hba
= B_FALSE
;
2501 *includes
= B_FALSE
;
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
2508 * Meaning: if the list has { "c3t0d0", ..., "c3", ... } and the
2509 * input device name is "c3t0d0s0", it will match "c3t0d0"
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.
2517 (*includes
== B_FALSE
) && (iter
!= NULL
);
2518 iter
= iter
->next
) {
2520 unavail_spec
= (device_spec_t
*)iter
->obj
;
2521 error
= spec_includes_device_name(
2522 unavail_spec
, device_name
, check_aliases
, includes
);
2526 if ((error
== 0) && (*includes
== B_TRUE
)) {
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",
2533 unavail_spec
->data
.ctd
->ctrl
,
2534 unavail_spec
->data
.ctd
->target
,
2535 unavail_spec
->data
.ctd
->lun
,
2536 unavail_spec
->data
.ctd
->slice
);
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
)) {
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.
2548 check_for_alternate_hba
= B_TRUE
;
2552 if ((error
== 0) && (check_for_alternate_hba
== B_TRUE
)) {
2554 dm_descriptor_t slice
= (dm_descriptor_t
)0;
2555 dm_descriptor_t disk
= (dm_descriptor_t
)0;
2557 ((error
= slice_get_by_name(device_name
, &slice
)) != 0) ||
2558 (error
= disk_get_by_name(device_name
, &disk
));
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
);
2568 if ((error
== 0) && (disk
!= (dm_descriptor_t
)0)) {
2570 /* see if all the disk's HBAs are unavailable */
2571 dlist_t
*hbas
= NULL
;
2572 dlist_t
*iter
= NULL
;
2574 error
= disk_get_hbas(disk
, &hbas
);
2577 oprintf(OUTPUT_DEBUG
,
2578 gettext(" checking alternate paths for %s\n"),
2581 oprintf(OUTPUT_DEBUG
,
2582 gettext(" no alternate paths for %s\n"),
2586 /* for each of the disk's HBAs */
2588 (iter
!= NULL
) && (*includes
== B_TRUE
) && (error
== 0);
2589 iter
= iter
->next
) {
2591 dm_descriptor_t hba
= (uintptr_t)iter
->obj
;
2592 device_spec_t
*hbaspec
;
2593 char *hbaname
= NULL
;
2594 dlist_t
*iter2
= NULL
;
2596 *includes
= B_FALSE
;
2598 ((error
= get_display_name(hba
, &hbaname
)) != 0) ||
2599 (error
= get_spec_for_name(hbaname
, &hbaspec
));
2601 /* is HBA unavailable? */
2603 (iter2
!= NULL
) && (error
== 0) &&
2604 (*includes
== B_FALSE
);
2605 iter2
= iter2
->next
) {
2607 device_spec_t
*spec
=
2608 (device_spec_t
*)iter2
->obj
;
2610 *includes
= spec_includes_device(spec
, hbaspec
);
2613 dlist_free_items(hbas
, NULL
);
2615 /* if *includes==B_TRUE here, all HBAs are unavailable */
2623 * FUNCTION: spec_includes_device_name(device_spec_t *spec,
2624 * char *device_name, boolean_t check_aliases,
2625 * boolean_t *includes)
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.
2632 * OUTPUT: includes - B_TRUE - if device is "included" by the input
2634 * B_FALSE - otherwise
2636 * RETURNS: int - 0 on success
2639 * PURPOSE: Helper used by (un)avail_specs_includes_device_name() that
2640 * determines if the input device specification "includes"
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
2648 spec_includes_device_name(
2649 device_spec_t
*spec
,
2651 boolean_t check_aliases
,
2652 boolean_t
*includes
)
2654 device_spec_t
*device_spec
;
2657 error
= get_spec_for_name(device_name
, &device_spec
);
2660 *includes
= spec_includes_device(spec
, device_spec
);
2662 if ((*includes
== B_FALSE
) && (check_aliases
== B_TRUE
)) {
2664 /* spec doesn't include name, check aliases */
2666 dm_descriptor_t device
= (dm_descriptor_t
)0;
2667 dlist_t
*aliases
= NULL
;
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
);
2680 if ((error
== 0) && (aliases
!= NULL
)) {
2683 for (iter
= aliases
;
2684 (iter
!= NULL
) && (*includes
== B_FALSE
) &&
2686 iter
= iter
->next
) {
2688 char *alias
= (char *)iter
->obj
;
2689 device_spec_t
*alias_spec
;
2691 error
= get_spec_for_name(alias
, &alias_spec
);
2693 /* does spec include alias? */
2694 *includes
= spec_includes_device(spec
, alias_spec
);
2698 dlist_free_items(aliases
, free
);
2706 * FUNCTION: destroy_device_spec(device_spec_t *spec)
2708 * INPUT: spec - pointer to a device_spec_t
2712 * PURPOSE: Function which reclaims memory allocated to a device_spec_t.
2714 * Frees memory allocated to hold the specific data in the spec.
2717 destroy_device_spec(
2718 device_spec_t
*spec
)
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
);
2731 * FUNCTION: create_device_spec(char *name, device_spec_t **spec);
2733 * INPUT: name - pointer to a char* device name
2735 * OUTPUT: spec - pointer to a device_spec_t to hold the result
2737 * RETURNS: int - 0 on success
2740 * PURPOSE: Function which creates a device_spec_t for the input
2747 device_spec_t
**spec
)
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
) {
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
);
2767 * FUNCTION: create_device_ctd_spec(char *name, device_spec_t **spec);
2769 * INPUT: name - pointer to a char* device name
2771 * OUTPUT: spec - pointer to a device_spec_t updated with the parsed
2772 * CTD spec, if successful
2774 * RETURNS: int - 0 on success
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.
2781 * If the name doesn't contain a segment, the id is set to
2784 * The input name must be well-formed.
2786 * These are the acceptable forms:
2796 create_device_ctd_spec(
2798 device_spec_t
**spec
)
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
;
2823 ctd_str
= strdup(name
);
2824 if (ctd_str
== NULL
) {
2828 /* trim any leading path (/dev/dsk/cXtXdXsX) */
2829 if ((device_str
= strrchr(ctd_str
, '/')) != NULL
) {
2832 device_str
= ctd_str
;
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');
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
2845 if (s_ptr
!= NULL
) {
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
)) {
2853 oprintf(OUTPUT_DEBUG
,
2854 gettext("no slice component in device "
2864 if ((error
== 0) && (d_ptr
!= NULL
)) {
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
)) {
2872 oprintf(OUTPUT_DEBUG
,
2873 gettext("no disk/lun component "
2874 "in device name \"%s\".\n"),
2883 if ((error
== 0) && (t_ptr
!= NULL
)) {
2885 /* found 't', try to get target, it may be a hex WWN id */
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
) {
2895 strcpy(target_str
, "0X");
2896 strcpy(target_str
+2, t_ptr
+ 1);
2897 target_str
[nchars
] = '\0';
2899 if ((sscanf(target_str
, "%x%n", &target
, &nscan
) != 1) ||
2900 (nscan
!= nchars
)) {
2903 oprintf(OUTPUT_DEBUG
,
2904 gettext("no target/WWN component "
2905 "in device name \"%s\".\n"),
2909 got_target
= B_TRUE
;
2920 if ((error
== 0) && (device_str
!= NULL
)) {
2922 /* get controller/hba/channel */
2923 nchars
= strlen(device_str
);
2924 if ((sscanf(device_str
, "c%u%n", &ctrl
, &nscan
) != 1) ||
2925 (nscan
!= nchars
)) {
2928 oprintf(OUTPUT_DEBUG
,
2929 gettext("no channel/HBA component "
2930 "in device name \"%s\".\n"),
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
));
2946 if ((*spec
)->data
.ctd
== NULL
) {
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
;
2955 if (got_slice
== B_TRUE
) {
2956 (*spec
)->data
.ctd
->slice
= slice
;
2959 if (got_lun
== B_TRUE
) {
2960 (*spec
)->data
.ctd
->lun
= lun
;
2963 if (got_target
== B_TRUE
) {
2964 (*spec
)->data
.ctd
->target
= target
;
2967 if (got_ctrl
== B_TRUE
) {
2968 (*spec
)->data
.ctd
->ctrl
= ctrl
;
2971 (*spec
)->data
.ctd
->is_ide
= is_ide
;
2978 * FUNCTION: create_device_raw_spec(char *name, device_spec_t **spec);
2980 * INPUT: name - pointer to a char* device name
2982 * OUTPUT: spec - pointer to a device_spec_t updated with the raw spec
2984 * RETURNS: int - 0 on success
2987 * PURPOSE: Function which creates a "raw" spec for the input name.
2989 * This is a last resort if all other spec parsing schemes failed,
2990 * the "raw" spec is just the input device name.
2993 create_device_raw_spec(
2995 device_spec_t
**spec
)
2998 char *ctd_str
= strdup(name
);
3000 if (ctd_str
== NULL
) {
3004 (*spec
)->type
= SPEC_TYPE_RAW
;
3005 (*spec
)->data
.raw
= ctd_str
;
3007 oprintf(OUTPUT_DEBUG
,
3008 gettext("made raw device spec for \"%s\"\n"), ctd_str
);
3014 * FUNCTION: get_spec_for_name(char *name, device_spec_t **id);
3016 * INPUT: name - pointer to a char* device name
3018 * OUTPUT: id - pointer to a device_spec_t to hold the result
3020 * RETURNS: int - 0 on success
3023 * PURPOSE: Function which finds the device_spec_t that already
3024 * exists for the input name or creates it.
3026 * The returned struct should not be freed, it is maintained
3027 * in a cache that will be purged when the layout process
3038 item
= dlist_find(_spec_cache
, (void *)name
,
3039 compare_name_to_spec_cache_name
);
3042 if ((error
= create_device_spec(name
, id
)) == 0) {
3044 spec_cache_t
*entry
= (spec_cache_t
*)
3045 calloc(1, sizeof (spec_cache_t
));
3047 if (entry
== NULL
) {
3048 destroy_device_spec(*id
);
3051 char *dup
= strdup(name
);
3054 destroy_device_spec(*id
);
3059 entry
->device_spec
= *id
;
3063 dlist_t
*item
= dlist_new_item((void *)entry
);
3066 destroy_device_spec(*id
);
3071 dlist_append(item
, _spec_cache
, AT_HEAD
);
3077 *id
= ((spec_cache_t
*)item
->obj
)->device_spec
;
3084 * FUNCTION: spec_includes_device(device_spec_t *spec,
3085 * device_spec_t *device)
3087 * INPUT: spec - pointer to a device_spec struct
3088 * device - pointer to a device_spec struct
3090 * RETURNS: boolean_t - B_TRUE if the device is included in the spec
3093 * PURPOSE: Function which determines if the input device matches the
3096 * If both specs are of the same type, the appropriate
3097 * comparison function is called.
3099 * If the two specs are of different types, no comparison
3100 * is done and B_FALSE is returned.
3103 spec_includes_device(
3104 device_spec_t
*spec
,
3105 device_spec_t
*device
)
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
));
3118 * FUNCTION: ctd_spec_includes_device(device_spec_t *spec,
3119 * device_spec_t *device)
3121 * INPUT: spec - pointer to a device_spec struct
3122 * device - pointer to a device_spec struct
3124 * RETURNS: boolean_t - B_TRUE if the device is included in the spec
3127 * PURPOSE: Function which determines if the input CTD device spec
3128 * matches the input CTD spec.
3130 * The device_spec_t structs contain component "ids" for
3131 * both the specification and the device.
3133 * The device must match each of the ids in the spec that
3136 * spec devices matched
3137 * --------------------------------------------------------
3138 * cX cX, cXtX, cXtXdX, cXtXdXsX, cXdX, cXdXsX
3139 * cXtX cXtX, cXtXdX, cXtXdXsX
3140 * cXtXdX cXtXdX, cXtXdXsX
3146 ctd_spec_includes_device(
3147 device_spec_t
*spec
,
3148 device_spec_t
*device
)
3150 boolean_t match
= B_FALSE
;
3152 if (spec
->data
.ctd
->is_ide
) {
3154 /* valid IDE names are cX, cXdX, cXdXsX, no target */
3156 if ((spec
->data
.ctd
->ctrl
!= ID_UNSPECIFIED
) &&
3157 (spec
->data
.ctd
->lun
!= ID_UNSPECIFIED
) &&
3158 (spec
->data
.ctd
->slice
!= ID_UNSPECIFIED
)) {
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
);
3164 } else if ((spec
->data
.ctd
->ctrl
!= ID_UNSPECIFIED
) &&
3165 (spec
->data
.ctd
->lun
!= ID_UNSPECIFIED
)) {
3167 match
= (spec
->data
.ctd
->ctrl
== device
->data
.ctd
->ctrl
) &&
3168 (spec
->data
.ctd
->lun
== device
->data
.ctd
->lun
);
3170 } else if (spec
->data
.ctd
->ctrl
!= ID_UNSPECIFIED
) {
3172 match
= (spec
->data
.ctd
->ctrl
== device
->data
.ctd
->ctrl
);
3178 /* valid names are cX, cXtX, cXtXdX, cXtXdXsX */
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
)) {
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
);
3190 } else if ((spec
->data
.ctd
->ctrl
!= ID_UNSPECIFIED
) &&
3191 (spec
->data
.ctd
->target
!= ID_UNSPECIFIED
) &&
3192 (spec
->data
.ctd
->lun
!= ID_UNSPECIFIED
)) {
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
);
3198 } else if ((spec
->data
.ctd
->ctrl
!= ID_UNSPECIFIED
) &&
3199 (spec
->data
.ctd
->target
!= ID_UNSPECIFIED
)) {
3201 match
= (spec
->data
.ctd
->ctrl
== device
->data
.ctd
->ctrl
) &&
3202 (spec
->data
.ctd
->target
== device
->data
.ctd
->target
);
3204 } else if (spec
->data
.ctd
->ctrl
!= ID_UNSPECIFIED
) {
3206 match
= (spec
->data
.ctd
->ctrl
== device
->data
.ctd
->ctrl
);
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
);
3224 * FUNCTION: raw_spec_includes_device(device_spec_t *spec,
3225 * device_spec_t *device)
3227 * INPUT: spec - pointer to a device_spec struct
3228 * device - pointer to a device_spec struct
3230 * RETURNS: boolean_t - B_TRUE if the device is included in the spec
3233 * PURPOSE: Function which determines if the input raw device spec
3234 * matches the input spec.
3236 * The device_spec_t raw elements are checked.
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.
3243 raw_spec_includes_device(
3244 device_spec_t
*spec
,
3245 device_spec_t
*device
)
3247 return (strncasecmp(spec
->data
.raw
,
3248 device
->data
.raw
, strlen(spec
->data
.raw
)) == 0);
3252 * FUNCTION: compare_name_to_spec_cache_name(void *name, void *list_item)
3254 * INPUT: name - opaque pointer to a char * device name
3255 * list_item - opaque pointer to a spec_cache_t entry
3257 * RETURNS: int - 0 - if request is the same as list_item->request
3260 * PURPOSE: dlist_t helper which compares the input device name
3261 * to the list_item's device name for equality.
3263 * This function is the lookup mechanism for the device_spec
3264 * associated with the name.
3267 compare_name_to_spec_cache_name(
3271 spec_cache_t
*entry
= (spec_cache_t
*)list_item
;
3273 assert(name
!= NULL
);
3274 assert(entry
!= NULL
);
3276 return (string_case_compare((char *)name
, entry
->name
));
3280 * FUNCTION: destroy_spec_cache_entry(void *entry)
3282 * INPUT: entry - opaque pointer to a spec_cache_t
3286 * PURPOSE: Function which reclaims memory allocated to a
3287 * spec_cache_t entry.
3289 * Frees memory allocated to hold the CTD name and the
3290 * corresponding device_spec_t.
3293 destroy_spec_cache_entry(
3296 spec_cache_t
*entry
= (spec_cache_t
*)obj
;
3298 if (entry
!= NULL
) {
3300 destroy_device_spec(entry
->device_spec
);
3306 * FUNCTION: destroy_spec_cache()
3308 * RETURNS: int - 0 on success
3311 * PURPOSE: Function which destroys all entries in the device_spec
3315 destroy_spec_cache()
3317 dlist_free_items(_spec_cache
, destroy_spec_cache_entry
);
3324 * FUNCTION: get_device_access_name(devconfig_t *request,
3325 * dm_descriptor_t desc, char **name)
3327 * INPUT: request - a devconfig_t request
3328 * desc - a dm_descriptor_t device handle
3330 * OUTPUT: name - a char * pointer to hold the preferred name
3332 * RETURNS: int - 0 - if request is the same as list_item->request
3335 * PURPOSE: Utility function to determine which of the possible device
3336 * names should be used to access a known available device.
3338 * Devices handled are slices and disks.
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.
3347 get_device_access_name(
3348 devconfig_t
*request
,
3349 dm_descriptor_t desc
,
3353 boolean_t avail
= B_FALSE
;
3354 dlist_t
*aliases
= NULL
;
3356 assert(desc
!= (dm_descriptor_t
)0);
3360 if ((error
= get_display_name(desc
, name
)) != 0) {
3364 if (is_did_name(*name
) == B_TRUE
) {
3365 oprintf(OUTPUT_DEBUG
,
3366 gettext("device DID name %s is preferred\n"),
3371 error
= is_named_device_avail(request
, *name
, B_FALSE
, &avail
);
3376 if (avail
== B_TRUE
) {
3377 oprintf(OUTPUT_DEBUG
,
3378 gettext("device name %s is accessible\n"),
3383 /* search aliases for an 'available' name, prefer DID names */
3384 if ((error
= get_aliases(desc
, &aliases
)) == 0) {
3386 dlist_t
*iter
= aliases
;
3387 char *availname
= NULL
;
3388 char *didname
= NULL
;
3390 for (; (iter
!= NULL
) && (error
== 0); iter
= iter
->next
) {
3392 char *alias
= (char *)iter
->obj
;
3393 error
= is_named_device_avail(request
, alias
, B_FALSE
, &avail
);
3395 if ((error
== 0) && (avail
== B_TRUE
)) {
3396 oprintf(OUTPUT_DEBUG
,
3397 gettext("device alias %s is accessible for %s\n"),
3402 if (is_did_name(availname
) == B_TRUE
) {
3410 if (didname
!= NULL
) {
3412 } else if (availname
!= NULL
) {