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 <limits.h>
30 #include <libdiskmgt.h>
31 #include <libintl.h>
32
33 #include <meta.h>
34
35 #define _LAYOUT_DISCOVERY_C
36
37 #include "volume_dlist.h"
38 #include "volume_error.h"
39 #include "volume_nvpair.h"
40 #include "volume_output.h"
41
42 #include "layout_device_cache.h"
43 #include "layout_device_util.h"
44 #include "layout_dlist_util.h"
45 #include "layout_discovery.h"
46 #include "layout_request.h"
47 #include "layout_slice.h"
48 #include "layout_svm_util.h"
49
50 /*
51 * lists of device dm_descriptor_t handles discovered during
52 * the initial system probe. Lists are populated by
53 * discover_known_devices.
54 *
55 * "bad" slices are those that are known to libdiskmgt but
56 * cannot be accessed. An example would be a slice that has
57 * disappeared due to disk re-slicing: libdiskmgt may have a
58 * cached handle for it, but the slice no longer exists.
59 *
60 * "bad" disks are thoese that are known to libdiskmgt but
61 * cannot be accessed. An example would be a disk that has
62 * failed or has gone offline: libdiskmgt may have a cached
63 * handle for it, but the disk does not respond.
64 */
65 static dlist_t *_bad_slices = NULL;
66 static dlist_t *_bad_disks = NULL;
67
68 static dlist_t *_known_slices = NULL;
69 static dlist_t *_known_disks = NULL;
70 static dlist_t *_known_hbas = NULL;
71
72 /*
73 * helper functions for building known device lists, used by
74 * discover_known_devices.
75 */
76 static int generate_known_slices(dlist_t *disks, dlist_t **known,
77 dlist_t **bad);
78 static int generate_known_disks(dlist_t **known, dlist_t **bad);
79 static int generate_known_hbas(dlist_t *disks, dlist_t **known);
80 static int generate_known_hba_name(
81 dm_descriptor_t hba,
82 dm_descriptor_t alias,
83 dm_descriptor_t disk);
84
85 static void print_known_devices();
86 static void print_device_list(dlist_t *devices);
87
88 /*
89 * lists of device dm_descriptor_t handles that are usable by layout.
90 * These devices must still pass the user specified available/unavailable
91 * filter before they're actually considered available.
92 *
93 * Lists are populated by discover_usable_devices.
94 */
95 static dlist_t *_usable_slices = NULL;
96 static dlist_t *_usable_disks = NULL;
97 static dlist_t *_usable_hbas = NULL;
98
99 /*
100 * private flag that remembers if any HBA is known to support MPXIO
101 */
102 static boolean_t _mpxio_enabled = B_FALSE;
103
104 /*
105 * The slice_class struct is used to group slices by usage class.
106 */
107 typedef struct {
108 char *usage; /* usage description */
109 dlist_t *sliceinfo; /* list with info about each slice with usage */
110 } slice_class_t;
111
112 #define USE_DISKSET "diskset"
113
114 static int check_slice_usage(
115 char *dsname,
116 dm_descriptor_t slice,
117 dm_descriptor_t disk,
118 boolean_t *avail,
119 dlist_t **bad,
120 dlist_t **classes);
121
122 static int check_svm_slice_usage(
123 char *dsname,
124 dm_descriptor_t slice,
125 dm_descriptor_t disk,
126 boolean_t *avail,
127 dlist_t **classes);
128
129 static int save_slice_classification(
130 char *dsname,
131 dm_descriptor_t slice,
132 dm_descriptor_t disk,
133 char *usage,
134 char *usage_detail,
135 dlist_t **classes);
136
137 static int generate_usable_disks_and_slices_in_local_set(
138 dlist_t **classes,
139 dlist_t **bad_disks,
140 dlist_t **usable_disks,
141 dlist_t **usable_slices);
142
143 static int generate_usable_disks_and_slices_in_named_set(
144 char *dsname,
145 dlist_t **classes,
146 dlist_t **bad_slices,
147 dlist_t **usable_disks,
148 dlist_t **usable_slices);
149
150 static int create_usable_slices(
151 dm_descriptor_t disk,
152 dlist_t *used,
153 dlist_t *unused,
154 dlist_t **usable);
155
156 static int add_new_usable(
157 dm_descriptor_t disk,
158 uint64_t stblk,
159 uint64_t nblks,
160 dlist_t **next_unused,
161 dlist_t **usable);
162
163 static int update_slice_attributes(
164 dm_descriptor_t slice,
165 uint64_t stblk,
166 uint64_t nblks,
167 uint64_t nbytes);
168
169 static int generate_usable_hbas(
170 dlist_t *disks,
171 dlist_t **usable);
172
173 static void print_usable_devices();
174
175 static void print_unusable_devices(
176 dlist_t *badslices,
177 dlist_t *baddisks,
178 dlist_t *usedslices);
179
180 static char *get_slice_usage_msg(
181 char *usage);
182
183 /*
184 * virtual slices...
185 */
186 static int generate_virtual_slices(
187 dlist_t *avail_disks_local_set,
188 dlist_t **usable);
189
190 /*
191 * multipathed disks have aliases, as do slices on those disks.
192 * these need to be tracked since the user may specify them.
193 * A multi-pathed disk is one connected to the system thru
194 * more than one physical HBA, each connection gets a distinct
195 * name in the device tree and they're all more or less equivalent.
196 * No indication as to how many possible physical connections a
197 * disk may have, so we pick an arbitrary number of aliases to
198 * support. There is nothing significant about this number,
199 * it just controls the number of alias slots that get allocated.
200 */
201 #define MAX_ALIASES 8
202
203 /*
204 * attribute name for layout private information stored in
205 * device nvpair attribute lists.
206 */
207 static char *ATTR_DEVICE_ALIASES = "layout_device_aliases";
208
209 static int compare_start_blocks(
210 void *desc1, void *desc2);
211
212 static int compare_desc_display_names(
213 void *desc1, void *desc2);
214
215 /*
216 * FUNCTION: is_mpxio_enabled()
217 *
218 * RETURNS: boolean_t - B_TRUE - if MPXIO appears enabled for the system
219 * B_FALSE - otherwise
220 *
221 * PURPOSE: returns the value of _mpxio_enabled which is set to B_TRUE
222 * during system configuration discovery if any of the knwon
223 * HBAs advertises itself as a "multiplex" controller.
224 */
225 boolean_t
is_mpxio_enabled()226 is_mpxio_enabled()
227 {
228 return (_mpxio_enabled);
229 }
230
231 /*
232 * FUNCTION: discover_known_devices()
233 *
234 * SIDEEFFECT: populates the module private lists of known devices
235 * (_known_slices, _known_disks, _known_hbas).
236 *
237 * All known devices will also have had their CTD
238 * short names inferred and stored.
239 *
240 * RETURNS: int - 0 on success
241 * !0 otherwise
242 *
243 * PURPOSE: Load physical devices discovered thru libdiskmgt.
244 */
245 int
discover_known_devices()246 discover_known_devices()
247 {
248 int error = 0;
249
250 oprintf(OUTPUT_TERSE,
251 gettext("\nScanning system physical "
252 "device configuration...\n"));
253
254 /* initialize layout_device_cache */
255 ((error = create_device_caches()) != 0) ||
256
257 (error = generate_known_disks(&_known_disks, &_bad_disks)) ||
258 (error = generate_known_slices(_known_disks, &_known_slices,
259 &_bad_slices)) ||
260 (error = generate_known_hbas(_known_disks, &_known_hbas));
261
262 if (error == 0) {
263 print_known_devices();
264 }
265
266 return (error);
267 }
268
269 /*
270 * FUNCTION: release_known_devices()
271 *
272 * RETURNS: int - 0 on success
273 * !0 otherwise
274 *
275 * PURPOSE: Unloads all state currently held for known
276 * physical devices.
277 */
278 int
release_known_devices(char * diskset)279 release_known_devices(
280 char *diskset)
281 {
282 /* these lists are module private */
283 dlist_free_items(_bad_slices, NULL);
284 dlist_free_items(_bad_disks, NULL);
285 dlist_free_items(_known_slices, NULL);
286 dlist_free_items(_known_disks, NULL);
287 dlist_free_items(_known_hbas, NULL);
288
289 _bad_slices = NULL;
290 _bad_disks = NULL;
291 _known_slices = NULL;
292 _known_disks = NULL;
293 _known_hbas = NULL;
294
295 /* clean up state kept in layout_device_cache */
296 release_device_caches();
297
298 return (0);
299 }
300
301 /*
302 * FUNCTION: discover_usable_devices(char *diskset)
303 *
304 * INPUT: diskset - a char * diskset name.
305 *
306 * SIDEEFFECT: Traverses the lists of known devices and populates the
307 * module private lists of usable devices (_usable_slices,
308 * _usable_disks, _usable_hbas), as well as the module
309 * private list of used slices.
310 *
311 * RETURNS: int - 0 on success
312 * !0 otherwise
313 *
314 * PURPOSE: Process the known devices and determine which of them are
315 * usable for generating volumes in the specified diskset.
316 *
317 * The specified diskset's name cannot be NULL or 0 length.
318 */
319 int
discover_usable_devices(char * diskset)320 discover_usable_devices(
321 char *diskset)
322 {
323 int error = 0;
324
325 dlist_t *used_classes = NULL;
326 dlist_t *iter = NULL;
327
328 if (diskset == NULL || diskset[0] == '\0') {
329 volume_set_error(
330 gettext("a diskset name must be specified in "
331 "the request\n"));
332 return (-1);
333 }
334
335 oprintf(OUTPUT_TERSE,
336 gettext("\nDetermining usable physical devices "
337 "for disk set \"%s\"...\n"),
338 diskset);
339
340 error = generate_usable_disks_and_slices_in_local_set(
341 &used_classes, &_bad_slices, &_usable_disks, &_usable_slices);
342 if (error == 0) {
343
344 error = generate_usable_disks_and_slices_in_named_set(
345 diskset, &used_classes, &_bad_slices, &_usable_disks,
346 &_usable_slices);
347 if (error == 0) {
348
349 error = generate_usable_hbas(_usable_disks, &_usable_hbas);
350 if (error == 0) {
351
352 print_usable_devices();
353 print_unusable_devices(
354 _bad_slices, _bad_disks, used_classes);
355 }
356 }
357 }
358
359 /*
360 * free slice classification usage and lists, items are char*
361 * the used_classes structure is only filled in if verbose
362 * output was requested.
363 */
364 for (iter = used_classes; iter != NULL; iter = iter->next) {
365 slice_class_t *class = (slice_class_t *)iter->obj;
366 free(class->usage);
367 dlist_free_items(class->sliceinfo, free);
368 }
369
370 dlist_free_items(used_classes, free);
371 return (error);
372 }
373
374 /*
375 * FUNCTION: release_usable_devices()
376 *
377 * RETURNS: int - 0 on success
378 * !0 otherwise
379 *
380 * PURPOSE: Unloads all state currently held for usable
381 * physical devices.
382 */
383 int
release_usable_devices()384 release_usable_devices()
385 {
386 /* list items are shared with _known_XXX lists */
387
388 dlist_free_items(_usable_slices, NULL);
389 dlist_free_items(_usable_disks, NULL);
390 dlist_free_items(_usable_hbas, NULL);
391
392 _usable_slices = NULL;
393 _usable_disks = NULL;
394 _usable_hbas = NULL;
395
396 /* clean up state kept in layout_device_util */
397 release_virtual_slices();
398
399 return (0);
400 }
401
402 /*
403 * FUNCTION: get_known_slices(dlist_t **list)
404 * get_known_disks(dlist_t **list)
405 * get_known_hbas(dlist_t **list)
406 *
407 * OUTPUT: list - a dlist_t pointer to hold the returned list of
408 * devices.
409 *
410 * RETURNS: int - 0 on success
411 * !0 otherwise
412 *
413 * PURPOSE: Public accessors for the module private lists of
414 * available devices.
415 */
416 int
get_known_slices(dlist_t ** list)417 get_known_slices(
418 dlist_t **list)
419 {
420 *list = _known_slices;
421
422 return (0);
423 }
424
425 int
get_known_disks(dlist_t ** list)426 get_known_disks(
427 dlist_t **list)
428 {
429 *list = _known_disks;
430
431 return (0);
432 }
433
434 int
get_known_hbas(dlist_t ** list)435 get_known_hbas(
436 dlist_t **list)
437 {
438 *list = _known_hbas;
439
440 return (0);
441 }
442
443 /* make fully qualified DID device name */
444 static char *
make_fully_qualified_did_device_name(char * device)445 make_fully_qualified_did_device_name(
446 char *device)
447 {
448 static char buf[MAXPATHLEN];
449
450 if (device != NULL && strrchr(device, '/') == NULL) {
451 (void) snprintf(buf, MAXPATHLEN-1, "%s/%s",
452 "/dev/did/dsk", device);
453 return (buf);
454 }
455
456 return (device);
457 }
458
459 /*
460 * FUNCTION: generate_known_disks(dlist_t **known,
461 * dlist_t **bad)
462 *
463 * INPUT: NONE
464 *
465 * OUTPUT: known - populated list of known disks
466 * bad - populated list of known bad disks
467 *
468 * RETURNS: int - 0 on success
469 * !0 otherwise
470 *
471 * PURPOSE: Does the system configuration discovery to determine
472 * what disks are known to be attached to the system.
473 *
474 * Determines the CTD name for each disk and saves it.
475 */
476 static int
generate_known_disks(dlist_t ** known,dlist_t ** bad)477 generate_known_disks(
478 dlist_t **known,
479 dlist_t **bad)
480 {
481 int i;
482 int error = 0;
483 dm_descriptor_t *ddp;
484
485 ddp = dm_get_descriptors(DM_DRIVE, NULL, &error);
486 (void) add_descriptors_to_free(ddp);
487
488 *known = NULL;
489
490 if (error != 0) {
491 volume_set_error(
492 gettext("Error discovering system hardware configuration,\n"
493 "unable to communicate with libdiskmgt or diskmgtd.\n"));
494 return (-1);
495 }
496
497 if ((ddp == NULL) || (ddp[0] == NULL)) {
498 volume_set_error(gettext("there are no known disks\n"));
499 return (-1);
500 }
501
502 /* iterate all returned disks and add them to the known list */
503 for (i = 0; (ddp[i] != NULL) && (error == 0); i++) {
504 dm_descriptor_t disk = (dm_descriptor_t)ddp[i];
505 dlist_t *aliases = NULL;
506 uint32_t mtype = DM_MT_UNKNOWN;
507 uint32_t dtype = DM_DT_UNKNOWN;
508 boolean_t bad_disk = B_FALSE;
509 boolean_t online = B_TRUE;
510
511 #if defined(i386)
512 /* on X86, disks must have a solaris FDISK partition */
513 boolean_t solpart = B_FALSE;
514 #endif /* defined(i386) */
515
516 if (((error = disk_get_is_online(disk, &online)) == 0 &&
517 online == B_FALSE) || error == ENODEV) {
518 /* if the disk is offline, report it as bad */
519 bad_disk = B_TRUE;
520 error = 0;
521 } else
522
523 if (error == 0 &&
524 (((error = disk_get_media_type(disk, &mtype)) != 0) ||
525 ((error = disk_get_drive_type(disk, &dtype)) != 0)) &&
526 error == ENODEV) {
527 /*
528 * if any disk attribute access fails with ENODEV
529 * report it as bad
530 */
531 bad_disk = B_TRUE;
532 error = 0;
533 } else {
534
535 /*
536 * Determine whether disk is fixed by checking its
537 * drive type. If drive type is unknown, check media
538 * type.
539 */
540 int isfixed = (dtype == DM_DT_FIXED ||
541 (dtype == DM_DT_UNKNOWN && mtype == DM_MT_FIXED));
542
543 if (!isfixed) {
544 continue; /* ignore non-fixed disks */
545 }
546
547 #if defined(i386)
548 if (((error = disk_get_has_solaris_partition(disk,
549 &solpart)) != 0) || (solpart != B_TRUE)) {
550
551 /* X86 drive has no solaris partition, report as bad */
552 oprintf(OUTPUT_DEBUG,
553 gettext("%s has no solaris FDISK partition.\n"));
554
555 bad_disk = B_TRUE;
556 }
557 #endif /* defined(i386) */
558
559 }
560
561 if (bad_disk) {
562 /* remember bad disks and continue */
563 if (dlist_contains(*bad, (void *)(uintptr_t)disk,
564 compare_descriptor_names) != B_TRUE) {
565 dlist_t *item = dlist_new_item((void *)(uintptr_t)disk);
566 if (item == NULL) {
567 error = ENOMEM;
568 } else {
569 *bad = dlist_append(item, *bad, AT_TAIL);
570 }
571 }
572 continue;
573 }
574
575 /* get disk name and multipath aliases */
576 if ((error = disk_get_aliases(disk, &aliases)) == 0) {
577 dlist_t *iter;
578 boolean_t disk_name_set = B_FALSE;
579
580 for (iter = aliases;
581 (iter != NULL) && (error == 0);
582 iter = iter->next) {
583
584 dm_descriptor_t ap = (uintptr_t)iter->obj;
585 char *alias;
586
587 if ((error = get_name(ap, &alias)) == 0) {
588 /* save first alias as display name */
589 if (disk_name_set != B_TRUE) {
590 /* make sure DID disk alias is fully qualified */
591
592 if (is_did_disk_name(alias) == B_TRUE) {
593 char *qual_name =
594 make_fully_qualified_did_device_name(alias);
595
596 set_display_name(disk, qual_name);
597 oprintf(OUTPUT_DEBUG,
598 gettext("DID disk name: %s\n"),
599 qual_name);
600 } else {
601 set_display_name(disk, alias);
602 oprintf(OUTPUT_DEBUG,
603 gettext("disk name: %s\n"),
604 alias);
605 }
606 disk_name_set = B_TRUE;
607
608 } else {
609 /* save others as aliases */
610 set_alias(disk, alias);
611 oprintf(OUTPUT_DEBUG,
612 gettext(" alias: %s\n"),
613 alias);
614 }
615 }
616 }
617
618 dlist_free_items(aliases, NULL);
619 }
620
621 if (error == 0) {
622 dlist_t *item = dlist_new_item((void *)(uintptr_t)disk);
623 if (item == NULL) {
624 error = ENOMEM;
625 } else {
626 *known =
627 dlist_insert_ordered(item, *known,
628 ASCENDING, compare_desc_display_names);
629 }
630 }
631 }
632
633 if (ddp != NULL) {
634 free(ddp);
635 }
636
637 return (error);
638 }
639
640 /*
641 * FUNCTION: generate_known_slices(dlist_t *disks,
642 * dlist_t **known, dlist_t **bad)
643 *
644 * OUTPUT: disks - a pointer to a list of known disks
645 * known - a pointer to a dlist_t list to hold the known slices
646 * bad - a pointer to a dlist_t to hold the bad slices
647 *
648 * RETURNS: int - 0 on success
649 * !0 otherwise.
650 *
651 * PURPOSE: Examines input list of known disks and determines the slices
652 * attached to each.
653 *
654 * Some slices returned from libdiskmgt may not really exist,
655 * this is detected when trying to get more information about
656 * the slice -- ENODEV is returned. Any such slices will be
657 * added to the bad slice list.
658 */
659 static int
generate_known_slices(dlist_t * disks,dlist_t ** known,dlist_t ** bad)660 generate_known_slices(
661 dlist_t *disks,
662 dlist_t **known,
663 dlist_t **bad)
664 {
665 dlist_t *iter;
666 int error = 0;
667
668 /* iterate list of disks and add their slices to the known list */
669 for (iter = disks; (iter != NULL) && (error == 0); iter = iter->next) {
670
671 dm_descriptor_t disk = (uintptr_t)iter->obj;
672 dlist_t *slices = NULL;
673 dlist_t *iter1;
674 char *dname = NULL;
675 boolean_t disk_ctd_alias_derived = B_FALSE;
676
677 if (((error = disk_get_slices(disk, &slices)) != 0) ||
678 ((error = get_display_name(disk, &dname)) != 0)) {
679 continue;
680 }
681
682 for (iter1 = slices;
683 (iter1 != NULL) && (error == 0);
684 iter1 = iter1->next) {
685
686 dm_descriptor_t slice = (uintptr_t)iter1->obj;
687 uint32_t index = 0;
688 nvlist_t *attrs = NULL;
689 char *sname = NULL;
690
691 if (((error = get_name(slice, &sname)) != 0) ||
692 ((error = slice_get_index(slice, &index)) != 0) ||
693 ((error = get_cached_attributes(slice, &attrs)) != 0)) {
694
695 if (error == ENODEV) {
696 /* bad slice, remember it and continue */
697 dlist_t *item =
698 dlist_new_item((void *)(uintptr_t)slice);
699 if (item == NULL) {
700 error = ENOMEM;
701 } else {
702 *bad = dlist_insert_ordered(
703 item, *bad,
704 ASCENDING, compare_descriptor_names);
705 error = 0;
706 }
707 }
708 continue;
709 }
710
711 if ((error == 0) && (is_did_slice_name(sname) == B_TRUE) &&
712 (disk_ctd_alias_derived == B_FALSE)) {
713 /* BEGIN CSTYLED */
714 /*
715 * If the slice name is a DID name, get the local CTD
716 * name for slice, extract the disk name and add it as
717 * an alias for the disk.
718 *
719 * This is the only way to derive the CTD alias for the
720 * disk when DID is enabled.
721 *
722 * The disk_ctd_alias_derived flag ensure the disk's
723 * CTD alias is only set once.
724 *
725 * The slice's CTD aliases are then derived from the
726 * disk's CTD alias in the normal, non-DID name processing
727 * which happens below.
728 */
729 /* END CSTYLED */
730 char *local = NULL;
731 if ((error = nvlist_lookup_string(attrs, DM_LOCALNAME,
732 &local)) != 0) {
733 if (error == ENOENT) {
734 /* no local name -> no DID */
735 error = 0;
736 }
737 } else {
738 char *localdisk = NULL;
739 char *diskonly = NULL;
740 if ((error = extract_diskname(local,
741 &localdisk)) == 0) {
742 if ((diskonly = strrchr(localdisk, '/')) != NULL) {
743 ++diskonly;
744 } else {
745 diskonly = localdisk;
746 }
747 oprintf(OUTPUT_DEBUG,
748 gettext(" set DID disk CTD alias: %s\n"),
749 diskonly);
750 error = set_alias(disk, diskonly);
751 free(localdisk);
752 disk_ctd_alias_derived = B_TRUE;
753 }
754 }
755 }
756
757 /* derive slice display name from disk's display name */
758 if (error == 0) {
759 if ((error = make_slicename_for_diskname_and_index(
760 dname, index, &sname)) == 0) {
761 error = set_display_name(slice, sname);
762 }
763 }
764
765 /* set slice aliases using disk aliases */
766 if (error == 0) {
767 dlist_t *aliases = NULL;
768 if ((error = get_aliases(disk, &aliases)) == 0) {
769
770 dlist_t *iter2 = aliases;
771 for (; (iter2 != NULL) && (error == 0);
772 iter2 = iter2->next) {
773
774 char *dalias = (char *)iter2->obj;
775 char *salias = NULL;
776
777 if ((error = make_slicename_for_diskname_and_index(
778 dalias, index, &salias)) == 0) {
779 error = set_alias(slice, salias);
780 free(salias);
781 }
782 }
783 dlist_free_items(aliases, free);
784 }
785 }
786
787 if (error == 0) {
788 dlist_t *item = dlist_new_item((void *)(uintptr_t)slice);
789 if (item == NULL) {
790 error = ENOMEM;
791 } else {
792 *known =
793 dlist_insert_ordered(
794 item, *known,
795 ASCENDING, compare_desc_display_names);
796 }
797 }
798 }
799
800 dlist_free_items(slices, NULL);
801 }
802
803 return (error);
804 }
805
806 /*
807 * FUNCTION: generate_known_hbas(dlist_t *disks, dlist_t **known)
808 *
809 * INPUT: diskset - a char * diskset name.
810 *
811 * OUTPUT: populates the list of known HBAs.
812 *
813 * RETURNS: int - 0 on success
814 * !0 otherwise
815 *
816 * PURPOSE: Examines known disk list and derives the list of known HBAs.
817 *
818 * Determines the CTD name for an HBA and saves it.
819 */
820 static int
generate_known_hbas(dlist_t * disks,dlist_t ** known)821 generate_known_hbas(
822 dlist_t *disks,
823 dlist_t **known)
824 {
825 dlist_t *iter;
826 int error = 0;
827
828 /*
829 * for each known disk follow its HBA connections and
830 * assemble the list of known HBAs.
831 */
832 for (iter = disks;
833 (iter != NULL) && (error == 0);
834 iter = iter->next) {
835
836 dm_descriptor_t disk = (uintptr_t)iter->obj;
837 dlist_t *hbas = NULL;
838 dlist_t *iter2 = NULL;
839 dlist_t *iter3 = NULL;
840 dlist_t *aliases = NULL;
841 char *dname = NULL;
842
843 ((error = get_display_name(disk, &dname)) != 0) ||
844 (error = disk_get_aliases(disk, &aliases)) ||
845 (error = disk_get_hbas(disk, &hbas));
846
847 if (error == 0) {
848
849 if ((hbas == NULL) || (dlist_length(hbas) == 0)) {
850
851 oprintf(OUTPUT_DEBUG,
852 gettext("Disk %s has no HBA/Controller?!\n"),
853 dname);
854 error = -1;
855
856 dlist_free_items(hbas, NULL);
857 dlist_free_items(aliases, NULL);
858
859 continue;
860 }
861
862 for (iter2 = hbas, iter3 = aliases;
863 iter2 != NULL && iter3 != NULL;
864 iter2 = iter2->next, iter3 = iter3->next) {
865
866 dm_descriptor_t hba = (uintptr_t)iter2->obj;
867 dm_descriptor_t alias = (uintptr_t)iter3->obj;
868 dlist_t *item = NULL;
869
870 /* scan list of known HBAs and see if known */
871 if (dlist_contains(*known, (void*)(uintptr_t)hba,
872 compare_descriptor_names) == B_TRUE) {
873 /* known HBA */
874 continue;
875 }
876
877 /* see if HBA supports MPXIO */
878 if ((error == 0) && (_mpxio_enabled != B_TRUE)) {
879 hba_is_multiplex(hba, &_mpxio_enabled);
880 }
881
882 /* generate a CTD name for HBA */
883 error = generate_known_hba_name(hba, alias, disk);
884 if (error == 0) {
885 /* add to known HBA list */
886 if ((item = dlist_new_item((void *)(uintptr_t)hba)) ==
887 NULL) {
888 error = ENOMEM;
889 } else {
890 *known =
891 dlist_insert_ordered(item, *known,
892 ASCENDING, compare_desc_display_names);
893 }
894 }
895 }
896 }
897
898 dlist_free_items(aliases, NULL);
899 dlist_free_items(hbas, NULL);
900 }
901
902 return (error);
903 }
904
905 /*
906 * FUNCTION: generate_known_hba_name(dm_descriptor_t hba,
907 * dm_descriptor_t alias, char *diskname)
908 *
909 * INPUT: hba - a dm_descriptor_t HBA handle.
910 * alias - a dm_descriptor_t disk alias handle.
911 * diskname - a char * disk name
912 *
913 * RETURNS: int - 0 on success
914 * !0 otherwise
915 *
916 * PURPOSE: Sets the CTD name for the input HBA.
917 *
918 * The CTD name for the HBA is generated from the input
919 * disk alias (ex: cXdXtXsX) or from the disk name if
920 * the input alias is a DID name (ex: dX).
921 */
922 static int
generate_known_hba_name(dm_descriptor_t hba,dm_descriptor_t alias,dm_descriptor_t disk)923 generate_known_hba_name(
924 dm_descriptor_t hba,
925 dm_descriptor_t alias,
926 dm_descriptor_t disk)
927 {
928 char *hbaname = NULL;
929 char *aliasname = NULL;
930 int error = 0;
931
932 ((error = get_name(alias, &aliasname)) != 0) ||
933 (error = extract_hbaname(aliasname, &hbaname));
934 if (error != 0) {
935 free(hbaname);
936 return (error);
937 }
938
939 /* see if the input alias is a DID name... */
940 if (is_did_disk_name(aliasname) == B_TRUE) {
941
942 /* look for a non-DID name in disk's aliases */
943 dlist_t *aliases = NULL;
944 error = get_aliases(disk, &aliases);
945
946 for (; (error == 0) && (aliases != NULL);
947 aliases = aliases->next) {
948
949 aliasname = (char *)aliases->obj;
950 if (is_did_disk_name(aliasname) != B_TRUE) {
951 /* this is the "local" CTD name generated by */
952 /* generate_known_disks() above */
953 error = extract_hbaname(aliasname, &hbaname);
954 if ((error == 0) && (hbaname != NULL)) {
955 set_display_name(hba, hbaname);
956 break;
957 }
958 }
959 }
960 dlist_free_items(aliases, free);
961
962 } else {
963 /* use whatever was derived from the alias name */
964 set_display_name(hba, hbaname);
965 }
966
967 return (error);
968 }
969
970 /*
971 * FUNCTION: print_known_devices()
972 *
973 * PURPOSE: Print out the known devices.
974 *
975 * Iterates the lists of known slices, disks and HBAs
976 * and prints out their CTD and device names.
977 */
978 static void
print_known_devices(char * diskset)979 print_known_devices(
980 char *diskset)
981 {
982 int i = 0;
983 struct {
984 char *msg;
985 dlist_t *list;
986 } devs[3];
987
988 devs[0].msg = gettext("HBA/Controllers");
989 devs[0].list = _known_hbas;
990 devs[1].msg = gettext("disks");
991 devs[1].list = _known_disks;
992 devs[2].msg = gettext("slices");
993 devs[2].list = _known_slices;
994
995 for (i = 0; i < 3; i++) {
996
997 oprintf(OUTPUT_VERBOSE,
998 gettext("\n These %s are known:\n\n"),
999 devs[i].msg);
1000
1001 print_device_list(devs[i].list);
1002 }
1003 }
1004
1005 /*
1006 * FUNCTION: get_usable_slices(dlist_t **list)
1007 *
1008 * OUTPUT: list - a dlist_t pointer to hold the returned list of
1009 * devices.
1010 *
1011 * RETURNS: int - 0 on success
1012 * !0 otherwise
1013 *
1014 * PURPOSE: Public accessors the the modules private lists of
1015 * available devices.
1016 *
1017 * The functions are keyed by diskset name in the event
1018 * objects in different disksets are loaded concurrently.
1019 */
1020 int
get_usable_slices(dlist_t ** list)1021 get_usable_slices(
1022 dlist_t **list)
1023 {
1024 *list = _usable_slices;
1025
1026 return (0);
1027 }
1028
1029 int
get_usable_disks(dlist_t ** list)1030 get_usable_disks(
1031 dlist_t **list)
1032 {
1033 *list = _usable_disks;
1034
1035 return (0);
1036 }
1037
1038 int
get_usable_hbas(dlist_t ** list)1039 get_usable_hbas(
1040 dlist_t **list)
1041 {
1042 *list = _usable_hbas;
1043
1044 return (0);
1045 }
1046
1047 /*
1048 * FUNCTION: generate_usable_disks_and_slices_in_local_set(dlist_t **classes,
1049 * dlist_t **bad_disks, dlist_t **usable_disks,
1050 * dlist_t **usable_slices)
1051 *
1052 * OUTPUT: used_classes - a pointer to a list of slice_class_t structs
1053 * updated with known slices that have detected uses
1054 * added to the correct class'e list of slices.
1055 * bad_disks - a pointer to a list of bad/unusable disks updated
1056 * with any bad disks that were detected
1057 * useable_disks - a pointer to a list of usable disks
1058 * useable_slices - a pointer to a list of usable slices
1059 *
1060 * RETURNS: int - 0 on success
1061 * !0 otherwise.
1062 *
1063 * PURPOSE: Scans the disks in the local set to determine which are
1064 * usable during layout processing.
1065 *
1066 * Determines which are usable by layout using usages detected
1067 * by libdiskmgt.
1068 */
1069 static int
generate_usable_disks_and_slices_in_local_set(dlist_t ** classes,dlist_t ** bad_slices,dlist_t ** usable_disks,dlist_t ** usable_slices)1070 generate_usable_disks_and_slices_in_local_set(
1071 dlist_t **classes,
1072 dlist_t **bad_slices,
1073 dlist_t **usable_disks,
1074 dlist_t **usable_slices)
1075 {
1076 char *dsname = MD_LOCAL_NAME;
1077 dlist_t *disks;
1078 dlist_t *iter;
1079 int error;
1080
1081 /* Get disks in local set */
1082 error = get_disks_in_diskset(dsname, &disks);
1083 if (error != 0) {
1084 return (error);
1085 }
1086
1087 /* For each disk in this set... */
1088 for (iter = disks; iter != NULL && error == 0; iter = iter->next) {
1089 dm_descriptor_t disk = (uintptr_t)iter->obj;
1090 dlist_t *slices;
1091
1092 /* Get slices on this disk */
1093 error = disk_get_slices(disk, &slices);
1094 if (error == 0) {
1095 dlist_t *iter2;
1096
1097 /*
1098 * Assume disk is available until a bad or unavailable
1099 * slice is found
1100 */
1101 boolean_t avail = B_TRUE;
1102 boolean_t bad_disk = B_FALSE;
1103
1104 /* For each slice on this disk... */
1105 for (iter2 = slices;
1106 iter2 != NULL && error == 0 &&
1107 avail == B_TRUE && bad_disk == B_FALSE;
1108 iter2 = iter2->next) {
1109
1110 dm_descriptor_t slice = (uintptr_t)iter2->obj;
1111 dlist_t *bad_slices_on_this_disk = NULL;
1112
1113 /* Is this slice available? */
1114 error = check_slice_usage(dsname, slice,
1115 disk, &avail, &bad_slices_on_this_disk, classes);
1116
1117 /* Is the slice bad (inaccessible)? */
1118 if (error != 0 && bad_slices_on_this_disk != NULL) {
1119 bad_disk = B_TRUE;
1120 *bad_slices = dlist_append_list(
1121 *bad_slices, bad_slices_on_this_disk);
1122 }
1123 }
1124
1125 /* Is the disk available? */
1126 if (error == 0 && bad_disk == B_FALSE && avail == B_TRUE) {
1127 error = dlist_append_object(
1128 (void *)(uintptr_t)disk, usable_disks, AT_TAIL);
1129 }
1130
1131 dlist_free_items(slices, NULL);
1132 }
1133 }
1134
1135 dlist_free_items(disks, NULL);
1136
1137 if (error == 0) {
1138 /* BEGIN CSTYLED */
1139 /*
1140 * Now reslice usable disks in the local set to
1141 * simulate the slices they'll have when they're added
1142 * to the named disk set, and add these resulting
1143 * virtual slices to the list of available slices.
1144 */
1145 /* END CSTYLED */
1146 error = generate_virtual_slices(*usable_disks, usable_slices);
1147 }
1148
1149 return (error);
1150 }
1151
1152 /*
1153 * FUNCTION: generate_virtual_slices(dlist_t *unused, dlist_t **usable)
1154 *
1155 * INPUT: slice_classes - a list of unused slice dm_descriptor_t handles.
1156 *
1157 * OUTPUT: usable - pointer to the list of usable slices, updated
1158 * with any created virtual slices.
1159 *
1160 * RETURNS: int - 0 on success
1161 * !0 otherwise.
1162 *
1163 * PURPOSE: Helper which creates virtual slices for each disk which
1164 * could be added to a diskset if necessary...
1165 *
1166 * Search the input list of slice classes for the entry
1167 * containing slices known to be available for use by layout.
1168 *
1169 * Iterate the list of unused slices and determine the set
1170 * of unique disks.
1171 *
1172 * For each unique disk, create virtual slice descriptors to
1173 * represent those that will exist if/when the disk is added
1174 * to the diskset.
1175 *
1176 * Add theese virtual slices to the list of usable slices.
1177 */
1178 static int
generate_virtual_slices(dlist_t * avail_disks_local_set,dlist_t ** usable)1179 generate_virtual_slices(
1180 dlist_t *avail_disks_local_set,
1181 dlist_t **usable)
1182 {
1183 dlist_t *iter = NULL;
1184 int error = 0;
1185
1186 /* generate virtual slices */
1187 error = create_virtual_slices(avail_disks_local_set);
1188 if (error == 0) {
1189
1190 get_virtual_slices(&iter);
1191 for (; (iter != NULL) && (error == 0); iter = iter->next) {
1192
1193 dlist_t *item = dlist_new_item((void *) iter->obj);
1194 if (item == NULL) {
1195 error = ENOMEM;
1196 } else {
1197 *usable =
1198 dlist_insert_ordered(item, *usable,
1199 ASCENDING, compare_desc_display_names);
1200 }
1201 }
1202 }
1203
1204 return (error);
1205 }
1206
1207 /*
1208 * FUNCTION: generate_usable_disks_and_slices_in_named_set(char *dsname,
1209 * dlist_t **classes, dlist_t **bad_slices,
1210 * dlist_t **usable_slices, dlist_t **usable_disks)
1211 *
1212 * INPUT: dsname - a char * diskset name.
1213 *
1214 * OUTPUT: classes - pointer to a list of slice_class_t structs,
1215 * updated to include slices in the disk set with
1216 * known uses.
1217 * bad_slices - pointer to a list of bad/unusable slices,
1218 * updated to include slices in the disk set that
1219 * are inaccessible or no longer existent.
1220 * usable_slices - pointer to a list of usable slices in the
1221 * disk set.
1222 * usable_disks - pointer to a list of usable disks in the
1223 * disk set.
1224 *
1225 * RETURNS: int - 0 on success
1226 * !0 otherwise.
1227 *
1228 * PURPOSE: 1. determine the disks in the named disk set
1229 * 2. determine the used slices on the disks
1230 * 3. determine the unused slices on the disks
1231 * 4. look for unused space on the disks and collect it
1232 * into an existing unused slice, or create a new
1233 * virtual slice.
1234 */
1235 static int
generate_usable_disks_and_slices_in_named_set(char * dsname,dlist_t ** classes,dlist_t ** bad_slices,dlist_t ** usable_disks,dlist_t ** usable_slices)1236 generate_usable_disks_and_slices_in_named_set(
1237 char *dsname,
1238 dlist_t **classes,
1239 dlist_t **bad_slices,
1240 dlist_t **usable_disks,
1241 dlist_t **usable_slices)
1242 {
1243 dlist_t *disks = NULL;
1244 dlist_t *iter = NULL;
1245 int error = 0;
1246
1247 error = get_disks_in_diskset(dsname, &disks);
1248 if (error != 0) {
1249 return (error);
1250 }
1251
1252 /* For each disk... */
1253 for (iter = disks;
1254 iter != NULL && error == 0;
1255 iter = iter->next) {
1256
1257 dm_descriptor_t disk = (uintptr_t)iter->obj;
1258 dlist_t *iter2;
1259 dlist_t *slices = NULL;
1260 dlist_t *bad_slices_on_this_disk = NULL;
1261 dlist_t *used_slices_on_this_disk = NULL;
1262 dlist_t *unused_slices_on_this_disk = NULL;
1263 boolean_t bad_disk = B_FALSE;
1264
1265 error = disk_get_slices(disk, &slices);
1266 if (error != 0) {
1267 break;
1268 }
1269
1270 /* Determine the used, unused, and bad slices on the disk */
1271
1272 /* For each slice... */
1273 for (iter2 = slices;
1274 iter2 != NULL && error == 0 && bad_disk == B_FALSE;
1275 iter2 = iter2->next) {
1276
1277 dm_descriptor_t slice = (uintptr_t)iter2->obj;
1278
1279 boolean_t rsvd = B_FALSE;
1280 boolean_t avail = B_FALSE;
1281
1282 /* Get slice usage */
1283 if (((error = is_reserved_slice(slice, &rsvd)) == 0) &&
1284 ((error = check_slice_usage(dsname, slice, disk, &avail,
1285 &bad_slices_on_this_disk, classes)) == 0)) {
1286
1287 /* Is the slice bad (inaccessible)? */
1288 if (bad_slices_on_this_disk != NULL) {
1289 *bad_slices = dlist_append_list(
1290 *bad_slices, bad_slices_on_this_disk);
1291 /*
1292 * Since one slice on this disk is bad, don't
1293 * use any slices on this disk
1294 */
1295 bad_disk = B_TRUE;
1296 } else {
1297
1298 dlist_t *item =
1299 dlist_new_item((void *)(uintptr_t)slice);
1300 if (item == NULL) {
1301 error = ENOMEM;
1302 } else {
1303 /* Add slice to used/unused list as appropriate */
1304 if (avail == B_TRUE && rsvd == B_FALSE) {
1305 unused_slices_on_this_disk = dlist_append(
1306 item, unused_slices_on_this_disk, AT_TAIL);
1307 } else {
1308 used_slices_on_this_disk =
1309 dlist_insert_ordered(item,
1310 used_slices_on_this_disk,
1311 ASCENDING, compare_start_blocks);
1312 }
1313 }
1314 }
1315 }
1316 }
1317
1318 /* Done iterating slices */
1319
1320 if (error == 0 && bad_disk == B_FALSE) {
1321 /* For each unused slice... */
1322 for (iter2 = unused_slices_on_this_disk;
1323 iter2 != NULL && error == 0;
1324 iter2 = iter2->next) {
1325
1326 dm_descriptor_t slice = (uintptr_t)iter2->obj;
1327 error = update_slice_attributes(slice, 0, 0, 0);
1328
1329 /* Only do this once */
1330 if (error == 0 && iter2 == unused_slices_on_this_disk) {
1331 error = add_modified_disk(NULL, disk);
1332 }
1333 }
1334
1335 if (error == 0) {
1336 /* Create usable slices from the used/unused slice lists */
1337 error = create_usable_slices(disk, used_slices_on_this_disk,
1338 unused_slices_on_this_disk, usable_slices);
1339 if (error == 0) {
1340 error = dlist_append_object((void *)(uintptr_t)disk,
1341 usable_disks, AT_TAIL);
1342 }
1343 }
1344 }
1345
1346 dlist_free_items(slices, NULL);
1347 dlist_free_items(used_slices_on_this_disk, NULL);
1348 dlist_free_items(unused_slices_on_this_disk, NULL);
1349 }
1350
1351 return (error);
1352 }
1353
1354 /*
1355 * FUNCTION: create_usable_slices(dm_descriptor_t disk, dlist_t *used,
1356 * dlist_t *unused, dlist_t **usable);
1357 *
1358 * INPUT: disk - a dm_descriptor_t disk handle
1359 * used - pointer to a list of pvt_t structs
1360 * representing existing used slices
1361 * on the input disk.
1362 * unused - pointer to a list of pvt_t structs
1363 * representing existing unused slices
1364 * on the input disk.
1365 *
1366 * OUTPUT: usable - pointer to a list of pvts representing slices
1367 * which can be used for new volume layouts.
1368 *
1369 * Slices in this list have any available space on the
1370 * disk collected into the fewest, lowest indexed slices
1371 * possible.
1372 *
1373 * RETURNS: int - 0 on success
1374 * !0 otherwise.
1375 *
1376 * PURPOSE: helper for generate_usable_slices_and_disks_in_diskset() which
1377 * turns any detected free space on the input disk into one or
1378 * more slices.
1379 */
1380 static int
create_usable_slices(dm_descriptor_t disk,dlist_t * used,dlist_t * unused,dlist_t ** usable)1381 create_usable_slices(
1382 dm_descriptor_t disk,
1383 dlist_t *used,
1384 dlist_t *unused,
1385 dlist_t **usable)
1386 {
1387 dlist_t *iter;
1388 int error = 0;
1389 boolean_t first = B_TRUE;
1390 dlist_t *next_unused = unused;
1391
1392 char *dname = NULL;
1393 uint64_t disk_firstblk = 0;
1394 uint64_t disk_nblks = 0;
1395 uint64_t disk_endblk = 0;
1396
1397 oprintf(OUTPUT_DEBUG,
1398 gettext("\n create_usable_slices for disk\n"));
1399
1400 /* get necessary info about disk: */
1401 error = get_display_name(disk, &dname);
1402 if (error != 0) {
1403 return (error);
1404 }
1405
1406 /* disk start block is first usable block */
1407 error = disk_get_start_block(disk, &disk_firstblk);
1408 if (error != 0) {
1409 return (error);
1410 }
1411
1412 /* disk size determines last usable disk block */
1413 error = disk_get_size_in_blocks(disk, &disk_nblks);
1414 if (error != 0) {
1415 return (error);
1416 }
1417
1418 disk_endblk = disk_firstblk + disk_nblks - 1;
1419
1420 /* search for gaps before, between and after used slices */
1421 for (iter = used; iter != NULL && error == 0; iter = iter->next) {
1422
1423 dm_descriptor_t cur = (uintptr_t)iter->obj;
1424
1425 uint64_t cur_stblk = 0;
1426 uint64_t cur_nblks = 0;
1427 uint64_t cur_endblk = 0;
1428 uint32_t cur_index = 0;
1429
1430 uint64_t new_stblk = 0;
1431 uint64_t new_endblk = 0;
1432
1433 char *sname = NULL;
1434 (void) get_display_name(cur, &sname);
1435
1436 if (((error = slice_get_index(cur, &cur_index)) != 0) ||
1437 ((error = slice_get_start_block(cur, &cur_stblk)) != 0) ||
1438 ((error = slice_get_size_in_blocks(cur, &cur_nblks)) != 0)) {
1439 continue;
1440 }
1441
1442 cur_endblk = cur_stblk + cur_nblks - 1;
1443
1444 oprintf(OUTPUT_DEBUG,
1445 gettext(" used slice %d (%10llu to %10llu)\n"),
1446 cur_index, cur_stblk, cur_endblk);
1447
1448 if (first == B_TRUE) {
1449 /* first slice: make sure it starts at disk_firstblk */
1450 first = B_FALSE;
1451 if (cur_stblk != disk_firstblk) {
1452 /* close gap at beginning of disk */
1453 new_stblk = disk_firstblk;
1454 new_endblk = cur_stblk - 1;
1455
1456 oprintf(OUTPUT_DEBUG,
1457 gettext(" unused space before first "
1458 "used slice\n"));
1459 }
1460 }
1461
1462 if (iter->next != NULL) {
1463 /* check for gap between slices */
1464 dm_descriptor_t next = (uintptr_t)iter->next->obj;
1465 uint64_t next_stblk = 0;
1466 uint32_t next_index = 0;
1467
1468 if (((error = slice_get_start_block(next, &next_stblk)) == 0) &&
1469 ((error = slice_get_index(next, &next_index)) == 0)) {
1470 if (cur_endblk != next_stblk - 1) {
1471 /* close gap between slices */
1472 new_stblk = cur_endblk + 1;
1473 new_endblk = next_stblk - 1;
1474
1475 oprintf(OUTPUT_DEBUG,
1476 gettext(" unused space between slices "
1477 "%d and %d\n"), cur_index, next_index);
1478 }
1479 }
1480
1481 } else {
1482 /* last slice: make sure it includes last block on disk */
1483 if (cur_endblk != disk_endblk) {
1484 /* close gap at end of disk */
1485 new_stblk = cur_endblk + 1;
1486 new_endblk = disk_endblk;
1487
1488 oprintf(OUTPUT_DEBUG,
1489 gettext(" unused space after last slice "
1490 "cur_endblk: %llu disk_endblk: %llu\n"),
1491 cur_endblk, disk_endblk);
1492 }
1493 }
1494
1495 if ((error == 0) && (new_endblk != 0)) {
1496 error = add_new_usable(disk, new_stblk,
1497 new_endblk - new_stblk + 1, &next_unused, usable);
1498 }
1499 }
1500
1501 if (error != 0) {
1502 dlist_free_items(*usable, free);
1503 *usable = NULL;
1504 }
1505
1506 return (error);
1507 }
1508
1509 /*
1510 * FUNCTION: add_new_usable(dm_descriptor_t disk, uint64_t stblk,
1511 * uint64_t nblks, dlist_t **next_unused,
1512 * dlist_t **usable);
1513 *
1514 * INPUT: disk - a dm_descriptor_t disk handle
1515 * stblk - start block of the usable space
1516 * nblks - number of usable blocks
1517 * next_unused - pointer to the next unused slice
1518 *
1519 * OUTPUT: next_unused - updated pointer to the next unused slice
1520 * usable - possibly updated pointer to a list of slices on
1521 * the disk with usable space
1522 *
1523 * RETURNS: int - 0 on success
1524 * !0 otherwise.
1525 *
1526 * PURPOSE: helper for create_usable_slices() which turns free space
1527 * on the input disk into a usable slice.
1528 *
1529 * If possible an existing unused slice will be recycled
1530 * into a usable slice. If there are none, a new virtual
1531 * slice will be created.
1532 */
1533 static int
add_new_usable(dm_descriptor_t disk,uint64_t stblk,uint64_t nblks,dlist_t ** next_unused,dlist_t ** usable)1534 add_new_usable(
1535 dm_descriptor_t disk,
1536 uint64_t stblk,
1537 uint64_t nblks,
1538 dlist_t **next_unused,
1539 dlist_t **usable)
1540 {
1541 dm_descriptor_t new_usable = 0;
1542 int error = 0;
1543
1544 /* try to use an existing unused slice for the usable slice */
1545 if (*next_unused != NULL) {
1546 new_usable = (uintptr_t)((*next_unused)->obj);
1547 *next_unused = (*next_unused)->next;
1548
1549 oprintf(OUTPUT_DEBUG,
1550 gettext("\trecyling used slice into usable slice "
1551 "start: %llu, end: %llu\n"),
1552 stblk, stblk + nblks + 1);
1553 }
1554
1555 if (new_usable == NULL) {
1556 /* no unused slices, try to make a new virtual slice */
1557 uint32_t index = UINT32_MAX;
1558 error = disk_get_available_slice_index(disk, &index);
1559 if ((error == 0) && (index != UINT32_MAX)) {
1560
1561 char *dname = NULL;
1562 error = get_display_name(disk, &dname);
1563 if (error == 0) {
1564
1565 char buf[MAXNAMELEN];
1566 (void) snprintf(buf, MAXNAMELEN-1, "%ss%d", dname, index);
1567 error = add_virtual_slice(buf, index, 0, 0, disk);
1568 if (error == 0) {
1569 /* retrieve the virtual slice */
1570 error = slice_get_by_name(buf, &new_usable);
1571 }
1572 }
1573 }
1574 }
1575
1576 if ((error == 0) && (new_usable != (dm_descriptor_t)0)) {
1577 /* BEGIN CSTYLED */
1578 /*
1579 * have an unused slice, update its attributes to reflect
1580 * the usable space it represents
1581 */
1582 /* END CSTYLED */
1583 uint64_t disk_blksz = 0;
1584 error = disk_get_blocksize(disk, &disk_blksz);
1585 if (error == 0) {
1586 error = update_slice_attributes(new_usable, stblk,
1587 nblks, nblks * disk_blksz);
1588 if (error == 0) {
1589 error = dlist_append_object(
1590 (void *)(uintptr_t)new_usable, usable, AT_TAIL);
1591 }
1592 }
1593 }
1594
1595 return (error);
1596 }
1597
1598 /*
1599 * FUNCTION: update_slice_attributes(dm_descriptor_t slice, uint64_t stblk,
1600 * uint64_t nblks, uint64_t nbytes)
1601 *
1602 * INPUT: slice - a dm_descriptor_t slice handle
1603 * stblk - start block of the usable space
1604 * nblks - size of slice in blocks
1605 * nbytes - size of slice in bytes
1606 *
1607 * SIDEEFFECT: adds a modification record for the slice.
1608 *
1609 * RETURNS: int - 0 on success
1610 * !0 otherwise.
1611 *
1612 * PURPOSE: utility which updates several slice attributes in one call.
1613 */
1614 static int
update_slice_attributes(dm_descriptor_t slice,uint64_t stblk,uint64_t nblks,uint64_t nbytes)1615 update_slice_attributes(
1616 dm_descriptor_t slice,
1617 uint64_t stblk,
1618 uint64_t nblks,
1619 uint64_t nbytes)
1620 {
1621 char *sname = NULL;
1622 uint32_t index = 0;
1623 int error = 0;
1624
1625 if ((error = get_display_name(slice, &sname)) == 0) {
1626 if ((error = slice_get_index(slice, &index)) == 0) {
1627 if ((error = slice_set_start_block(slice, stblk)) == 0) {
1628 if ((error = slice_set_size_in_blocks(slice, nblks)) == 0) {
1629 if (nblks == 0) {
1630 error = add_slice_to_remove(sname, index);
1631 } else {
1632 error = assemble_modified_slice((dm_descriptor_t)0,
1633 sname, index, stblk, nblks, nbytes, NULL);
1634 }
1635 }
1636 }
1637 }
1638 }
1639
1640 return (error);
1641 }
1642
1643 /*
1644 * FUNCTION: generate_usable_hbas(dlist_t *slices,
1645 * dlist_t **usable)
1646 *
1647 * INPUT: disks - a list of usable disks.
1648 *
1649 * OUTPUT: usable - a populated list of usable HBAs.
1650 *
1651 * RETURNS: int - 0 on success
1652 * !0 otherwise
1653 *
1654 * PURPOSE: Examines usable disk list and derives the list of usable HBAs.
1655 *
1656 */
1657 static int
generate_usable_hbas(dlist_t * disks,dlist_t ** usable)1658 generate_usable_hbas(
1659 dlist_t *disks,
1660 dlist_t **usable)
1661 {
1662 dlist_t *iter;
1663 int error = 0;
1664
1665 /*
1666 * for each usable disk, follow its HBA connections and
1667 * add them to the list of usable HBAs.
1668 */
1669 for (iter = disks; (iter != NULL) && (error == 0); iter = iter->next) {
1670
1671 dm_descriptor_t dp = NULL;
1672 dlist_t *hbas = NULL;
1673 dlist_t *iter2 = NULL;
1674
1675 dp = (uintptr_t)iter->obj;
1676
1677 error = disk_get_hbas(dp, &hbas);
1678 if (error == 0) {
1679
1680 for (iter2 = hbas;
1681 (iter2 != NULL) && (error == 0);
1682 iter2 = iter2->next) {
1683
1684 dm_descriptor_t hba = (uintptr_t)iter2->obj;
1685 dlist_t *item = NULL;
1686
1687 /* scan list of usable HBAs and see if known */
1688 if (dlist_contains(*usable, (void*)(uintptr_t)hba,
1689 compare_descriptor_names) == B_TRUE) {
1690 /* known HBA, continue to next HBA/alias */
1691 continue;
1692 }
1693
1694 /* add this HBA to the usable list */
1695 if ((item = dlist_new_item((void *)(uintptr_t)hba)) ==
1696 NULL) {
1697 error = ENOMEM;
1698 } else {
1699 *usable =
1700 dlist_insert_ordered(item, *usable,
1701 ASCENDING, compare_desc_display_names);
1702 }
1703 }
1704 }
1705
1706 dlist_free_items(hbas, NULL);
1707 }
1708
1709 return (error);
1710 }
1711
1712 /*
1713 * FUNCTION: check_slice_usage(char *dsname, dm_descriptor_t slice,
1714 * dm_descriptor_t disk, boolean_t *avail,
1715 * dlist_t **bad, dlist_t **classes)
1716 *
1717 * INPUT: dsname - a char * diskset name.
1718 * slice - a dm_descriptor_t handle for a known slices.
1719 * disk - a dm_descriptor_t handle the slice's disk.
1720 *
1721 * OUTPUT: avail - a boolean_t to hold the slice's availability.
1722 * bad - pointer to a list of bad/unusable slices,
1723 * possibly updated if the input slice
1724 * was determined to be inaccessible.
1725 * classes - pointer to a list of slice_class_t structs,
1726 * possibly updated to include the input slice
1727 * if it has a known use.
1728 *
1729 * RETURNS: int - 0 on success
1730 * !0 otherwise.
1731 *
1732 * PURPOSE: Handles the details of
1733 * determining usage and/or availability of a single slice.
1734 *
1735 * Queries the device library for the input slice's detectable
1736 * usage status.
1737 *
1738 * If the slice has a detected usage, its name is added to
1739 * the appropriate slice_class_t list in the input list of
1740 * slice classes, this is only done if verbose output was
1741 * requested.
1742 */
1743 static int
check_slice_usage(char * dsname,dm_descriptor_t slice,dm_descriptor_t disk,boolean_t * avail,dlist_t ** bad,dlist_t ** classes)1744 check_slice_usage(
1745 char *dsname,
1746 dm_descriptor_t slice,
1747 dm_descriptor_t disk,
1748 boolean_t *avail,
1749 dlist_t **bad,
1750 dlist_t **classes)
1751 {
1752 boolean_t online = B_FALSE;
1753 boolean_t used = B_FALSE;
1754 nvlist_t *stats = NULL;
1755 char *name = NULL;
1756 char *used_by = NULL;
1757 char *use_detail = NULL;
1758 int error = 0;
1759
1760 *avail = B_FALSE;
1761
1762 if (((error = get_display_name(slice, &name)) != 0) ||
1763 (error = disk_get_is_online(disk, &online))) {
1764 return (error);
1765 }
1766
1767 /*
1768 * if the disk is known to be offline, skip getting status
1769 * for the slice since it will just fail and return ENODEV.
1770 */
1771 if (online != B_TRUE) {
1772 error = ENODEV;
1773 } else {
1774 stats = dm_get_stats(slice, DM_SLICE_STAT_USE, &error);
1775 }
1776
1777 if (error != 0) {
1778 if (error == ENODEV) {
1779 dlist_t *item = dlist_new_item((void *)(uintptr_t)slice);
1780 oprintf(OUTPUT_TERSE,
1781 gettext("Warning: unable to get slice information "
1782 "for %s, it will not be used.\n"), name);
1783
1784 if (item == NULL) {
1785 error = ENOMEM;
1786 } else {
1787 error = 0;
1788 *bad = dlist_insert_ordered(item, *bad, ASCENDING,
1789 compare_desc_display_names);
1790 }
1791 } else {
1792 oprintf(OUTPUT_TERSE,
1793 gettext("check_slice_usage: dm_get_stats for "
1794 "%s failed %d\n"),
1795 name, error);
1796 }
1797
1798 return (error);
1799 }
1800
1801 /*
1802 * check if/how the slice is currently being used,
1803 * device library provides this info in the nvpair_t list:
1804 *
1805 * stat_type is DM_SLICE_STAT_USE
1806 * used_by: string (mount, svm, lu, vxvm, fs)
1807 * used_name: string
1808 *
1809 */
1810 if (stats != NULL) {
1811 error = get_string(stats, DM_USED_BY, &used_by);
1812 if (error != 0) {
1813 if (error == ENOENT) {
1814 used_by = NULL;
1815 error = 0;
1816 } else {
1817 oprintf(OUTPUT_TERSE,
1818 gettext("check_slice_usage: dm_get_stats.%s for "
1819 "%s failed %d\n"),
1820 DM_USED_BY, name, error);
1821 }
1822 }
1823
1824 if (error == 0) {
1825 error = get_string(stats, DM_USED_NAME, &use_detail);
1826 if (error != 0) {
1827 if (error == ENOENT) {
1828 use_detail = NULL;
1829 error = 0;
1830 } else {
1831 oprintf(OUTPUT_TERSE,
1832 gettext("check_slice_usage: "
1833 "dm_get_stats.%s for "
1834 "%s failed %d\n"),
1835 DM_USED_NAME, name, error);
1836 }
1837 }
1838 }
1839 }
1840
1841 if ((error == 0) && (used_by != NULL) && (used_by[0] != '\0')) {
1842
1843 /* was detected usage SVM? */
1844 if (string_case_compare(used_by, DM_USE_SVM) == 0) {
1845
1846 /* check use_detail, it is in the form diskset:name */
1847 if (strncmp("diskset:", use_detail, 8) == 0) {
1848
1849 /* check disk set name */
1850 char *str = strrchr(use_detail, ':');
1851 if ((str != NULL) &&
1852 (string_case_compare(str+1, dsname) == 0)) {
1853
1854 /* slice in the right diskset */
1855 error = check_svm_slice_usage(
1856 dsname, slice, disk, &used, classes);
1857
1858 } else {
1859
1860 /* slice in other diskset */
1861 save_slice_classification(
1862 dsname, slice, disk, used_by, use_detail,
1863 classes);
1864 used = B_TRUE;
1865 }
1866
1867 } else {
1868
1869 /* slice is volume component */
1870 save_slice_classification(
1871 dsname, slice, disk, used_by, use_detail,
1872 classes);
1873 used = B_TRUE;
1874 }
1875
1876 } else {
1877
1878 /* save usage */
1879 save_slice_classification(
1880 dsname, slice, disk, used_by, use_detail,
1881 classes);
1882 used = B_TRUE;
1883 }
1884 }
1885
1886 nvlist_free(stats);
1887
1888 if (error == 0) {
1889 if (used == B_TRUE) {
1890 *avail = B_FALSE;
1891 } else {
1892 *avail = B_TRUE;
1893 }
1894 }
1895
1896 return (error);
1897 }
1898
1899 /*
1900 * FUNCTION: check_svm_slice_usage(char *dsname, dm_descriptor_t slice,
1901 * dm_descriptor_t disk, boolean_t *used,
1902 * dlist_t **classes)
1903 *
1904 * INPUT: dsname - a char * diskset name.
1905 * slice - a dm_descriptor_t handle for a known slices.
1906 * disk - a dm_descriptor_t handle the slice's disk.
1907 *
1908 * OUTPUT: used - a boolean_t to hold the slice usage status.
1909 * classes - pointer to a list of slice_class_t possibly updated
1910 * with the input slice's SVM specific usage
1911 * classification.
1912 *
1913 * RETURNS: int - 0 on success
1914 * !0 otherwise.
1915 *
1916 * PURPOSE: Handles the finer details of
1917 * a single slice is being used in the context of SVM.
1918 *
1919 * Currently, one thing is checked:
1920 *
1921 * 1. determine if the slice is reserved for metadb replicas.
1922 * The convention for disks in disksets is that a single slice
1923 * (index 6 or 7) is set aside for metadb replicas.
1924 *
1925 * If this condition does not hold, the slice is considered
1926 * available for use by layout and 'used' is set to B_FALSE.
1927 */
1928 static int
check_svm_slice_usage(char * dsname,dm_descriptor_t slice,dm_descriptor_t disk,boolean_t * used,dlist_t ** classes)1929 check_svm_slice_usage(
1930 char *dsname,
1931 dm_descriptor_t slice,
1932 dm_descriptor_t disk,
1933 boolean_t *used,
1934 dlist_t **classes)
1935 {
1936 boolean_t is_replica = B_FALSE;
1937 uint32_t index = 0;
1938 char *diskname = NULL;
1939 int error = 0;
1940
1941 ((error = slice_get_index(slice, &index)) != 0) ||
1942 (error = get_display_name(disk, &diskname)) ||
1943 (error = is_reserved_replica_slice_index(
1944 dsname, diskname, index, &is_replica));
1945
1946 if (error == 0) {
1947 if (is_replica == B_TRUE) {
1948 /* is replica slice -> used */
1949 save_slice_classification(dsname, slice, disk, DM_USE_SVM,
1950 gettext("reserved for metadb replicas"), classes);
1951 *used = B_TRUE;
1952 } else {
1953 *used = B_FALSE;
1954 }
1955 }
1956
1957 return (error);
1958 }
1959
1960 /*
1961 * FUNCTION: save_slice_classification(char *dsname, dm_descriptor_t slice,
1962 * dm_descriptor_t disk, char *used_by, char *usage_detail,
1963 * dlist_t **classes)
1964 *
1965 * INPUT: dsname - a char * disk set name
1966 * slice - a dm_descriptor_t slice handle.
1967 * disk - a dm_descriptor_t handle for the slice's disk.
1968 * used_by - a char * usage classification.
1969 * usage_detail - a char * usage description for the slice.
1970 *
1971 * OUTPUT: classes - a list of slice_class_t updated to hold a usage
1972 * entry for the input slicexs.
1973 *
1974 * SIDEEFFECT: adds the input slice to the list of known, used slices.
1975 *
1976 * RETURNS: int - 0 on success
1977 * !0 otherwise.
1978 *
1979 * PURPOSE: Adds an entry to the
1980 * appropriate slice_class_t list of slices. If there is
1981 * not an appropriate slice_class_t entry in the input list
1982 * of classes, one is added.
1983 *
1984 * As a performance optimization the slice usage classification
1985 * information is only saved if verbose output was requested by
1986 * the user.
1987 */
1988 static int
save_slice_classification(char * dsname,dm_descriptor_t slice,dm_descriptor_t disk,char * usage,char * usage_detail,dlist_t ** classes)1989 save_slice_classification(
1990 char *dsname,
1991 dm_descriptor_t slice,
1992 dm_descriptor_t disk,
1993 char *usage,
1994 char *usage_detail,
1995 dlist_t **classes)
1996 {
1997 int error = 0;
1998
1999 error = add_used_slice(slice);
2000
2001 if ((error == 0) && (get_max_verbosity() >= OUTPUT_VERBOSE)) {
2002
2003 dlist_t *iter;
2004 dlist_t *item;
2005 slice_class_t *class = NULL;
2006
2007 /* locate class struct matching 'usage' */
2008 for (iter = *classes; iter != NULL; iter = iter->next) {
2009 class = (slice_class_t *)iter->obj;
2010 if (string_case_compare(usage, class->usage) == 0) {
2011 break;
2012 }
2013 }
2014
2015 if (iter == NULL) {
2016 /* add a new class to the list of classes */
2017 class = (slice_class_t *)calloc(1, sizeof (slice_class_t));
2018 if (class == NULL) {
2019 error = ENOMEM;
2020 } else {
2021 class->usage = strdup(usage);
2022 if (class->usage == NULL) {
2023 free(class);
2024 class = NULL;
2025 error = ENOMEM;
2026 } else {
2027 item = dlist_new_item((void *)class);
2028 if (item == NULL) {
2029 free(class->usage);
2030 free(class);
2031 class = NULL;
2032 error = ENOMEM;
2033 } else {
2034 *classes = dlist_append(item, *classes, AT_TAIL);
2035 }
2036 }
2037 }
2038 }
2039
2040 if ((error == 0) && (class != NULL)) {
2041
2042 char buf[BUFSIZ];
2043 char *dup = NULL;
2044 char *slicename = NULL;
2045
2046 (void) get_display_name(slice, &slicename);
2047 (void) snprintf(buf, BUFSIZ-1, " %s: %s",
2048 slicename, usage_detail);
2049 if ((dup = strdup(buf)) == NULL) {
2050 error = ENOMEM;
2051 } else {
2052 if ((item = dlist_new_item((void *)dup)) == NULL) {
2053 free(dup);
2054 error = ENOMEM;
2055 } else {
2056 class->sliceinfo =
2057 dlist_insert_ordered(
2058 item, class->sliceinfo,
2059 ASCENDING, compare_strings);
2060 }
2061 }
2062 }
2063 }
2064
2065 return (error);
2066 }
2067
2068 /*
2069 * FUNCTION: print_usable_devices()
2070 *
2071 * PURPOSE: Print out the devices determined to be available for
2072 * use by layout.
2073 *
2074 * Iterates the lists of usable slices, disks and HBAs
2075 * and prints out their CTD and device names.
2076 */
2077 static void
print_usable_devices()2078 print_usable_devices()
2079 {
2080 int i = 0;
2081
2082 struct {
2083 char *msg;
2084 dlist_t *list;
2085 } devs[3];
2086
2087 devs[0].msg = gettext("HBA/Controllers");
2088 devs[0].list = _usable_hbas;
2089 devs[1].msg = gettext("disks");
2090 devs[1].list = _usable_disks;
2091 devs[2].msg = gettext("slices");
2092 devs[2].list = _usable_slices;
2093
2094 for (i = 0; i < 3; i++) {
2095
2096 oprintf(OUTPUT_VERBOSE,
2097 gettext("\n These %s are usable:\n\n"),
2098 devs[i].msg);
2099
2100 print_device_list(devs[i].list);
2101 }
2102 }
2103
2104 /*
2105 * FUNCTION: print_device_list(dlist_t *devices)
2106 *
2107 * INPUT: devices - a list of device descriptor handles
2108 *
2109 * PURPOSE: A helper for the print_XXX_devices() routines which iterates
2110 * the input list and prints out each device name, CTD name and
2111 * alias(es).
2112 */
2113 static void
print_device_list(dlist_t * devices)2114 print_device_list(
2115 dlist_t *devices)
2116 {
2117 dlist_t *iter = NULL;
2118
2119 for (iter = devices; iter != NULL; iter = iter->next) {
2120
2121 dm_descriptor_t device = ((uintptr_t)iter->obj);
2122 char *name = NULL;
2123 char *ctd = NULL;
2124 dlist_t *aliases = NULL;
2125
2126 (void) get_display_name(device, &ctd);
2127 (void) get_name(device, &name);
2128 oprintf(OUTPUT_VERBOSE,
2129 " %-25s %s\n", (ctd != NULL ? ctd : ""), name);
2130
2131 (void) get_aliases(device, &aliases);
2132 for (; aliases != NULL; aliases = aliases->next) {
2133 oprintf(OUTPUT_VERBOSE,
2134 gettext(" (alias: %s)\n"),
2135 (char *)aliases->obj);
2136 }
2137
2138 dlist_free_items(aliases, free);
2139 }
2140 }
2141
2142 /*
2143 * FUNCTION: print_unusable_devices(
2144 * dlist_t *bad_slices, dlist_t *bad_disks,
2145 * dlist_t *used_classes)
2146 *
2147 * INPUT: used_classes - a list of slice_class_t structs
2148 *
2149 * PURPOSE: Print out the devices determined to be unavailable for
2150 * use by layout.
2151 *
2152 * Iterates the input list of slice classifications and prints
2153 * out a description of the class and the slices so classified.
2154 *
2155 * Also iterates the lists of bad slices and disks (those that
2156 * libdiskmgt returned descriptors for but cannot be accessed)
2157 * and notes them as unusable.
2158 */
2159 static void
print_unusable_devices(dlist_t * bad_slices,dlist_t * bad_disks,dlist_t * used_classes)2160 print_unusable_devices(
2161 dlist_t *bad_slices,
2162 dlist_t *bad_disks,
2163 dlist_t *used_classes)
2164 {
2165 dlist_t *iter = NULL;
2166 dlist_t *slices = NULL;
2167 char *preamble;
2168
2169 struct {
2170 char *msg;
2171 dlist_t *list;
2172 } devs[2];
2173
2174 /* report bad disks and slices */
2175 devs[0].msg = gettext("disks");
2176 devs[0].list = bad_disks;
2177 devs[1].msg = gettext("slices");
2178 devs[1].list = bad_slices;
2179
2180 if (bad_disks != NULL) {
2181 oprintf(OUTPUT_VERBOSE,
2182 #if defined(sparc)
2183 gettext("\n These disks are not usable, they may "
2184 "may be offline or cannot be accessed:\n\n"));
2185 #elif defined(i386)
2186 gettext("\n These disks are not usable, they may "
2187 "may be offline,\n missing a Solaris FDISK "
2188 "partition or cannot be accessed:\n\n"));
2189 #endif
2190 print_device_list(bad_disks);
2191 }
2192
2193 if (bad_slices != NULL) {
2194 oprintf(OUTPUT_VERBOSE, gettext(
2195 "\n These slices, and subsequently the disks on which they\n"
2196 "reside, are not usable, they cannot be accessed:\n\n"));
2197 print_device_list(bad_slices);
2198 }
2199
2200 /* report used slices and usages */
2201 preamble = gettext("\n These slices are not usable, %s:\n\n");
2202 for (iter = used_classes; iter != NULL; iter = iter->next) {
2203 slice_class_t *class = (slice_class_t *)iter->obj;
2204
2205 if (class->sliceinfo != NULL) {
2206
2207 oprintf(OUTPUT_VERBOSE, preamble,
2208 get_slice_usage_msg(class->usage));
2209
2210 slices = class->sliceinfo;
2211 for (; slices != NULL; slices = slices->next) {
2212 oprintf(OUTPUT_VERBOSE, " %s\n", (char *)slices->obj);
2213 }
2214 }
2215 }
2216
2217 }
2218
2219 /*
2220 * FUNCTION: char * get_slice_usage_msg(char *usage)
2221 *
2222 * INPUT: usage - char * string representing a slice usage classification
2223 *
2224 * OUTPUT: char * "friendly" usage message
2225 *
2226 * PURPOSE: the input usage string comes from libdiskmgt and is very terse.
2227 *
2228 * Convert it into a friendlier usage description suitable for user
2229 * consumption.
2230 */
2231 static char *
get_slice_usage_msg(char * usage)2232 get_slice_usage_msg(
2233 char *usage)
2234 {
2235 char *str = NULL;
2236
2237 if (string_case_compare(usage, DM_USE_MOUNT) == 0) {
2238 str = gettext("they have mounted filesystems");
2239 } else if (string_case_compare(usage, DM_USE_FS) == 0) {
2240 str = gettext("they appear to have unmounted filesystems");
2241 } else if (string_case_compare(usage, DM_USE_SVM) == 0) {
2242 str = gettext("they are utilized by SVM");
2243 } else if (string_case_compare(usage, DM_USE_VXVM) == 0) {
2244 str = gettext("they are utilized by VxVm");
2245 } else if (string_case_compare(usage, DM_USE_LU) == 0) {
2246 str = gettext("they are utilized by LiveUpgrade");
2247 } else if (string_case_compare(usage, DM_USE_DUMP) == 0) {
2248 str = gettext("they are reserved as dump devices");
2249 } else if (string_case_compare(usage, USE_DISKSET) == 0) {
2250 str = gettext("they have disk set issues");
2251 } else {
2252 /* libdiskmgt has detected a usage unknown to layout */
2253 str = usage;
2254 }
2255
2256 return (str);
2257 }
2258
2259 /*
2260 * FUNCTION: set_alias(dm_descriptor_t desc, char *alias)
2261 *
2262 * INPUT: desc - a dm_descriptor_t handle.
2263 * alias - a char * alias for the device represented
2264 * by the descriptor.
2265 *
2266 * RETURNS: int - 0 on success
2267 * !0 otherwise
2268 *
2269 * PURPOSE: Adds the specified alias to the known aliases for the
2270 * device associated with the input descriptor.
2271 */
2272 int
set_alias(dm_descriptor_t desc,char * alias)2273 set_alias(
2274 dm_descriptor_t desc,
2275 char *alias)
2276 {
2277 nvlist_t *attrs = NULL;
2278 char **old_aliases = NULL;
2279 char **new_aliases = NULL;
2280 uint_t nelem = 0;
2281 int error = 0;
2282 int i = 0;
2283
2284 if ((error = get_cached_attributes(desc, &attrs)) != 0) {
2285 return (error);
2286 }
2287
2288 if ((error = get_string_array(
2289 attrs, ATTR_DEVICE_ALIASES, &old_aliases, &nelem)) != 0) {
2290 if (error != ENOENT) {
2291 return (error);
2292 }
2293 /* no aliases yet */
2294 error = 0;
2295 }
2296
2297 /* add new alias */
2298 new_aliases = (char **)calloc(MAX_ALIASES, sizeof (char *));
2299 if (new_aliases != NULL) {
2300
2301 for (i = 0; i < nelem && i < MAX_ALIASES; i++) {
2302 char *dup = strdup(old_aliases[i]);
2303 if (dup != NULL) {
2304 new_aliases[i] = dup;
2305 } else {
2306 error = ENOMEM;
2307 }
2308 }
2309
2310 if (error == 0) {
2311 if (i == MAX_ALIASES) {
2312 volume_set_error(
2313 gettext("Maximum number of device aliases "
2314 "(8) reached\n"),
2315 MAX_ALIASES);
2316 error = -1;
2317
2318 } else {
2319 new_aliases[i] = alias;
2320 error = set_string_array(attrs, ATTR_DEVICE_ALIASES,
2321 new_aliases, i + 1);
2322 }
2323 }
2324
2325 free(new_aliases);
2326 }
2327
2328 if (error == 0) {
2329 /* cache descriptor under this alias */
2330 error = add_cached_descriptor(alias, desc);
2331 }
2332
2333 return (error);
2334 }
2335
2336 /*
2337 * FUNCTION: get_aliases(dm_descriptor_t desc, dlist_t **list)
2338 *
2339 * INPUT: desc - a dm_descriptor_t handle.
2340 *
2341 * OUTPUT: list - a dlist_t list pointing to the list of
2342 * aliases associated with the device
2343 * represented by the descriptor.
2344 *
2345 * RETURNS: int - 0 on success
2346 * !0 otherwise
2347 *
2348 * PURPOSE: Retrieves aliases for the input descriptor and
2349 * appends them to the input list.
2350 *
2351 * The list of returned items must be freed by calling
2352 * dlist_free_items(list, free)
2353 */
2354 int
get_aliases(dm_descriptor_t desc,dlist_t ** list)2355 get_aliases(
2356 dm_descriptor_t desc,
2357 dlist_t **list)
2358 {
2359 nvlist_t *attrs = NULL;
2360 char **aliases = NULL;
2361 uint_t nelem = 0;
2362 int error = 0;
2363 int i;
2364
2365 if ((error = get_cached_attributes(desc, &attrs)) != 0) {
2366 return (error);
2367 }
2368
2369 if ((error = get_string_array(
2370 attrs, ATTR_DEVICE_ALIASES, &aliases, &nelem)) != 0) {
2371 if (error == ENOENT) {
2372 /* no aliases */
2373 return (0);
2374 }
2375 }
2376
2377 for (i = 0; i < nelem; i++) {
2378 dlist_t *item;
2379 char *dup;
2380
2381 if ((dup = strdup(aliases[i])) == NULL) {
2382 error = ENOMEM;
2383 break;
2384 }
2385
2386 if ((item = dlist_new_item(dup)) == NULL) {
2387 free(dup);
2388 error = ENOMEM;
2389 break;
2390 }
2391
2392 *list = dlist_append(item, *list, AT_TAIL);
2393 }
2394
2395 return (error);
2396 }
2397
2398 /*
2399 * FUNCTION: compare_start_blocks(
2400 * void *obj1, void *obj2)
2401 *
2402 * INPUT: desc1 - opaque pointer to a dm_descriptor_t
2403 * desc2 - opaque pointer to a dm_descriptor_t
2404 *
2405 * RETURNS: int - <0 - if desc1.stblk < desc2.stblk
2406 * 0 - if desc1.stblk == desc2.stblk
2407 * >0 - if desc1.stblk > desc.stblk
2408 *
2409 * PURPOSE: dlist_t helper which compares the start blocks of
2410 * the two input dm_descriptor_t slice handles.
2411 */
2412 static int
compare_start_blocks(void * desc1,void * desc2)2413 compare_start_blocks(
2414 void *desc1,
2415 void *desc2)
2416 {
2417 uint64_t stblk1 = 0;
2418 uint64_t stblk2 = 0;
2419
2420 assert(desc1 != (dm_descriptor_t)0);
2421 assert(desc2 != (dm_descriptor_t)0);
2422
2423 (void) slice_get_start_block((uintptr_t)desc1, &stblk1);
2424 (void) slice_get_start_block((uintptr_t)desc2, &stblk2);
2425
2426 return (stblk1 - stblk2);
2427 }
2428
2429 /*
2430 * FUNCTION: compare_desc_display_names(
2431 * void *desc1, void *desc2)
2432 *
2433 * INPUT: desc1 - opaque pointer to a dm_descriptor_t
2434 * desc2 - opaque pointer to a dm_descriptor_t
2435 *
2436 * RETURNS: int - <0 - if desc1.name < desc2.name
2437 * 0 - if desc1.name == desc2.name
2438 * >0 - if desc1.name > desc.name
2439 *
2440 * PURPOSE: dlist_t helper which compares the CTD names of the
2441 * two input dm_descriptor_t objects.
2442 */
2443 static int
compare_desc_display_names(void * desc1,void * desc2)2444 compare_desc_display_names(
2445 void *desc1,
2446 void *desc2)
2447 {
2448 char *name1 = NULL;
2449 char *name2 = NULL;
2450
2451 assert(desc1 != (dm_descriptor_t)0);
2452 assert(desc2 != (dm_descriptor_t)0);
2453
2454 (void) get_display_name((uintptr_t)desc1, &name1);
2455 (void) get_display_name((uintptr_t)desc2, &name2);
2456
2457 return (string_case_compare(name1, name2));
2458 }
2459