7712 mandoc -Tlint does always exit with error code 0
[unleashed.git] / usr / src / cmd / lvm / metassist / layout / layout_request.c
blob2ba31a2d31938cbe2111600a3e19e81decbdbbfd
1 /*
2 * CDDL HEADER START
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.
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]
20 * CDDL HEADER END
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
29 #include <assert.h>
30 #include <string.h>
31 #include <libintl.h>
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
55 * module.
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
78 * the context.
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
102 typedef struct {
103 int ctrl;
104 int target;
105 int lun;
106 int slice;
107 boolean_t is_ide;
108 } ctd_spec_t;
110 typedef enum {
111 SPEC_TYPE_CTD = 0,
112 SPEC_TYPE_RAW,
113 SPEC_TYPE_OTHER
114 } spec_type_t;
116 typedef struct {
117 spec_type_t type;
118 union {
119 ctd_spec_t *ctd;
120 char *raw;
121 } data;
122 } device_spec_t;
124 static int get_spec_for_name(
125 char *name,
126 device_spec_t **id);
128 static int create_device_spec(
129 char *name,
130 device_spec_t **spec);
132 static int create_device_ctd_spec(
133 char *name,
134 device_spec_t **spec);
136 static int create_device_raw_spec(
137 char *name,
138 device_spec_t **spec);
140 static void destroy_device_spec(
141 device_spec_t *spec);
143 static boolean_t ctd_spec_includes_device(
144 device_spec_t *spec,
145 device_spec_t *device);
147 static boolean_t raw_spec_includes_device(
148 device_spec_t *spec,
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:
161 typedef struct {
163 char *name;
164 device_spec_t *device_spec;
166 } spec_cache_t;
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:
182 typedef 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(
208 void *object,
209 void *list_item);
211 static int convert_usernames_to_specs(
212 char **specs,
213 dlist_t **list);
215 /* other private functions */
216 static int is_device_avail(
217 dm_descriptor_t desc,
218 devconfig_t *request,
219 boolean_t *avail);
221 static int is_named_device_avail(
222 devconfig_t *request,
223 char *device_name,
224 boolean_t check_aliases,
225 boolean_t *avail);
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);
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);
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);
245 static boolean_t spec_includes_device(
246 device_spec_t *spec,
247 device_spec_t *device);
249 static int disk_get_avail_space(
250 devconfig_t *request,
251 dm_descriptor_t disk,
252 uint64_t *avail);
254 static int compare_hba_n_avail_disks(
255 void *obj1,
256 void *obj2);
259 * FUNCTION: release_request_caches()
261 * RETURNS: 0
263 * PURPOSE: cleanup the module private caches.
266 release_request_caches()
268 (void) destroy_request_spec_list_cache();
269 (void) destroy_spec_cache();
271 return (0);
274 * FUNCTION: int set_request_diskset(char *)
276 * INPUT: char * - pointer to the diskset name
277 * OUTPUT: 0 - success
278 * !0 - validation failure
279 * RETURNS:
281 * PURPOSE: set the module global diskset name.
284 set_request_diskset(
285 char *dsname)
287 _request_diskset = dsname;
289 if (dsname == NULL || dsname[0] == '\0') {
290 volume_set_error(
291 gettext("No disk set specified in request\n"));
292 return (-1);
295 return (0);
299 * FUNCTION: char *get_request_diskset()
301 * INPUT: none -
302 * OUTPUT: none -
303 * RETURNS: char * - pointer to the currently set diskset name
305 * PURPOSE: get the global name of the current diskset.
307 char *
308 get_request_diskset()
310 return (_request_diskset);
314 * FUNCTION: void unset_request_diskset()
316 * PURPOSE: unset the module global diskset name.
318 void
319 unset_request_diskset(
320 char *dsname)
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
331 * RETURNS:
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(
342 devconfig_t *req)
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.
355 void
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
367 * RETURNS:
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)
380 int error = 0;
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 */
392 error = 0;
395 return (error);
399 * FUNCTION: void unset_request_defaults()
401 * PURPOSE: unset the layout module global defaults struct.
404 void
405 unset_request_defaults()
407 _defaults = NULL;
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
416 * !0 - otherwise
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
423 * global defaults.
426 get_stripe_min_comp(
427 devconfig_t *req,
428 uint16_t *val)
430 int error = 0;
432 *val = 0;
434 if ((error = devconfig_get_stripe_mincomp(req, val)) != 0) {
435 if (error != ERR_ATTR_UNSET) {
436 return (error);
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);
449 return (error);
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
458 * !0 - otherwise
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
465 * global defaults.
468 get_stripe_max_comp(
469 devconfig_t *req,
470 uint16_t *val)
472 int error = 0;
474 *val = 0;
476 if ((error = devconfig_get_stripe_maxcomp(req, val)) != 0) {
477 if (error != ERR_ATTR_UNSET) {
478 return (error);
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);
491 return (error);
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
500 * !0 - otherwise
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
507 * global defaults.
509 * If no value is explictly specified, ERR_ATTR_UNSET is
510 * returned.
513 get_stripe_interlace(
514 devconfig_t *req,
515 uint64_t *val)
517 int error = 0;
519 *val = 0;
521 if ((error = devconfig_get_stripe_interlace(req, val)) != 0) {
522 if (error != ERR_ATTR_UNSET) {
523 return (error);
525 error = 0;
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);
537 return (error);
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
547 * !0 - otherwise
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
554 * global defaults.
556 * If no value is explictly specified, ERR_ATTR_UNSET is
557 * returned.
560 get_mirror_read_strategy(
561 devconfig_t *req,
562 mirror_read_strategy_t *val)
564 int error = 0;
566 *val = 0;
568 if ((error = devconfig_get_mirror_read(req, val)) != 0) {
569 if (error != ERR_ATTR_UNSET) {
570 return (error);
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);
583 return (error);
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
593 * !0 - otherwise
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
600 * global defaults.
602 * If no value is explictly specified, ERR_ATTR_UNSET is
603 * returned.
606 get_mirror_write_strategy(
607 devconfig_t *req,
608 mirror_write_strategy_t *val)
610 int error = 0;
612 *val = 0;
614 if ((error = devconfig_get_mirror_write(req, val)) != 0) {
615 if (error != ERR_ATTR_UNSET) {
616 return (error);
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);
629 return (error);
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
638 * !0 - otherwise
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
645 * global defaults.
647 * If no value is explictly specified, ERR_ATTR_UNSET is
648 * returned.
651 get_mirror_pass(
652 devconfig_t *req,
653 uint16_t *val)
655 int error = 0;
657 *val = 0;
659 if ((error = devconfig_get_mirror_pass(req, val)) != 0) {
660 if (error != ERR_ATTR_UNSET) {
661 return (error);
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);
674 return (error);
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
683 * !0 - otherwise
685 * PURPOSE: helper which determines how many submirrors mirrored
686 * volumes should have for volumes satisfying the input
687 * request.
689 * The value to use is taken from the input request, the
690 * toplevel diskset request, the diskset defaults or the
691 * global defaults.
694 get_mirror_nsubs(
695 devconfig_t *req,
696 uint16_t *val)
698 int error = 0;
700 *val = 0;
702 if ((error = devconfig_get_mirror_nsubs(req, val)) != 0) {
703 if (error != ERR_ATTR_UNSET) {
704 return (error);
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);
717 return (error);
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
726 * !0 - otherwise
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
734 * global defaults.
737 get_volume_faultrecov(
738 devconfig_t *req,
739 boolean_t *val)
741 int error = 0;
743 *val = B_FALSE;
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);
750 switch (type) {
751 case TYPE_MIRROR:
752 error = defaults_get_mirror_usehsp(
753 _defaults, get_request_diskset(), val);
754 break;
756 case TYPE_STRIPE:
757 error = defaults_get_stripe_usehsp(
758 _defaults, get_request_diskset(), val);
759 break;
761 case TYPE_CONCAT:
762 error = defaults_get_concat_usehsp(
763 _defaults, get_request_diskset(), val);
764 break;
766 case TYPE_VOLUME:
767 error = defaults_get_volume_usehsp(
768 _defaults, get_request_diskset(), val);
769 break;
774 return (error);
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
783 * !0 - otherwise
785 * PURPOSE: helper which determines the appropriate level of data
786 * redundancy a volume should have for volumes satisfying
787 * the input request.
789 * The value to use is taken from the input request, the
790 * toplevel diskset request, the diskset defaults or the
791 * global defaults.
794 get_volume_redundancy_level(
795 devconfig_t *req,
796 uint16_t *val)
798 int error = 0;
800 *val = 0;
802 if ((error = devconfig_get_volume_redundancy_level(req, val)) != 0) {
803 if (error != ERR_ATTR_UNSET) {
804 return (error);
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);
817 return (error);
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
826 * !0 - otherwise
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
834 * global defaults.
837 get_volume_npaths(
838 devconfig_t *req,
839 uint16_t *val)
841 int error = 0;
843 *val = 0;
845 if ((error = devconfig_get_volume_npaths(req, val)) != 0) {
846 if (error != ERR_ATTR_UNSET) {
847 return (error);
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);
860 return (error);
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
869 * !0 - otherwise
871 * PURPOSE: helper which determines the default HSP name for the
872 * input request.
874 * The value to use is taken from the input request, the
875 * toplevel diskset request, the diskset defaults or the
876 * global defaults.
879 get_default_hsp_name(
880 devconfig_t *req,
881 char **name)
883 int error = 0;
885 *name = NULL;
887 if ((error = defaults_get_hsp_name(_defaults,
888 get_request_diskset(), name)) != 0) {
889 if (error != ENOENT) {
890 return (error);
892 error = 0;
895 return (error);
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
906 * RETURNS: int - 0 - on success
907 * !0 - otherwise
909 * PURPOSE: Validation helper which determines if the named slice can
910 * be used as a volume component when satisfying the input
911 * request.
913 * Check if the slice appears in the known slice list,
914 * then check the request's available and unavailable
915 * device specifications.
918 slice_is_available(
919 char *sname,
920 devconfig_t *request,
921 boolean_t *bool)
923 dm_descriptor_t slice = (dm_descriptor_t)0;
924 int error = 0;
926 *bool = B_FALSE;
928 if ((error = slice_get_by_name(sname, &slice)) != 0) {
929 return (error);
932 if (slice == (dm_descriptor_t)0) {
933 /* no slice found */
934 return (ENODEV);
937 if (error == 0) {
938 error = is_named_device_avail(request, sname, B_TRUE, bool);
941 return (error);
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
952 * !0 otherwise
954 * PURPOSE: Validation helper function which finds all disks "on" the
955 * input target.
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(
966 char *name,
967 dlist_t **disks)
969 int error = 0;
970 device_spec_t *targetid = NULL;
972 error = get_spec_for_name(name, &targetid);
973 if (error == 0) {
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);
980 iter = iter->next) {
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;
988 ((error = get_display_name(disk, &diskname)) != 0) ||
989 (error = get_aliases(disk, &diskaliases)) ||
990 (error = get_spec_for_name(diskname, &diskid));
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);
1001 } else {
1002 /* check disk's aliases */
1003 dlist_t *iter2;
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);
1018 if (item == NULL) {
1019 error = ENOMEM;
1020 } else {
1021 *disks =
1022 dlist_append(item, *disks, AT_HEAD);
1031 return (error);
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
1050 * !0 - otherwise
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
1066 * each list item.
1068 * for (each HBA) {
1070 * select HBA
1071 * get available disks on HBA
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
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,
1088 dlist_t *hbas,
1089 int mindisks,
1090 dlist_t **selhbas,
1091 dlist_t **seldisks)
1093 dlist_t *iter = NULL;
1094 int error = 0;
1096 *selhbas = NULL;
1097 *seldisks = 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;
1105 uint64_t space = 0;
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 */
1111 for (iter2 = 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);
1123 if (item == NULL) {
1124 error = ENOMEM;
1125 } else {
1127 *seldisks =
1128 dlist_append(item, *seldisks, AT_HEAD);
1130 /* increment this HBA's distinct disk count */
1131 ++ndistinct;
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);
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);
1148 /* save # of disks for ordering the list */
1149 hba_set_n_avail_disks(hba, ndistinct);
1153 dlist_free_items(disks, NULL);
1156 if (error != 0) {
1157 oprintf(OUTPUT_TERSE,
1158 gettext("failed selecting HBAs with n disks: %d\n"),
1159 error);
1161 dlist_free_items(*selhbas, NULL);
1162 *selhbas = NULL;
1163 dlist_free_items(*seldisks, NULL);
1164 *seldisks = NULL;
1167 return (error);
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
1179 * disks
1180 * avail - pointer to a uint64_t to hold the aggregate
1181 * available space on the available disks
1183 * RETURNS: int - 0 - on success
1184 * !0 - otherwise
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,
1200 dlist_t **disks,
1201 uint64_t *space)
1203 dlist_t *usable_disks = NULL;
1204 dlist_t *iter = NULL;
1205 int error = 0;
1207 *disks = 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);
1221 if (error != 0) {
1222 continue;
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);
1238 if (item == NULL) {
1239 error = ENOMEM;
1240 } else {
1241 *disks = dlist_append(item, *disks, AT_HEAD);
1244 *space += disk_space;
1249 dlist_free_items(hbas, NULL);
1252 if (error != 0) {
1253 dlist_free_items(*disks, NULL);
1254 *disks = NULL;
1257 return (error);
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
1268 * space
1270 * RETURNS: int - 0 - on success
1271 * !0 - otherwise
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.
1280 static int
1281 disk_get_avail_space(
1282 devconfig_t *request,
1283 dm_descriptor_t disk,
1284 uint64_t *space)
1286 dlist_t *usable_slices = NULL;
1287 dlist_t *iter = NULL;
1288 int error = 0;
1290 *space = 0;
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)) {
1308 continue;
1311 /* is slice reserved by an explicit layout request? */
1312 if (((error = is_reserved_slice(slice, &reserved)) != 0) ||
1313 (reserved == B_TRUE)) {
1314 continue;
1317 /* is slice used by a pending layout request? */
1318 if (((error = is_used_slice(slice, &used)) != 0) ||
1319 (used == B_TRUE)) {
1320 continue;
1323 /* is slice available? */
1324 if (((error = is_device_avail(slice, request, &avail)) == 0) &&
1325 (avail == B_TRUE)) {
1327 /* does slice have usable space? */
1328 uint64_t size = 0;
1329 if ((error = slice_get_size(slice, &size)) == 0) {
1330 *space += size;
1335 return (error);
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
1348 * !0 - otherwise
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
1356 * to be available.
1359 disks_get_avail_slices(
1360 devconfig_t *request,
1361 dlist_t *disks,
1362 dlist_t **slices)
1364 dlist_t *usable_slices = NULL;
1365 dlist_t *iter = NULL;
1366 int error = 0;
1368 *slices = 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)) {
1386 continue;
1389 /* is slice reserved by an explicit layout request? */
1390 if (((error = is_reserved_slice(slice, &reserved)) != 0) ||
1391 (reserved == B_TRUE)) {
1392 continue;
1395 /* is slice used by a pending layout request? */
1396 if (((error = is_used_slice(slice, &used)) != 0) ||
1397 (used == B_TRUE)) {
1398 continue;
1401 /* is slice available? */
1402 if (((error = is_device_avail(slice, request, &avail)) == 0) &&
1403 (avail == B_TRUE)) {
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);
1419 if (error != 0) {
1420 dlist_free_items(*slices, NULL);
1421 *slices = NULL;
1424 return (error);
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
1438 * !0 otherwise
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(
1449 dlist_t *volumes,
1450 dlist_t **hbas,
1451 dlist_t **disks)
1453 dlist_t *iter = NULL;
1454 int error = 0;
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);
1463 return (error);
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
1478 * !0 otherwise
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,
1489 dlist_t **hbas,
1490 dlist_t **disks)
1492 dlist_t *iter = NULL;
1493 int error = 0;
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;
1503 char *name = NULL;
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);
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);
1526 } else if (devconfig_isA(dev, TYPE_MIRROR)) {
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);
1540 return (error);
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.
1562 static int
1563 compare_hba_n_avail_disks(
1564 void *obj1,
1565 void *obj2)
1567 uint16_t n1 = 0;
1568 uint16_t n2 = 0;
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
1589 * !0 - otherwise
1591 * PURPOSE: Internal helper which determines if the input device can
1592 * be used as a volume component when satisfying the input
1593 * request.
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.
1603 static int
1604 is_device_avail(
1605 dm_descriptor_t desc,
1606 devconfig_t *request,
1607 boolean_t *avail)
1609 char *name = NULL;
1610 int error = 0;
1612 *avail = B_FALSE;
1614 if ((error = get_display_name(desc, &name)) == 0) {
1615 error = is_named_device_avail(request, name, B_TRUE, avail);
1618 return (error);
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
1629 * !0 - otherwise
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.
1640 static int
1641 compare_request_to_request_spec_list_request(
1642 void *request,
1643 void *list_item)
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.
1670 static int
1671 compare_device_spec_specificity(
1672 void *spec1,
1673 void *spec2)
1675 if (spec1 == NULL || spec2 == NULL) {
1676 return (-1);
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);
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);
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);
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);
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);
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);
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);
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);
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);
1733 /* both specify just ctrl */
1734 return (0);
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,
1757 (void *)request,
1758 compare_request_to_request_spec_list_request);
1760 if (list_item != NULL) {
1761 entry = (request_spec_list_t *)list_item->obj;
1764 return (entry);
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
1776 * !0 otherwise.
1778 * PURPOSE: Function which encapsulates the details of adding a
1779 * device_spec_list_t cache entry.
1781 static int
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) {
1788 return (ENOMEM);
1791 _request_spec_list_cache = dlist_append(list_item,
1792 _request_spec_list_cache, AT_HEAD);
1794 return (0);
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
1807 * request
1809 * RETURNS: int - 0 on success
1810 * !0 otherwise.
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
1820 * cache.
1822 static int
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)
1829 int error = 0;
1830 dlist_t *list = NULL;
1832 *entry = calloc(1, sizeof (request_spec_list_t));
1833 if (*entry == NULL) {
1834 return (ENOMEM);
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);
1846 if (error == 0) {
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
1854 list = NULL;
1855 error = convert_usernames_to_specs(
1856 unavail_device_specs, &list);
1858 if (error == 0) {
1859 (*entry)->unavail_specs_list = list;
1862 if (error != 0) {
1863 /* delete the partial entry */
1864 destroy_request_spec_list_entry((void *)*entry);
1865 *entry = NULL;
1868 return (error);
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
1880 * !0 otherwise.
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().
1888 static int
1889 convert_usernames_to_specs(
1890 char **specs,
1891 dlist_t **list)
1893 int i = 0;
1894 int error = 0;
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.
1903 for (i = 0;
1904 (specs != NULL) && (specs[i] != NULL) && (error == 0);
1905 i++) {
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);
1913 if (spec == NULL) {
1914 error = ENOMEM;
1915 } else {
1916 *list = dlist_insert_ordered
1917 (list_item, *list, DESCENDING,
1918 compare_device_spec_specificity);
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;
1930 return (error);
1934 * FUNCTION: destroy_request_spec_list_entry(void *entry)
1936 * INPUT: entry - opaque pointer to a request_spec_list_t
1938 * RETURNS: nothing
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.
1947 static void
1948 destroy_request_spec_list_entry(
1949 void *obj)
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);
1958 free(entry);
1963 * FUNCTION: destroy_request_spec_list_cache()
1965 * RETURNS: int - 0 on success
1966 * !0 otherwise.
1968 * PURPOSE: Function which destroys all entries in the request_spec_list
1969 * cache.
1971 static int
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;
1978 return (0);
1982 * FUNCTION: get_request_avail_spec_list(devconfig_t *request,
1983 * dlist_t **list)
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
1989 * input request.
1991 * RETURNS: int - 0 on success
1992 * !0 otherwise.
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.
2003 static int
2004 get_request_avail_spec_list(
2005 devconfig_t *request,
2006 dlist_t **list)
2008 request_spec_list_t *entry = NULL;
2009 int error = 0;
2011 if ((entry = find_request_spec_list_entry(request)) == NULL) {
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);
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;
2028 if ((error == 0) && (entry != NULL)) {
2029 *list = entry->avail_specs_list;
2032 return (error);
2036 * FUNCTION: get_request_unavail_spec_list(devconfig_t *request,
2037 * dlist_t **list)
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
2043 * input request.
2045 * RETURNS: int - 0 on success
2046 * !0 otherwise.
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.
2057 static int
2058 get_request_unavail_spec_list(
2059 devconfig_t *request,
2060 dlist_t **list)
2062 request_spec_list_t *entry = NULL;
2063 int error = 0;
2065 if ((entry = find_request_spec_list_entry(request)) == NULL) {
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);
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;
2082 if ((error == 0) && (entry != NULL)) {
2083 *list = entry->unavail_specs_list;
2086 return (error);
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
2102 * !0 otherwise.
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.
2118 static int
2119 get_default_avail_spec_list(
2120 defaults_t *alldefaults,
2121 char *dsname,
2122 dlist_t **list)
2124 request_spec_list_t *entry = NULL;
2125 devconfig_t *defaults = NULL;
2126 int error = 0;
2128 /* Get diskset defaults, or global if none for diskset */
2129 error = defaults_get_diskset_by_name(
2130 alldefaults, dsname, &defaults);
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);
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;
2150 if ((entry = find_request_spec_list_entry(defaults)) == NULL) {
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);
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;
2167 if ((error == 0) && (entry != NULL)) {
2168 *list = entry->avail_specs_list;
2171 return (error);
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
2187 * !0 otherwise.
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.
2203 static int
2204 get_default_unavail_spec_list(
2205 defaults_t *alldefaults,
2206 char *dsname,
2207 dlist_t **list)
2209 request_spec_list_t *entry = NULL;
2210 devconfig_t *defaults = NULL;
2211 int error = 0;
2213 /* Get diskset defaults, or global if none for diskset */
2214 error = defaults_get_diskset_by_name(
2215 alldefaults, dsname, &defaults);
2217 if (error != 0) {
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);
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;
2236 if ((entry = find_request_spec_list_entry(defaults)) == NULL) {
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);
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;
2253 if ((error == 0) && (entry != NULL)) {
2254 *list = entry->unavail_specs_list;
2257 return (error);
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
2277 * input request.
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
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.
2304 static int
2305 is_named_device_avail(
2306 devconfig_t *request,
2307 char *device_name,
2308 boolean_t check_aliases,
2309 boolean_t *avail)
2311 typedef enum check_types {
2312 DEVICE_REQUEST = 0,
2313 DISKSET_REQUEST,
2314 DEFAULTS,
2315 N_CHECKS
2316 } check_type_t;
2318 check_type_t check_type;
2320 typedef enum list_types {
2321 AVAIL = 0,
2322 UNAVAIL,
2323 N_LISTS
2324 } list_type_t;
2326 dlist_t *lists[N_CHECKS][N_LISTS];
2327 boolean_t includes;
2328 int error = 0;
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]));
2356 if (error != 0) {
2357 return (error);
2360 *avail = B_TRUE;
2362 for (check_type = DEVICE_REQUEST;
2363 (check_type < N_CHECKS) && (error == 0);
2364 check_type++) {
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,
2371 &includes)) == 0) {
2373 if (includes != B_TRUE) {
2374 *avail = B_FALSE;
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) {
2386 *avail = B_FALSE;
2392 /* lists at this level checked, skip remainder */
2393 break;
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) {
2403 *avail = B_FALSE;
2407 /* list at this level checked, skip remainder */
2408 break;
2412 return (error);
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
2424 * checking.
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
2431 * - !0 otherwise
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().
2440 static int
2441 avail_list_includes_device_name(
2442 dlist_t *list,
2443 char *device_name,
2444 boolean_t check_aliases,
2445 boolean_t *includes)
2447 dlist_t *iter = NULL;
2448 int error = 0;
2450 *includes = B_FALSE;
2452 for (iter = list;
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);
2461 return (0);
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
2473 * checking.
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
2480 * - !0 otherwise
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().
2489 static int
2490 unavail_list_includes_device_name(
2491 dlist_t *list,
2492 char *device_name,
2493 boolean_t check_aliases,
2494 boolean_t *includes)
2496 dlist_t *iter = NULL;
2497 int error = 0;
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
2506 * exact spec.
2508 * Meaning: if the list has { "c3t0d0", ..., "c3", ... } and the
2509 * input device name is "c3t0d0s0", it will match "c3t0d0"
2510 * before "c3".
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.
2516 for (iter = list;
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",
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);
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));
2559 if (error != 0) {
2560 return (error);
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);
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);
2586 /* for each of the disk's HBAs */
2587 for (iter = 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? */
2602 for (iter2 = list;
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 */
2619 return (error);
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
2633 * specification
2634 * B_FALSE - otherwise
2636 * RETURNS: int - 0 on success
2637 * - !0 otherwise
2639 * PURPOSE: Helper used by (un)avail_specs_includes_device_name() that
2640 * determines if the input device specification "includes"
2641 * the named device.
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.
2647 static int
2648 spec_includes_device_name(
2649 device_spec_t *spec,
2650 char *device_name,
2651 boolean_t check_aliases,
2652 boolean_t *includes)
2654 device_spec_t *device_spec;
2655 int error = 0;
2657 error = get_spec_for_name(device_name, &device_spec);
2658 if (error == 0) {
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)) {
2682 dlist_t *iter;
2683 for (iter = aliases;
2684 (iter != NULL) && (*includes == B_FALSE) &&
2685 (error == 0);
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);
2692 if (error == 0) {
2693 /* does spec include alias? */
2694 *includes = spec_includes_device(spec, alias_spec);
2698 dlist_free_items(aliases, free);
2702 return (error);
2706 * FUNCTION: destroy_device_spec(device_spec_t *spec)
2708 * INPUT: spec - pointer to a device_spec_t
2710 * RETURNS: nothing
2712 * PURPOSE: Function which reclaims memory allocated to a device_spec_t.
2714 * Frees memory allocated to hold the specific data in the spec.
2716 static void
2717 destroy_device_spec(
2718 device_spec_t *spec)
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);
2726 free(spec);
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
2738 * !0 otherwise
2740 * PURPOSE: Function which creates a device_spec_t for the input
2741 * device name.
2744 static int
2745 create_device_spec(
2746 char *name,
2747 device_spec_t **spec)
2749 int error = 0;
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);
2763 return (error);
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
2775 * !0 otherwise
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
2782 * ID_UNSPECIFIED.
2784 * The input name must be well-formed.
2786 * These are the acceptable forms:
2788 * cXtXdXsX
2789 * cXtXdX
2790 * cXtX
2791 * cXdXsX
2792 * cXdX
2793 * cX
2795 static int
2796 create_device_ctd_spec(
2797 char *name,
2798 device_spec_t **spec)
2800 uint_t ctrl;
2801 uint_t target;
2802 uint_t lun;
2803 uint_t slice;
2805 uint_t nscan;
2806 uint_t nchars;
2808 char *device_str;
2809 char *target_str;
2810 char *ctd_str;
2811 char *t_ptr;
2812 char *d_ptr;
2813 char *s_ptr;
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;
2821 int error = 0;
2823 ctd_str = strdup(name);
2824 if (ctd_str == NULL) {
2825 return (ENOMEM);
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;
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)) {
2852 error = -1;
2853 oprintf(OUTPUT_DEBUG,
2854 gettext("no slice component in device "
2855 "name \"%s\".\n"),
2856 name);
2858 } else {
2859 got_slice = B_TRUE;
2860 *s_ptr = '\0';
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)) {
2871 error = -1;
2872 oprintf(OUTPUT_DEBUG,
2873 gettext("no disk/lun component "
2874 "in device name \"%s\".\n"),
2875 name);
2877 } else {
2878 got_lun = B_TRUE;
2879 *d_ptr = '\0';
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) {
2891 error = ENOMEM;
2893 } else {
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)) {
2902 error = -1;
2903 oprintf(OUTPUT_DEBUG,
2904 gettext("no target/WWN component "
2905 "in device name \"%s\".\n"),
2906 name);
2908 } else {
2909 got_target = B_TRUE;
2910 *t_ptr = '\0';
2913 free(target_str);
2916 } else {
2917 is_ide = 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)) {
2927 error = -1;
2928 oprintf(OUTPUT_DEBUG,
2929 gettext("no channel/HBA component "
2930 "in device name \"%s\".\n"),
2931 name);
2933 } else {
2934 got_ctrl = B_TRUE;
2938 free(ctd_str);
2940 if (error == 0) {
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) {
2947 error = ENOMEM;
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;
2974 return (error);
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
2985 * !0 otherwise
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.
2992 static int
2993 create_device_raw_spec(
2994 char *name,
2995 device_spec_t **spec)
2997 int error = 0;
2998 char *ctd_str = strdup(name);
3000 if (ctd_str == NULL) {
3001 return (ENOMEM);
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);
3010 return (error);
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
3021 * !0 otherwise
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
3028 * is complete.
3031 get_spec_for_name(
3032 char *name,
3033 device_spec_t **id)
3035 dlist_t *item;
3036 int error = 0;
3038 item = dlist_find(_spec_cache, (void *)name,
3039 compare_name_to_spec_cache_name);
3041 if (item == NULL) {
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);
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;
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);
3076 } else {
3077 *id = ((spec_cache_t *)item->obj)->device_spec;
3080 return (error);
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
3091 * B_FALSE otherwise
3093 * PURPOSE: Function which determines if the input device matches the
3094 * input spec.
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.
3102 boolean_t
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));
3114 return (B_FALSE);
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
3125 * B_FALSE otherwise
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
3134 * are specified.
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
3145 static boolean_t
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);
3176 } else {
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);
3220 return (match);
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
3231 * B_FALSE otherwise
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.
3242 static boolean_t
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
3258 * !0 - otherwise
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.
3266 static int
3267 compare_name_to_spec_cache_name(
3268 void *name,
3269 void *list_item)
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
3284 * RETURNS: nothing
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.
3292 static void
3293 destroy_spec_cache_entry(
3294 void *obj)
3296 spec_cache_t *entry = (spec_cache_t *)obj;
3298 if (entry != NULL) {
3299 free(entry->name);
3300 destroy_device_spec(entry->device_spec);
3301 free(entry);
3306 * FUNCTION: destroy_spec_cache()
3308 * RETURNS: int - 0 on success
3309 * !0 otherwise.
3311 * PURPOSE: Function which destroys all entries in the device_spec
3312 * cache.
3314 static int
3315 destroy_spec_cache()
3317 dlist_free_items(_spec_cache, destroy_spec_cache_entry);
3318 _spec_cache = NULL;
3320 return (0);
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
3333 * !0 - otherwise
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,
3350 char **name)
3352 int error = 0;
3353 boolean_t avail = B_FALSE;
3354 dlist_t *aliases = NULL;
3356 assert(desc != (dm_descriptor_t)0);
3358 *name = NULL;
3360 if ((error = get_display_name(desc, name)) != 0) {
3361 return (error);
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);
3371 error = is_named_device_avail(request, *name, B_FALSE, &avail);
3372 if (error != 0) {
3373 return (error);
3376 if (avail == B_TRUE) {
3377 oprintf(OUTPUT_DEBUG,
3378 gettext("device name %s is accessible\n"),
3379 *name);
3380 return (0);
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"),
3398 alias, *name);
3400 availname = alias;
3402 if (is_did_name(availname) == B_TRUE) {
3403 didname = alias;
3404 break;
3409 if (error == 0) {
3410 if (didname != NULL) {
3411 *name = didname;
3412 } else if (availname != NULL) {
3413 *name = availname;
3418 return (error);