4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright (c) 2011 by Delphix. All rights reserved.
25 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
29 #include <libdevinfo.h>
34 #include <sys/sunddi.h>
35 #include <sys/types.h>
36 #include <sys/mkdev.h>
41 #include <sys/fs/zfs.h>
43 #include "libdiskmgt.h"
44 #include "disks_private.h"
46 #define CLUSTER_DEV "did"
48 /* specify which disk links to use in the /dev directory */
49 #define DEVLINK_REGEX "rdsk/.*"
50 #define DEVLINK_FLOPPY_REGEX "rdiskette[0-9]"
51 #define DEVLINK_DID_REGEX "did/rdsk/.*"
53 #define FLOPPY_NAME "rdiskette"
55 #define MAXPROPLEN 1024
56 #define DEVICE_ID_PROP "devid"
57 #define PROD_ID_PROP "inquiry-product-id"
58 #define PROD_ID_USB_PROP "usb-product-name"
59 #define REMOVABLE_PROP "removable-media"
60 #define HOTPLUGGABLE_PROP "hotpluggable"
61 #define SCSI_OPTIONS_PROP "scsi-options"
62 #define VENDOR_ID_PROP "inquiry-vendor-id"
63 #define VENDOR_ID_USB_PROP "usb-vendor-name"
64 #define WWN_PROP "node-wwn"
66 static char *ctrltypes
[] = {
68 DDI_NT_SCSI_ATTACHMENT_POINT
,
69 DDI_NT_FC_ATTACHMENT_POINT
,
73 static char *bustypes
[] = {
80 static bus_t
*add_bus(struct search_args
*args
, di_node_t node
,
81 di_minor_t minor
, controller_t
*cp
);
82 static int add_cluster_devs(di_node_t node
, di_minor_t minor
,
84 static controller_t
*add_controller(struct search_args
*args
,
85 di_node_t node
, di_minor_t minor
);
86 static int add_devpath(di_devlink_t devlink
, void *arg
);
87 static int add_devs(di_node_t node
, di_minor_t minor
, void *arg
);
88 static int add_disk2controller(disk_t
*diskp
,
89 struct search_args
*args
);
90 static int add_disk2path(disk_t
*dp
, path_t
*pp
,
91 di_path_state_t st
, char *wwn
);
92 static int add_int2array(int p
, int **parray
);
93 static int add_ptr2array(void *p
, void ***parray
);
94 static char *bus_type(di_node_t node
, di_minor_t minor
,
96 static void remove_controller(controller_t
*cp
,
98 static void clean_paths(struct search_args
*args
);
99 static disk_t
*create_disk(char *deviceid
, char *kernel_name
,
100 struct search_args
*args
);
101 static char *ctype(di_node_t node
, di_minor_t minor
);
102 static boolean_t
disk_is_cdrom(const char *type
);
103 static alias_t
*find_alias(disk_t
*diskp
, char *kernel_name
);
104 static bus_t
*find_bus(struct search_args
*args
, char *name
);
105 static controller_t
*find_controller(struct search_args
*args
, char *name
);
106 static int fix_cluster_devpath(di_devlink_t devlink
, void *arg
);
107 static disk_t
*get_disk_by_deviceid(disk_t
*listp
, char *devid
);
108 static void get_disk_name_from_path(char *path
, char *name
,
110 static char *get_byte_prop(char *prop_name
, di_node_t node
);
111 static di_node_t
get_parent_bus(di_node_t node
,
112 struct search_args
*args
);
113 static int get_prom_int(char *prop_name
, di_node_t node
,
114 di_prom_handle_t ph
);
115 static char *get_prom_str(char *prop_name
, di_node_t node
,
116 di_prom_handle_t ph
);
117 static int get_prop(char *prop_name
, di_node_t node
);
118 static char *get_str_prop(char *prop_name
, di_node_t node
);
119 static int have_disk(struct search_args
*args
, char *devid
,
120 char *kernel_name
, disk_t
**diskp
);
121 static int is_cluster_disk(di_node_t node
, di_minor_t minor
);
122 static int is_ctds(char *name
);
123 static int is_drive(di_minor_t minor
);
124 static int is_zvol(di_node_t node
, di_minor_t minor
);
125 static int is_HBA(di_node_t node
, di_minor_t minor
);
126 static int new_alias(disk_t
*diskp
, char *kernel_path
,
127 char *devlink_path
, struct search_args
*args
);
128 static int new_devpath(alias_t
*ap
, char *devpath
);
129 static path_t
*new_path(controller_t
*cp
, disk_t
*diskp
,
130 di_node_t node
, di_path_state_t st
, char *wwn
);
131 static void remove_invalid_controller(char *name
,
132 controller_t
*currp
, struct search_args
*args
);
133 static char *str_case_index(register char *s1
, register char *s2
);
136 * The functions in this file do a dev tree walk to build up a model of the
137 * disks, controllers and paths on the system. This model is returned in the
138 * args->disk_listp and args->controller_listp members of the args param.
139 * There is no global data for this file so it is thread safe. It is up to
140 * the caller to merge the resulting model with any existing model that is
141 * cached. The caller must also free the memory for this model when it is
145 findevs(struct search_args
*args
)
150 args
->dev_walk_status
= 0;
151 args
->disk_listp
= NULL
;
152 args
->controller_listp
= NULL
;
153 args
->bus_listp
= NULL
;
155 args
->handle
= di_devlink_init(NULL
, 0);
158 * Have to make several passes at this with the new devfs caching.
159 * First, we find non-mpxio devices. Then we find mpxio/multipath
160 * devices. Finally, we get cluster devices.
163 di_root
= di_init("/", flags
);
164 args
->ph
= di_prom_init();
165 (void) di_walk_minor(di_root
, NULL
, 0, args
, add_devs
);
168 flags
= DINFOCPYALL
| DINFOPATH
;
169 di_root
= di_init("/", flags
);
170 (void) di_walk_minor(di_root
, NULL
, 0, args
, add_devs
);
173 /* do another pass to clean up cluster devpaths */
175 di_root
= di_init("/", flags
);
176 (void) di_walk_minor(di_root
, DDI_PSEUDO
, 0, args
, add_cluster_devs
);
177 if (args
->ph
!= DI_PROM_HANDLE_NIL
) {
178 (void) di_prom_fini(args
->ph
);
182 (void) di_devlink_fini(&(args
->handle
));
188 * Definitions of private functions
192 add_bus(struct search_args
*args
, di_node_t node
, di_minor_t minor
,
198 char kstat_name
[MAXPATHLEN
];
201 if (node
== DI_NODE_NIL
) {
205 if ((btype
= bus_type(node
, minor
, args
->ph
)) == NULL
) {
206 return (add_bus(args
, di_parent_node(node
),
207 di_minor_next(di_parent_node(node
), NULL
), cp
));
210 devpath
= di_devfs_path(node
);
212 if ((bp
= find_bus(args
, devpath
)) != NULL
) {
213 di_devfs_path_free((void *) devpath
);
216 if (add_ptr2array(cp
,
217 (void ***)&bp
->controllers
) != 0) {
218 args
->dev_walk_status
= ENOMEM
;
225 /* Special handling for root node. */
226 if (strcmp(devpath
, "/") == 0) {
227 di_devfs_path_free((void *) devpath
);
232 (void) fprintf(stderr
, "INFO: add_bus %s\n", devpath
);
235 bp
= (bus_t
*)calloc(1, sizeof (bus_t
));
240 bp
->name
= strdup(devpath
);
241 di_devfs_path_free((void *) devpath
);
242 if (bp
->name
== NULL
) {
243 args
->dev_walk_status
= ENOMEM
;
248 bp
->btype
= strdup(btype
);
249 if (bp
->btype
== NULL
) {
250 args
->dev_walk_status
= ENOMEM
;
255 (void) snprintf(kstat_name
, sizeof (kstat_name
), "%s%d",
256 di_node_name(node
), di_instance(node
));
258 if ((bp
->kstat_name
= strdup(kstat_name
)) == NULL
) {
259 args
->dev_walk_status
= ENOMEM
;
264 /* if parent node is a bus, get its name */
265 if ((pnode
= get_parent_bus(node
, args
)) != NULL
) {
266 devpath
= di_devfs_path(pnode
);
267 bp
->pname
= strdup(devpath
);
268 di_devfs_path_free((void *) devpath
);
269 if (bp
->pname
== NULL
) {
270 args
->dev_walk_status
= ENOMEM
;
279 bp
->freq
= get_prom_int("clock-frequency", node
, args
->ph
);
281 bp
->controllers
= (controller_t
**)calloc(1, sizeof (controller_t
*));
282 if (bp
->controllers
== NULL
) {
283 args
->dev_walk_status
= ENOMEM
;
287 bp
->controllers
[0] = NULL
;
290 if (add_ptr2array(cp
, (void ***)&bp
->controllers
) != 0) {
291 args
->dev_walk_status
= ENOMEM
;
296 bp
->next
= args
->bus_listp
;
297 args
->bus_listp
= bp
;
303 add_cluster_devs(di_node_t node
, di_minor_t minor
, void *arg
)
305 struct search_args
*args
;
307 char slice_path
[MAXPATHLEN
];
308 int result
= DI_WALK_CONTINUE
;
310 if (!is_cluster_disk(node
, minor
)) {
311 return (DI_WALK_CONTINUE
);
314 args
= (struct search_args
*)arg
;
317 /* This is all just debugging code */
319 char dev_name
[MAXPATHLEN
];
321 devpath
= di_devfs_path(node
);
322 (void) snprintf(dev_name
, sizeof (dev_name
), "%s:%s", devpath
,
323 di_minor_name(minor
));
324 di_devfs_path_free((void *) devpath
);
326 (void) fprintf(stderr
, "INFO: cluster dev: %s\n", dev_name
);
331 args
->dev_walk_status
= 0;
334 * Fix the devpaths for the cluster drive.
336 * We will come through here once for each raw slice device name.
338 devpath
= di_devfs_path(node
);
339 (void) snprintf(slice_path
, sizeof (slice_path
), "%s:%s", devpath
,
340 di_minor_name(minor
));
341 di_devfs_path_free((void *) devpath
);
343 /* Walk the /dev tree to get the cluster devlinks. */
344 (void) di_devlink_walk(args
->handle
, DEVLINK_DID_REGEX
, slice_path
,
345 DI_PRIMARY_LINK
, arg
, fix_cluster_devpath
);
347 if (args
->dev_walk_status
!= 0) {
348 result
= DI_WALK_TERMINATE
;
354 static controller_t
*
355 add_controller(struct search_args
*args
, di_node_t node
, di_minor_t minor
)
359 char kstat_name
[MAXPATHLEN
];
360 char *c_type
= DM_CTYPE_UNKNOWN
;
362 devpath
= di_devfs_path(node
);
364 if ((cp
= find_controller(args
, devpath
)) != NULL
) {
365 di_devfs_path_free((void *) devpath
);
369 /* Special handling for fp attachment node. */
370 if (strcmp(di_node_name(node
), "fp") == 0) {
373 pnode
= di_parent_node(node
);
374 if (pnode
!= DI_NODE_NIL
) {
375 di_devfs_path_free((void *) devpath
);
376 devpath
= di_devfs_path(pnode
);
378 if ((cp
= find_controller(args
, devpath
)) != NULL
) {
379 di_devfs_path_free((void *) devpath
);
383 /* not in the list, create it */
385 c_type
= DM_CTYPE_FIBRE
;
390 (void) fprintf(stderr
, "INFO: add_controller %s\n", devpath
);
393 cp
= (controller_t
*)calloc(1, sizeof (controller_t
));
398 cp
->name
= strdup(devpath
);
399 di_devfs_path_free((void *) devpath
);
400 if (cp
->name
== NULL
) {
401 cache_free_controller(cp
);
405 if (strcmp(c_type
, DM_CTYPE_UNKNOWN
) == 0) {
406 c_type
= ctype(node
, minor
);
410 (void) snprintf(kstat_name
, sizeof (kstat_name
), "%s%d",
411 di_node_name(node
), di_instance(node
));
413 if ((cp
->kstat_name
= strdup(kstat_name
)) == NULL
) {
414 cache_free_controller(cp
);
418 if (libdiskmgt_str_eq(cp
->ctype
, "scsi")) {
419 cp
->scsi_options
= get_prop(SCSI_OPTIONS_PROP
, node
);
422 if (libdiskmgt_str_eq(di_node_name(node
), "scsi_vhci")) {
428 cp
->freq
= get_prom_int("clock-frequency", node
, args
->ph
);
430 cp
->disks
= (disk_t
**)calloc(1, sizeof (disk_t
*));
431 if (cp
->disks
== NULL
) {
432 cache_free_controller(cp
);
437 cp
->next
= args
->controller_listp
;
438 args
->controller_listp
= cp
;
440 cp
->bus
= add_bus(args
, di_parent_node(node
),
441 di_minor_next(di_parent_node(node
), NULL
), cp
);
447 add_devpath(di_devlink_t devlink
, void *arg
)
449 struct search_args
*args
;
452 char kernel_name
[MAXPATHLEN
];
454 args
= (struct search_args
*)arg
;
457 * Get the diskp value from calling have_disk. Can either be found
458 * by kernel name or devid.
462 devidstr
= get_str_prop(DEVICE_ID_PROP
, args
->node
);
463 (void) snprintf(kernel_name
, sizeof (kernel_name
), "%s%d",
464 di_node_name(args
->node
), di_instance(args
->node
));
466 (void) have_disk(args
, devidstr
, kernel_name
, &diskp
);
469 * The devlink_path is usually of the form /dev/rdsk/c0t0d0s0.
470 * For diskettes it is /dev/rdiskette*.
471 * On Intel we would also get each fdisk partition as well
472 * (e.g. /dev/rdsk/c0t0d0p0).
478 if (diskp
->drv_type
!= DM_DT_FLOPPY
) {
480 * Add other controllers for multipath disks.
481 * This will have no effect if the controller
482 * relationship is already set up.
484 if (add_disk2controller(diskp
, args
) != 0) {
485 args
->dev_walk_status
= ENOMEM
;
489 (void) snprintf(kernel_name
, sizeof (kernel_name
), "%s%d",
490 di_node_name(args
->node
), di_instance(args
->node
));
491 devlink_path
= (char *)di_devlink_path(devlink
);
494 (void) fprintf(stderr
,
495 "INFO: devpath %s\n", devlink_path
);
498 if ((ap
= find_alias(diskp
, kernel_name
)) == NULL
) {
499 if (new_alias(diskp
, kernel_name
, devlink_path
,
501 args
->dev_walk_status
= ENOMEM
;
505 * It is possible that we have already added this
506 * devpath. Do not add it again. new_devpath will
507 * return a 0 if found, and not add the path.
509 if (new_devpath(ap
, devlink_path
) != 0) {
510 args
->dev_walk_status
= ENOMEM
;
515 return (DI_WALK_CONTINUE
);
519 add_devs(di_node_t node
, di_minor_t minor
, void *arg
)
521 struct search_args
*args
;
522 int result
= DI_WALK_CONTINUE
;
524 args
= (struct search_args
*)arg
;
527 /* This is all just debugging code */
529 char dev_name
[MAXPATHLEN
];
531 devpath
= di_devfs_path(node
);
532 (void) snprintf(dev_name
, sizeof (dev_name
), "%s:%s", devpath
,
533 di_minor_name(minor
));
534 di_devfs_path_free((void *) devpath
);
536 (void) fprintf(stderr
,
537 "INFO: dev: %s, node: %s%d, minor: 0x%x, type: %s\n",
538 dev_name
, di_node_name(node
), di_instance(node
),
539 di_minor_spectype(minor
),
540 (di_minor_nodetype(minor
) != NULL
?
541 di_minor_nodetype(minor
) : "NULL"));
544 if (bus_type(node
, minor
, args
->ph
) != NULL
) {
545 if (add_bus(args
, node
, minor
, NULL
) == NULL
) {
546 args
->dev_walk_status
= ENOMEM
;
547 result
= DI_WALK_TERMINATE
;
550 } else if (is_HBA(node
, minor
)) {
551 if (add_controller(args
, node
, minor
) == NULL
) {
552 args
->dev_walk_status
= ENOMEM
;
553 result
= DI_WALK_TERMINATE
;
556 } else if (di_minor_spectype(minor
) == S_IFCHR
&&
557 (is_drive(minor
) || is_zvol(node
, minor
))) {
559 char kernel_name
[MAXPATHLEN
];
562 (void) snprintf(kernel_name
, sizeof (kernel_name
), "%s%d",
563 di_node_name(node
), di_instance(node
));
564 devidstr
= get_str_prop(DEVICE_ID_PROP
, node
);
569 * Check if we already got this disk and
570 * this is another slice.
572 if (!have_disk(args
, devidstr
, kernel_name
, &diskp
)) {
573 args
->dev_walk_status
= 0;
575 * This is a newly found disk, create the
578 diskp
= create_disk(devidstr
, kernel_name
, args
);
580 args
->dev_walk_status
= ENOMEM
;
583 if (diskp
->drv_type
!= DM_DT_FLOPPY
) {
584 /* add the controller relationship */
585 if (args
->dev_walk_status
== 0) {
586 if (add_disk2controller(diskp
,
588 args
->dev_walk_status
= ENOMEM
;
593 if (is_zvol(node
, minor
)) {
594 char zvdsk
[MAXNAMELEN
];
598 if (di_prop_lookup_strings(di_minor_devt(minor
),
599 node
, "name", &str
) == -1)
600 return (DI_WALK_CONTINUE
);
601 (void) snprintf(zvdsk
, MAXNAMELEN
, "/dev/zvol/rdsk/%s",
603 if ((ap
= find_alias(diskp
, kernel_name
)) == NULL
) {
604 if (new_alias(diskp
, kernel_name
,
606 args
->dev_walk_status
= ENOMEM
;
610 * It is possible that we have already added
612 * Do not add it again. new_devpath will
613 * return a 0 if found, and not add the path.
615 if (new_devpath(ap
, zvdsk
) != 0) {
616 args
->dev_walk_status
= ENOMEM
;
621 /* Add the devpaths for the drive. */
622 if (args
->dev_walk_status
== 0) {
624 char slice_path
[MAXPATHLEN
];
628 * We will come through here once for each of
629 * the raw slice device names.
631 devpath
= di_devfs_path(node
);
632 (void) snprintf(slice_path
,
633 sizeof (slice_path
), "%s:%s",
634 devpath
, di_minor_name(minor
));
635 di_devfs_path_free((void *) devpath
);
637 if (libdiskmgt_str_eq(di_minor_nodetype(minor
),
639 pattern
= DEVLINK_FLOPPY_REGEX
;
641 pattern
= DEVLINK_REGEX
;
644 /* Walk the /dev tree to get the devlinks. */
645 (void) di_devlink_walk(args
->handle
, pattern
,
646 slice_path
, DI_PRIMARY_LINK
, arg
, add_devpath
);
649 if (args
->dev_walk_status
!= 0) {
650 result
= DI_WALK_TERMINATE
;
658 add_disk2controller(disk_t
*diskp
, struct search_args
*args
)
668 pnode
= di_parent_node(node
);
669 if (pnode
== DI_NODE_NIL
) {
673 minor
= di_minor_next(pnode
, NULL
);
678 if ((cp
= add_controller(args
, pnode
, minor
)) == NULL
) {
682 /* check if the disk <-> ctrl assoc is already there */
683 for (i
= 0; diskp
->controllers
[i
]; i
++) {
684 if (cp
== diskp
->controllers
[i
]) {
689 /* this is a new controller for this disk */
691 /* add the disk to the controlller */
692 if (add_ptr2array(diskp
, (void ***)&cp
->disks
) != 0) {
696 /* add the controlller to the disk */
697 if (add_ptr2array(cp
, (void ***)&diskp
->controllers
) != 0) {
702 * Set up paths for mpxio controlled drives.
704 if (libdiskmgt_str_eq(di_node_name(pnode
), "scsi_vhci")) {
705 /* note: mpxio di_path stuff is all consolidation private */
706 di_path_t pi
= DI_PATH_NIL
;
709 (pi
= di_path_client_next_path(node
, pi
)) != DI_PATH_NIL
) {
712 char str
[MAXPATHLEN
];
715 di_node_t phci_node
= di_path_phci_node(pi
);
717 /* get the node wwn */
718 cnt
= di_path_prop_lookup_bytes(pi
, WWN_PROP
, &bytes
);
724 for (i
= 0; i
< cnt
; i
++) {
726 * A byte is only 2 hex chars + null.
730 (void) snprintf(bstr
,
731 sizeof (bstr
), "%.2x", bytes
[i
]);
732 (void) strlcat(str
, bstr
, sizeof (str
));
737 if (new_path(cp
, diskp
, phci_node
,
738 di_path_state(pi
), wwn
) == NULL
) {
748 add_disk2path(disk_t
*dp
, path_t
*pp
, di_path_state_t st
, char *wwn
)
750 /* add the disk to the path */
751 if (add_ptr2array(dp
, (void ***)&pp
->disks
) != 0) {
756 /* add the path to the disk */
757 if (add_ptr2array(pp
, (void ***)&dp
->paths
) != 0) {
762 /* add the path state for this disk */
763 if (add_int2array(st
, &pp
->states
) != 0) {
768 /* add the path state for this disk */
772 if ((wp
= strdup(wwn
)) != NULL
) {
773 if (add_ptr2array(wp
, (void ***)(&pp
->wwns
)) != 0) {
784 add_int2array(int p
, int **parray
)
795 for (; pa
[cnt
] != -1; cnt
++)
799 new_array
= (int *)calloc(cnt
+ 2, sizeof (int *));
800 if (new_array
== NULL
) {
804 /* copy the existing array */
805 for (i
= 0; i
< cnt
; i
++) {
806 new_array
[i
] = pa
[i
];
810 new_array
[i
+ 1] = -1;
819 add_ptr2array(void *p
, void ***parray
)
830 for (; pa
[cnt
]; cnt
++)
834 new_array
= (void **)calloc(cnt
+ 2, sizeof (void *));
835 if (new_array
== NULL
) {
839 /* copy the existing array */
840 for (i
= 0; i
< cnt
; i
++) {
841 new_array
[i
] = pa
[i
];
845 new_array
[i
+ 1] = NULL
;
854 * This function checks to see if a controller has other associations
855 * that may be valid. If we are calling this function, we have found that
856 * a controller for an mpxio device is showing up independently of the
857 * mpxio controller, noted as /scsi_vhci. This can happen with some FC
858 * cards that have inbound management devices that show up as well, with
859 * the real controller data associated. We do not want to display these
860 * 'devices' as real devices in libdiskmgt.
863 remove_controller(controller_t
*cp
, controller_t
*currp
)
869 (void) fprintf(stderr
, "ERROR: removing current"
875 if (cp
->disks
!= NULL
&& cp
->disks
[0] != NULL
) {
877 (void) fprintf(stderr
,
878 "INFO: removing inbound management controller"
879 " with disk ptrs.\n");
882 * loop through the disks and remove the reference to the
883 * controller for this disk structure. The disk itself
884 * is still a valid device, the controller being removed
885 * is a 'path' so any disk that has a reference to it
886 * as a controller needs to have this reference removed.
888 for (i
= 0; cp
->disks
[i
]; i
++) {
889 disk_t
*dp
= cp
->disks
[i
];
892 for (j
= 0; dp
->controllers
[j
]; j
++) {
895 if (libdiskmgt_str_eq(dp
->controllers
[j
]->name
,
899 (void) fprintf(stderr
,
900 "INFO: REMOVING disk %s on "
902 dp
->kernel_name
, cp
->name
);
904 for (k
= j
; dp
->controllers
[k
]; k
++) {
906 dp
->controllers
[k
+ 1];
913 * Paths are removed with the call to cache_free_controller()
917 if (cp
->paths
!= NULL
&& cp
->paths
[0] != NULL
) {
919 (void) fprintf(stderr
,
920 "INFO: removing inbound management controller"
921 " with path ptrs. \n");
924 cache_free_controller(cp
);
928 * If we have a controller in the list that is really a path then we need to
929 * take that controller out of the list since nodes that are paths are not
930 * considered to be controllers.
933 clean_paths(struct search_args
*args
)
937 cp
= args
->controller_listp
;
945 for (i
= 0; pp
[i
]; i
++) {
946 remove_invalid_controller(pp
[i
]->name
, cp
,
955 create_disk(char *deviceid
, char *kernel_name
, struct search_args
*args
)
963 (void) fprintf(stderr
, "INFO: create_disk %s\n", kernel_name
);
966 diskp
= calloc(1, sizeof (disk_t
));
971 diskp
->controllers
= (controller_t
**)
972 calloc(1, sizeof (controller_t
*));
973 if (diskp
->controllers
== NULL
) {
974 cache_free_disk(diskp
);
977 diskp
->controllers
[0] = NULL
;
980 if (deviceid
!= NULL
) {
981 if ((diskp
->device_id
= strdup(deviceid
)) == NULL
) {
982 cache_free_disk(diskp
);
985 (void) devid_str_decode(deviceid
, &(diskp
->devid
), NULL
);
988 if (kernel_name
!= NULL
) {
989 diskp
->kernel_name
= strdup(kernel_name
);
990 if (diskp
->kernel_name
== NULL
) {
991 cache_free_disk(diskp
);
997 diskp
->aliases
= NULL
;
1001 diskp
->solid_state
= -1;
1002 type
= di_minor_nodetype(args
->minor
);
1004 prod_id
= get_str_prop(PROD_ID_PROP
, args
->node
);
1005 if (prod_id
!= NULL
) {
1006 if ((diskp
->product_id
= strdup(prod_id
)) == NULL
) {
1007 cache_free_disk(diskp
);
1011 prod_id
= get_str_prop(PROD_ID_USB_PROP
, args
->node
);
1012 if (prod_id
!= NULL
) {
1013 if ((diskp
->product_id
= strdup(prod_id
)) == NULL
) {
1014 cache_free_disk(diskp
);
1020 vendor_id
= get_str_prop(VENDOR_ID_PROP
, args
->node
);
1021 if (vendor_id
!= NULL
) {
1022 if ((diskp
->vendor_id
= strdup(vendor_id
)) == NULL
) {
1023 cache_free_disk(diskp
);
1027 vendor_id
= get_str_prop(VENDOR_ID_USB_PROP
, args
->node
);
1028 if (vendor_id
!= NULL
) {
1029 if ((diskp
->vendor_id
= strdup(vendor_id
)) == NULL
) {
1030 cache_free_disk(diskp
);
1037 * DVD, CD-ROM, CD-RW, MO, etc. are all reported as CD-ROMS.
1038 * We try to use uscsi later to determine the real type.
1039 * The cd_rom flag tells us that the kernel categorized the drive
1040 * as a CD-ROM. We leave the drv_type as UKNOWN for now.
1041 * The combination of the cd_rom flag being set with the drv_type of
1042 * unknown is what triggers the uscsi probe in drive.c.
1044 if (disk_is_cdrom(type
)) {
1045 diskp
->drv_type
= DM_DT_UNKNOWN
;
1047 diskp
->removable
= 1;
1048 } else if (libdiskmgt_str_eq(type
, DDI_NT_FD
)) {
1049 diskp
->drv_type
= DM_DT_FLOPPY
;
1050 diskp
->removable
= 1;
1052 /* not a "CD-ROM" or Floppy */
1053 diskp
->removable
= get_prop(REMOVABLE_PROP
, args
->node
);
1055 if (diskp
->removable
== -1) {
1056 diskp
->removable
= 0;
1057 #if defined(i386) || defined(__amd64)
1059 * x86 does not have removable property.
1060 * Check for common removable drives, zip & jaz,
1061 * and mark those correctly.
1063 if (vendor_id
!= NULL
&& prod_id
!= NULL
) {
1064 if (str_case_index(vendor_id
,
1065 "iomega") != NULL
) {
1066 if (str_case_index(prod_id
,
1068 diskp
->removable
= 1;
1069 } else if (str_case_index(prod_id
,
1071 diskp
->removable
= 1;
1078 if (diskp
->removable
) {
1080 * For removable jaz or zip drives there is no way
1081 * to get the drive type unless media is inserted,so
1082 * we look at the product-id for a hint.
1084 diskp
->drv_type
= DM_DT_UNKNOWN
;
1086 if (prod_id
!= NULL
) {
1087 if (str_case_index(prod_id
, "jaz") != NULL
) {
1088 diskp
->drv_type
= DM_DT_JAZ
;
1089 } else if (str_case_index(prod_id
,
1091 diskp
->drv_type
= DM_DT_ZIP
;
1095 diskp
->drv_type
= DM_DT_FIXED
;
1099 diskp
->next
= args
->disk_listp
;
1100 args
->disk_listp
= diskp
;
1106 ctype(di_node_t node
, di_minor_t minor
)
1111 type
= di_minor_nodetype(minor
);
1112 name
= di_node_name(node
);
1114 /* IDE disks use SCSI nexus as the type, so handle this special case */
1115 if (libdiskmgt_str_eq(name
, "ide")) {
1116 return (DM_CTYPE_ATA
);
1119 if (libdiskmgt_str_eq(di_minor_name(minor
), "scsa2usb")) {
1120 return (DM_CTYPE_USB
);
1123 if (libdiskmgt_str_eq(type
, DDI_NT_SCSI_NEXUS
) ||
1124 libdiskmgt_str_eq(type
, DDI_NT_SCSI_ATTACHMENT_POINT
)) {
1125 return (DM_CTYPE_SCSI
);
1128 if (libdiskmgt_str_eq(type
, DDI_NT_FC_ATTACHMENT_POINT
)) {
1129 return (DM_CTYPE_FIBRE
);
1132 if (libdiskmgt_str_eq(type
, DDI_NT_NEXUS
) &&
1133 libdiskmgt_str_eq(name
, "fp")) {
1134 return (DM_CTYPE_FIBRE
);
1137 if (libdiskmgt_str_eq(type
, DDI_PSEUDO
) &&
1138 libdiskmgt_str_eq(name
, "ide")) {
1139 return (DM_CTYPE_ATA
);
1142 return (DM_CTYPE_UNKNOWN
);
1146 disk_is_cdrom(const char *type
)
1148 return (strncmp(type
, DDI_NT_CD
, strlen(DDI_NT_CD
)) == 0);
1152 find_alias(disk_t
*diskp
, char *kernel_name
)
1156 ap
= diskp
->aliases
;
1157 while (ap
!= NULL
) {
1158 if (libdiskmgt_str_eq(ap
->kstat_name
, kernel_name
)) {
1168 find_bus(struct search_args
*args
, char *name
)
1172 listp
= args
->bus_listp
;
1173 while (listp
!= NULL
) {
1174 if (libdiskmgt_str_eq(listp
->name
, name
)) {
1177 listp
= listp
->next
;
1183 static controller_t
*
1184 find_controller(struct search_args
*args
, char *name
)
1186 controller_t
*listp
;
1188 listp
= args
->controller_listp
;
1189 while (listp
!= NULL
) {
1190 if (libdiskmgt_str_eq(listp
->name
, name
)) {
1193 listp
= listp
->next
;
1200 fix_cluster_devpath(di_devlink_t devlink
, void *arg
)
1203 struct search_args
*args
;
1205 disk_t
*diskp
= NULL
;
1209 * The devlink_path is of the form /dev/did/rdsk/d1s0.
1212 args
= (struct search_args
*)arg
;
1214 /* Find the disk by the deviceid we read from the cluster disk. */
1215 devlink_path
= (char *)di_devlink_path(devlink
);
1216 if (devlink_path
== NULL
) {
1217 return (DI_WALK_CONTINUE
);
1220 if ((fd
= open(devlink_path
, O_RDONLY
|O_NDELAY
)) >= 0) {
1224 (void) fprintf(stderr
, "INFO: cluster devpath %s\n",
1228 if (devid_get(fd
, &devid
) == 0) {
1232 minor
= di_minor_name(args
->minor
);
1235 devid_str_encode(devid
, minor
)) != NULL
) {
1236 diskp
= get_disk_by_deviceid(args
->disk_listp
,
1239 * This really shouldn't happen, since
1240 * we should have found all of the disks
1241 * during our first pass through
1242 * the dev tree, but just in case...
1244 if (diskp
== NULL
) {
1246 (void) fprintf(stderr
,
1247 "INFO: cluster create"
1251 diskp
= create_disk(devidstr
,
1253 if (diskp
== NULL
) {
1254 args
->dev_walk_status
= ENOMEM
;
1257 /* add the controller relationship */
1258 if (args
->dev_walk_status
== 0) {
1259 if (add_disk2controller(diskp
,
1261 args
->dev_walk_status
1266 if (new_alias(diskp
, NULL
,
1267 devlink_path
, args
) != 0) {
1268 args
->dev_walk_status
= ENOMEM
;
1271 devid_str_free(devidstr
);
1279 if (diskp
!= NULL
) {
1281 (void) fprintf(stderr
, "INFO: cluster found"
1284 ap
= diskp
->aliases
;
1289 * NOTE: if ap->next != NULL have cluster
1290 * disks w/ multiple paths.
1298 char alias
[MAXPATHLEN
];
1301 * First time; save the /dev/rdsk devpaths and
1302 * update the alias info with the new alias name.
1304 ap
->orig_paths
= ap
->devpaths
;
1305 ap
->devpaths
= NULL
;
1309 /* get the new cluster alias name */
1310 basep
= strrchr(devlink_path
, '/');
1311 if (basep
== NULL
) {
1312 basep
= devlink_path
;
1316 size
= sizeof (alias
) - 1;
1319 while (*basep
!= 0 && *basep
!= 's' && cnt
< size
) {
1320 *namep
++ = *basep
++;
1325 if ((ap
->alias
= strdup(alias
)) == NULL
) {
1326 args
->dev_walk_status
= ENOMEM
;
1332 if (new_devpath(ap
, devlink_path
) != 0) {
1333 args
->dev_walk_status
= ENOMEM
;
1337 return (DI_WALK_CONTINUE
);
1341 * Check if we have the drive in our list, based upon the device id.
1342 * We got the device id from the dev tree walk. This is encoded
1343 * using devid_str_encode(3DEVID). In order to check the device ids we need
1344 * to use the devid_compare(3DEVID) function, so we need to decode the
1345 * string representation of the device id.
1348 get_disk_by_deviceid(disk_t
*listp
, char *devidstr
)
1352 if (devidstr
== NULL
|| devid_str_decode(devidstr
, &devid
, NULL
) != 0) {
1356 while (listp
!= NULL
) {
1357 if (listp
->devid
!= NULL
&&
1358 devid_compare(listp
->devid
, devid
) == 0) {
1361 listp
= listp
->next
;
1369 * Get the base disk name with no path prefix and no slice (if there is one).
1370 * The name parameter should be big enough to hold the name.
1371 * This handles diskette names ok (/dev/rdiskette0) since there is no slice,
1372 * and converts the raw diskette name.
1373 * But, we don't know how to strip off the slice from third party drive
1374 * names. That just means that their drive name will include a slice on
1378 get_disk_name_from_path(char *path
, char *name
, int size
)
1383 basep
= strrchr(path
, '/');
1384 if (basep
== NULL
) {
1390 size
= size
- 1; /* leave room for terminating 0 */
1392 if (is_ctds(basep
)) {
1393 while (*basep
!= 0 && *basep
!= 's' && cnt
< size
) {
1399 if (strncmp(basep
, FLOPPY_NAME
,
1400 sizeof (FLOPPY_NAME
) - 1) == 0) {
1402 * a floppy, convert rdiskette name to diskette name,
1403 * by skipping over the 'r' for raw diskette
1408 /* not a ctds name, just copy it */
1409 (void) strlcpy(name
, basep
, size
);
1414 get_byte_prop(char *prop_name
, di_node_t node
)
1419 char str
[MAXPATHLEN
];
1421 cnt
= di_prop_lookup_bytes(DDI_DEV_T_ANY
, node
, prop_name
, &bytes
);
1427 for (i
= 0; i
< cnt
; i
++) {
1428 char bstr
[8]; /* a byte is only 2 hex chars + null */
1430 (void) snprintf(bstr
, sizeof (bstr
), "%.2x", bytes
[i
]);
1431 (void) strlcat(str
, bstr
, sizeof (str
));
1433 return (strdup(str
));
1437 get_parent_bus(di_node_t node
, struct search_args
*args
)
1441 pnode
= di_parent_node(node
);
1442 if (pnode
== DI_NODE_NIL
) {
1446 if (bus_type(pnode
, di_minor_next(pnode
, NULL
), args
->ph
) != NULL
) {
1450 return (get_parent_bus(pnode
, args
));
1454 get_prom_int(char *prop_name
, di_node_t node
, di_prom_handle_t ph
)
1458 if (di_prom_prop_lookup_ints(ph
, node
, prop_name
, &n
) == 1) {
1466 get_prom_str(char *prop_name
, di_node_t node
, di_prom_handle_t ph
)
1470 if (di_prom_prop_lookup_strings(ph
, node
, prop_name
, &str
) == 1) {
1478 * Get one of the positive int or boolean properties.
1481 get_prop(char *prop_name
, di_node_t node
)
1486 if ((num
= di_prop_lookup_ints(DDI_DEV_T_ANY
, node
, prop_name
, &ip
))
1491 } else if (num
== 1) {
1500 get_str_prop(char *prop_name
, di_node_t node
)
1504 if (di_prop_lookup_strings(DDI_DEV_T_ANY
, node
, prop_name
, &str
) == 1) {
1512 * Check if we have the drive in our list, based upon the device id, if the
1513 * drive has a device id, or the kernel name, if it doesn't have a device id.
1516 have_disk(struct search_args
*args
, char *devidstr
, char *kernel_name
,
1522 listp
= args
->disk_listp
;
1523 if (devidstr
!= NULL
) {
1524 if ((*diskp
= get_disk_by_deviceid(listp
, devidstr
)) != NULL
) {
1529 /* no devid, try matching the kernel names on the drives */
1530 while (listp
!= NULL
) {
1531 if (libdiskmgt_str_eq(kernel_name
,
1532 listp
->kernel_name
)) {
1536 listp
= listp
->next
;
1543 bus_type(di_node_t node
, di_minor_t minor
, di_prom_handle_t ph
)
1548 type
= get_prom_str("device_type", node
, ph
);
1550 type
= di_node_name(node
);
1553 for (i
= 0; bustypes
[i
]; i
++) {
1554 if (libdiskmgt_str_eq(type
, bustypes
[i
])) {
1559 if (minor
!= NULL
&& strcmp(di_minor_nodetype(minor
),
1560 DDI_NT_USB_ATTACHMENT_POINT
) == 0) {
1568 is_cluster_disk(di_node_t node
, di_minor_t minor
)
1570 if (di_minor_spectype(minor
) == S_IFCHR
&&
1571 libdiskmgt_str_eq(di_minor_nodetype(minor
), DDI_PSEUDO
) &&
1572 libdiskmgt_str_eq(di_node_name(node
), CLUSTER_DEV
)) {
1580 * If the input name is in c[t]ds format then return 1, otherwise return 0.
1592 /* skip controller digits */
1593 while (isdigit(*p
)) {
1597 /* handle optional target */
1600 /* skip over target */
1601 while (isdigit(*p
) || isupper(*p
)) {
1609 while (isdigit(*p
)) {
1617 /* check the slice number */
1618 while (isdigit(*p
)) {
1630 is_drive(di_minor_t minor
)
1632 return (strncmp(di_minor_nodetype(minor
), DDI_NT_BLOCK
,
1633 strlen(DDI_NT_BLOCK
)) == 0);
1637 is_zvol(di_node_t node
, di_minor_t minor
)
1639 if ((strncmp(di_node_name(node
), ZFS_DRIVER
, 3) == 0) &&
1640 minor(di_minor_devt(minor
)))
1646 is_HBA(di_node_t node
, di_minor_t minor
)
1652 type
= di_minor_nodetype(minor
);
1655 while (ctrltypes
[type_index
] != NULL
) {
1656 if (libdiskmgt_str_eq(type
, ctrltypes
[type_index
])) {
1662 name
= di_node_name(node
);
1663 if (libdiskmgt_str_eq(type
, DDI_PSEUDO
) &&
1664 libdiskmgt_str_eq(name
, "ide")) {
1672 new_alias(disk_t
*diskp
, char *kernel_name
, char *devlink_path
,
1673 struct search_args
*args
)
1676 char alias
[MAXPATHLEN
];
1679 aliasp
= malloc(sizeof (alias_t
));
1680 if (aliasp
== NULL
) {
1684 aliasp
->alias
= NULL
;
1685 aliasp
->kstat_name
= NULL
;
1687 aliasp
->devpaths
= NULL
;
1688 aliasp
->orig_paths
= NULL
;
1690 get_disk_name_from_path(devlink_path
, alias
, sizeof (alias
));
1692 aliasp
->alias
= strdup(alias
);
1693 if (aliasp
->alias
== NULL
) {
1694 cache_free_alias(aliasp
);
1698 if (kernel_name
!= NULL
) {
1699 aliasp
->kstat_name
= strdup(kernel_name
);
1700 if (aliasp
->kstat_name
== NULL
) {
1701 cache_free_alias(aliasp
);
1705 aliasp
->kstat_name
= NULL
;
1708 aliasp
->cluster
= 0;
1709 aliasp
->lun
= get_prop(DM_LUN
, args
->node
);
1710 aliasp
->target
= get_prop(DM_TARGET
, args
->node
);
1711 aliasp
->wwn
= get_byte_prop(WWN_PROP
, args
->node
);
1713 pnode
= di_parent_node(args
->node
);
1714 if (pnode
!= DI_NODE_NIL
) {
1715 char prop_name
[MAXPROPLEN
];
1717 (void) snprintf(prop_name
, sizeof (prop_name
),
1718 "target%d-sync-speed", aliasp
->target
);
1719 diskp
->sync_speed
= get_prop(prop_name
, pnode
);
1720 (void) snprintf(prop_name
, sizeof (prop_name
), "target%d-wide",
1722 diskp
->wide
= get_prop(prop_name
, pnode
);
1725 if (new_devpath(aliasp
, devlink_path
) != 0) {
1726 cache_free_alias(aliasp
);
1730 aliasp
->next
= diskp
->aliases
;
1731 diskp
->aliases
= aliasp
;
1737 * Append the new devpath to the end of the devpath list. This is important
1738 * since we may want to use the order of the devpaths to match up the vtoc
1742 new_devpath(alias_t
*ap
, char *devpath
)
1748 * First, search the alias list to be sure that this devpath is
1749 * not already there.
1752 for (alistp
= ap
->devpaths
; alistp
!= NULL
; alistp
= alistp
->next
) {
1753 if (libdiskmgt_str_eq(alistp
->devpath
, devpath
)) {
1759 * Otherwise, not found so add this new devpath to the list.
1762 newdp
= malloc(sizeof (slice_t
));
1763 if (newdp
== NULL
) {
1767 newdp
->devpath
= strdup(devpath
);
1768 if (newdp
->devpath
== NULL
) {
1772 newdp
->slice_num
= -1;
1775 if (ap
->devpaths
== NULL
) {
1776 ap
->devpaths
= newdp
;
1778 /* append the devpath to the end of the list */
1782 while (dp
->next
!= NULL
) {
1793 new_path(controller_t
*cp
, disk_t
*dp
, di_node_t node
, di_path_state_t st
,
1800 /* Special handling for fp attachment node. */
1801 if (strcmp(di_node_name(node
), "fp") == 0) {
1804 pnode
= di_parent_node(node
);
1805 if (pnode
!= DI_NODE_NIL
) {
1810 devpath
= di_devfs_path(node
);
1812 /* check if the path is already there */
1814 if (cp
->paths
!= NULL
) {
1817 for (i
= 0; cp
->paths
[i
]; i
++) {
1818 if (libdiskmgt_str_eq(devpath
, cp
->paths
[i
]->name
)) {
1826 /* the path exists, add this disk to it */
1828 di_devfs_path_free((void *) devpath
);
1829 if (!add_disk2path(dp
, pp
, st
, wwn
)) {
1835 /* create a new path */
1837 pp
= calloc(1, sizeof (path_t
));
1839 di_devfs_path_free((void *) devpath
);
1843 pp
->name
= strdup(devpath
);
1844 di_devfs_path_free((void *) devpath
);
1845 if (pp
->name
== NULL
) {
1846 cache_free_path(pp
);
1850 /* add the disk to the path */
1851 if (!add_disk2path(dp
, pp
, st
, wwn
)) {
1855 /* add the path to the controller */
1856 if (add_ptr2array(pp
, (void ***)&cp
->paths
) != 0) {
1857 cache_free_path(pp
);
1861 /* add the controller to the path */
1862 pp
->controller
= cp
;
1864 minor
= di_minor_next(node
, NULL
);
1865 if (minor
!= NULL
) {
1866 pp
->ctype
= ctype(node
, minor
);
1868 pp
->ctype
= DM_CTYPE_UNKNOWN
;
1875 * We pass in the current controller pointer (currp) so we can double check
1876 * that we aren't corrupting the list by removing the element we are on. This
1877 * should never happen, but it doesn't hurt to double check.
1880 remove_invalid_controller(char *name
, controller_t
*currp
,
1881 struct search_args
*args
)
1885 controller_t
*prevp
;
1887 bp
= args
->bus_listp
;
1888 while (bp
!= NULL
) {
1891 for (i
= 0; bp
->controllers
[i
]; i
++) {
1892 if (libdiskmgt_str_eq(bp
->controllers
[i
]->name
, name
)) {
1895 * remove pointer to invalid controller.
1898 for (j
= i
; bp
->controllers
[j
]; j
++) {
1899 bp
->controllers
[j
] =
1900 bp
->controllers
[j
+ 1];
1907 if (args
->controller_listp
== NULL
) {
1911 cp
= args
->controller_listp
;
1912 if (libdiskmgt_str_eq(cp
->name
, name
)) {
1913 args
->controller_listp
= cp
->next
;
1915 (void) fprintf(stderr
,
1916 "INFO: Removed controller %s from list\n",
1919 remove_controller(cp
, currp
);
1925 while (cp
!= NULL
) {
1926 if (libdiskmgt_str_eq(cp
->name
, name
)) {
1928 (void) fprintf(stderr
,
1929 "INFO: Removed controller %s from list\n",
1932 prevp
->next
= cp
->next
;
1933 remove_controller(cp
, currp
);
1942 * This is the standard strstr code modified for case independence.
1945 str_case_index(register char *s1
, register char *s2
)
1947 uint_t s2len
= strlen(s2
); /* length of the second string */
1949 /* If the length of the second string is 0, return the first arg. */
1954 while (strlen(s1
) >= s2len
) {
1955 if (strncasecmp(s1
, s2
, s2len
) == 0) {