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