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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2017, Joyent, Inc.
28 * Functions in this file are shared between the disk and ses enumerators.
30 * A topo_list_t of all disks is returned by a successful disk_list_gather()
31 * call, and the list is freed by a disk_list_free(). To create a 'disk' topo
32 * node below a specific 'bay' parent node either disk_declare_path() or
33 * disk_declare_addr() are called. The caller determines which 'disk' is
34 * in which 'bay'. A disk's 'label' and 'authority' information come from
35 * its parent 'bay' node.
40 #include <libdevinfo.h>
42 #include <sys/libdevid.h>
46 #include <sys/scsi/scsi_types.h>
47 #include <fm/topo_mod.h>
48 #include <fm/topo_list.h>
49 #include <fm/libdiskstatus.h>
50 #include <sys/fm/protocol.h>
51 #include <sys/scsi/generic/inquiry.h>
54 /* common callback information for di_walk_node() and di_devlink_walk */
55 typedef struct disk_cbdata
{
57 topo_list_t
*dcb_list
;
59 di_devlink_handle_t dcb_devhdl
;
60 dev_di_node_t
*dcb_dnode
; /* for di_devlink_walk only */
64 * Given a /devices path for a whole disk, appending this extension gives the
65 * path to a raw device that can be opened.
67 #if defined(__i386) || defined(__amd64)
68 #define PHYS_EXTN ":q,raw"
69 #elif defined(__sparc) || defined(__sparcv9)
70 #define PHYS_EXTN ":c,raw"
72 #error Unknown architecture
76 * Methods for disks. This is used by the disk-transport module to
77 * generate ereports based off SCSI disk status.
79 static int disk_status(topo_mod_t
*, tnode_t
*, topo_version_t
,
80 nvlist_t
*, nvlist_t
**);
82 static const topo_method_t disk_methods
[] = {
83 { TOPO_METH_DISK_STATUS
, TOPO_METH_DISK_STATUS_DESC
,
84 TOPO_METH_DISK_STATUS_VERSION
, TOPO_STABILITY_INTERNAL
,
89 static const topo_pgroup_info_t io_pgroup
= {
91 TOPO_STABILITY_PRIVATE
,
92 TOPO_STABILITY_PRIVATE
,
96 static const topo_pgroup_info_t disk_auth_pgroup
= {
98 TOPO_STABILITY_PRIVATE
,
99 TOPO_STABILITY_PRIVATE
,
103 static const topo_pgroup_info_t storage_pgroup
= {
105 TOPO_STABILITY_PRIVATE
,
106 TOPO_STABILITY_PRIVATE
,
111 * Set the properties of the disk node, from dev_di_node_t data.
112 * Properties include:
113 * group: protocol properties: resource, asru, label, fru
114 * group: authority properties: product-id, chasis-id, server-id
115 * group: io properties: devfs-path, devid
116 * group: storage properties:
117 * - logical-disk, disk-model, disk-manufacturer, serial-number
118 * - firmware-revision, capacity-in-bytes
120 * NOTE: the io and storage groups won't be present if the dnode passed in is
121 * NULL. This happens when a disk is found through ses, but is not enumerated
122 * in the devinfo tree.
125 disk_set_props(topo_mod_t
*mod
, tnode_t
*parent
,
126 tnode_t
*dtn
, dev_di_node_t
*dnode
)
128 nvlist_t
*asru
= NULL
;
130 nvlist_t
*fmri
= NULL
;
133 /* pull the label property down from our parent 'bay' node */
134 if (topo_node_label(parent
, &label
, &err
) != 0) {
135 topo_mod_dprintf(mod
, "disk_set_props: "
136 "label error %s\n", topo_strerror(err
));
139 if (topo_node_label_set(dtn
, label
, &err
) != 0) {
140 topo_mod_dprintf(mod
, "disk_set_props: "
141 "label_set error %s\n", topo_strerror(err
));
145 /* get the resource fmri, and use it as the fru */
146 if (topo_node_resource(dtn
, &fmri
, &err
) != 0) {
147 topo_mod_dprintf(mod
, "disk_set_props: "
148 "resource error: %s\n", topo_strerror(err
));
151 if (topo_node_fru_set(dtn
, fmri
, 0, &err
) != 0) {
152 topo_mod_dprintf(mod
, "disk_set_props: "
153 "fru_set error: %s\n", topo_strerror(err
));
157 /* create/set the authority group */
158 if ((topo_pgroup_create(dtn
, &disk_auth_pgroup
, &err
) != 0) &&
159 (err
!= ETOPO_PROP_DEFD
)) {
160 topo_mod_dprintf(mod
, "disk_set_props: "
161 "create disk_auth error %s\n", topo_strerror(err
));
165 /* create the storage group */
166 if (topo_pgroup_create(dtn
, &storage_pgroup
, &err
) != 0) {
167 topo_mod_dprintf(mod
, "disk_set_props: "
168 "create storage error %s\n", topo_strerror(err
));
172 /* no dnode was found for this disk - skip the io and storage groups */
178 /* form and set the asru */
179 if ((asru
= topo_mod_devfmri(mod
, FM_DEV_SCHEME_VERSION
,
180 dnode
->ddn_dpath
, dnode
->ddn_devid
)) == NULL
) {
181 err
= ETOPO_FMRI_UNKNOWN
;
182 topo_mod_dprintf(mod
, "disk_set_props: "
183 "asru error %s\n", topo_strerror(err
));
186 if (topo_node_asru_set(dtn
, asru
, 0, &err
) != 0) {
187 topo_mod_dprintf(mod
, "disk_set_props: "
188 "asru_set error %s\n", topo_strerror(err
));
192 /* create/set the devfs-path and devid in the io group */
193 if (topo_pgroup_create(dtn
, &io_pgroup
, &err
) != 0) {
194 topo_mod_dprintf(mod
, "disk_set_props: "
195 "create io error %s\n", topo_strerror(err
));
199 if (topo_prop_set_string(dtn
, TOPO_PGROUP_IO
, TOPO_IO_DEV_PATH
,
200 TOPO_PROP_IMMUTABLE
, dnode
->ddn_dpath
, &err
) != 0) {
201 topo_mod_dprintf(mod
, "disk_set_props: "
202 "set dev error %s\n", topo_strerror(err
));
206 if (dnode
->ddn_devid
&& topo_prop_set_string(dtn
, TOPO_PGROUP_IO
,
207 TOPO_IO_DEVID
, TOPO_PROP_IMMUTABLE
, dnode
->ddn_devid
, &err
) != 0) {
208 topo_mod_dprintf(mod
, "disk_set_props: "
209 "set devid error %s\n", topo_strerror(err
));
213 if (dnode
->ddn_ppath_count
!= 0 &&
214 topo_prop_set_string_array(dtn
, TOPO_PGROUP_IO
, TOPO_IO_PHYS_PATH
,
215 TOPO_PROP_IMMUTABLE
, (const char **)dnode
->ddn_ppath
,
216 dnode
->ddn_ppath_count
, &err
) != 0) {
217 topo_mod_dprintf(mod
, "disk_set_props: "
218 "set phys-path error %s\n", topo_strerror(err
));
222 /* set the storage group public /dev name */
223 if (dnode
->ddn_lpath
!= NULL
&&
224 topo_prop_set_string(dtn
, TOPO_PGROUP_STORAGE
,
225 TOPO_STORAGE_LOGICAL_DISK_NAME
, TOPO_PROP_IMMUTABLE
,
226 dnode
->ddn_lpath
, &err
) != 0) {
227 topo_mod_dprintf(mod
, "disk_set_props: "
228 "set disk_name error %s\n", topo_strerror(err
));
232 /* populate other misc storage group properties */
233 if (dnode
->ddn_mfg
&& (topo_prop_set_string(dtn
, TOPO_PGROUP_STORAGE
,
234 TOPO_STORAGE_MANUFACTURER
, TOPO_PROP_IMMUTABLE
,
235 dnode
->ddn_mfg
, &err
) != 0)) {
236 topo_mod_dprintf(mod
, "disk_set_props: "
237 "set mfg error %s\n", topo_strerror(err
));
240 if (dnode
->ddn_model
&& (topo_prop_set_string(dtn
, TOPO_PGROUP_STORAGE
,
241 TOPO_STORAGE_MODEL
, TOPO_PROP_IMMUTABLE
,
242 dnode
->ddn_model
, &err
) != 0)) {
243 topo_mod_dprintf(mod
, "disk_set_props: "
244 "set model error %s\n", topo_strerror(err
));
247 if (dnode
->ddn_serial
&& (topo_prop_set_string(dtn
, TOPO_PGROUP_STORAGE
,
248 TOPO_STORAGE_SERIAL_NUM
, TOPO_PROP_IMMUTABLE
,
249 dnode
->ddn_serial
, &err
) != 0)) {
250 topo_mod_dprintf(mod
, "disk_set_props: "
251 "set serial error %s\n", topo_strerror(err
));
254 if (dnode
->ddn_firm
&& (topo_prop_set_string(dtn
, TOPO_PGROUP_STORAGE
,
255 TOPO_STORAGE_FIRMWARE_REV
, TOPO_PROP_IMMUTABLE
,
256 dnode
->ddn_firm
, &err
) != 0)) {
257 topo_mod_dprintf(mod
, "disk_set_props: "
258 "set firm error %s\n", topo_strerror(err
));
261 if (dnode
->ddn_cap
&& (topo_prop_set_string(dtn
, TOPO_PGROUP_STORAGE
,
262 TOPO_STORAGE_CAPACITY
, TOPO_PROP_IMMUTABLE
,
263 dnode
->ddn_cap
, &err
) != 0)) {
264 topo_mod_dprintf(mod
, "disk_set_props: "
265 "set cap error %s\n", topo_strerror(err
));
273 topo_mod_strfree(mod
, label
);
277 error
: err
= topo_mod_seterrno(mod
, err
);
282 * Trim leading and trailing whitespace from the string.
285 disk_trim_whitespace(topo_mod_t
*mod
, const char *begin
)
294 end
= begin
+ strlen(begin
);
296 while (begin
< end
&& isspace(*begin
))
298 while (begin
< end
&& isspace(*(end
- 1)))
302 if ((buf
= topo_mod_alloc(mod
, count
+ 1)) == NULL
)
305 (void) strlcpy(buf
, begin
, count
+ 1);
311 * Manufacturing strings can contain characters that are invalid for use in hc
312 * authority names. This trims leading and trailing whitespace, and
313 * substitutes any characters known to be bad.
316 disk_auth_clean(topo_mod_t
*mod
, const char *str
)
323 if ((buf
= topo_mod_strdup(mod
, str
)) == NULL
)
326 while ((p
= strpbrk(buf
, " :=")) != NULL
)
332 /* create the disk topo node */
334 disk_tnode_create(topo_mod_t
*mod
, tnode_t
*parent
,
335 dev_di_node_t
*dnode
, const char *name
, topo_instance_t i
, tnode_t
**rval
)
342 char *mfg
, *model
, *firm
, *serial
;
346 mfg
= disk_auth_clean(mod
, dnode
->ddn_mfg
);
347 model
= disk_auth_clean(mod
, dnode
->ddn_model
);
348 firm
= disk_auth_clean(mod
, dnode
->ddn_firm
);
349 serial
= disk_auth_clean(mod
, dnode
->ddn_serial
);
351 mfg
= model
= firm
= serial
= NULL
;
354 /* form 'part=' of fmri as "<mfg>-<model>" */
355 if (mfg
!= NULL
&& model
!= NULL
) {
356 len
= strlen(mfg
) + 1 + strlen(model
) + 1;
357 if ((part
= topo_mod_alloc(mod
, len
)) != NULL
)
358 (void) snprintf(part
, len
, "%s-%s",
362 auth
= topo_mod_auth(mod
, parent
);
363 fmri
= topo_mod_hcfmri(mod
, parent
, FM_HC_SCHEME_VERSION
, name
, i
, NULL
,
364 auth
, part
? part
: model
, firm
, serial
);
367 topo_mod_strfree(mod
, part
);
368 topo_mod_strfree(mod
, mfg
);
369 topo_mod_strfree(mod
, model
);
370 topo_mod_strfree(mod
, firm
);
371 topo_mod_strfree(mod
, serial
);
374 topo_mod_dprintf(mod
, "disk_tnode_create: "
375 "hcfmri (%s%d/%s%d) error %s\n",
376 topo_node_name(parent
), topo_node_instance(parent
),
377 name
, i
, topo_strerror(topo_mod_errno(mod
)));
381 if ((dtn
= topo_node_bind(mod
, parent
, name
, i
, fmri
)) == NULL
) {
382 if (topo_mod_errno(mod
) == EMOD_NODE_BOUND
) {
384 * if disk 0 is already there then we're done
389 topo_mod_dprintf(mod
, "disk_tnode_create: "
390 "bind (%s%d/%s%d) error %s\n",
391 topo_node_name(parent
), topo_node_instance(parent
),
392 name
, i
, topo_strerror(topo_mod_errno(mod
)));
398 /* add the properties of the disk */
399 if (disk_set_props(mod
, parent
, dtn
, dnode
) != 0) {
400 topo_mod_dprintf(mod
, "disk_tnode_create: "
401 "disk_set_props (%s%d/%s%d) error %s\n",
402 topo_node_name(parent
), topo_node_instance(parent
),
403 name
, i
, topo_strerror(topo_mod_errno(mod
)));
404 topo_node_unbind(dtn
);
412 disk_declare(topo_mod_t
*mod
, tnode_t
*parent
, dev_di_node_t
*dnode
,
418 rval
= disk_tnode_create(mod
, parent
, dnode
, DISK
, 0, &dtn
);
422 topo_mod_dprintf(mod
, "disk_declare: "
423 "disk_tnode_create error %s\n",
424 topo_strerror(topo_mod_errno(mod
)));
428 /* register disk_methods against the disk topo node */
429 if (topo_method_register(mod
, dtn
, disk_methods
) != 0) {
430 topo_mod_dprintf(mod
, "disk_declare: "
431 "topo_method_register error %s\n",
432 topo_strerror(topo_mod_errno(mod
)));
433 topo_node_unbind(dtn
);
442 disk_declare_path(topo_mod_t
*mod
, tnode_t
*parent
, topo_list_t
*listp
,
445 dev_di_node_t
*dnode
;
449 * Check for match using physical phci (ddn_ppath). Use
450 * di_devfs_path_match so generic.vs.non-generic names match.
452 for (dnode
= topo_list_next(listp
); dnode
!= NULL
;
453 dnode
= topo_list_next(dnode
)) {
454 if (dnode
->ddn_ppath
== NULL
)
457 for (i
= 0; i
< dnode
->ddn_ppath_count
; i
++) {
458 if (di_devfs_path_match(dnode
->ddn_ppath
[0], path
))
459 return (disk_declare(mod
, parent
, dnode
, NULL
));
463 topo_mod_dprintf(mod
, "disk_declare_path: "
464 "failed to find disk matching path %s", path
);
469 disk_declare_addr(topo_mod_t
*mod
, tnode_t
*parent
, topo_list_t
*listp
,
470 const char *addr
, tnode_t
**childp
)
472 dev_di_node_t
*dnode
;
475 /* Check for match using addr. */
476 for (dnode
= topo_list_next(listp
); dnode
!= NULL
;
477 dnode
= topo_list_next(dnode
)) {
478 if (dnode
->ddn_target_port
== NULL
)
481 for (i
= 0; i
< dnode
->ddn_ppath_count
; i
++) {
482 if ((dnode
->ddn_target_port
[i
] != NULL
) &&
483 (strncmp(dnode
->ddn_target_port
[i
], addr
,
484 strcspn(dnode
->ddn_target_port
[i
], ":"))) == 0) {
485 topo_mod_dprintf(mod
, "disk_declare_addr: "
486 "found disk matching addr %s", addr
);
487 return (disk_declare(mod
, parent
, dnode
,
493 topo_mod_dprintf(mod
, "disk_declare_addr: "
494 "failed to find disk matching addr %s", addr
);
500 * Try to find a disk based on the bridge-port property. This is most often used
501 * for SATA devices which are attached to a SAS controller and are therefore
502 * behind a SATL bridge port. SES only knows of devices based on this SAS WWN,
503 * not based on any SATA GUIDs.
506 disk_declare_bridge(topo_mod_t
*mod
, tnode_t
*parent
, topo_list_t
*listp
,
507 const char *addr
, tnode_t
**childp
)
509 dev_di_node_t
*dnode
;
512 /* Check for match using addr. */
513 for (dnode
= topo_list_next(listp
); dnode
!= NULL
;
514 dnode
= topo_list_next(dnode
)) {
515 if (dnode
->ddn_bridge_port
== NULL
)
518 for (i
= 0; i
< dnode
->ddn_ppath_count
; i
++) {
519 if ((dnode
->ddn_bridge_port
[i
] != NULL
) &&
520 (strncmp(dnode
->ddn_bridge_port
[i
], addr
,
521 strcspn(dnode
->ddn_bridge_port
[i
], ":"))) == 0) {
522 topo_mod_dprintf(mod
, "disk_declare_bridge: "
523 "found disk matching bridge %s", addr
);
524 return (disk_declare(mod
, parent
, dnode
,
530 topo_mod_dprintf(mod
, "disk_declare_bridge: "
531 "failed to find disk matching bridge %s", addr
);
537 * Used to declare a disk that has been discovered through other means (usually
538 * ses), that is not enumerated in the devinfo tree.
541 disk_declare_non_enumerated(topo_mod_t
*mod
, tnode_t
*parent
, tnode_t
**childp
)
543 return (disk_declare(mod
, parent
, NULL
, childp
));
546 /* di_devlink callback for dev_di_node_add */
548 disk_devlink_callback(di_devlink_t dl
, void *arg
)
550 disk_cbdata_t
*cbp
= (disk_cbdata_t
*)arg
;
551 topo_mod_t
*mod
= cbp
->dcb_mod
;
552 dev_di_node_t
*dnode
= cbp
->dcb_dnode
;
556 devpath
= di_devlink_path(dl
);
557 if ((dnode
== NULL
) || (devpath
== NULL
))
558 return (DI_WALK_TERMINATE
);
560 /* trim the slice off the public name */
561 if (((ctds
= strrchr(devpath
, '/')) != NULL
) &&
562 ((slice
= strchr(ctds
, 's')) != NULL
))
565 /* Establish the public /dev name (no slice) */
566 dnode
->ddn_lpath
= topo_mod_strdup(mod
, ctds
? ctds
+ 1 : devpath
);
570 return (DI_WALK_TERMINATE
);
574 dev_di_node_free(topo_mod_t
*mod
, dev_di_node_t
*dnode
)
578 /* free the stuff we point to */
579 if (dnode
->ddn_devid
)
580 topo_mod_strfree(mod
, dnode
->ddn_devid
);
581 for (i
= 0; i
< dnode
->ddn_ppath_count
; i
++) {
582 /* topo_mod_strfree does NULL checking. */
583 topo_mod_strfree(mod
, dnode
->ddn_ppath
[i
]);
584 topo_mod_strfree(mod
, dnode
->ddn_target_port
[i
]);
585 topo_mod_strfree(mod
, dnode
->ddn_attached_port
[i
]);
586 topo_mod_strfree(mod
, dnode
->ddn_bridge_port
[i
]);
588 topo_mod_free(mod
, dnode
->ddn_ppath
,
589 dnode
->ddn_ppath_count
* sizeof (char *));
590 topo_mod_free(mod
, dnode
->ddn_target_port
,
591 dnode
->ddn_ppath_count
* sizeof (char *));
592 topo_mod_free(mod
, dnode
->ddn_attached_port
,
593 dnode
->ddn_ppath_count
* sizeof (char *));
594 topo_mod_free(mod
, dnode
->ddn_bridge_port
,
595 dnode
->ddn_ppath_count
* sizeof (char *));
596 topo_mod_strfree(mod
, dnode
->ddn_dpath
);
597 topo_mod_strfree(mod
, dnode
->ddn_lpath
);
599 topo_mod_strfree(mod
, dnode
->ddn_mfg
);
600 topo_mod_strfree(mod
, dnode
->ddn_model
);
601 topo_mod_strfree(mod
, dnode
->ddn_serial
);
602 topo_mod_strfree(mod
, dnode
->ddn_firm
);
603 topo_mod_strfree(mod
, dnode
->ddn_cap
);
606 topo_mod_free(mod
, dnode
, sizeof (dev_di_node_t
));
610 dev_di_node_add(di_node_t node
, char *devid
, disk_cbdata_t
*cbp
)
612 topo_mod_t
*mod
= cbp
->dcb_mod
;
613 dev_di_node_t
*dnode
;
624 char lentry
[MAXPATHLEN
];
626 int *inq_dtype
, itype
;
631 * Check for list duplicate using devid search.
632 * Note if there is no devid, then we can end up with duplicates
633 * in the list, but this doesn't do any harm.
635 for (dnode
= topo_list_next(cbp
->dcb_list
);
636 dnode
!= NULL
; dnode
= topo_list_next(dnode
)) {
637 if (dnode
->ddn_devid
&&
638 devid_str_compare(dnode
->ddn_devid
, devid
) == 0) {
639 topo_mod_dprintf(mod
, "dev_di_node_add: "
640 "already there %s\n", devid
);
646 if ((dnode
= topo_mod_zalloc(mod
, sizeof (dev_di_node_t
))) == NULL
)
650 /* Establish the devid. */
651 dnode
->ddn_devid
= topo_mod_strdup(mod
, devid
);
652 if (dnode
->ddn_devid
== NULL
)
656 /* Establish the devinfo dpath */
657 if ((path
= di_devfs_path(node
)) == NULL
) {
658 (void) topo_mod_seterrno(mod
, errno
);
662 dnode
->ddn_dpath
= topo_mod_strdup(mod
, path
);
663 di_devfs_path_free(path
);
664 if (dnode
->ddn_dpath
== NULL
)
668 * Establish the physical ppath and target ports. If the device is
669 * non-mpxio then dpath and ppath are the same, and the target port is a
670 * property of the device node.
672 * If dpath is a client node under scsi_vhci, then iterate over all
673 * paths and get their physical paths and target port properrties.
674 * di_path_client_next_path call below will
675 * return non-NULL, and ppath is set to the physical path to the first
678 * NOTE: It is possible to get a generic.vs.non-generic path
679 * for di_devfs_path.vs.di_path_devfs_path like:
680 * xml: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/sd@2,0
681 * pnode: /pci@7b,0/pci1022,7458@11/pci1000,3060@2/disk@2,0
682 * To resolve this issue disk_declare_path() needs to use the
683 * special di_devfs_path_match() interface.
687 while ((pnode
= di_path_client_next_path(node
, pnode
)) != NULL
) {
691 if (pathcount
== 0) {
692 if ((dnode
->ddn_ppath
=
693 topo_mod_zalloc(mod
, sizeof (char *))) == NULL
)
696 dnode
->ddn_ppath_count
= 1;
697 if ((dnode
->ddn_ppath
[0] = topo_mod_strdup(mod
,
698 dnode
->ddn_dpath
)) == NULL
)
701 if ((dnode
->ddn_target_port
= topo_mod_zalloc(mod
,
702 sizeof (char *))) == NULL
)
705 if ((dnode
->ddn_attached_port
= topo_mod_zalloc(mod
,
706 sizeof (char *))) == NULL
)
709 if ((dnode
->ddn_bridge_port
= topo_mod_zalloc(mod
,
710 sizeof (char *))) == NULL
)
713 /* There should be only one target port for a devinfo node. */
714 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, node
,
715 SCSI_ADDR_PROP_TARGET_PORT
, &s
)) == 1) {
716 if ((dnode
->ddn_target_port
[0] =
718 scsi_wwnstr_skip_ua_prefix(s
))) ==
723 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, node
,
724 SCSI_ADDR_PROP_ATTACHED_PORT
, &s
)) == 1) {
725 /* There should be one attached port if any. */
726 if ((dnode
->ddn_attached_port
[0] =
728 scsi_wwnstr_skip_ua_prefix(s
))) ==
733 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, node
,
734 SCSI_ADDR_PROP_BRIDGE_PORT
, &s
)) == 1) {
735 /* There should be one bridge port if any. */
736 if ((dnode
->ddn_bridge_port
[0] =
738 scsi_wwnstr_skip_ua_prefix(s
))) ==
744 /* processing a scsi_vhci device. */
745 if ((dnode
->ddn_ppath
= topo_mod_zalloc(mod
,
746 pathcount
* sizeof (char *))) == NULL
)
749 dnode
->ddn_ppath_count
= pathcount
;
751 if ((dnode
->ddn_target_port
= topo_mod_zalloc(mod
,
752 pathcount
* sizeof (char *))) == NULL
)
755 if ((dnode
->ddn_attached_port
= topo_mod_zalloc(mod
,
756 pathcount
* sizeof (char *))) == NULL
)
759 if ((dnode
->ddn_bridge_port
= topo_mod_zalloc(mod
,
760 pathcount
* sizeof (char *))) == NULL
)
765 while ((pnode
= di_path_client_next_path(node
,
767 if ((path
= di_path_devfs_path(pnode
)) == NULL
) {
768 (void) topo_mod_seterrno(mod
, errno
);
772 dnode
->ddn_ppath
[pathcount
] =
773 topo_mod_strdup(mod
, path
);
774 di_devfs_path_free(path
);
775 if (dnode
->ddn_ppath
[pathcount
] == NULL
)
778 if ((di_path_prop_lookup_strings(pnode
,
779 SCSI_ADDR_PROP_TARGET_PORT
, &s
)) == 1) {
780 if ((dnode
->ddn_target_port
[pathcount
] =
782 scsi_wwnstr_skip_ua_prefix(s
))) ==
787 if ((di_path_prop_lookup_strings(pnode
,
788 SCSI_ADDR_PROP_ATTACHED_PORT
, &s
)) == 1) {
789 if ((dnode
->ddn_attached_port
[pathcount
] =
791 scsi_wwnstr_skip_ua_prefix(s
))) ==
796 if ((di_path_prop_lookup_strings(pnode
,
797 SCSI_ADDR_PROP_BRIDGE_PORT
, &s
)) == 1) {
798 if ((dnode
->ddn_bridge_port
[pathcount
] =
800 scsi_wwnstr_skip_ua_prefix(s
))) ==
810 * Find the public /dev name for a disk by adding a minor name and using
811 * di_devlink interface for reverse translation (use devinfo path).
813 if (di_prop_lookup_ints(DDI_DEV_T_ANY
, node
, "inquiry-device-type",
815 dnode
->ddn_dtype
= *inq_dtype
;
816 itype
= (*inq_dtype
) & DTYPE_MASK
;
817 if (itype
== DTYPE_DIRECT
) {
818 mlen
= strlen(dnode
->ddn_dpath
) + strlen(extn
) + 1;
819 if ((minorpath
= topo_mod_alloc(mod
, mlen
)) == NULL
)
821 (void) snprintf(minorpath
, mlen
, "%s%s",
822 dnode
->ddn_dpath
, extn
);
823 cbp
->dcb_dnode
= dnode
;
824 (void) di_devlink_walk(cbp
->dcb_devhdl
, "^dsk/",
825 minorpath
, DI_PRIMARY_LINK
, cbp
,
826 disk_devlink_callback
);
827 topo_mod_free(mod
, minorpath
, mlen
);
828 if (dnode
->ddn_lpath
== NULL
) {
829 topo_mod_dprintf(mod
, "dev_di_node_add: "
830 "failed to determine logical path");
834 dnode
->ddn_dtype
= DTYPE_UNKNOWN
;
837 /* cache various bits of optional information about the device. */
838 if (di_prop_lookup_strings(DDI_DEV_T_ANY
, node
,
839 INQUIRY_VENDOR_ID
, &s
) > 0) {
840 if ((dnode
->ddn_mfg
= disk_trim_whitespace(mod
, s
)) == NULL
)
843 if (di_prop_lookup_strings(DDI_DEV_T_ANY
, node
,
844 INQUIRY_PRODUCT_ID
, &s
) > 0) {
845 if ((dnode
->ddn_model
= disk_trim_whitespace(mod
, s
)) == NULL
)
848 if (di_prop_lookup_strings(DDI_DEV_T_ANY
, node
,
849 INQUIRY_REVISION_ID
, &s
) > 0) {
850 if ((dnode
->ddn_firm
= disk_trim_whitespace(mod
, s
)) == NULL
)
853 if (di_prop_lookup_strings(DDI_DEV_T_ANY
, node
,
854 INQUIRY_SERIAL_NO
, &s
) > 0) {
855 if ((dnode
->ddn_serial
= disk_trim_whitespace(mod
, s
)) == NULL
)
858 if (di_prop_lookup_int64(DDI_DEV_T_ANY
, node
,
859 "device-nblocks", &nblocksp
) > 0) {
860 nblocks
= (uint64_t)*nblocksp
;
862 * To save kernel memory, the driver may not define
863 * "device-dblksize" when its value is default DEV_BSIZE.
865 if (di_prop_lookup_ints(DDI_DEV_T_ANY
, node
,
866 "device-dblksize", &dblksizep
) > 0)
867 dblksize
= (uint_t
)*dblksizep
;
869 dblksize
= DEV_BSIZE
; /* default value */
870 (void) snprintf(lentry
, sizeof (lentry
),
871 "%" PRIu64
, nblocks
* dblksize
);
872 if ((dnode
->ddn_cap
= topo_mod_strdup(mod
, lentry
)) == NULL
)
876 topo_mod_dprintf(mod
, "dev_di_node_add: "
877 "adding %s\n", devid
? dnode
->ddn_devid
: "NULL devid");
878 topo_mod_dprintf(mod
, " "
879 " %s\n", dnode
->ddn_dpath
);
880 for (i
= 0; i
< dnode
->ddn_ppath_count
; i
++) {
881 topo_mod_dprintf(mod
, " "
882 " %s\n", dnode
->ddn_ppath
[i
]);
884 topo_list_append(cbp
->dcb_list
, dnode
);
888 dev_di_node_free(mod
, dnode
);
892 /* di_walk_node callback for disk_list_gather */
894 dev_walk_di_nodes(di_node_t node
, void *arg
)
896 char *devidstr
= NULL
;
901 * If it's not a scsi_vhci client and doesn't have a target_port
902 * property and doesn't have a target property then it's not a storage
903 * device and we're not interested.
905 if (di_path_client_next_path(node
, NULL
) == NULL
&&
906 di_prop_lookup_strings(DDI_DEV_T_ANY
, node
,
907 SCSI_ADDR_PROP_TARGET_PORT
, &s
) <= 0 &&
908 di_prop_lookup_ints(DDI_DEV_T_ANY
, node
,
909 SCSI_ADDR_PROP_TARGET
, &val
) <= 0) {
910 return (DI_WALK_CONTINUE
);
912 (void) di_prop_lookup_strings(DDI_DEV_T_ANY
, node
,
913 DEVID_PROP_NAME
, &devidstr
);
915 /* create/find the devid scsi topology node */
916 (void) dev_di_node_add(node
, devidstr
, arg
);
918 return (DI_WALK_CONTINUE
);
922 dev_list_gather(topo_mod_t
*mod
, topo_list_t
*listp
)
925 di_devlink_handle_t devhdl
;
928 if ((devtree
= topo_mod_devinfo(mod
)) == DI_NODE_NIL
) {
929 topo_mod_dprintf(mod
, "disk_list_gather: "
930 "topo_mod_devinfo() failed");
934 if ((devhdl
= di_devlink_init(NULL
, 0)) == DI_NODE_NIL
) {
935 topo_mod_dprintf(mod
, "disk_list_gather: "
936 "di_devlink_init() failed");
941 dcb
.dcb_list
= listp
;
942 dcb
.dcb_devhdl
= devhdl
;
944 /* walk the devinfo snapshot looking for disk nodes */
945 (void) di_walk_node(devtree
, DI_WALK_CLDFIRST
, &dcb
,
948 (void) di_devlink_fini(&devhdl
);
954 dev_list_free(topo_mod_t
*mod
, topo_list_t
*listp
)
956 dev_di_node_t
*dnode
;
958 while ((dnode
= topo_list_next(listp
)) != NULL
) {
959 /* order of delete/free is important */
960 topo_list_delete(listp
, dnode
);
961 dev_di_node_free(mod
, dnode
);
966 * Query the current disk status. If successful, the disk status is returned
967 * as an nvlist consisting of at least the following members:
969 * protocol string Supported protocol (currently "scsi")
971 * status nvlist Arbitrary protocol-specific information
972 * about the current state of the disk.
974 * faults nvlist A list of supported faults. Each
975 * element of this list is a boolean value.
976 * An element's existence indicates that
977 * the drive supports detecting this fault,
978 * and the value indicates the current
979 * state of the fault.
981 * <fault-name> nvlist For each fault named in 'faults', a
982 * nvlist describing protocol-specific
983 * attributes of the fault.
985 * This method relies on the libdiskstatus library to query this information.
988 disk_status(topo_mod_t
*mod
, tnode_t
*nodep
, topo_version_t vers
,
989 nvlist_t
*in_nvl
, nvlist_t
**out_nvl
)
992 char *devpath
, *fullpath
;
999 if (vers
!= TOPO_METH_DISK_STATUS_VERSION
)
1000 return (topo_mod_seterrno(mod
, EMOD_VER_NEW
));
1003 * If the caller specifies the "path" parameter, then this indicates
1004 * that we should use this instead of deriving it from the topo node
1007 if (nvlist_lookup_string(in_nvl
, "path", &fullpath
) == 0) {
1011 * Get the /devices path and attempt to open the disk status
1014 if (topo_prop_get_string(nodep
, TOPO_PGROUP_IO
,
1015 TOPO_IO_DEV_PATH
, &devpath
, &err
) != 0)
1016 return (topo_mod_seterrno(mod
, EMOD_METHOD_NOTSUP
));
1019 * Note that sizeof(string) includes the terminating NULL byte
1021 pathlen
= strlen(devpath
) + sizeof ("/devices") +
1022 sizeof (PHYS_EXTN
) - 1;
1024 if ((fullpath
= topo_mod_alloc(mod
, pathlen
)) == NULL
)
1025 return (topo_mod_seterrno(mod
, EMOD_NOMEM
));
1027 (void) snprintf(fullpath
, pathlen
, "/devices%s%s", devpath
,
1030 topo_mod_strfree(mod
, devpath
);
1033 if ((dsp
= disk_status_open(fullpath
, &err
)) == NULL
) {
1035 topo_mod_free(mod
, fullpath
, pathlen
);
1036 return (topo_mod_seterrno(mod
, err
== EDS_NOMEM
?
1037 EMOD_NOMEM
: EMOD_METHOD_NOTSUP
));
1041 topo_mod_free(mod
, fullpath
, pathlen
);
1043 if ((status
= disk_status_get(dsp
)) == NULL
) {
1044 err
= (disk_status_errno(dsp
) == EDS_NOMEM
?
1045 EMOD_NOMEM
: EMOD_METHOD_NOTSUP
);
1046 disk_status_close(dsp
);
1047 return (topo_mod_seterrno(mod
, err
));
1051 disk_status_close(dsp
);