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]
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Copyright (c) 2011 by Delphix. All rights reserved.
29 * Copyright 2017 Nexenta Systems, Inc.
33 #include <libdevinfo.h>
38 #include <sys/sunddi.h>
39 #include <sys/types.h>
40 #include <sys/mkdev.h>
45 #include <sys/fs/zfs.h>
47 #include "libdiskmgt.h"
48 #include "disks_private.h"
50 /* specify which disk links to use in the /dev directory */
51 #define DEVLINK_REGEX "rdsk/.*"
52 #define DEVLINK_FLOPPY_REGEX "rdiskette[0-9]"
54 #define FLOPPY_NAME "rdiskette"
56 #define MAXPROPLEN 1024
57 #define DEVICE_ID_PROP "devid"
58 #define PROD_ID_PROP "inquiry-product-id"
59 #define PROD_ID_USB_PROP "usb-product-name"
60 #define REMOVABLE_PROP "removable-media"
61 #define HOTPLUGGABLE_PROP "hotpluggable"
62 #define SCSI_OPTIONS_PROP "scsi-options"
63 #define VENDOR_ID_PROP "inquiry-vendor-id"
64 #define VENDOR_ID_USB_PROP "usb-vendor-name"
65 #define WWN_PROP "node-wwn"
67 static char *ctrltypes
[] = {
68 DDI_NT_FC_ATTACHMENT_POINT
,
69 DDI_NT_NVME_ATTACHMENT_POINT
,
70 DDI_NT_SATA_ATTACHMENT_POINT
,
72 DDI_NT_SCSI_ATTACHMENT_POINT
,
77 static char *bustypes
[] = {
84 static bus_t
*add_bus(struct search_args
*args
, di_node_t node
,
85 di_minor_t minor
, controller_t
*cp
);
86 static controller_t
*add_controller(struct search_args
*args
,
87 di_node_t node
, di_minor_t minor
);
88 static int add_devpath(di_devlink_t devlink
, void *arg
);
89 static int add_devs(di_node_t node
, di_minor_t minor
, void *arg
);
90 static int add_disk2controller(disk_t
*diskp
,
91 struct search_args
*args
);
92 static int add_disk2path(disk_t
*dp
, path_t
*pp
,
93 di_path_state_t st
, char *wwn
);
94 static int add_int2array(int p
, int **parray
);
95 static int add_ptr2array(void *p
, void ***parray
);
96 static char *bus_type(di_node_t node
, di_minor_t minor
,
98 static void remove_controller(controller_t
*cp
,
100 static void clean_paths(struct search_args
*args
);
101 static disk_t
*create_disk(char *deviceid
, char *kernel_name
,
102 struct search_args
*args
);
103 static char *ctype(di_node_t node
, di_minor_t minor
);
104 static boolean_t
disk_is_cdrom(const char *type
);
105 static alias_t
*find_alias(disk_t
*diskp
, char *kernel_name
);
106 static bus_t
*find_bus(struct search_args
*args
, char *name
);
107 static controller_t
*find_controller(struct search_args
*args
, char *name
);
108 static disk_t
*get_disk_by_deviceid(disk_t
*listp
, char *devid
);
109 static void get_disk_name_from_path(char *path
, char *name
,
111 static char *get_byte_prop(char *prop_name
, di_node_t node
);
112 static di_node_t
get_parent_bus(di_node_t node
,
113 struct search_args
*args
);
114 static int get_prom_int(char *prop_name
, di_node_t node
,
115 di_prom_handle_t ph
);
116 static char *get_prom_str(char *prop_name
, di_node_t node
,
117 di_prom_handle_t ph
);
118 static int get_prop(char *prop_name
, di_node_t node
);
119 static char *get_str_prop(char *prop_name
, di_node_t node
);
120 static int have_disk(struct search_args
*args
, char *devid
,
121 char *kernel_name
, disk_t
**diskp
);
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_ctrl(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
);
135 * The functions in this file do a dev tree walk to build up a model of the
136 * disks, controllers and paths on the system. This model is returned in the
137 * args->disk_listp and args->controller_listp members of the args param.
138 * There is no global data for this file so it is thread safe. It is up to
139 * the caller to merge the resulting model with any existing model that is
140 * cached. The caller must also free the memory for this model when it is
144 findevs(struct search_args
*args
)
148 args
->bus_listp
= NULL
;
149 args
->controller_listp
= NULL
;
150 args
->disk_listp
= NULL
;
152 args
->dev_walk_status
= 0;
153 args
->handle
= di_devlink_init(NULL
, 0);
156 * Have to make several passes at this with the new devfs caching.
157 * First, we find non-mpxio devices. Then we find mpxio/multipath
160 di_root
= di_init("/", DINFOCACHE
);
161 args
->ph
= di_prom_init();
162 (void) di_walk_minor(di_root
, NULL
, 0, args
, add_devs
);
165 di_root
= di_init("/", DINFOCPYALL
|DINFOPATH
);
166 (void) di_walk_minor(di_root
, NULL
, 0, args
, add_devs
);
169 (void) di_devlink_fini(&(args
->handle
));
175 * Definitions of private functions
179 add_bus(struct search_args
*args
, di_node_t node
, di_minor_t minor
,
185 char kstat_name
[MAXPATHLEN
];
188 if (node
== DI_NODE_NIL
) {
192 if ((btype
= bus_type(node
, minor
, args
->ph
)) == NULL
) {
193 return (add_bus(args
, di_parent_node(node
),
194 di_minor_next(di_parent_node(node
), NULL
), cp
));
197 devpath
= di_devfs_path(node
);
199 if ((bp
= find_bus(args
, devpath
)) != NULL
) {
200 di_devfs_path_free((void *) devpath
);
203 if (add_ptr2array(cp
,
204 (void ***)&bp
->controllers
) != 0) {
205 args
->dev_walk_status
= ENOMEM
;
212 /* Special handling for root node. */
213 if (strcmp(devpath
, "/") == 0) {
214 di_devfs_path_free((void *) devpath
);
219 (void) fprintf(stderr
, "INFO: add_bus %s\n", devpath
);
222 bp
= (bus_t
*)calloc(1, sizeof (bus_t
));
227 bp
->name
= strdup(devpath
);
228 di_devfs_path_free((void *) devpath
);
229 if (bp
->name
== NULL
) {
230 args
->dev_walk_status
= ENOMEM
;
235 bp
->btype
= strdup(btype
);
236 if (bp
->btype
== NULL
) {
237 args
->dev_walk_status
= ENOMEM
;
242 (void) snprintf(kstat_name
, sizeof (kstat_name
), "%s%d",
243 di_node_name(node
), di_instance(node
));
245 if ((bp
->kstat_name
= strdup(kstat_name
)) == NULL
) {
246 args
->dev_walk_status
= ENOMEM
;
251 /* if parent node is a bus, get its name */
252 if ((pnode
= get_parent_bus(node
, args
)) != NULL
) {
253 devpath
= di_devfs_path(pnode
);
254 bp
->pname
= strdup(devpath
);
255 di_devfs_path_free((void *) devpath
);
256 if (bp
->pname
== NULL
) {
257 args
->dev_walk_status
= ENOMEM
;
266 bp
->freq
= get_prom_int("clock-frequency", node
, args
->ph
);
268 bp
->controllers
= (controller_t
**)calloc(1, sizeof (controller_t
*));
269 if (bp
->controllers
== NULL
) {
270 args
->dev_walk_status
= ENOMEM
;
274 bp
->controllers
[0] = NULL
;
277 if (add_ptr2array(cp
, (void ***)&bp
->controllers
) != 0) {
278 args
->dev_walk_status
= ENOMEM
;
283 bp
->next
= args
->bus_listp
;
284 args
->bus_listp
= bp
;
289 static controller_t
*
290 add_controller(struct search_args
*args
, di_node_t node
, di_minor_t minor
)
294 char kstat_name
[MAXPATHLEN
];
295 char *c_type
= DM_CTYPE_UNKNOWN
;
297 devpath
= di_devfs_path(node
);
299 if ((cp
= find_controller(args
, devpath
)) != NULL
) {
300 di_devfs_path_free((void *) devpath
);
304 /* Special handling for fp attachment node. */
305 if (strcmp(di_node_name(node
), "fp") == 0) {
308 pnode
= di_parent_node(node
);
309 if (pnode
!= DI_NODE_NIL
) {
310 di_devfs_path_free((void *) devpath
);
311 devpath
= di_devfs_path(pnode
);
313 if ((cp
= find_controller(args
, devpath
)) != NULL
) {
314 di_devfs_path_free((void *) devpath
);
318 /* not in the list, create it */
320 c_type
= DM_CTYPE_FIBRE
;
325 (void) fprintf(stderr
, "INFO: add_controller %s\n", devpath
);
328 cp
= (controller_t
*)calloc(1, sizeof (controller_t
));
333 cp
->name
= strdup(devpath
);
334 di_devfs_path_free((void *) devpath
);
335 if (cp
->name
== NULL
) {
336 cache_free_controller(cp
);
340 if (strcmp(c_type
, DM_CTYPE_UNKNOWN
) == 0) {
341 c_type
= ctype(node
, minor
);
345 (void) snprintf(kstat_name
, sizeof (kstat_name
), "%s%d",
346 di_node_name(node
), di_instance(node
));
348 if ((cp
->kstat_name
= strdup(kstat_name
)) == NULL
) {
349 cache_free_controller(cp
);
353 if (libdiskmgt_str_eq(cp
->ctype
, "scsi")) {
354 cp
->scsi_options
= get_prop(SCSI_OPTIONS_PROP
, node
);
357 if (libdiskmgt_str_eq(di_node_name(node
), "scsi_vhci")) {
363 cp
->freq
= get_prom_int("clock-frequency", node
, args
->ph
);
365 cp
->disks
= (disk_t
**)calloc(1, sizeof (disk_t
*));
366 if (cp
->disks
== NULL
) {
367 cache_free_controller(cp
);
372 cp
->next
= args
->controller_listp
;
373 args
->controller_listp
= cp
;
375 cp
->bus
= add_bus(args
, di_parent_node(node
),
376 di_minor_next(di_parent_node(node
), NULL
), cp
);
382 add_devpath(di_devlink_t devlink
, void *arg
)
384 struct search_args
*args
;
387 char kernel_name
[MAXPATHLEN
];
389 args
= (struct search_args
*)arg
;
392 * Get the diskp value from calling have_disk. Can either be found
393 * by kernel name or devid.
397 devidstr
= get_str_prop(DEVICE_ID_PROP
, args
->node
);
398 (void) snprintf(kernel_name
, sizeof (kernel_name
), "%s%d",
399 di_node_name(args
->node
), di_instance(args
->node
));
401 (void) have_disk(args
, devidstr
, kernel_name
, &diskp
);
404 * The devlink_path is usually of the form /dev/rdsk/c0t0d0s0.
405 * For diskettes it is /dev/rdiskette*.
406 * On Intel we would also get each fdisk partition as well
407 * (e.g. /dev/rdsk/c0t0d0p0).
413 if (diskp
->drv_type
!= DM_DT_FLOPPY
) {
415 * Add other controllers for multipath disks.
416 * This will have no effect if the controller
417 * relationship is already set up.
419 if (add_disk2controller(diskp
, args
) != 0) {
420 args
->dev_walk_status
= ENOMEM
;
424 (void) snprintf(kernel_name
, sizeof (kernel_name
), "%s%d",
425 di_node_name(args
->node
), di_instance(args
->node
));
426 devlink_path
= (char *)di_devlink_path(devlink
);
429 (void) fprintf(stderr
,
430 "INFO: devpath %s\n", devlink_path
);
433 if ((ap
= find_alias(diskp
, kernel_name
)) == NULL
) {
434 if (new_alias(diskp
, kernel_name
, devlink_path
,
436 args
->dev_walk_status
= ENOMEM
;
440 * It is possible that we have already added this
441 * devpath. Do not add it again. new_devpath will
442 * return a 0 if found, and not add the path.
444 if (new_devpath(ap
, devlink_path
) != 0) {
445 args
->dev_walk_status
= ENOMEM
;
450 return (DI_WALK_CONTINUE
);
454 add_devs(di_node_t node
, di_minor_t minor
, void *arg
)
456 struct search_args
*args
;
457 int result
= DI_WALK_CONTINUE
;
459 args
= (struct search_args
*)arg
;
462 /* This is all just debugging code */
464 char dev_name
[MAXPATHLEN
];
466 devpath
= di_devfs_path(node
);
467 (void) snprintf(dev_name
, sizeof (dev_name
), "%s:%s", devpath
,
468 di_minor_name(minor
));
469 di_devfs_path_free((void *) devpath
);
471 (void) fprintf(stderr
,
472 "INFO: dev: %s, node: %s%d, minor: 0x%x, type: %s\n",
473 dev_name
, di_node_name(node
), di_instance(node
),
474 di_minor_spectype(minor
),
475 (di_minor_nodetype(minor
) != NULL
?
476 di_minor_nodetype(minor
) : "NULL"));
479 if (bus_type(node
, minor
, args
->ph
) != NULL
) {
480 if (add_bus(args
, node
, minor
, NULL
) == NULL
) {
481 args
->dev_walk_status
= ENOMEM
;
482 result
= DI_WALK_TERMINATE
;
485 } else if (is_ctrl(node
, minor
)) {
486 if (add_controller(args
, node
, minor
) == NULL
) {
487 args
->dev_walk_status
= ENOMEM
;
488 result
= DI_WALK_TERMINATE
;
491 } else if (di_minor_spectype(minor
) == S_IFCHR
&&
492 (is_drive(minor
) || is_zvol(node
, minor
))) {
494 char kernel_name
[MAXPATHLEN
];
497 (void) snprintf(kernel_name
, sizeof (kernel_name
), "%s%d",
498 di_node_name(node
), di_instance(node
));
499 devidstr
= get_str_prop(DEVICE_ID_PROP
, node
);
504 * Check if we already got this disk and
505 * this is another slice.
507 if (!have_disk(args
, devidstr
, kernel_name
, &diskp
)) {
508 args
->dev_walk_status
= 0;
510 * This is a newly found disk, create the
513 diskp
= create_disk(devidstr
, kernel_name
, args
);
515 args
->dev_walk_status
= ENOMEM
;
518 if (diskp
->drv_type
!= DM_DT_FLOPPY
) {
519 /* add the controller relationship */
520 if (args
->dev_walk_status
== 0) {
521 if (add_disk2controller(diskp
,
523 args
->dev_walk_status
= ENOMEM
;
528 if (is_zvol(node
, minor
)) {
529 char zvdsk
[MAXNAMELEN
];
533 if (di_prop_lookup_strings(di_minor_devt(minor
),
534 node
, "name", &str
) == -1)
535 return (DI_WALK_CONTINUE
);
536 (void) snprintf(zvdsk
, MAXNAMELEN
, "/dev/zvol/rdsk/%s",
538 if ((ap
= find_alias(diskp
, kernel_name
)) == NULL
) {
539 if (new_alias(diskp
, kernel_name
,
541 args
->dev_walk_status
= ENOMEM
;
545 * It is possible that we have already added
547 * Do not add it again. new_devpath will
548 * return a 0 if found, and not add the path.
550 if (new_devpath(ap
, zvdsk
) != 0) {
551 args
->dev_walk_status
= ENOMEM
;
556 /* Add the devpaths for the drive. */
557 if (args
->dev_walk_status
== 0) {
559 char slice_path
[MAXPATHLEN
];
563 * We will come through here once for each of
564 * the raw slice device names.
566 devpath
= di_devfs_path(node
);
567 (void) snprintf(slice_path
,
568 sizeof (slice_path
), "%s:%s",
569 devpath
, di_minor_name(minor
));
570 di_devfs_path_free((void *) devpath
);
572 if (libdiskmgt_str_eq(di_minor_nodetype(minor
),
574 pattern
= DEVLINK_FLOPPY_REGEX
;
576 pattern
= DEVLINK_REGEX
;
579 /* Walk the /dev tree to get the devlinks. */
580 (void) di_devlink_walk(args
->handle
, pattern
,
581 slice_path
, DI_PRIMARY_LINK
, arg
, add_devpath
);
584 if (args
->dev_walk_status
!= 0) {
585 result
= DI_WALK_TERMINATE
;
593 add_disk2controller(disk_t
*diskp
, struct search_args
*args
)
603 pnode
= di_parent_node(node
);
604 if (pnode
== DI_NODE_NIL
) {
608 minor
= di_minor_next(pnode
, NULL
);
613 if ((cp
= add_controller(args
, pnode
, minor
)) == NULL
) {
617 /* check if the disk <-> ctrl assoc is already there */
618 for (i
= 0; diskp
->controllers
[i
]; i
++) {
619 if (cp
== diskp
->controllers
[i
]) {
624 /* this is a new controller for this disk */
626 /* add the disk to the controller */
627 if (add_ptr2array(diskp
, (void ***)&cp
->disks
) != 0) {
631 /* add the controller to the disk */
632 if (add_ptr2array(cp
, (void ***)&diskp
->controllers
) != 0) {
637 * Set up paths for mpxio controlled drives.
639 if (libdiskmgt_str_eq(di_node_name(pnode
), "scsi_vhci")) {
640 /* note: mpxio di_path stuff is all consolidation private */
641 di_path_t pi
= DI_PATH_NIL
;
644 (pi
= di_path_client_next_path(node
, pi
)) != DI_PATH_NIL
) {
647 char str
[MAXPATHLEN
];
650 di_node_t phci_node
= di_path_phci_node(pi
);
652 /* get the node wwn */
653 cnt
= di_path_prop_lookup_bytes(pi
, WWN_PROP
, &bytes
);
659 for (i
= 0; i
< cnt
; i
++) {
661 * A byte is only 2 hex chars + null.
665 (void) snprintf(bstr
,
666 sizeof (bstr
), "%.2x", bytes
[i
]);
667 (void) strlcat(str
, bstr
, sizeof (str
));
672 if (new_path(cp
, diskp
, phci_node
,
673 di_path_state(pi
), wwn
) == NULL
) {
683 add_disk2path(disk_t
*dp
, path_t
*pp
, di_path_state_t st
, char *wwn
)
685 /* add the disk to the path */
686 if (add_ptr2array(dp
, (void ***)&pp
->disks
) != 0) {
691 /* add the path to the disk */
692 if (add_ptr2array(pp
, (void ***)&dp
->paths
) != 0) {
697 /* add the path state for this disk */
698 if (add_int2array(st
, &pp
->states
) != 0) {
703 /* add the path state for this disk */
707 if ((wp
= strdup(wwn
)) != NULL
) {
708 if (add_ptr2array(wp
, (void ***)(&pp
->wwns
)) != 0) {
719 add_int2array(int p
, int **parray
)
730 for (; pa
[cnt
] != -1; cnt
++)
734 new_array
= (int *)calloc(cnt
+ 2, sizeof (int *));
735 if (new_array
== NULL
) {
739 /* copy the existing array */
740 for (i
= 0; i
< cnt
; i
++) {
741 new_array
[i
] = pa
[i
];
745 new_array
[i
+ 1] = -1;
754 add_ptr2array(void *p
, void ***parray
)
765 for (; pa
[cnt
]; cnt
++)
769 new_array
= (void **)calloc(cnt
+ 2, sizeof (void *));
770 if (new_array
== NULL
) {
774 /* copy the existing array */
775 for (i
= 0; i
< cnt
; i
++) {
776 new_array
[i
] = pa
[i
];
780 new_array
[i
+ 1] = NULL
;
789 * This function checks to see if a controller has other associations
790 * that may be valid. If we are calling this function, we have found that
791 * a controller for an mpxio device is showing up independently of the
792 * mpxio controller, noted as /scsi_vhci. This can happen with some FC
793 * cards that have inbound management devices that show up as well, with
794 * the real controller data associated. We do not want to display these
795 * 'devices' as real devices in libdiskmgt.
798 remove_controller(controller_t
*cp
, controller_t
*currp
)
804 (void) fprintf(stderr
, "ERROR: removing current"
810 if (cp
->disks
!= NULL
&& cp
->disks
[0] != NULL
) {
812 (void) fprintf(stderr
,
813 "INFO: removing inbound management controller"
814 " with disk ptrs.\n");
817 * loop through the disks and remove the reference to the
818 * controller for this disk structure. The disk itself
819 * is still a valid device, the controller being removed
820 * is a 'path' so any disk that has a reference to it
821 * as a controller needs to have this reference removed.
823 for (i
= 0; cp
->disks
[i
]; i
++) {
824 disk_t
*dp
= cp
->disks
[i
];
827 for (j
= 0; dp
->controllers
[j
]; j
++) {
830 if (libdiskmgt_str_eq(dp
->controllers
[j
]->name
,
834 (void) fprintf(stderr
,
835 "INFO: REMOVING disk %s on "
837 dp
->kernel_name
, cp
->name
);
839 for (k
= j
; dp
->controllers
[k
]; k
++) {
841 dp
->controllers
[k
+ 1];
848 * Paths are removed with the call to cache_free_controller()
852 if (cp
->paths
!= NULL
&& cp
->paths
[0] != NULL
) {
854 (void) fprintf(stderr
,
855 "INFO: removing inbound management controller"
856 " with path ptrs. \n");
859 cache_free_controller(cp
);
863 * If we have a controller in the list that is really a path then we need to
864 * take that controller out of the list since nodes that are paths are not
865 * considered to be controllers.
868 clean_paths(struct search_args
*args
)
872 cp
= args
->controller_listp
;
880 for (i
= 0; pp
[i
]; i
++) {
881 remove_invalid_controller(pp
[i
]->name
, cp
,
890 create_disk(char *deviceid
, char *kernel_name
, struct search_args
*args
)
898 (void) fprintf(stderr
, "INFO: create_disk %s\n", kernel_name
);
901 diskp
= calloc(1, sizeof (disk_t
));
906 diskp
->controllers
= (controller_t
**)
907 calloc(1, sizeof (controller_t
*));
908 if (diskp
->controllers
== NULL
) {
909 cache_free_disk(diskp
);
912 diskp
->controllers
[0] = NULL
;
915 if (deviceid
!= NULL
) {
916 if ((diskp
->device_id
= strdup(deviceid
)) == NULL
) {
917 cache_free_disk(diskp
);
920 (void) devid_str_decode(deviceid
, &(diskp
->devid
), NULL
);
923 if (kernel_name
!= NULL
) {
924 diskp
->kernel_name
= strdup(kernel_name
);
925 if (diskp
->kernel_name
== NULL
) {
926 cache_free_disk(diskp
);
932 diskp
->aliases
= NULL
;
936 diskp
->solid_state
= -1;
937 type
= di_minor_nodetype(args
->minor
);
939 prod_id
= get_str_prop(PROD_ID_PROP
, args
->node
);
940 if (prod_id
!= NULL
) {
941 if ((diskp
->product_id
= strdup(prod_id
)) == NULL
) {
942 cache_free_disk(diskp
);
946 prod_id
= get_str_prop(PROD_ID_USB_PROP
, args
->node
);
947 if (prod_id
!= NULL
) {
948 if ((diskp
->product_id
= strdup(prod_id
)) == NULL
) {
949 cache_free_disk(diskp
);
955 vendor_id
= get_str_prop(VENDOR_ID_PROP
, args
->node
);
956 if (vendor_id
!= NULL
) {
957 if ((diskp
->vendor_id
= strdup(vendor_id
)) == NULL
) {
958 cache_free_disk(diskp
);
962 vendor_id
= get_str_prop(VENDOR_ID_USB_PROP
, args
->node
);
963 if (vendor_id
!= NULL
) {
964 if ((diskp
->vendor_id
= strdup(vendor_id
)) == NULL
) {
965 cache_free_disk(diskp
);
972 * DVD, CD-ROM, CD-RW, MO, etc. are all reported as CD-ROMS.
973 * We try to use uscsi later to determine the real type.
974 * The cd_rom flag tells us that the kernel categorized the drive
975 * as a CD-ROM. We leave the drv_type as UNKNOWN for now.
976 * The combination of the cd_rom flag being set with the drv_type of
977 * unknown is what triggers the uscsi probe in drive.c.
979 if (disk_is_cdrom(type
)) {
980 diskp
->drv_type
= DM_DT_UNKNOWN
;
982 diskp
->removable
= 1;
983 } else if (libdiskmgt_str_eq(type
, DDI_NT_FD
)) {
984 diskp
->drv_type
= DM_DT_FLOPPY
;
985 diskp
->removable
= 1;
987 /* not a CD-ROM or Floppy */
988 diskp
->removable
= get_prop(REMOVABLE_PROP
, args
->node
);
990 if (diskp
->removable
== -1) {
991 diskp
->removable
= 0;
992 diskp
->drv_type
= DM_DT_FIXED
;
996 diskp
->next
= args
->disk_listp
;
997 args
->disk_listp
= diskp
;
1003 ctype(di_node_t node
, di_minor_t minor
)
1008 type
= di_minor_nodetype(minor
);
1009 name
= di_node_name(node
);
1011 /* IDE disks use SCSI nexus as the type, so handle this special case */
1012 if ((libdiskmgt_str_eq(type
, DDI_NT_SCSI_NEXUS
) ||
1013 libdiskmgt_str_eq(type
, DDI_PSEUDO
)) &&
1014 libdiskmgt_str_eq(name
, "ide"))
1015 return (DM_CTYPE_ATA
);
1017 if (libdiskmgt_str_eq(type
, DDI_NT_FC_ATTACHMENT_POINT
) ||
1018 (libdiskmgt_str_eq(type
, DDI_NT_NEXUS
) &&
1019 libdiskmgt_str_eq(name
, "fp")))
1020 return (DM_CTYPE_FIBRE
);
1022 if (libdiskmgt_str_eq(type
, DDI_NT_NVME_ATTACHMENT_POINT
))
1023 return (DM_CTYPE_NVME
);
1025 if (libdiskmgt_str_eq(type
, DDI_NT_SATA_NEXUS
) ||
1026 libdiskmgt_str_eq(type
, DDI_NT_SATA_ATTACHMENT_POINT
))
1027 return (DM_CTYPE_SATA
);
1029 if (libdiskmgt_str_eq(type
, DDI_NT_SCSI_NEXUS
) ||
1030 libdiskmgt_str_eq(type
, DDI_NT_SCSI_ATTACHMENT_POINT
))
1031 return (DM_CTYPE_SCSI
);
1033 if (libdiskmgt_str_eq(di_minor_name(minor
), "scsa2usb"))
1034 return (DM_CTYPE_USB
);
1036 if (libdiskmgt_str_eq(type
, DDI_PSEUDO
) &&
1037 libdiskmgt_str_eq(name
, "xpvd"))
1038 return (DM_CTYPE_XEN
);
1041 (void) fprintf(stderr
,
1042 "INFO: unknown controller type=%s name=%s\n", type
, name
);
1045 return (DM_CTYPE_UNKNOWN
);
1049 disk_is_cdrom(const char *type
)
1051 return (strncmp(type
, DDI_NT_CD
, strlen(DDI_NT_CD
)) == 0);
1055 find_alias(disk_t
*diskp
, char *kernel_name
)
1059 ap
= diskp
->aliases
;
1060 while (ap
!= NULL
) {
1061 if (libdiskmgt_str_eq(ap
->kstat_name
, kernel_name
)) {
1071 find_bus(struct search_args
*args
, char *name
)
1075 listp
= args
->bus_listp
;
1076 while (listp
!= NULL
) {
1077 if (libdiskmgt_str_eq(listp
->name
, name
)) {
1080 listp
= listp
->next
;
1086 static controller_t
*
1087 find_controller(struct search_args
*args
, char *name
)
1089 controller_t
*listp
;
1091 listp
= args
->controller_listp
;
1092 while (listp
!= NULL
) {
1093 if (libdiskmgt_str_eq(listp
->name
, name
)) {
1096 listp
= listp
->next
;
1103 * Check if we have the drive in our list, based upon the device id.
1104 * We got the device id from the dev tree walk. This is encoded
1105 * using devid_str_encode(3DEVID). In order to check the device ids we need
1106 * to use the devid_compare(3DEVID) function, so we need to decode the
1107 * string representation of the device id.
1110 get_disk_by_deviceid(disk_t
*listp
, char *devidstr
)
1114 if (devidstr
== NULL
|| devid_str_decode(devidstr
, &devid
, NULL
) != 0) {
1118 while (listp
!= NULL
) {
1119 if (listp
->devid
!= NULL
&&
1120 devid_compare(listp
->devid
, devid
) == 0) {
1123 listp
= listp
->next
;
1131 * Get the base disk name with no path prefix and no slice (if there is one).
1132 * The name parameter should be big enough to hold the name.
1133 * This handles diskette names ok (/dev/rdiskette0) since there is no slice,
1134 * and converts the raw diskette name.
1135 * But, we don't know how to strip off the slice from third party drive
1136 * names. That just means that their drive name will include a slice on
1140 get_disk_name_from_path(char *path
, char *name
, int size
)
1145 basep
= strrchr(path
, '/');
1146 if (basep
== NULL
) {
1152 size
= size
- 1; /* leave room for terminating 0 */
1154 if (is_ctds(basep
)) {
1155 while (*basep
!= 0 && *basep
!= 's' && cnt
< size
) {
1161 if (strncmp(basep
, FLOPPY_NAME
,
1162 sizeof (FLOPPY_NAME
) - 1) == 0) {
1164 * a floppy, convert rdiskette name to diskette name,
1165 * by skipping over the 'r' for raw diskette
1170 /* not a ctds name, just copy it */
1171 (void) strlcpy(name
, basep
, size
);
1176 get_byte_prop(char *prop_name
, di_node_t node
)
1181 char str
[MAXPATHLEN
];
1183 cnt
= di_prop_lookup_bytes(DDI_DEV_T_ANY
, node
, prop_name
, &bytes
);
1189 for (i
= 0; i
< cnt
; i
++) {
1190 char bstr
[8]; /* a byte is only 2 hex chars + null */
1192 (void) snprintf(bstr
, sizeof (bstr
), "%.2x", bytes
[i
]);
1193 (void) strlcat(str
, bstr
, sizeof (str
));
1195 return (strdup(str
));
1199 get_parent_bus(di_node_t node
, struct search_args
*args
)
1203 pnode
= di_parent_node(node
);
1204 if (pnode
== DI_NODE_NIL
) {
1208 if (bus_type(pnode
, di_minor_next(pnode
, NULL
), args
->ph
) != NULL
) {
1212 return (get_parent_bus(pnode
, args
));
1216 get_prom_int(char *prop_name
, di_node_t node
, di_prom_handle_t ph
)
1220 if (di_prom_prop_lookup_ints(ph
, node
, prop_name
, &n
) == 1) {
1228 get_prom_str(char *prop_name
, di_node_t node
, di_prom_handle_t ph
)
1232 if (di_prom_prop_lookup_strings(ph
, node
, prop_name
, &str
) == 1) {
1240 * Get one of the positive int or boolean properties.
1243 get_prop(char *prop_name
, di_node_t node
)
1248 if ((num
= di_prop_lookup_ints(DDI_DEV_T_ANY
, node
, prop_name
, &ip
))
1253 } else if (num
== 1) {
1262 get_str_prop(char *prop_name
, di_node_t node
)
1266 if (di_prop_lookup_strings(DDI_DEV_T_ANY
, node
, prop_name
, &str
) == 1) {
1274 * Check if we have the drive in our list, based upon the device id, if the
1275 * drive has a device id, or the kernel name, if it doesn't have a device id.
1278 have_disk(struct search_args
*args
, char *devidstr
, char *kernel_name
,
1284 listp
= args
->disk_listp
;
1285 if (devidstr
!= NULL
) {
1286 if ((*diskp
= get_disk_by_deviceid(listp
, devidstr
)) != NULL
) {
1291 /* no devid, try matching the kernel names on the drives */
1292 while (listp
!= NULL
) {
1293 if (libdiskmgt_str_eq(kernel_name
,
1294 listp
->kernel_name
)) {
1298 listp
= listp
->next
;
1305 bus_type(di_node_t node
, di_minor_t minor
, di_prom_handle_t ph
)
1310 type
= get_prom_str("device_type", node
, ph
);
1312 type
= di_node_name(node
);
1315 for (i
= 0; bustypes
[i
]; i
++) {
1316 if (libdiskmgt_str_eq(type
, bustypes
[i
])) {
1321 if (minor
!= NULL
&& strcmp(di_minor_nodetype(minor
),
1322 DDI_NT_USB_ATTACHMENT_POINT
) == 0) {
1330 * If the input name is in c[t]ds format then return 1, otherwise return 0.
1342 /* skip controller digits */
1343 while (isdigit(*p
)) {
1347 /* handle optional target */
1350 /* skip over target */
1351 while (isdigit(*p
) || isupper(*p
)) {
1359 while (isdigit(*p
)) {
1367 /* check the slice number */
1368 while (isdigit(*p
)) {
1380 is_drive(di_minor_t minor
)
1382 return (strncmp(di_minor_nodetype(minor
), DDI_NT_BLOCK
,
1383 strlen(DDI_NT_BLOCK
)) == 0);
1387 is_zvol(di_node_t node
, di_minor_t minor
)
1389 if ((strncmp(di_node_name(node
), ZFS_DRIVER
, 3) == 0) &&
1390 minor(di_minor_devt(minor
)))
1396 is_ctrl(di_node_t node
, di_minor_t minor
)
1402 type
= di_minor_nodetype(minor
);
1405 while (ctrltypes
[type_index
] != NULL
) {
1406 if (libdiskmgt_str_eq(type
, ctrltypes
[type_index
])) {
1412 name
= di_node_name(node
);
1413 if (libdiskmgt_str_eq(type
, DDI_PSEUDO
) &&
1414 (libdiskmgt_str_eq(name
, "ide") ||
1415 libdiskmgt_str_eq(name
, "xpvd")))
1422 new_alias(disk_t
*diskp
, char *kernel_name
, char *devlink_path
,
1423 struct search_args
*args
)
1426 char alias
[MAXPATHLEN
];
1429 aliasp
= malloc(sizeof (alias_t
));
1430 if (aliasp
== NULL
) {
1434 aliasp
->alias
= NULL
;
1435 aliasp
->kstat_name
= NULL
;
1437 aliasp
->devpaths
= NULL
;
1438 aliasp
->orig_paths
= NULL
;
1440 get_disk_name_from_path(devlink_path
, alias
, sizeof (alias
));
1442 aliasp
->alias
= strdup(alias
);
1443 if (aliasp
->alias
== NULL
) {
1444 cache_free_alias(aliasp
);
1448 if (kernel_name
!= NULL
) {
1449 aliasp
->kstat_name
= strdup(kernel_name
);
1450 if (aliasp
->kstat_name
== NULL
) {
1451 cache_free_alias(aliasp
);
1455 aliasp
->kstat_name
= NULL
;
1458 aliasp
->lun
= get_prop(DM_LUN
, args
->node
);
1459 aliasp
->target
= get_prop(DM_TARGET
, args
->node
);
1460 aliasp
->wwn
= get_byte_prop(WWN_PROP
, args
->node
);
1462 pnode
= di_parent_node(args
->node
);
1463 if (pnode
!= DI_NODE_NIL
) {
1464 char prop_name
[MAXPROPLEN
];
1466 (void) snprintf(prop_name
, sizeof (prop_name
),
1467 "target%d-sync-speed", aliasp
->target
);
1468 diskp
->sync_speed
= get_prop(prop_name
, pnode
);
1469 (void) snprintf(prop_name
, sizeof (prop_name
), "target%d-wide",
1471 diskp
->wide
= get_prop(prop_name
, pnode
);
1474 if (new_devpath(aliasp
, devlink_path
) != 0) {
1475 cache_free_alias(aliasp
);
1479 aliasp
->next
= diskp
->aliases
;
1480 diskp
->aliases
= aliasp
;
1486 * Append the new devpath to the end of the devpath list. This is important
1487 * since we may want to use the order of the devpaths to match up the vtoc
1491 new_devpath(alias_t
*ap
, char *devpath
)
1497 * First, search the alias list to be sure that this devpath is
1498 * not already there.
1501 for (alistp
= ap
->devpaths
; alistp
!= NULL
; alistp
= alistp
->next
) {
1502 if (libdiskmgt_str_eq(alistp
->devpath
, devpath
)) {
1508 * Otherwise, not found so add this new devpath to the list.
1511 newdp
= malloc(sizeof (slice_t
));
1512 if (newdp
== NULL
) {
1516 newdp
->devpath
= strdup(devpath
);
1517 if (newdp
->devpath
== NULL
) {
1521 newdp
->slice_num
= -1;
1524 if (ap
->devpaths
== NULL
) {
1525 ap
->devpaths
= newdp
;
1527 /* append the devpath to the end of the list */
1531 while (dp
->next
!= NULL
) {
1542 new_path(controller_t
*cp
, disk_t
*dp
, di_node_t node
, di_path_state_t st
,
1549 /* Special handling for fp attachment node. */
1550 if (strcmp(di_node_name(node
), "fp") == 0) {
1553 pnode
= di_parent_node(node
);
1554 if (pnode
!= DI_NODE_NIL
) {
1559 devpath
= di_devfs_path(node
);
1561 /* check if the path is already there */
1563 if (cp
->paths
!= NULL
) {
1566 for (i
= 0; cp
->paths
[i
]; i
++) {
1567 if (libdiskmgt_str_eq(devpath
, cp
->paths
[i
]->name
)) {
1575 /* the path exists, add this disk to it */
1577 di_devfs_path_free((void *) devpath
);
1578 if (!add_disk2path(dp
, pp
, st
, wwn
)) {
1584 /* create a new path */
1586 pp
= calloc(1, sizeof (path_t
));
1588 di_devfs_path_free((void *) devpath
);
1592 pp
->name
= strdup(devpath
);
1593 di_devfs_path_free((void *) devpath
);
1594 if (pp
->name
== NULL
) {
1595 cache_free_path(pp
);
1599 /* add the disk to the path */
1600 if (!add_disk2path(dp
, pp
, st
, wwn
)) {
1604 /* add the path to the controller */
1605 if (add_ptr2array(pp
, (void ***)&cp
->paths
) != 0) {
1606 cache_free_path(pp
);
1610 /* add the controller to the path */
1611 pp
->controller
= cp
;
1613 minor
= di_minor_next(node
, NULL
);
1614 if (minor
!= NULL
) {
1615 pp
->ctype
= ctype(node
, minor
);
1617 pp
->ctype
= DM_CTYPE_UNKNOWN
;
1624 * We pass in the current controller pointer (currp) so we can double check
1625 * that we aren't corrupting the list by removing the element we are on. This
1626 * should never happen, but it doesn't hurt to double check.
1629 remove_invalid_controller(char *name
, controller_t
*currp
,
1630 struct search_args
*args
)
1634 controller_t
*prevp
;
1636 bp
= args
->bus_listp
;
1637 while (bp
!= NULL
) {
1640 for (i
= 0; bp
->controllers
[i
]; i
++) {
1641 if (libdiskmgt_str_eq(bp
->controllers
[i
]->name
, name
)) {
1644 * remove pointer to invalid controller.
1647 for (j
= i
; bp
->controllers
[j
]; j
++) {
1648 bp
->controllers
[j
] =
1649 bp
->controllers
[j
+ 1];
1656 if (args
->controller_listp
== NULL
) {
1660 cp
= args
->controller_listp
;
1661 if (libdiskmgt_str_eq(cp
->name
, name
)) {
1662 args
->controller_listp
= cp
->next
;
1664 (void) fprintf(stderr
,
1665 "INFO: Removed controller %s from list\n",
1668 remove_controller(cp
, currp
);
1674 while (cp
!= NULL
) {
1675 if (libdiskmgt_str_eq(cp
->name
, name
)) {
1677 (void) fprintf(stderr
,
1678 "INFO: Removed controller %s from list\n",
1681 prevp
->next
= cp
->next
;
1682 remove_controller(cp
, currp
);