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 2012 Milan Jurik. All rights reserved.
25 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
26 * Copyright (c) 2017, Joyent, Inc.
32 #include <fm/libdiskstatus.h>
39 #include <sys/fm/protocol.h>
40 #include <sys/libdevid.h>
41 #include <sys/scsi/scsi_types.h>
42 #include <sys/byteorder.h>
47 #include <libcontract.h>
49 #include <sys/contract/device.h>
50 #include <libsysevent.h>
51 #include <sys/sysevent/eventdefs.h>
52 #include <scsi/plugins/ses/vendor/sun.h>
59 #define SES_STARTING_SUBCHASSIS 256 /* valid subchassis IDs are uint8_t */
60 #define NO_SUBCHASSIS ((uint64_t)-1)
62 static int ses_snap_freq
= 250; /* in milliseconds */
64 #define SES_STATUS_UNAVAIL(s) \
65 ((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED)
67 #define HR_SECOND 1000000000
70 * Because multiple SES targets can be part of a single chassis, we construct
71 * our own hierarchy that takes this into account. These SES targets may refer
72 * to the same devices (multiple paths) or to different devices (managing
73 * different portions of the space). We arrange things into a
74 * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all
77 typedef struct ses_alt_node
{
82 typedef struct ses_enum_node
{
85 topo_list_t sen_alt_nodes
;
87 uint64_t sen_instance
;
88 ses_enum_target_t
*sen_target
;
91 typedef struct ses_enum_chassis
{
93 topo_list_t sec_subchassis
;
94 topo_list_t sec_nodes
;
95 topo_list_t sec_targets
;
97 ses_node_t
*sec_enclosure
;
98 ses_enum_target_t
*sec_target
;
99 topo_instance_t sec_instance
;
100 topo_instance_t sec_scinstance
;
101 topo_instance_t sec_maxinstance
;
102 boolean_t sec_hasdev
;
103 boolean_t sec_internal
;
104 } ses_enum_chassis_t
;
106 typedef struct ses_enum_data
{
107 topo_list_t sed_devs
;
108 topo_list_t sed_chassis
;
109 ses_enum_chassis_t
*sed_current
;
110 ses_enum_target_t
*sed_target
;
114 topo_instance_t sed_instance
;
117 typedef struct sas_connector_phy_data
{
120 } sas_connector_phy_data_t
;
122 typedef struct sas_connector_type
{
125 } sas_connector_type_t
;
127 static const sas_connector_type_t sas_connector_type_list
[] = {
128 { 0x0, "Information unknown" },
129 { 0x1, "External SAS 4x receptacle (see SAS-2 and SFF-8470)" },
130 { 0x2, "Exteranl Mini SAS 4x receptacle (see SAS-2 and SFF-8088)" },
131 { 0x3, "QSFP+ receptacle (see SAS-2.1 and SFF-8436)" },
132 { 0x4, "Mini SAS 4x active receptacle (see SAS-2.1 and SFF-8088)" },
133 { 0x5, "Mini SAS HD 4x receptacle (see SAS-2.1 and SFF-8644)" },
134 { 0x6, "Mini SAS HD 8x receptacle (see SAS-2.1 and SFF-8644)" },
135 { 0x7, "Mini SAS HD 16x receptacle (see SAS-2.1 and SFF-8644)" },
136 { 0xF, "Vendor-specific external connector" },
137 { 0x10, "Internal wide SAS 4i plug (see SAS-2 and SFF-8484)" },
139 "Internal wide Mini SAS 4i receptacle (see SAS-2 and SFF-8087)" },
140 { 0x12, "Mini SAS HD 4i receptacle (see SAS-2.1 and SFF-8643)" },
141 { 0x20, "Internal SAS Drive receptacle (see SAS-2 and SFF-8482)" },
142 { 0x21, "Internal SATA host plug (see SAS-2 and SATA-2)" },
143 { 0x22, "Internal SAS Drive plug (see SAS-2 and SFF-8482)" },
144 { 0x23, "Internal SATA device plug (see SAS-2 and SATA-2)" },
145 { 0x24, "Micro SAS receptacle (see SAS-2.14)" },
146 { 0x25, "Micro SATA device plug (see SAS-2.1 and SATA)" },
147 { 0x26, "Micro SAS plug (see SAS-2.1 and SFF-8486)" },
148 { 0x27, "Micro SAS/SATA plug (see SAS-2.1 and SFF-8486)" },
150 "12 Gb/s SAS Drive backplane receptacle (see SAS-34 and SFF-8680)" },
151 { 0x29, "12Gb/s SAS Drive Plug (see SAS-3 and SFF-8680)" },
152 { 0x2A, "Multifunction 12 Gb/s 6x Unshielded receptacle connector "
153 "receptacle (see SAS-3 and SFF-8639)" },
154 { 0x2B, "Multifunction 12 Gb/s 6x Unshielded receptable connector "
155 "plug (see SAS-3 and SFF-8639)" },
156 { 0x2F, "Internal SAS virtual connector" },
157 { 0x3F, "Vendor-specific internal connector" },
158 { 0x70, "Other Vendor-specific connector" },
159 { 0x71, "Other Vendor-specific connector" },
160 { 0x72, "Other Vendor-specific connector" },
161 { 0x73, "Other Vendor-specific connector" },
162 { 0x74, "Other Vendor-specific connector" },
163 { 0x75, "Other Vendor-specific connector" },
164 { 0x76, "Other Vendor-specific connector" },
165 { 0x77, "Other Vendor-specific connector" },
166 { 0x78, "Other Vendor-specific connector" },
167 { 0x79, "Other Vendor-specific connector" },
168 { 0x7A, "Other Vendor-specific connector" },
169 { 0x7B, "Other Vendor-specific connector" },
170 { 0x7C, "Other Vendor-specific connector" },
171 { 0x7D, "Other Vendor-specific connector" },
172 { 0x7E, "Other Vendor-specific connector" },
173 { 0x7F, "Other Vendor-specific connector" },
174 { 0x80, "Not Defined" }
177 #define SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED 0x80
178 #define SAS_CONNECTOR_TYPE_NOT_DEFINED \
179 "Connector type not defined by SES-2 standard"
180 #define SAS_CONNECTOR_TYPE_RESERVED \
181 "Connector type reserved by SES-2 standard"
183 typedef struct phys_enum_type
{
186 char *pet_defaultlabel
;
187 boolean_t pet_dorange
;
190 static const phys_enum_type_t phys_enum_type_list
[] = {
191 { SES_ET_ARRAY_DEVICE
, BAY
, "BAY", B_TRUE
},
192 { SES_ET_COOLING
, FAN
, "FAN", B_TRUE
},
193 { SES_ET_DEVICE
, BAY
, "BAY", B_TRUE
},
194 { SES_ET_ESC_ELECTRONICS
, CONTROLLER
, "CONTROLLER", B_TRUE
},
195 { SES_ET_POWER_SUPPLY
, PSU
, "PSU", B_TRUE
},
196 { SES_ET_SUNW_FANBOARD
, FANBOARD
, "FANBOARD", B_TRUE
},
197 { SES_ET_SUNW_FANMODULE
, FANMODULE
, "FANMODULE", B_TRUE
},
198 { SES_ET_SUNW_POWERBOARD
, POWERBOARD
, "POWERBOARD", B_TRUE
},
199 { SES_ET_SUNW_POWERMODULE
, POWERMODULE
, "POWERMODULE", B_TRUE
}
202 #define N_PHYS_ENUM_TYPES (sizeof (phys_enum_type_list) / \
203 sizeof (phys_enum_type_list[0]))
206 * Structure for the hierarchical tree for element nodes.
208 typedef struct ses_phys_tree
{
209 ses_node_t
*spt_snode
;
210 ses_enum_node_t
*spt_senumnode
;
212 uint64_t spt_eonlyindex
;
215 uint64_t spt_maxinst
;
216 struct ses_phys_tree
*spt_parent
;
217 struct ses_phys_tree
*spt_child
;
218 struct ses_phys_tree
*spt_sibling
;
223 SES_NEW_CHASSIS
= 0x1,
224 SES_NEW_SUBCHASSIS
= 0x2,
225 SES_DUP_CHASSIS
= 0x4,
226 SES_DUP_SUBCHASSIS
= 0x8
227 } ses_chassis_type_e
;
230 static const topo_pgroup_info_t storage_pgroup
= {
232 TOPO_STABILITY_PRIVATE
,
233 TOPO_STABILITY_PRIVATE
,
237 static const topo_pgroup_info_t smp_pgroup
= {
239 TOPO_STABILITY_PRIVATE
,
240 TOPO_STABILITY_PRIVATE
,
244 static const topo_pgroup_info_t ses_pgroup
= {
246 TOPO_STABILITY_PRIVATE
,
247 TOPO_STABILITY_PRIVATE
,
251 static int ses_present(topo_mod_t
*, tnode_t
*, topo_version_t
, nvlist_t
*,
253 static int ses_contains(topo_mod_t
*, tnode_t
*, topo_version_t
, nvlist_t
*,
256 static const topo_method_t ses_component_methods
[] = {
257 { TOPO_METH_PRESENT
, TOPO_METH_PRESENT_DESC
,
258 TOPO_METH_PRESENT_VERSION0
, TOPO_STABILITY_INTERNAL
, ses_present
},
259 { TOPO_METH_FAC_ENUM
, TOPO_METH_FAC_ENUM_DESC
, 0,
260 TOPO_STABILITY_INTERNAL
, ses_node_enum_facility
},
261 { TOPO_METH_SENSOR_FAILURE
, TOPO_METH_SENSOR_FAILURE_DESC
,
262 TOPO_METH_SENSOR_FAILURE_VERSION
, TOPO_STABILITY_INTERNAL
,
263 topo_method_sensor_failure
},
267 static const topo_method_t ses_bay_methods
[] = {
268 { TOPO_METH_FAC_ENUM
, TOPO_METH_FAC_ENUM_DESC
, 0,
269 TOPO_STABILITY_INTERNAL
, ses_node_enum_facility
},
273 static const topo_method_t ses_enclosure_methods
[] = {
274 { TOPO_METH_CONTAINS
, TOPO_METH_CONTAINS_DESC
,
275 TOPO_METH_CONTAINS_VERSION
, TOPO_STABILITY_INTERNAL
, ses_contains
},
276 { TOPO_METH_FAC_ENUM
, TOPO_METH_FAC_ENUM_DESC
, 0,
277 TOPO_STABILITY_INTERNAL
, ses_enc_enum_facility
},
282 * Functions for tracking ses devices which we were unable to open. We retry
283 * these at regular intervals using ses_recheck_dir() and if we find that we
284 * can now open any of them then we send a sysevent to indicate that a new topo
285 * snapshot should be taken.
287 typedef struct ses_open_fail_list
{
288 struct ses_open_fail_list
*sof_next
;
290 } ses_open_fail_list_t
;
292 static ses_open_fail_list_t
*ses_sofh
;
293 static pthread_mutex_t ses_sofmt
;
294 static void ses_ct_print(char *ptr
);
299 ses_target_t
*target
;
302 ses_open_fail_list_t
*sof
;
305 * check list of "unable to open" devices
307 (void) pthread_mutex_lock(&ses_sofmt
);
308 for (sof
= ses_sofh
; sof
!= NULL
; sof
= sof
->sof_next
) {
310 * see if we can open it now
312 if ((target
= ses_open(LIBSES_VERSION
,
313 sof
->sof_path
)) == NULL
) {
314 (void) snprintf(buf
, sizeof (buf
),
315 "recheck_dir - still can't open %s", sof
->sof_path
);
321 * ok - better force a new snapshot
323 (void) snprintf(buf
, sizeof (buf
),
324 "recheck_dir - can now open %s", sof
->sof_path
);
326 (void) sysevent_post_event(EC_PLATFORM
, ESC_PLATFORM_SP_RESET
,
327 SUNW_VENDOR
, "fmd", NULL
, &eid
);
331 (void) pthread_mutex_unlock(&ses_sofmt
);
335 ses_sof_alloc(topo_mod_t
*mod
, char *path
)
337 ses_open_fail_list_t
*sof
;
339 (void) pthread_mutex_lock(&ses_sofmt
);
340 sof
= topo_mod_zalloc(mod
, sizeof (*sof
));
341 topo_mod_dprintf(mod
, "sof_alloc %s", path
);
342 sof
->sof_path
= path
;
343 sof
->sof_next
= ses_sofh
;
345 (void) pthread_mutex_unlock(&ses_sofmt
);
349 ses_sof_freeall(topo_mod_t
*mod
)
351 ses_open_fail_list_t
*sof
, *next_sof
;
353 (void) pthread_mutex_lock(&ses_sofmt
);
354 for (sof
= ses_sofh
; sof
!= NULL
; sof
= next_sof
) {
355 next_sof
= sof
->sof_next
;
356 topo_mod_dprintf(mod
, "sof_freeall %s", sof
->sof_path
);
357 topo_mod_strfree(mod
, sof
->sof_path
);
358 topo_mod_free(mod
, sof
, sizeof (*sof
));
361 (void) pthread_mutex_unlock(&ses_sofmt
);
365 * functions for verifying that the ses_enum_target_t held in a device
366 * contract's cookie field is still valid (it may have been freed by
369 typedef struct ses_stp_list
{
370 struct ses_stp_list
*ssl_next
;
371 ses_enum_target_t
*ssl_tgt
;
374 static ses_stp_list_t
*ses_sslh
;
375 static pthread_mutex_t ses_sslmt
;
378 ses_ssl_alloc(topo_mod_t
*mod
, ses_enum_target_t
*stp
)
382 (void) pthread_mutex_lock(&ses_sslmt
);
383 ssl
= topo_mod_zalloc(mod
, sizeof (*ssl
));
384 topo_mod_dprintf(mod
, "ssl_alloc %p", stp
);
386 ssl
->ssl_next
= ses_sslh
;
388 (void) pthread_mutex_unlock(&ses_sslmt
);
392 ses_ssl_free(topo_mod_t
*mod
, ses_enum_target_t
*stp
)
394 ses_stp_list_t
*ssl
, *prev_ssl
;
396 (void) pthread_mutex_lock(&ses_sslmt
);
398 for (ssl
= ses_sslh
; ssl
!= NULL
; ssl
= ssl
->ssl_next
) {
399 if (ssl
->ssl_tgt
== stp
) {
400 topo_mod_dprintf(mod
, "ssl_free %p", ssl
->ssl_tgt
);
401 if (prev_ssl
== NULL
)
402 ses_sslh
= ssl
->ssl_next
;
404 prev_ssl
->ssl_next
= ssl
->ssl_next
;
405 topo_mod_free(mod
, ssl
, sizeof (*ssl
));
410 (void) pthread_mutex_unlock(&ses_sslmt
);
414 ses_ssl_valid(ses_enum_target_t
*stp
)
418 for (ssl
= ses_sslh
; ssl
!= NULL
; ssl
= ssl
->ssl_next
)
419 if (ssl
->ssl_tgt
== stp
)
425 * Functions for creating and destroying a background thread
426 * (ses_contract_thread) used for detecting when ses devices have been
429 static struct ses_thread_s
{
436 PTHREAD_MUTEX_INITIALIZER
,
443 typedef struct ses_mod_list
{
444 struct ses_mod_list
*smod_next
;
445 topo_mod_t
*smod_mod
;
448 static ses_mod_list_t
*ses_smod
;
451 ses_ct_print(char *ptr
)
453 (void) pthread_mutex_lock(&sesthread
.mt
);
454 if (ses_smod
!= NULL
&& ses_smod
->smod_mod
!= NULL
)
455 topo_mod_dprintf(ses_smod
->smod_mod
, ptr
);
456 (void) pthread_mutex_unlock(&sesthread
.mt
);
461 ses_contract_thread(void *arg
)
463 int efd
, ctlfd
, statfd
;
469 ses_enum_target_t
*stp
;
470 ct_stathdl_t stathdl
;
476 ses_ct_print("start contract event thread");
477 efd
= open64(CTFS_ROOT
"/device/pbundle", O_RDONLY
);
481 (void) sigaddset(&sigset
, sesthread
.thr_sig
);
482 (void) pthread_sigmask(SIG_UNBLOCK
, &sigset
, NULL
);
484 /* check if we've been asked to exit */
485 (void) pthread_mutex_lock(&sesthread
.mt
);
486 if (sesthread
.doexit
) {
487 (void) pthread_mutex_unlock(&sesthread
.mt
);
490 (void) pthread_mutex_unlock(&sesthread
.mt
);
492 /* poll until an event arrives */
493 if ((pollret
= poll(&fds
, 1, 10000)) <= 0) {
500 (void) pthread_mutex_lock(&ses_sslmt
);
501 ses_ct_print("read contract event");
502 if (ct_event_read(efd
, &ev
) != 0) {
503 (void) pthread_mutex_unlock(&ses_sslmt
);
507 /* see if it is an event we are expecting */
508 ctid
= ct_event_get_ctid(ev
);
509 (void) snprintf(buf
, sizeof (buf
),
510 "got contract event ctid=%d", ctid
);
512 event
= ct_event_get_type(ev
);
513 if (event
!= CT_DEV_EV_OFFLINE
&& event
!= CT_EV_NEGEND
) {
514 (void) snprintf(buf
, sizeof (buf
),
515 "bad contract event %x", event
);
518 (void) pthread_mutex_unlock(&ses_sslmt
);
522 /* find target pointer saved in cookie */
523 evid
= ct_event_get_evid(ev
);
524 (void) snprintf(path
, PATH_MAX
, CTFS_ROOT
"/device/%ld/status",
526 statfd
= open64(path
, O_RDONLY
);
527 (void) ct_status_read(statfd
, CTD_COMMON
, &stathdl
);
528 stp
= (ses_enum_target_t
*)(uintptr_t)
529 ct_status_get_cookie(stathdl
);
530 ct_status_free(stathdl
);
531 (void) close(statfd
);
533 /* check if target pointer is still valid */
534 if (ses_ssl_valid(stp
) == 0) {
535 (void) snprintf(buf
, sizeof (buf
),
536 "contract already abandoned %x", event
);
538 (void) snprintf(path
, PATH_MAX
,
539 CTFS_ROOT
"/device/%ld/ctl", ctid
);
540 ctlfd
= open64(path
, O_WRONLY
);
541 if (event
!= CT_EV_NEGEND
)
542 (void) ct_ctl_ack(ctlfd
, evid
);
544 (void) ct_ctl_abandon(ctlfd
);
547 (void) pthread_mutex_unlock(&ses_sslmt
);
551 /* find control device for ack/abandon */
552 (void) pthread_mutex_lock(&stp
->set_lock
);
553 (void) snprintf(path
, PATH_MAX
, CTFS_ROOT
"/device/%ld/ctl",
555 ctlfd
= open64(path
, O_WRONLY
);
556 if (event
!= CT_EV_NEGEND
) {
557 /* if this is an offline event, do the offline */
558 ses_ct_print("got contract offline event");
559 if (stp
->set_target
) {
560 ses_ct_print("contract thread rele");
561 ses_snap_rele(stp
->set_snap
);
562 ses_close(stp
->set_target
);
563 stp
->set_target
= NULL
;
565 (void) ct_ctl_ack(ctlfd
, evid
);
567 /* if this is the negend, then abandon the contract */
568 ses_ct_print("got contract negend");
570 (void) snprintf(buf
, sizeof (buf
),
571 "abandon old contract %d", stp
->set_ctid
);
575 (void) ct_ctl_abandon(ctlfd
);
578 (void) pthread_mutex_unlock(&stp
->set_lock
);
580 (void) pthread_mutex_unlock(&ses_sslmt
);
591 int sig
[] = {SIGTERM
, SIGUSR1
, SIGUSR2
};
592 int sig_sz
= sizeof (sig
) / sizeof (int);
595 /* prefered set of signals that are likely used to terminate threads */
596 (void) sigemptyset(&oset
);
597 (void) pthread_sigmask(SIG_SETMASK
, NULL
, &oset
);
598 for (i
= 0; i
< sig_sz
; i
++) {
599 if (sigismember(&oset
, sig
[i
]) == 0) {
604 /* reserved set of signals that are not allowed to terminate thread */
605 (void) sigemptyset(&rset
);
606 (void) sigaddset(&rset
, SIGABRT
);
607 (void) sigaddset(&rset
, SIGKILL
);
608 (void) sigaddset(&rset
, SIGSTOP
);
609 (void) sigaddset(&rset
, SIGCANCEL
);
611 /* Find signal that is not masked and not in the reserved list. */
612 for (i
= 1; i
< MAXSIG
; i
++) {
613 if (sigismember(&rset
, i
) == 1) {
616 if (sigismember(&oset
, i
) == 0) {
631 ses_thread_init(topo_mod_t
*mod
)
633 pthread_attr_t
*attr
= NULL
;
634 struct sigaction act
;
635 ses_mod_list_t
*smod
;
637 (void) pthread_mutex_lock(&sesthread
.mt
);
639 smod
= topo_mod_zalloc(mod
, sizeof (*smod
));
640 smod
->smod_mod
= mod
;
641 smod
->smod_next
= ses_smod
;
643 if (sesthread
.tid
== 0) {
644 /* find a suitable signal to use for killing the thread below */
645 sesthread
.thr_sig
= find_thr_sig();
647 /* if don't have a handler for this signal, create one */
648 (void) sigaction(sesthread
.thr_sig
, NULL
, &act
);
649 if (act
.sa_handler
== SIG_DFL
|| act
.sa_handler
== SIG_IGN
)
650 act
.sa_handler
= ses_handler
;
651 (void) sigaction(sesthread
.thr_sig
, &act
, NULL
);
653 /* create a thread to listen for offline events */
654 (void) pthread_create(&sesthread
.tid
,
655 attr
, ses_contract_thread
, NULL
);
657 (void) pthread_mutex_unlock(&sesthread
.mt
);
661 ses_thread_fini(topo_mod_t
*mod
)
663 ses_mod_list_t
*smod
, *prev_smod
;
665 (void) pthread_mutex_lock(&sesthread
.mt
);
667 for (smod
= ses_smod
; smod
!= NULL
; smod
= smod
->smod_next
) {
668 if (smod
->smod_mod
== mod
) {
669 if (prev_smod
== NULL
)
670 ses_smod
= smod
->smod_next
;
672 prev_smod
->smod_next
= smod
->smod_next
;
673 topo_mod_free(mod
, smod
, sizeof (*smod
));
678 if (--sesthread
.count
> 0) {
679 (void) pthread_mutex_unlock(&sesthread
.mt
);
682 sesthread
.doexit
= 1;
683 (void) pthread_mutex_unlock(&sesthread
.mt
);
684 (void) pthread_kill(sesthread
.tid
, sesthread
.thr_sig
);
685 (void) pthread_join(sesthread
.tid
, NULL
);
690 ses_create_contract(topo_mod_t
*mod
, ses_enum_target_t
*stp
)
693 char link_path
[PATH_MAX
];
697 /* convert "/dev" path into "/devices" path */
698 if ((len
= readlink(stp
->set_devpath
, link_path
, PATH_MAX
)) < 0) {
699 topo_mod_dprintf(mod
, "readlink failed");
702 link_path
[len
] = '\0';
704 /* set up template to create new contract */
705 tfd
= open64(CTFS_ROOT
"/device/template", O_RDWR
);
706 (void) ct_tmpl_set_critical(tfd
, CT_DEV_EV_OFFLINE
);
707 (void) ct_tmpl_set_cookie(tfd
, (uint64_t)(uintptr_t)stp
);
709 /* strip "../../devices" off the front and create the contract */
710 if ((rval
= ct_dev_tmpl_set_minor(tfd
, &link_path
[13])) != 0)
711 topo_mod_dprintf(mod
, "failed to set minor %s rval = %d",
712 &link_path
[13], rval
);
713 else if ((rval
= ct_tmpl_create(tfd
, &stp
->set_ctid
)) != 0)
714 topo_mod_dprintf(mod
, "failed to create ctid rval = %d", rval
);
716 topo_mod_dprintf(mod
, "created ctid=%d", stp
->set_ctid
);
721 ses_target_free(topo_mod_t
*mod
, ses_enum_target_t
*stp
)
723 if (--stp
->set_refcount
== 0) {
724 /* check if already closed due to contract offline request */
725 (void) pthread_mutex_lock(&stp
->set_lock
);
726 if (stp
->set_target
) {
727 ses_snap_rele(stp
->set_snap
);
728 ses_close(stp
->set_target
);
729 stp
->set_target
= NULL
;
735 topo_mod_dprintf(mod
, "abandon old contract %d",
737 (void) snprintf(path
, PATH_MAX
,
738 CTFS_ROOT
"/device/%ld/ctl", stp
->set_ctid
);
739 ctlfd
= open64(path
, O_WRONLY
);
740 (void) ct_ctl_abandon(ctlfd
);
744 (void) pthread_mutex_unlock(&stp
->set_lock
);
745 ses_ssl_free(mod
, stp
);
746 topo_mod_strfree(mod
, stp
->set_devpath
);
747 topo_mod_free(mod
, stp
, sizeof (ses_enum_target_t
));
752 ses_data_free(ses_enum_data_t
*sdp
, ses_enum_chassis_t
*pcp
)
754 topo_mod_t
*mod
= sdp
->sed_mod
;
755 ses_enum_chassis_t
*cp
;
757 ses_enum_target_t
*tp
;
763 cpl
= &pcp
->sec_subchassis
;
765 cpl
= &sdp
->sed_chassis
;
767 while ((cp
= topo_list_next(cpl
)) != NULL
) {
768 topo_list_delete(cpl
, cp
);
770 while ((np
= topo_list_next(&cp
->sec_nodes
)) != NULL
) {
771 while ((ap
= topo_list_next(&np
->sen_alt_nodes
)) !=
773 topo_list_delete(&np
->sen_alt_nodes
, ap
);
774 topo_mod_free(mod
, ap
, sizeof (ses_alt_node_t
));
776 topo_list_delete(&cp
->sec_nodes
, np
);
777 topo_mod_free(mod
, np
, sizeof (ses_enum_node_t
));
780 while ((tp
= topo_list_next(&cp
->sec_targets
)) != NULL
) {
781 topo_list_delete(&cp
->sec_targets
, tp
);
782 ses_target_free(mod
, tp
);
785 topo_mod_free(mod
, cp
, sizeof (ses_enum_chassis_t
));
789 dev_list_free(mod
, &sdp
->sed_devs
);
790 topo_mod_free(mod
, sdp
, sizeof (ses_enum_data_t
));
795 * For enclosure nodes, we have a special contains method. By default, the hc
796 * walker will compare the node name and instance number to determine if an
797 * FMRI matches. For enclosures where the enumeration order is impossible to
798 * predict, we instead use the chassis-id as a unique identifier, and ignore
799 * the instance number.
802 fmri_contains(topo_mod_t
*mod
, nvlist_t
*nv1
, nvlist_t
*nv2
)
805 nvlist_t
**hcp1
, **hcp2
;
812 if (nvlist_lookup_uint8(nv1
, FM_VERSION
, &v1
) != 0 ||
813 nvlist_lookup_uint8(nv2
, FM_VERSION
, &v2
) != 0 ||
814 v1
> FM_HC_SCHEME_VERSION
|| v2
> FM_HC_SCHEME_VERSION
)
815 return (topo_mod_seterrno(mod
, EMOD_FMRI_VERSION
));
817 err
= nvlist_lookup_nvlist_array(nv1
, FM_FMRI_HC_LIST
, &hcp1
, &nhcp1
);
818 err
|= nvlist_lookup_nvlist_array(nv2
, FM_FMRI_HC_LIST
, &hcp2
, &nhcp2
);
820 return (topo_mod_seterrno(mod
, EMOD_FMRI_NVL
));
823 * If the chassis-id doesn't match, then these FMRIs are not
824 * equivalent. If one of the FMRIs doesn't have a chassis ID, then we
825 * have no choice but to fall back to the instance ID.
827 if (nvlist_lookup_nvlist(nv1
, FM_FMRI_AUTHORITY
, &a1
) == 0 &&
828 nvlist_lookup_nvlist(nv2
, FM_FMRI_AUTHORITY
, &a2
) == 0 &&
829 nvlist_lookup_string(a1
, FM_FMRI_AUTH_CHASSIS
, &c1
) == 0 &&
830 nvlist_lookup_string(a2
, FM_FMRI_AUTH_CHASSIS
, &c2
) == 0) {
831 if (strcmp(c1
, c2
) != 0)
842 for (i
= 0; i
< nhcp1
; i
++) {
848 (void) nvlist_lookup_string(hcp1
[i
], FM_FMRI_HC_NAME
, &nm1
);
849 (void) nvlist_lookup_string(hcp2
[i
], FM_FMRI_HC_NAME
, &nm2
);
850 (void) nvlist_lookup_string(hcp1
[i
], FM_FMRI_HC_ID
, &id1
);
851 (void) nvlist_lookup_string(hcp2
[i
], FM_FMRI_HC_ID
, &id2
);
852 if (nm1
== NULL
|| nm2
== NULL
|| id1
== NULL
|| id2
== NULL
)
853 return (topo_mod_seterrno(mod
, EMOD_FMRI_NVL
));
855 if (strcmp(nm1
, nm2
) == 0 &&
856 (i
< mindepth
|| strcmp(id1
, id2
) == 0))
867 ses_contains(topo_mod_t
*mod
, tnode_t
*tn
, topo_version_t version
,
868 nvlist_t
*in
, nvlist_t
**out
)
873 if (version
> TOPO_METH_CONTAINS_VERSION
)
874 return (topo_mod_seterrno(mod
, EMOD_VER_NEW
));
876 if (nvlist_lookup_nvlist(in
, TOPO_METH_FMRI_ARG_FMRI
, &nv1
) != 0 ||
877 nvlist_lookup_nvlist(in
, TOPO_METH_FMRI_ARG_SUBFMRI
, &nv2
) != 0)
878 return (topo_mod_seterrno(mod
, EMOD_METHOD_INVAL
));
880 ret
= fmri_contains(mod
, nv1
, nv2
);
884 if (topo_mod_nvalloc(mod
, out
, NV_UNIQUE_NAME
) == 0) {
885 if (nvlist_add_uint32(*out
, TOPO_METH_CONTAINS_RET
,
897 * Return a current instance of the node. This is somewhat complicated because
898 * we need to take a new snapshot in order to get the new data, but we don't
899 * want to be constantly taking SES snapshots if the consumer is going to do a
900 * series of queries. So we adopt the strategy of assuming that the SES state
901 * is not going to be rapidly changing, and limit our snapshot frequency to
902 * some defined bounds.
905 ses_node_lock(topo_mod_t
*mod
, tnode_t
*tn
)
907 ses_enum_target_t
*tp
= topo_node_getspecific(tn
);
915 (void) topo_mod_seterrno(mod
, EMOD_METHOD_NOTSUP
);
919 (void) pthread_mutex_lock(&tp
->set_lock
);
922 * Determine if we need to take a new snapshot.
926 if (tp
->set_target
== NULL
) {
928 * We may have closed the device but not yet abandoned the
929 * contract (ie we've had the offline event but not yet the
930 * negend). If so, just return failure.
932 if (tp
->set_ctid
!= 0) {
933 (void) topo_mod_seterrno(mod
, EMOD_METHOD_NOTSUP
);
934 (void) pthread_mutex_unlock(&tp
->set_lock
);
939 * The device has been closed due to a contract offline
940 * request, then we need to reopen it and create a new contract.
942 if ((tp
->set_target
=
943 ses_open(LIBSES_VERSION
, tp
->set_devpath
)) == NULL
) {
946 (void) topo_mod_seterrno(mod
, EMOD_METHOD_NOTSUP
);
947 (void) pthread_mutex_unlock(&tp
->set_lock
);
948 topo_mod_dprintf(mod
, "recheck_dir - "
949 "can no longer open %s", tp
->set_devpath
);
950 (void) sysevent_post_event(EC_PLATFORM
,
951 ESC_PLATFORM_SP_RESET
, SUNW_VENDOR
, "fmd", NULL
,
955 topo_mod_dprintf(mod
, "reopen contract");
956 ses_create_contract(mod
, tp
);
957 tp
->set_snap
= ses_snap_hold(tp
->set_target
);
958 tp
->set_snaptime
= gethrtime();
959 } else if (now
- tp
->set_snaptime
> (ses_snap_freq
* 1000 * 1000) &&
960 (snap
= ses_snap_new(tp
->set_target
)) != NULL
) {
961 if (ses_snap_generation(snap
) !=
962 ses_snap_generation(tp
->set_snap
)) {
964 * If we find ourselves in this situation, we're in
965 * trouble. The generation count has changed, which
966 * indicates that our current topology is out of date.
967 * But we need to consult the new topology in order to
968 * determine presence at this moment in time. We can't
969 * go back and change the topo snapshot in situ, so
970 * we'll just have to fail the call in this unlikely
974 (void) topo_mod_seterrno(mod
, EMOD_METHOD_NOTSUP
);
975 (void) pthread_mutex_unlock(&tp
->set_lock
);
978 ses_snap_rele(tp
->set_snap
);
981 tp
->set_snaptime
= gethrtime();
986 verify(topo_prop_get_uint64(tn
, TOPO_PGROUP_SES
,
987 TOPO_PROP_NODE_ID
, &nodeid
, &err
) == 0);
988 verify((np
= ses_node_lookup(snap
, nodeid
)) != NULL
);
995 ses_node_unlock(topo_mod_t
*mod
, tnode_t
*tn
)
997 ses_enum_target_t
*tp
= topo_node_getspecific(tn
);
1001 (void) pthread_mutex_unlock(&tp
->set_lock
);
1005 * Determine if the element is present.
1009 ses_present(topo_mod_t
*mod
, tnode_t
*tn
, topo_version_t version
,
1010 nvlist_t
*in
, nvlist_t
**out
)
1014 nvlist_t
*props
, *nvl
;
1017 if ((np
= ses_node_lock(mod
, tn
)) == NULL
)
1020 verify((props
= ses_node_props(np
)) != NULL
);
1021 verify(nvlist_lookup_uint64(props
,
1022 SES_PROP_STATUS_CODE
, &status
) == 0);
1024 ses_node_unlock(mod
, tn
);
1026 present
= (status
!= SES_ESC_NOT_INSTALLED
);
1028 if (topo_mod_nvalloc(mod
, &nvl
, NV_UNIQUE_NAME
) != 0)
1029 return (topo_mod_seterrno(mod
, EMOD_FMRI_NVL
));
1031 if (nvlist_add_uint32(nvl
, TOPO_METH_PRESENT_RET
,
1034 return (topo_mod_seterrno(mod
, EMOD_FMRI_NVL
));
1043 * Sets standard properties for a ses node (enclosure, bay, controller
1045 * This includes setting the FRU, as well as setting the
1046 * authority information. When the fru topo node(frutn) is not NULL
1047 * its resouce should be used as FRU.
1050 ses_set_standard_props(topo_mod_t
*mod
, tnode_t
*frutn
, tnode_t
*tn
,
1051 nvlist_t
*auth
, uint64_t nodeid
, const char *path
)
1054 char *product
, *chassis
;
1058 * Set the authority explicitly if specified.
1061 verify(nvlist_lookup_string(auth
, FM_FMRI_AUTH_PRODUCT
,
1063 verify(nvlist_lookup_string(auth
, FM_FMRI_AUTH_CHASSIS
,
1065 if (topo_prop_set_string(tn
, FM_FMRI_AUTHORITY
,
1066 FM_FMRI_AUTH_PRODUCT
, TOPO_PROP_IMMUTABLE
, product
,
1068 topo_prop_set_string(tn
, FM_FMRI_AUTHORITY
,
1069 FM_FMRI_AUTH_CHASSIS
, TOPO_PROP_IMMUTABLE
, chassis
,
1071 topo_prop_set_string(tn
, FM_FMRI_AUTHORITY
,
1072 FM_FMRI_AUTH_SERVER
, TOPO_PROP_IMMUTABLE
, "",
1074 topo_mod_dprintf(mod
, "failed to add authority "
1075 "properties: %s\n", topo_strerror(err
));
1076 return (topo_mod_seterrno(mod
, err
));
1081 * Copy the resource and set that as the FRU.
1083 if (frutn
!= NULL
) {
1084 if (topo_node_resource(frutn
, &fmri
, &err
) != 0) {
1085 topo_mod_dprintf(mod
,
1086 "topo_node_resource() failed : %s\n",
1087 topo_strerror(err
));
1088 return (topo_mod_seterrno(mod
, err
));
1091 if (topo_node_resource(tn
, &fmri
, &err
) != 0) {
1092 topo_mod_dprintf(mod
,
1093 "topo_node_resource() failed : %s\n",
1094 topo_strerror(err
));
1095 return (topo_mod_seterrno(mod
, err
));
1099 if (topo_node_fru_set(tn
, fmri
, 0, &err
) != 0) {
1100 topo_mod_dprintf(mod
,
1101 "topo_node_fru_set() failed : %s\n",
1102 topo_strerror(err
));
1104 return (topo_mod_seterrno(mod
, err
));
1110 * Set the SES-specific properties so that consumers can query
1111 * additional information about the particular SES element.
1113 if (topo_pgroup_create(tn
, &ses_pgroup
, &err
) != 0) {
1114 topo_mod_dprintf(mod
, "failed to create propgroup "
1115 "%s: %s\n", TOPO_PGROUP_SES
, topo_strerror(err
));
1119 if (topo_prop_set_uint64(tn
, TOPO_PGROUP_SES
,
1120 TOPO_PROP_NODE_ID
, TOPO_PROP_IMMUTABLE
,
1121 nodeid
, &err
) != 0) {
1122 topo_mod_dprintf(mod
,
1123 "failed to create property %s: %s\n",
1124 TOPO_PROP_NODE_ID
, topo_strerror(err
));
1128 if (topo_prop_set_string(tn
, TOPO_PGROUP_SES
,
1129 TOPO_PROP_TARGET_PATH
, TOPO_PROP_IMMUTABLE
,
1131 topo_mod_dprintf(mod
,
1132 "failed to create property %s: %s\n",
1133 TOPO_PROP_TARGET_PATH
, topo_strerror(err
));
1141 * Iterate over the SES phy information. If any of the ports indicates that it's
1142 * a SATA device and we haven't matched any disk devices yet, that means
1143 * that the HBA was able to create a WWN for the SATA device based on its GUID,
1144 * which is good. However, SES includes the WWN for the device's STP bridge. In
1145 * theory, if the driver includes the WWN based on the SATA guid then it should
1146 * also set the bridge-port property indicating the WWN that should match the
1150 ses_create_disk_bridge(ses_enum_data_t
*sdp
, tnode_t
*pnode
, nvlist_t
*props
,
1155 topo_mod_t
*mod
= sdp
->sed_mod
;
1157 if (nvlist_lookup_nvlist_array(props
, SES_SAS_PROP_PHYS
, &phys
,
1161 for (i
= 0; i
< n_phys
; i
++) {
1166 if (nvlist_lookup_uint64(phys
[i
], SES_SAS_PROP_ADDR
,
1167 &wwn
) != 0 || wwn
== 0) {
1171 if (nvlist_lookup_boolean_value(phys
[i
],
1172 SES_SAS_PROP_SATA_DEVICE
, &sata
) != 0 || !sata
) {
1176 if (scsi_wwn_to_wwnstr(wwn
, 0, wwnstr
) == NULL
)
1179 if (disk_declare_bridge(mod
, pnode
, &sdp
->sed_devs
,
1180 wwnstr
, child
) == 0) {
1190 * Callback to add a disk to a given bay. We first check the status-code to
1191 * determine if a disk is present, ignoring those that aren't in an appropriate
1192 * state. We then scan the parent bay node's SAS address array to determine
1193 * possible attached SAS addresses. We create a disk node if the disk is not
1194 * SAS or the SES target does not support the necessary pages for this; if we
1195 * find the SAS address, we create a disk node and also correlate it with
1196 * the corresponding Solaris device node to fill in the rest of the data.
1199 ses_create_disk(ses_enum_data_t
*sdp
, tnode_t
*pnode
, nvlist_t
*props
)
1201 topo_mod_t
*mod
= sdp
->sed_mod
;
1206 tnode_t
*child
= NULL
;
1209 * Skip devices that are not in a present (and possibly damaged) state.
1211 if (nvlist_lookup_uint64(props
, SES_PROP_STATUS_CODE
, &status
) != 0)
1214 if (status
!= SES_ESC_UNSUPPORTED
&&
1215 status
!= SES_ESC_OK
&&
1216 status
!= SES_ESC_CRITICAL
&&
1217 status
!= SES_ESC_NONCRITICAL
&&
1218 status
!= SES_ESC_UNRECOVERABLE
&&
1219 status
!= SES_ESC_NO_ACCESS
&&
1220 status
!= SES_ESC_UNKNOWN
)
1223 topo_mod_dprintf(mod
, "found attached disk");
1226 * Create the disk range.
1228 if (topo_node_range_create(mod
, pnode
, DISK
, 0, 0) != 0) {
1229 topo_mod_dprintf(mod
,
1230 "topo_node_create_range() failed: %s",
1231 topo_mod_errmsg(mod
));
1236 * Look through all SAS addresses and attempt to correlate them to a
1237 * known Solaris device. If we don't find a matching node, then we
1238 * don't enumerate the disk node.
1239 * Note that TOPO_PROP_SAS_ADDR prop includes SAS address from
1240 * alternate elements that represent the same device.
1242 if (topo_prop_get_string_array(pnode
, TOPO_PGROUP_SES
,
1243 TOPO_PROP_SAS_ADDR
, &paths
, &nsas
, &err
) != 0)
1248 for (s
= 0; s
< nsas
; s
++) {
1249 ret
= disk_declare_addr(mod
, pnode
, &sdp
->sed_devs
, paths
[s
],
1253 } else if (ret
< 0) {
1260 * We need to take another pass through the properties for this bay by
1261 * iterating over the phys and noting if any of these are SATA. Note,
1262 * this information isn't commonly part of the topo tree at this time,
1263 * hence why we end up going back and iterating over the properties
1267 if (ses_create_disk_bridge(sdp
, pnode
, props
, &child
) != 0)
1268 (void) disk_declare_non_enumerated(mod
, pnode
, &child
);
1271 /* copy sas_addresses (target-ports) from parent (with 'w'added) */
1272 if (child
!= NULL
) {
1277 tports
= topo_mod_zalloc(mod
, sizeof (char *) * nsas
);
1278 if (tports
!= NULL
) {
1279 for (i
= 0; i
< nsas
; i
++) {
1280 if (scsi_wwnstr_to_wwn(paths
[i
], &wwn
) !=
1283 tports
[i
] = scsi_wwn_to_wwnstr(wwn
, 1, NULL
);
1284 if (tports
[i
] == NULL
)
1287 /* if they all worked then create the property */
1289 (void) topo_prop_set_string_array(child
,
1290 TOPO_PGROUP_STORAGE
,
1291 TOPO_STORAGE_TARGET_PORT_L0IDS
,
1292 TOPO_PROP_IMMUTABLE
, (const char **)tports
,
1295 for (i
= 0; i
< nsas
; i
++)
1296 if (tports
[i
] != NULL
)
1297 scsi_free_wwnstr(tports
[i
]);
1298 topo_mod_free(mod
, tports
, sizeof (char *) * nsas
);
1302 for (s
= 0; s
< nsas
; s
++)
1303 topo_mod_free(mod
, paths
[s
], strlen(paths
[s
]) + 1);
1304 topo_mod_free(mod
, paths
, nsas
* sizeof (char *));
1310 ses_add_bay_props(topo_mod_t
*mod
, tnode_t
*tn
, ses_enum_node_t
*snp
)
1317 uint_t i
, j
, n_phys
, all_phys
= 0;
1323 for (ap
= topo_list_next(&snp
->sen_alt_nodes
); ap
!= NULL
;
1324 ap
= topo_list_next(ap
)) {
1326 props
= ses_node_props(np
);
1328 if (nvlist_lookup_nvlist_array(props
, SES_SAS_PROP_PHYS
,
1329 &phys
, &n_phys
) != 0)
1338 if ((paths
= topo_mod_zalloc(mod
, all_phys
* sizeof (char *))) == NULL
)
1341 for (i
= 0, ap
= topo_list_next(&snp
->sen_alt_nodes
); ap
!= NULL
;
1342 ap
= topo_list_next(ap
)) {
1344 props
= ses_node_props(np
);
1346 if (nvlist_lookup_nvlist_array(props
, SES_SAS_PROP_PHYS
,
1347 &phys
, &n_phys
) != 0)
1350 for (j
= 0; j
< n_phys
; j
++) {
1351 if (nvlist_lookup_uint64(phys
[j
], SES_SAS_PROP_ADDR
,
1355 len
= snprintf(NULL
, 0, "%016llx", addr
) + 1;
1356 if ((paths
[i
] = topo_mod_alloc(mod
, len
)) == NULL
)
1359 (void) snprintf(paths
[i
], len
, "%016llx", addr
);
1365 err
= topo_prop_set_string_array(tn
, TOPO_PGROUP_SES
,
1366 TOPO_PROP_SAS_ADDR
, TOPO_PROP_IMMUTABLE
,
1367 (const char **)paths
, i
, &terr
);
1369 err
= topo_mod_seterrno(mod
, terr
);
1372 for (i
= 0; i
< all_phys
&& paths
[i
] != NULL
; i
++)
1373 topo_mod_free(mod
, paths
[i
], strlen(paths
[i
]) + 1);
1374 topo_mod_free(mod
, paths
, all_phys
* sizeof (char *));
1380 * Callback to create a basic node (bay, psu, fan, or controller and expander).
1383 ses_create_generic(ses_enum_data_t
*sdp
, ses_enum_node_t
*snp
, tnode_t
*pnode
,
1384 tnode_t
*frutn
, const char *nodename
, const char *labelname
,
1387 ses_node_t
*np
= snp
->sen_node
;
1389 uint64_t instance
= snp
->sen_instance
;
1390 topo_mod_t
*mod
= sdp
->sed_mod
;
1391 nvlist_t
*props
, *aprops
;
1392 nvlist_t
*auth
= NULL
, *fmri
= NULL
;
1396 char *part
= NULL
, *serial
= NULL
, *revision
= NULL
;
1400 props
= ses_node_props(np
);
1402 (void) nvlist_lookup_string(props
, LIBSES_PROP_PART
, &part
);
1403 (void) nvlist_lookup_string(props
, LIBSES_PROP_SERIAL
, &serial
);
1405 topo_mod_dprintf(mod
, "adding %s %llu", nodename
, instance
);
1408 * Create the node. The interesting information is all copied from the
1409 * parent enclosure node, so there is not much to do.
1411 if ((auth
= topo_mod_auth(mod
, pnode
)) == NULL
)
1415 * We want to report revision information for the controller nodes, but
1416 * we do not get per-element revision information. However, we do have
1417 * revision information for the entire enclosure, and we can use the
1418 * 'reported-via' property to know that this controller corresponds to
1419 * the given revision information. This means we cannot get revision
1420 * information for targets we are not explicitly connected to, but
1421 * there is little we can do about the situation.
1423 if (strcmp(nodename
, CONTROLLER
) == 0 &&
1424 nvlist_lookup_boolean_value(props
, SES_PROP_REPORT
, &report
) == 0 &&
1426 for (parent
= ses_node_parent(np
); parent
!= NULL
;
1427 parent
= ses_node_parent(parent
)) {
1428 if (ses_node_type(parent
) == SES_NODE_ENCLOSURE
) {
1429 (void) nvlist_lookup_string(
1430 ses_node_props(parent
),
1431 SES_EN_PROP_REV
, &revision
);
1437 if ((fmri
= topo_mod_hcfmri(mod
, pnode
, FM_HC_SCHEME_VERSION
,
1438 nodename
, (topo_instance_t
)instance
, NULL
, auth
, part
, revision
,
1440 topo_mod_dprintf(mod
, "topo_mod_hcfmri() failed: %s",
1441 topo_mod_errmsg(mod
));
1445 if ((tn
= topo_node_bind(mod
, pnode
, nodename
,
1446 instance
, fmri
)) == NULL
) {
1447 topo_mod_dprintf(mod
, "topo_node_bind() failed: %s",
1448 topo_mod_errmsg(mod
));
1453 * For the node label, we look for the following in order:
1456 * <ses-class-description> <instance>
1457 * <default-type-label> <instance>
1459 if (nvlist_lookup_string(props
, SES_PROP_DESCRIPTION
, &desc
) != 0 ||
1461 parent
= ses_node_parent(np
);
1462 aprops
= ses_node_props(parent
);
1463 if (nvlist_lookup_string(aprops
, SES_PROP_CLASS_DESCRIPTION
,
1464 &desc
) != 0 || desc
[0] == '\0')
1465 desc
= (char *)labelname
;
1466 (void) snprintf(label
, sizeof (label
), "%s %llu", desc
,
1471 if (topo_node_label_set(tn
, desc
, &err
) != 0)
1474 if (ses_set_standard_props(mod
, frutn
, tn
, NULL
, ses_node_id(np
),
1475 snp
->sen_target
->set_devpath
) != 0)
1478 if (strcmp(nodename
, BAY
) == 0) {
1479 if (ses_add_bay_props(mod
, tn
, snp
) != 0)
1482 if (ses_create_disk(sdp
, tn
, props
) != 0)
1485 if (topo_method_register(mod
, tn
, ses_bay_methods
) != 0) {
1486 topo_mod_dprintf(mod
,
1487 "topo_method_register() failed: %s",
1488 topo_mod_errmsg(mod
));
1491 } else if ((strcmp(nodename
, FAN
) == 0) ||
1492 (strcmp(nodename
, PSU
) == 0) ||
1493 (strcmp(nodename
, CONTROLLER
) == 0)) {
1495 * Only fan, psu, and controller nodes have a 'present' method.
1496 * Bay nodes are always present, and disk nodes are present by
1497 * virtue of being enumerated and SAS expander nodes and
1498 * SAS connector nodes are also always present once
1499 * the parent controller is found.
1501 if (topo_method_register(mod
, tn
, ses_component_methods
) != 0) {
1502 topo_mod_dprintf(mod
,
1503 "topo_method_register() failed: %s",
1504 topo_mod_errmsg(mod
));
1510 snp
->sen_target
->set_refcount
++;
1511 topo_node_setspecific(tn
, snp
->sen_target
);
1515 if (node
!= NULL
) *node
= tn
;
1525 * Create SAS expander specific props.
1529 ses_set_expander_props(ses_enum_data_t
*sdp
, ses_enum_node_t
*snp
,
1530 tnode_t
*ptnode
, tnode_t
*tnode
, int *phycount
, int64_t *connlist
)
1532 ses_node_t
*np
= snp
->sen_node
;
1533 topo_mod_t
*mod
= sdp
->sed_mod
;
1534 nvlist_t
*auth
= NULL
, *fmri
= NULL
;
1535 nvlist_t
*props
, **phylist
;
1538 uint64_t sasaddr
, connidx
;
1539 char sasaddr_str
[17];
1540 boolean_t found
= B_FALSE
, ses_found
= B_FALSE
;
1541 dev_di_node_t
*dnode
, *sesdnode
;
1543 props
= ses_node_props(np
);
1546 * the uninstalled expander is not enumerated by checking
1547 * the element status code. No present present' method provided.
1550 * Get the Expander SAS address. It should exist.
1552 if (nvlist_lookup_uint64(props
, SES_EXP_PROP_SAS_ADDR
,
1554 topo_mod_dprintf(mod
,
1555 "Failed to get prop %s.", SES_EXP_PROP_SAS_ADDR
);
1559 (void) sprintf(sasaddr_str
, "%llx", sasaddr
);
1561 /* search matching dev_di_node. */
1562 for (dnode
= topo_list_next(&sdp
->sed_devs
); dnode
!= NULL
;
1563 dnode
= topo_list_next(dnode
)) {
1564 for (i
= 0; i
< dnode
->ddn_ppath_count
; i
++) {
1565 if ((dnode
->ddn_target_port
[i
] != NULL
) &&
1566 (strstr(dnode
->ddn_target_port
[i
],
1567 sasaddr_str
) != NULL
)) {
1577 topo_mod_dprintf(mod
,
1578 "ses_set_expander_props: Failed to find matching "
1579 "devinfo node for Exapnder SAS address %s",
1580 SES_EXP_PROP_SAS_ADDR
);
1581 /* continue on to get storage group props. */
1583 /* create/set the devfs-path and devid in the smp group */
1584 if (topo_pgroup_create(tnode
, &smp_pgroup
, &err
) != 0) {
1585 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1586 "failed to create smp property group %s\n",
1587 topo_strerror(err
));
1590 if (topo_prop_set_string(tnode
, TOPO_PGROUP_SMP
,
1591 TOPO_PROP_SMP_TARGET_PORT
, TOPO_PROP_IMMUTABLE
,
1592 dnode
->ddn_target_port
[i
], &err
) != 0) {
1593 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1594 "set %S error %s\n", TOPO_PROP_SAS_ADDR
,
1595 topo_strerror(err
));
1597 if (topo_prop_set_string(tnode
, TOPO_PGROUP_SMP
,
1598 TOPO_PROP_SMP_DEV_PATH
, TOPO_PROP_IMMUTABLE
,
1599 dnode
->ddn_dpath
, &err
) != 0) {
1600 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1601 "set dev error %s\n", topo_strerror(err
));
1603 if (topo_prop_set_string(tnode
, TOPO_PGROUP_SMP
,
1604 TOPO_PROP_SMP_DEVID
, TOPO_PROP_IMMUTABLE
,
1605 dnode
->ddn_devid
, &err
) != 0) {
1606 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1607 "set devid error %s\n", topo_strerror(err
));
1609 if (dnode
->ddn_ppath_count
!= 0 &&
1610 topo_prop_set_string_array(tnode
, TOPO_PGROUP_SMP
,
1611 TOPO_PROP_SMP_PHYS_PATH
, TOPO_PROP_IMMUTABLE
,
1612 (const char **)dnode
->ddn_ppath
,
1613 dnode
->ddn_ppath_count
, &err
) != 0) {
1614 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1615 "set phys-path error %s\n",
1616 topo_strerror(err
));
1621 /* update the ses property group with SES target info */
1622 if ((topo_pgroup_create(tnode
, &ses_pgroup
, &err
) != 0) &&
1623 (err
!= ETOPO_PROP_DEFD
)) {
1624 /* SES prop group doesn't exist but failed to be created. */
1625 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1626 "ses pgroup create error %s\n", topo_strerror(err
));
1629 /* locate assciated enclosure dev_di_node. */
1630 for (sesdnode
= topo_list_next(&sdp
->sed_devs
);
1631 sesdnode
!= NULL
; sesdnode
= topo_list_next(sesdnode
)) {
1632 for (i
= 0; i
< sesdnode
->ddn_ppath_count
; i
++) {
1634 * check if attached port exists and
1635 * its node type is enclosure and
1636 * attached port is same as sas address of
1638 * bridge port for virtual phy indication
1641 if ((sesdnode
->ddn_attached_port
[i
] != NULL
) &&
1642 (sesdnode
->ddn_dtype
== DTYPE_ESI
) &&
1643 (strstr(sesdnode
->ddn_attached_port
[i
],
1644 sasaddr_str
) != NULL
) &&
1645 (sesdnode
->ddn_bridge_port
[i
] != NULL
)) {
1650 if (ses_found
) break;
1654 if (topo_prop_set_string(tnode
, TOPO_PGROUP_SES
,
1655 TOPO_PROP_SES_TARGET_PORT
, TOPO_PROP_IMMUTABLE
,
1656 sesdnode
->ddn_target_port
[i
], &err
) != 0) {
1657 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1658 "set ses %S error %s\n", TOPO_PROP_SAS_ADDR
,
1659 topo_strerror(err
));
1661 if (topo_prop_set_string(tnode
, TOPO_PGROUP_SES
,
1662 TOPO_PROP_SES_DEV_PATH
, TOPO_PROP_IMMUTABLE
,
1663 sesdnode
->ddn_dpath
, &err
) != 0) {
1664 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1665 "set ses dev error %s\n",
1666 topo_strerror(err
));
1668 if (topo_prop_set_string(tnode
, TOPO_PGROUP_SES
,
1669 TOPO_PROP_SES_DEVID
, TOPO_PROP_IMMUTABLE
,
1670 sesdnode
->ddn_devid
, &err
) != 0) {
1671 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1672 "set ses devid error %s\n",
1673 topo_strerror(err
));
1675 if (sesdnode
->ddn_ppath_count
!= 0 &&
1676 topo_prop_set_string_array(tnode
, TOPO_PGROUP_SES
,
1677 TOPO_PROP_SES_PHYS_PATH
, TOPO_PROP_IMMUTABLE
,
1678 (const char **)sesdnode
->ddn_ppath
,
1679 sesdnode
->ddn_ppath_count
, &err
) != 0) {
1680 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1681 "set ses phys-path error %s\n",
1682 topo_strerror(err
));
1688 /* create the storage group */
1689 if (topo_pgroup_create(tnode
, &storage_pgroup
, &err
) != 0) {
1690 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1691 "create storage error %s\n", topo_strerror(err
));
1694 /* set the SAS address prop out of expander element status. */
1695 if (topo_prop_set_string(tnode
, TOPO_PGROUP_STORAGE
,
1696 TOPO_PROP_SAS_ADDR
, TOPO_PROP_IMMUTABLE
, sasaddr_str
,
1698 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1699 "set %S error %s\n", TOPO_PROP_SAS_ADDR
,
1700 topo_strerror(err
));
1703 /* Get the phy information for the expander */
1704 if (nvlist_lookup_nvlist_array(props
, SES_SAS_PROP_PHYS
,
1705 &phylist
, &pcount
) != 0) {
1706 topo_mod_dprintf(mod
,
1707 "Failed to get prop %s.", SES_SAS_PROP_PHYS
);
1710 * For each phy, get the connector element index and
1711 * stores into connector element index array.
1714 for (i
= 0; i
< pcount
; i
++) {
1715 if (nvlist_lookup_uint64(phylist
[i
],
1716 SES_PROP_CE_IDX
, &connidx
) == 0) {
1717 if (connidx
!= 0xff) {
1718 connlist
[i
] = connidx
;
1723 /* Fail to get the index. set to -1. */
1728 /* set the phy count prop of the expander. */
1729 if (topo_prop_set_uint64(tnode
, TOPO_PGROUP_STORAGE
,
1730 TOPO_PROP_PHY_COUNT
, TOPO_PROP_IMMUTABLE
, pcount
,
1732 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1733 "set %S error %s\n", TOPO_PROP_PHY_COUNT
,
1734 topo_strerror(err
));
1738 * set the connector element index of
1739 * the expander phys.
1743 /* populate other misc storage group properties */
1745 if (dnode
->ddn_mfg
&& (topo_prop_set_string(tnode
,
1746 TOPO_PGROUP_STORAGE
, TOPO_STORAGE_MANUFACTURER
,
1747 TOPO_PROP_IMMUTABLE
, dnode
->ddn_mfg
, &err
) != 0)) {
1748 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1749 "set mfg error %s\n", topo_strerror(err
));
1752 if (dnode
->ddn_model
&& (topo_prop_set_string(tnode
,
1753 TOPO_PGROUP_STORAGE
, TOPO_STORAGE_MODEL
,
1754 TOPO_PROP_IMMUTABLE
,
1755 dnode
->ddn_model
, &err
) != 0)) {
1756 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1757 "set model error %s\n", topo_strerror(err
));
1760 if (dnode
->ddn_serial
&& (topo_prop_set_string(tnode
,
1761 TOPO_PGROUP_STORAGE
, TOPO_STORAGE_SERIAL_NUM
,
1762 TOPO_PROP_IMMUTABLE
,
1763 dnode
->ddn_serial
, &err
) != 0)) {
1764 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1765 "set serial error %s\n",
1766 topo_strerror(err
));
1769 if (dnode
->ddn_firm
&& (topo_prop_set_string(tnode
,
1770 TOPO_PGROUP_STORAGE
,
1771 TOPO_STORAGE_FIRMWARE_REV
, TOPO_PROP_IMMUTABLE
,
1772 dnode
->ddn_firm
, &err
) != 0)) {
1773 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1774 "set firm error %s\n", topo_strerror(err
));
1788 * Create SAS expander specific props.
1792 ses_set_connector_props(ses_enum_data_t
*sdp
, ses_enum_node_t
*snp
,
1793 tnode_t
*tnode
, int64_t phy_mask
)
1795 ses_node_t
*np
= snp
->sen_node
;
1796 topo_mod_t
*mod
= sdp
->sed_mod
;
1800 char phymask_str
[17], *conntype_str
;
1803 props
= ses_node_props(np
);
1806 * convert phy mask to string.
1808 (void) snprintf(phymask_str
, 17, "%llx", phy_mask
);
1810 /* create the storage group */
1811 if (topo_pgroup_create(tnode
, &storage_pgroup
, &err
) != 0) {
1812 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1813 "create storage error %s\n", topo_strerror(err
));
1816 /* set the SAS address prop of the expander. */
1817 if (topo_prop_set_string(tnode
, TOPO_PGROUP_STORAGE
,
1818 TOPO_STORAGE_SAS_PHY_MASK
, TOPO_PROP_IMMUTABLE
,
1819 phymask_str
, &err
) != 0) {
1820 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1821 "set %S error %s\n", TOPO_STORAGE_SAS_PHY_MASK
,
1822 topo_strerror(err
));
1825 /* Get the connector type information for the expander */
1826 if (nvlist_lookup_uint64(props
,
1827 SES_SC_PROP_CONNECTOR_TYPE
, &conntype
) != 0) {
1828 topo_mod_dprintf(mod
, "Failed to get prop %s.",
1829 TOPO_STORAGE_SAS_PHY_MASK
);
1832 for (i
= 0; ; i
++) {
1833 if (sas_connector_type_list
[i
].sct_type
==
1834 SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED
) {
1837 if (sas_connector_type_list
[i
].sct_type
==
1840 sas_connector_type_list
[i
].sct_name
;
1848 SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED
) {
1850 SAS_CONNECTOR_TYPE_RESERVED
;
1853 SAS_CONNECTOR_TYPE_NOT_DEFINED
;
1857 /* set the phy count prop of the expander. */
1858 if (topo_prop_set_string(tnode
, TOPO_PGROUP_STORAGE
,
1859 TOPO_STORAGE_SAS_CONNECTOR_TYPE
,
1860 TOPO_PROP_IMMUTABLE
, conntype_str
, &err
) != 0) {
1861 topo_mod_dprintf(mod
, "ses_set_expander_props: "
1862 "set %S error %s\n", TOPO_PROP_PHY_COUNT
,
1863 topo_strerror(err
));
1872 * Instantiate SAS expander nodes for a given ESC Electronics node(controller)
1877 ses_create_esc_sasspecific(ses_enum_data_t
*sdp
, ses_enum_node_t
*snp
,
1878 tnode_t
*pnode
, ses_enum_chassis_t
*cp
,
1881 topo_mod_t
*mod
= sdp
->sed_mod
;
1882 tnode_t
*exptn
, *contn
;
1884 sas_connector_phy_data_t connectors
[64] = {0};
1886 ses_enum_node_t
*ctlsnp
, *xsnp
, *consnp
;
1887 ses_node_t
*np
= snp
->sen_node
;
1888 nvlist_t
*props
, *psprops
;
1889 uint64_t index
, psindex
, conindex
, psstatus
, i
, j
, count
;
1890 int64_t cidxlist
[256] = {0};
1893 props
= ses_node_props(np
);
1895 if (nvlist_lookup_uint64(props
, SES_PROP_ELEMENT_ONLY_INDEX
,
1900 * For SES constroller node, check to see if there are
1901 * associated SAS expanders.
1905 for (ctlsnp
= topo_list_next(&cp
->sec_nodes
); ctlsnp
!= NULL
;
1906 ctlsnp
= topo_list_next(ctlsnp
)) {
1907 if (ctlsnp
->sen_type
== SES_ET_SAS_EXPANDER
) {
1909 if (ctlsnp
->sen_instance
> max
)
1910 max
= ctlsnp
->sen_instance
;
1915 * No SAS expander found notthing to process.
1920 topo_mod_dprintf(mod
, "%s Controller %d: creating "
1921 "%llu %s nodes", cp
->sec_csn
, index
, max
+ 1, SASEXPANDER
);
1924 * The max number represent the number of elements
1925 * deducted from the highest SES_PROP_ELEMENT_CLASS_INDEX
1926 * of SET_ET_SAS_EXPANDER type element.
1928 * There may be multiple ESC Electronics element(controllers)
1929 * within JBOD(typicall two for redundancy) and SAS expander
1930 * elements are associated with only one of them. We are
1931 * still creating the range based max number here.
1932 * That will cover the case that all expanders are associated
1933 * with one SES controller.
1935 if (dorange
&& topo_node_range_create(mod
, pnode
,
1936 SASEXPANDER
, 0, max
) != 0) {
1937 topo_mod_dprintf(mod
,
1938 "topo_node_create_range() failed: %s",
1939 topo_mod_errmsg(mod
));
1944 * Search exapnders with the parent index matching with
1945 * ESC Electronics element index.
1946 * Note the index used here is a global index across
1949 for (xsnp
= topo_list_next(&cp
->sec_nodes
); xsnp
!= NULL
;
1950 xsnp
= topo_list_next(xsnp
)) {
1951 if (xsnp
->sen_type
== SES_ET_SAS_EXPANDER
) {
1953 * get the parent ESC controller.
1955 psprops
= ses_node_props(xsnp
->sen_node
);
1956 if (nvlist_lookup_uint64(psprops
,
1957 SES_PROP_STATUS_CODE
, &psstatus
) == 0) {
1958 if (psstatus
== SES_ESC_NOT_INSTALLED
) {
1961 * Don't create a ndoe.
1967 * The element should have status code.
1968 * If not there is no way to find
1969 * out if the expander element exist or
1975 /* Get the physical parent index to compare. */
1976 if (nvlist_lookup_uint64(psprops
,
1977 LIBSES_PROP_PHYS_PARENT
, &psindex
) == 0) {
1978 if (index
== psindex
) {
1979 /* indentation moved forward */
1981 * Handle basic node information of SAS expander
1982 * element - binding to parent node and
1983 * allocating FMRI...
1985 if (ses_create_generic(sdp
, xsnp
, pnode
, pnode
, SASEXPANDER
,
1986 "SAS-EXPANDER", &exptn
) != 0)
1989 * Now handle SAS expander unique portion of node creation.
1990 * The max nubmer of the phy count is 256 since SES-2
1991 * defines as 1 byte field. The cidxlist has the same
1992 * number of elements.
1994 * We use size 64 array to store the connectors.
1995 * Typically a connectors associated with 4 phys so that
1996 * matches with the max number of connecters associated
1998 * The phy count goes up to 38 for Sun supported
2001 (void) memset(cidxlist
, 0, sizeof (int64_t) * 64);
2002 if (ses_set_expander_props(sdp
, xsnp
, pnode
, exptn
, &phycount
,
2005 * error on getting specific prop failed.
2006 * continue on. Note that the node is
2013 * count represetns the number of connectors discovered so far.
2016 (void) memset(connectors
, 0,
2017 sizeof (sas_connector_phy_data_t
) * 64);
2018 for (i
= 0; i
< phycount
; i
++) {
2019 if (cidxlist
[i
] != -1) {
2020 /* connector index is valid. */
2021 for (j
= 0; j
< count
; j
++) {
2022 if (connectors
[j
].scpd_index
==
2025 * Just update phy mask.
2026 * The postion for connector
2027 * index lists(cidxlist index)
2030 connectors
[j
].scpd_pm
=
2031 connectors
[j
].scpd_pm
|
2037 * If j and count matche a new connector
2041 /* add a new index and phy mask. */
2042 connectors
[count
].scpd_index
=
2044 connectors
[count
].scpd_pm
=
2045 connectors
[count
].scpd_pm
|
2053 * create range for the connector nodes.
2054 * The class index of the ses connector element
2055 * is set as the instance nubmer for the node.
2056 * Even though one expander may not have all connectors
2057 * are associated with we are creating the range with
2058 * max possible instance number.
2062 for (consnp
= topo_list_next(&cp
->sec_nodes
);
2063 consnp
!= NULL
; consnp
= topo_list_next(consnp
)) {
2064 if (consnp
->sen_type
== SES_ET_SAS_CONNECTOR
) {
2065 psprops
= ses_node_props(consnp
->sen_node
);
2067 if (consnp
->sen_instance
> max
)
2068 max
= consnp
->sen_instance
;
2073 * No SAS connector found nothing to process.
2078 if (dorange
&& topo_node_range_create(mod
, exptn
,
2079 RECEPTACLE
, 0, max
) != 0) {
2080 topo_mod_dprintf(mod
,
2081 "topo_node_create_range() failed: %s",
2082 topo_mod_errmsg(mod
));
2086 /* search matching connector element using the index. */
2087 for (i
= 0; i
< count
; i
++) {
2089 for (consnp
= topo_list_next(&cp
->sec_nodes
);
2090 consnp
!= NULL
; consnp
= topo_list_next(consnp
)) {
2091 if (consnp
->sen_type
== SES_ET_SAS_CONNECTOR
) {
2092 psprops
= ses_node_props(
2095 * Get the physical parent index to
2097 * The connector elements are children
2098 * of ESC Electronics element even
2099 * though we enumerate them under
2100 * an expander in libtopo.
2102 if (nvlist_lookup_uint64(psprops
,
2103 SES_PROP_ELEMENT_ONLY_INDEX
,
2106 connectors
[i
].scpd_index
) {
2114 /* now create a libtopo node. */
2116 /* Create generic props. */
2117 if (ses_create_generic(sdp
, consnp
, exptn
,
2118 topo_node_parent(exptn
),
2119 RECEPTACLE
, "RECEPTACLE", &contn
) !=
2123 /* Create connector specific props. */
2124 if (ses_set_connector_props(sdp
, consnp
,
2125 contn
, connectors
[i
].scpd_pm
) != 0) {
2130 /* end indentation change */
2140 * Instantiate any protocol specific portion of a node.
2144 ses_create_protocol_specific(ses_enum_data_t
*sdp
, ses_enum_node_t
*snp
,
2145 tnode_t
*pnode
, uint64_t type
, ses_enum_chassis_t
*cp
,
2149 if (type
== SES_ET_ESC_ELECTRONICS
) {
2150 /* create SAS specific children(expanders and connectors. */
2151 return (ses_create_esc_sasspecific(sdp
, snp
, pnode
, cp
,
2159 * Instantiate any children of a given type.
2162 ses_create_children(ses_enum_data_t
*sdp
, tnode_t
*pnode
, uint64_t type
,
2163 const char *nodename
, const char *defaultlabel
, ses_enum_chassis_t
*cp
,
2166 topo_mod_t
*mod
= sdp
->sed_mod
;
2169 ses_enum_node_t
*snp
;
2173 * First go through and count how many matching nodes we have.
2177 for (snp
= topo_list_next(&cp
->sec_nodes
); snp
!= NULL
;
2178 snp
= topo_list_next(snp
)) {
2179 if (snp
->sen_type
== type
) {
2181 if (snp
->sen_instance
> max
)
2182 max
= snp
->sen_instance
;
2187 * No enclosure should export both DEVICE and ARRAY_DEVICE elements.
2188 * Since we map both of these to 'disk', if an enclosure does this, we
2189 * just ignore the array elements.
2192 (type
== SES_ET_ARRAY_DEVICE
&& cp
->sec_hasdev
))
2195 topo_mod_dprintf(mod
, "%s: creating %llu %s nodes",
2196 cp
->sec_csn
, max
+ 1, nodename
);
2198 if (dorange
&& topo_node_range_create(mod
, pnode
,
2199 nodename
, 0, max
) != 0) {
2200 topo_mod_dprintf(mod
,
2201 "topo_node_create_range() failed: %s",
2202 topo_mod_errmsg(mod
));
2206 for (snp
= topo_list_next(&cp
->sec_nodes
); snp
!= NULL
;
2207 snp
= topo_list_next(snp
)) {
2208 if (snp
->sen_type
== type
) {
2210 * With flat layout of ses nodes there is no
2211 * way to find out the direct FRU for a node.
2212 * Passing NULL for fru topo node. Note that
2213 * ses_create_children_from_phys_tree() provides
2214 * the actual direct FRU for a node.
2216 if (ses_create_generic(sdp
, snp
, pnode
, NULL
,
2217 nodename
, defaultlabel
, &tn
) != 0)
2220 * For some SES element there may be protocol specific
2221 * information to process. Here we are processing
2222 * the association between enclosure controller and
2225 if (type
== SES_ET_ESC_ELECTRONICS
) {
2226 /* create SAS expander node */
2227 if (ses_create_protocol_specific(sdp
, snp
,
2228 tn
, type
, cp
, dorange
) != 0) {
2240 * Instantiate a new subchassis instance in the topology.
2243 ses_create_subchassis(ses_enum_data_t
*sdp
, tnode_t
*pnode
,
2244 ses_enum_chassis_t
*scp
)
2246 topo_mod_t
*mod
= sdp
->sed_mod
;
2249 nvlist_t
*auth
= NULL
, *fmri
= NULL
;
2250 uint64_t instance
= scp
->sec_instance
;
2255 ses_enum_target_t
*stp
;
2259 * Copy authority information from parent enclosure node
2261 if ((auth
= topo_mod_auth(mod
, pnode
)) == NULL
)
2265 * Record the subchassis serial number in the FMRI.
2266 * For now, we assume that logical id is the subchassis serial number.
2267 * If this assumption changes in future, then the following
2268 * piece of code will need to be updated via an RFE.
2270 if ((fmri
= topo_mod_hcfmri(mod
, pnode
, FM_HC_SCHEME_VERSION
,
2271 SUBCHASSIS
, (topo_instance_t
)instance
, NULL
, auth
, NULL
, NULL
,
2273 topo_mod_dprintf(mod
, "topo_mod_hcfmri() failed: %s",
2274 topo_mod_errmsg(mod
));
2278 if ((tn
= topo_node_bind(mod
, pnode
, SUBCHASSIS
,
2279 instance
, fmri
)) == NULL
) {
2280 topo_mod_dprintf(mod
, "topo_node_bind() failed: %s",
2281 topo_mod_errmsg(mod
));
2285 props
= ses_node_props(scp
->sec_enclosure
);
2288 * Look for the subchassis label in the following order:
2290 * <ses-class-description> <instance>
2291 * <default-type-label> <instance>
2293 * For subchassis, the default label is "SUBCHASSIS"
2295 if (nvlist_lookup_string(props
, SES_PROP_DESCRIPTION
, &desc
) != 0 ||
2297 if (nvlist_lookup_string(props
, SES_PROP_CLASS_DESCRIPTION
,
2298 &desc
) == 0 && desc
[0] != '\0')
2299 (void) snprintf(label
, sizeof (label
), "%s %llu", desc
,
2302 (void) snprintf(label
, sizeof (label
),
2303 "SUBCHASSIS %llu", instance
);
2307 if (topo_node_label_set(tn
, desc
, &err
) != 0)
2310 if (ses_set_standard_props(mod
, NULL
, tn
, NULL
,
2311 ses_node_id(scp
->sec_enclosure
), scp
->sec_target
->set_devpath
) != 0)
2315 * Set the 'chassis-type' property for this subchassis. This is either
2316 * 'ses-class-description' or 'subchassis'.
2318 if (nvlist_lookup_string(props
, SES_PROP_CLASS_DESCRIPTION
, &desc
) != 0)
2319 desc
= "subchassis";
2321 if (topo_prop_set_string(tn
, TOPO_PGROUP_SES
,
2322 TOPO_PROP_CHASSIS_TYPE
, TOPO_PROP_IMMUTABLE
, desc
, &err
) != 0) {
2323 topo_mod_dprintf(mod
, "failed to create property %s: %s\n",
2324 TOPO_PROP_CHASSIS_TYPE
, topo_strerror(err
));
2329 * For enclosures, we want to include all possible targets (for upgrade
2332 for (i
= 0, stp
= topo_list_next(&scp
->sec_targets
); stp
!= NULL
;
2333 stp
= topo_list_next(stp
), i
++)
2337 paths
= alloca(i
* sizeof (char *));
2339 for (i
= 0, stp
= topo_list_next(&scp
->sec_targets
); stp
!= NULL
;
2340 stp
= topo_list_next(stp
), i
++)
2341 paths
[i
] = stp
->set_devpath
;
2343 if (topo_prop_set_string_array(tn
, TOPO_PGROUP_SES
,
2344 TOPO_PROP_PATHS
, TOPO_PROP_IMMUTABLE
, (const char **)paths
,
2346 topo_mod_dprintf(mod
, "failed to create property %s: %s\n",
2347 TOPO_PROP_PATHS
, topo_strerror(err
));
2351 if (topo_method_register(mod
, tn
, ses_enclosure_methods
) != 0) {
2352 topo_mod_dprintf(mod
, "topo_method_register() failed: %s",
2353 topo_mod_errmsg(mod
));
2358 * Create the nodes for controllers and bays.
2360 if (ses_create_children(sdp
, tn
, SES_ET_ESC_ELECTRONICS
,
2361 CONTROLLER
, "CONTROLLER", scp
, B_TRUE
) != 0 ||
2362 ses_create_children(sdp
, tn
, SES_ET_DEVICE
,
2363 BAY
, "BAY", scp
, B_TRUE
) != 0 ||
2364 ses_create_children(sdp
, tn
, SES_ET_ARRAY_DEVICE
,
2365 BAY
, "BAY", scp
, B_TRUE
) != 0)
2377 * Function we use to insert a node.
2380 ses_phys_tree_insert(topo_mod_t
*mod
, ses_phys_tree_t
**sproot
,
2381 ses_phys_tree_t
*child
)
2383 uint64_t ppindex
, eindex
, pindex
;
2384 ses_phys_tree_t
*node_ptr
;
2387 assert(sproot
!= NULL
);
2388 assert(child
!= NULL
);
2390 if (*sproot
== NULL
) {
2395 pindex
= child
->spt_pindex
;
2396 ppindex
= (*sproot
)->spt_pindex
;
2397 eindex
= (*sproot
)->spt_eonlyindex
;
2400 * If the element only index of the root is same as the physical
2401 * parent index of a node to be added, add the node as a child of
2404 if (eindex
== pindex
) {
2405 (void) ses_phys_tree_insert(mod
, &(*sproot
)->spt_child
, child
);
2406 child
->spt_parent
= *sproot
;
2407 } else if (ppindex
== pindex
) {
2409 * if the physical parent of the current root and the child
2410 * is same, then this should be a sibling node.
2411 * Siblings can be different element types and arrange
2414 if ((*sproot
)->spt_senumnode
->sen_type
==
2415 child
->spt_senumnode
->sen_type
) {
2416 child
->spt_sibling
= *sproot
;
2419 /* add a node in front of matching element type. */
2421 while (node_ptr
->spt_sibling
!= NULL
) {
2422 if (node_ptr
->spt_sibling
->
2423 spt_senumnode
->sen_type
==
2424 child
->spt_senumnode
->sen_type
) {
2425 child
->spt_sibling
=
2426 node_ptr
->spt_sibling
;
2427 node_ptr
->spt_sibling
= child
;
2430 node_ptr
= node_ptr
->spt_sibling
;
2432 /* no matching. Add the child at the end. */
2433 if (node_ptr
->spt_sibling
== NULL
) {
2434 node_ptr
->spt_sibling
= child
;
2437 child
->spt_parent
= (*sproot
)->spt_parent
;
2440 * The root and the node is not directly related.
2441 * Try to insert to the child sub-tree first and then try to
2442 * insert to the sibling sub-trees. If fails for both
2443 * the caller will retry insertion later.
2445 if ((*sproot
)->spt_child
) {
2446 ret
= ses_phys_tree_insert(mod
, &(*sproot
)->spt_child
,
2449 if ((*sproot
)->spt_child
== NULL
|| ret
!= 0) {
2450 if ((*sproot
)->spt_sibling
) {
2451 ret
= ses_phys_tree_insert(mod
,
2452 &(*sproot
)->spt_sibling
, child
);
2463 * Construct tree view of ses elements through parent phyiscal element index.
2464 * The root of tree is already constructed using the enclosure element.
2467 ses_construct_phys_tree(ses_enum_data_t
*sdp
, ses_enum_chassis_t
*cp
,
2468 ses_phys_tree_t
*sproot
)
2470 ses_enum_node_t
*snp
;
2471 ses_phys_tree_t
*child
;
2472 ses_phys_tree_t
*u_watch
= NULL
;
2473 ses_phys_tree_t
*u_head
= NULL
;
2474 ses_phys_tree_t
*u_tail
= NULL
;
2475 int u_inserted
= 0, u_left
= 0;
2477 topo_mod_t
*mod
= sdp
->sed_mod
;
2479 for (snp
= topo_list_next(&cp
->sec_nodes
); snp
!= NULL
;
2480 snp
= topo_list_next(snp
)) {
2481 if ((child
= topo_mod_zalloc(mod
,
2482 sizeof (ses_phys_tree_t
))) == NULL
) {
2483 topo_mod_dprintf(mod
,
2484 "failed to allocate root.");
2487 child
->spt_snode
= snp
->sen_node
;
2488 props
= ses_node_props(snp
->sen_node
);
2489 if (nvlist_lookup_uint64(props
,
2490 LIBSES_PROP_PHYS_PARENT
, &child
->spt_pindex
) != 0) {
2492 * the prop should exist. continue to see if
2493 * we can build a partial tree with other elements.
2495 topo_mod_dprintf(mod
,
2496 "ses_construct_phys_tree(): Failed to find prop %s "
2497 "on ses element type %d and instance %d "
2498 "(CSN %s).", LIBSES_PROP_PHYS_PARENT
,
2499 snp
->sen_type
, snp
->sen_instance
, cp
->sec_csn
);
2500 topo_mod_free(mod
, child
, sizeof (ses_phys_tree_t
));
2503 if (nvlist_lookup_boolean_value(props
,
2504 LIBSES_PROP_FRU
, &child
->spt_isfru
) != 0) {
2505 topo_mod_dprintf(mod
,
2506 "ses_construct_phys_tree(): Failed to "
2507 "find prop %s on ses element type %d "
2508 "and instance %d (CSN %s).",
2510 snp
->sen_type
, snp
->sen_instance
,
2513 * Ignore if the prop doesn't exist.
2514 * Note that the enclosure itself should be
2515 * a FRU so if no FRU found the enclosure FRU
2516 * can be a direct FRU.
2519 verify(nvlist_lookup_uint64(props
,
2520 SES_PROP_ELEMENT_ONLY_INDEX
,
2521 &child
->spt_eonlyindex
) == 0);
2522 verify(nvlist_lookup_uint64(props
,
2523 SES_PROP_ELEMENT_CLASS_INDEX
,
2524 &child
->spt_cindex
) == 0);
2526 child
->spt_senumnode
= snp
;
2527 if (ses_phys_tree_insert(mod
, &sproot
, child
) != 0) {
2528 /* collect unresolved element to process later. */
2529 if (u_head
== NULL
) {
2533 child
->spt_sibling
= u_head
;
2540 * The parent of a child node may not be inserted yet.
2541 * Trying to insert the child until no child is left or
2542 * no child is not added further. For the latter
2543 * the hierarchical relationship between elements
2544 * should be checked through SUNW,FRUID page.
2545 * u_watch is a watch dog to check the prgress of unresolved
2551 u_head
= u_head
->spt_sibling
;
2554 child
->spt_sibling
= NULL
;
2555 if (ses_phys_tree_insert(mod
, &sproot
, child
) != 0) {
2556 u_tail
->spt_sibling
= child
;
2558 if (child
== u_watch
) {
2560 * We just scanned one round for the
2561 * unresolved list. Check to see whether we
2562 * have nodes inserted, if none, we should
2563 * break in case of an indefinite loop.
2565 if (u_inserted
== 0) {
2567 * Indicate there is unhandled node.
2568 * Chain free the whole unsolved
2580 * We just inserted one rpnode, increment the
2581 * unsolved_inserted counter. We will utilize this
2582 * counter to detect an indefinite insertion loop.
2585 if (child
== u_watch
) {
2587 * watch dog node itself is inserted.
2588 * Set it to the tail and refresh the watching.
2597 /* check if there is left out unresolved nodes. */
2599 topo_mod_dprintf(mod
, "ses_construct_phys_tree(): "
2600 "Failed to construct physical view of the following "
2601 "ses elements of Chassis CSN %s.", cp
->sec_csn
);
2603 u_tail
= u_head
->spt_sibling
;
2604 topo_mod_dprintf(mod
,
2605 "\telement type (%d) and instance (%d)",
2606 u_head
->spt_senumnode
->sen_type
,
2607 u_head
->spt_senumnode
->sen_instance
);
2608 topo_mod_free(mod
, u_head
, sizeof (ses_phys_tree_t
));
2618 * Free the whole phys tree.
2620 static void ses_phys_tree_free(topo_mod_t
*mod
, ses_phys_tree_t
*sproot
)
2625 /* Free child tree. */
2626 if (sproot
->spt_child
) {
2627 ses_phys_tree_free(mod
, sproot
->spt_child
);
2630 /* Free sibling trees. */
2631 if (sproot
->spt_sibling
) {
2632 ses_phys_tree_free(mod
, sproot
->spt_sibling
);
2635 /* Free root node itself. */
2636 topo_mod_free(mod
, sproot
, sizeof (ses_phys_tree_t
));
2640 * Parses phys_enum_type table to get the index of the given type.
2643 is_type_enumerated(ses_phys_tree_t
*node
, int *index
)
2647 for (i
= 0; i
< N_PHYS_ENUM_TYPES
; i
++) {
2648 if (node
->spt_senumnode
->sen_type
==
2649 phys_enum_type_list
[i
].pet_type
) {
2658 * Recusrive routine for top-down enumeration of the tree.
2661 ses_enumerate_node(ses_enum_data_t
*sdp
, tnode_t
*pnode
, ses_enum_chassis_t
*cp
,
2662 ses_phys_tree_t
*parent
, int mrange
[])
2664 topo_mod_t
*mod
= sdp
->sed_mod
;
2665 ses_phys_tree_t
*child
= NULL
;
2666 int i
, ret
= 0, ret_ch
;
2667 uint64_t prevtype
= SES_ET_UNSPECIFIED
;
2668 ses_phys_tree_t
*dirfru
= NULL
;
2669 tnode_t
*tn
= NULL
, *frutn
= NULL
;
2671 if (parent
== NULL
) {
2675 for (child
= parent
->spt_child
; child
!= NULL
;
2676 child
= child
->spt_sibling
) {
2677 if (is_type_enumerated(child
, &i
)) {
2678 if (prevtype
!= phys_enum_type_list
[i
].pet_type
) {
2679 /* check if range needs to be created. */
2680 if (phys_enum_type_list
[i
].pet_dorange
&&
2681 topo_node_range_create(mod
, pnode
,
2682 phys_enum_type_list
[i
].pet_nodename
, 0,
2684 topo_mod_dprintf(mod
,
2685 "topo_node_create_range() failed: "
2686 "%s", topo_mod_errmsg(mod
));
2689 prevtype
= phys_enum_type_list
[i
].pet_type
;
2692 if (!(child
->spt_isfru
)) {
2693 for (dirfru
= parent
; dirfru
!= NULL
;
2694 dirfru
= dirfru
->spt_parent
) {
2695 if (dirfru
->spt_isfru
) {
2699 /* found direct FRU node. */
2701 frutn
= dirfru
->spt_tnode
;
2709 if (ses_create_generic(sdp
, child
->spt_senumnode
,
2710 pnode
, frutn
, phys_enum_type_list
[i
].pet_nodename
,
2711 phys_enum_type_list
[i
].pet_defaultlabel
, &tn
) != 0)
2714 child
->spt_tnode
= tn
;
2716 * For some SES element there may be protocol specific
2717 * information to process. Here we are processing
2718 * the association between enclosure controller and
2721 if (phys_enum_type_list
[i
].pet_type
==
2722 SES_ET_ESC_ELECTRONICS
) {
2723 /* create SAS expander node */
2724 if (ses_create_protocol_specific(sdp
,
2725 child
->spt_senumnode
, tn
,
2726 phys_enum_type_list
[i
].pet_type
,
2727 cp
, phys_enum_type_list
[i
].pet_dorange
) !=
2735 ret_ch
= ses_enumerate_node(sdp
, tn
, cp
, child
, mrange
);
2737 ret
= ret_ch
; /* there was an error and set the ret. */
2744 * Instantiate types of nodes that are specified in the hierarchy
2745 * element type list.
2748 ses_create_children_from_phys_tree(ses_enum_data_t
*sdp
, tnode_t
*pnode
,
2749 ses_enum_chassis_t
*cp
, ses_phys_tree_t
*phys_tree
)
2751 topo_mod_t
*mod
= sdp
->sed_mod
;
2752 int mrange
[N_PHYS_ENUM_TYPES
] = { 0 };
2753 ses_enum_node_t
*snp
;
2757 * First get max range for each type of element to be enumerated.
2759 for (i
= 0; i
< N_PHYS_ENUM_TYPES
; i
++) {
2760 if (phys_enum_type_list
[i
].pet_dorange
) {
2761 for (snp
= topo_list_next(&cp
->sec_nodes
); snp
!= NULL
;
2762 snp
= topo_list_next(snp
)) {
2763 if (snp
->sen_type
==
2764 phys_enum_type_list
[i
].pet_type
) {
2765 if (snp
->sen_instance
> mrange
[i
])
2773 topo_mod_dprintf(mod
, "%s: creating nodes from FRU hierarchy tree.",
2776 if ((ret
= ses_enumerate_node(sdp
, pnode
, cp
, phys_tree
, mrange
)) !=
2778 topo_mod_dprintf(mod
,
2779 "ses_create_children_from_phys_tree() failed: ");
2787 * Instantiate a new chassis instance in the topology.
2790 ses_create_chassis(ses_enum_data_t
*sdp
, tnode_t
*pnode
, ses_enum_chassis_t
*cp
)
2792 topo_mod_t
*mod
= sdp
->sed_mod
;
2794 char *raw_manufacturer
, *raw_model
, *raw_revision
;
2795 char *manufacturer
= NULL
, *model
= NULL
, *product
= NULL
;
2796 char *revision
= NULL
;
2801 nvlist_t
*fmri
= NULL
, *auth
= NULL
;
2803 ses_enum_node_t
*snp
;
2804 ses_enum_target_t
*stp
;
2805 ses_enum_chassis_t
*scp
;
2807 uint64_t sc_count
= 0, pindex
;
2808 ses_phys_tree_t
*sproot
= NULL
;
2814 * Ignore any internal enclosures.
2816 if (cp
->sec_internal
)
2820 * Check to see if there are any devices presennt in the chassis. If
2821 * not, ignore the chassis alltogether. This is most useful for
2822 * ignoring internal HBAs that present a SES target but don't actually
2823 * manage any of the devices.
2825 for (snp
= topo_list_next(&cp
->sec_nodes
); snp
!= NULL
;
2826 snp
= topo_list_next(snp
)) {
2827 if (snp
->sen_type
== SES_ET_DEVICE
||
2828 snp
->sen_type
== SES_ET_ARRAY_DEVICE
)
2835 props
= ses_node_props(cp
->sec_enclosure
);
2838 * We use the following property mappings:
2840 * manufacturer vendor-id
2842 * serial-number libses-chassis-serial
2844 verify(nvlist_lookup_string(props
, SES_EN_PROP_VID
,
2845 &raw_manufacturer
) == 0);
2846 verify(nvlist_lookup_string(props
, SES_EN_PROP_PID
, &raw_model
) == 0);
2847 verify(nvlist_lookup_string(props
, SES_EN_PROP_REV
,
2848 &raw_revision
) == 0);
2849 verify(nvlist_lookup_string(props
, LIBSES_EN_PROP_CSN
, &serial
) == 0);
2852 * To construct the authority information, we 'clean' each string by
2853 * removing any offensive characters and trimmming whitespace. For the
2854 * 'product-id', we use a concatenation of 'manufacturer-model'. We
2855 * also take the numerical serial number and convert it to a string.
2857 if ((manufacturer
= disk_auth_clean(mod
, raw_manufacturer
)) == NULL
||
2858 (model
= disk_auth_clean(mod
, raw_model
)) == NULL
||
2859 (revision
= disk_auth_clean(mod
, raw_revision
)) == NULL
) {
2863 prodlen
= strlen(manufacturer
) + strlen(model
) + 2;
2864 if ((product
= topo_mod_alloc(mod
, prodlen
)) == NULL
)
2867 (void) snprintf(product
, prodlen
, "%s-%s", manufacturer
, model
);
2870 * Construct the topo node and bind it to our parent.
2872 if (topo_mod_nvalloc(mod
, &auth
, NV_UNIQUE_NAME
) != 0)
2875 if (nvlist_add_string(auth
, FM_FMRI_AUTH_PRODUCT
, product
) != 0 ||
2876 nvlist_add_string(auth
, FM_FMRI_AUTH_CHASSIS
, serial
) != 0) {
2877 (void) topo_mod_seterrno(mod
, EMOD_NVL_INVAL
);
2882 * We pass NULL for the parent FMRI because there is no resource
2883 * associated with it. For the toplevel enclosure, we leave the
2884 * serial/part/revision portions empty, which are reserved for
2885 * individual components within the chassis.
2887 if ((fmri
= topo_mod_hcfmri(mod
, NULL
, FM_HC_SCHEME_VERSION
,
2888 SES_ENCLOSURE
, cp
->sec_instance
, NULL
, auth
,
2889 model
, revision
, serial
)) == NULL
) {
2890 topo_mod_dprintf(mod
, "topo_mod_hcfmri() failed: %s",
2891 topo_mod_errmsg(mod
));
2895 if ((tn
= topo_node_bind(mod
, pnode
, SES_ENCLOSURE
,
2896 cp
->sec_instance
, fmri
)) == NULL
) {
2897 topo_mod_dprintf(mod
, "topo_node_bind() failed: %s",
2898 topo_mod_errmsg(mod
));
2902 if (topo_method_register(mod
, tn
, ses_enclosure_methods
) != 0) {
2903 topo_mod_dprintf(mod
,
2904 "topo_method_register() failed: %s",
2905 topo_mod_errmsg(mod
));
2909 if (ses_set_standard_props(mod
, NULL
, tn
, auth
,
2910 ses_node_id(cp
->sec_enclosure
), cp
->sec_target
->set_devpath
) != 0)
2914 * For enclosures, we want to include all possible targets (for upgrade
2917 for (i
= 0, stp
= topo_list_next(&cp
->sec_targets
); stp
!= NULL
;
2918 stp
= topo_list_next(stp
), i
++)
2922 paths
= alloca(i
* sizeof (char *));
2924 for (i
= 0, stp
= topo_list_next(&cp
->sec_targets
); stp
!= NULL
;
2925 stp
= topo_list_next(stp
), i
++)
2926 paths
[i
] = stp
->set_devpath
;
2929 if (topo_prop_set_string_array(tn
, TOPO_PGROUP_SES
,
2930 TOPO_PROP_PATHS
, TOPO_PROP_IMMUTABLE
, (const char **)paths
,
2932 topo_mod_dprintf(mod
,
2933 "failed to create property %s: %s\n",
2934 TOPO_PROP_PATHS
, topo_strerror(err
));
2938 if (nvlist_lookup_uint64(props
,
2939 LIBSES_PROP_PHYS_PARENT
, &pindex
) == 0) {
2940 start
= gethrtime(); /* to mearusre performance */
2942 * The enclosure is supported through SUNW,FRUID.
2943 * Need to enumerate the nodes through hierarchical order.
2945 if ((sproot
= topo_mod_zalloc(mod
,
2946 sizeof (ses_phys_tree_t
))) == NULL
) {
2947 topo_mod_dprintf(mod
,
2948 "failed to allocate root: %s\n",
2949 topo_strerror(err
));
2952 sproot
->spt_pindex
= pindex
;
2953 if (nvlist_lookup_boolean_value(props
,
2954 LIBSES_PROP_FRU
, &sproot
->spt_isfru
) != 0) {
2955 topo_mod_dprintf(mod
,
2956 "ses_create_chassis(): Failed to find prop %s "
2957 "on enclosure element (CSN %s).",
2958 LIBSES_PROP_FRU
, cp
->sec_csn
);
2959 /* an enclosure should be a FRU. continue to process. */
2960 sproot
->spt_isfru
= B_TRUE
;
2962 if (nvlist_lookup_uint64(props
,
2963 SES_PROP_ELEMENT_ONLY_INDEX
,
2964 &sproot
->spt_eonlyindex
) != 0) {
2965 topo_mod_dprintf(mod
,
2966 "ses_create_chassis(): Failed to find prop %s "
2967 "on enclosure element (CSN %s).",
2968 LIBSES_PROP_PHYS_PARENT
, cp
->sec_csn
);
2969 topo_mod_free(mod
, sproot
, sizeof (ses_phys_tree_t
));
2972 if (sproot
->spt_pindex
!= sproot
->spt_eonlyindex
) {
2973 topo_mod_dprintf(mod
, "ses_create_chassis(): "
2974 "Enclosure element(CSN %s) should have "
2975 "itself as the parent to be the root node "
2976 "of FRU hierarchical tree.)", cp
->sec_csn
);
2977 topo_mod_free(mod
, sproot
, sizeof (ses_phys_tree_t
));
2980 sproot
->spt_snode
= cp
->sec_enclosure
;
2981 sproot
->spt_tnode
= tn
;
2982 /* construct a tree. */
2983 if (ses_construct_phys_tree(sdp
, cp
, sproot
) != 0) {
2984 topo_mod_dprintf(mod
, "ses_create_chassis(): "
2985 "Failed to construct FRU hierarchical "
2986 "tree on enclosure (CSN %s.)",
2990 /* enumerate elements from the tree. */
2991 if (ses_create_children_from_phys_tree(sdp
, tn
, cp
,
2993 topo_mod_dprintf(mod
, "ses_create_chassis(): "
2994 "Failed to create children topo nodes out "
2995 "of FRU hierarchical tree on enclosure "
2996 "(CSN %s).", cp
->sec_csn
);
2998 /* destroy the phys tree. */
2999 ses_phys_tree_free(mod
, sproot
);
3003 duration
= end
- start
;
3004 duration
/= HR_SECOND
;
3005 topo_mod_dprintf(mod
,
3006 "FRU boundary tree based enumeration: %.6f seconds",
3010 * Create the nodes for power supplies, fans, controllers and
3011 * devices. Note that SAS exopander nodes and connector nodes
3012 * are handled through protocol specific processing of
3015 if (ses_create_children(sdp
, tn
, SES_ET_POWER_SUPPLY
,
3016 PSU
, "PSU", cp
, B_TRUE
) != 0 ||
3017 ses_create_children(sdp
, tn
, SES_ET_COOLING
,
3018 FAN
, "FAN", cp
, B_TRUE
) != 0 ||
3019 ses_create_children(sdp
, tn
, SES_ET_ESC_ELECTRONICS
,
3020 CONTROLLER
, "CONTROLLER", cp
, B_TRUE
) != 0 ||
3021 ses_create_children(sdp
, tn
, SES_ET_DEVICE
,
3022 BAY
, "BAY", cp
, B_TRUE
) != 0 ||
3023 ses_create_children(sdp
, tn
, SES_ET_ARRAY_DEVICE
,
3024 BAY
, "BAY", cp
, B_TRUE
) != 0)
3028 if (cp
->sec_maxinstance
>= 0 &&
3029 (topo_node_range_create(mod
, tn
, SUBCHASSIS
, 0,
3030 cp
->sec_maxinstance
) != 0)) {
3031 topo_mod_dprintf(mod
, "topo_node_create_range() failed: %s",
3032 topo_mod_errmsg(mod
));
3036 for (scp
= topo_list_next(&cp
->sec_subchassis
); scp
!= NULL
;
3037 scp
= topo_list_next(scp
)) {
3039 if (ses_create_subchassis(sdp
, tn
, scp
) != 0)
3042 topo_mod_dprintf(mod
, "created Subchassis node with "
3043 "instance %u\nand target (%s) under Chassis with CSN %s",
3044 scp
->sec_instance
, scp
->sec_target
->set_devpath
,
3050 topo_mod_dprintf(mod
, "%s: created %llu %s nodes",
3051 cp
->sec_csn
, sc_count
, SUBCHASSIS
);
3053 cp
->sec_target
->set_refcount
++;
3054 topo_node_setspecific(tn
, cp
->sec_target
);
3058 topo_mod_strfree(mod
, manufacturer
);
3059 topo_mod_strfree(mod
, model
);
3060 topo_mod_strfree(mod
, revision
);
3061 topo_mod_strfree(mod
, product
);
3069 * Create a bay node explicitly enumerated via XML.
3072 ses_create_bays(ses_enum_data_t
*sdp
, tnode_t
*pnode
)
3074 topo_mod_t
*mod
= sdp
->sed_mod
;
3075 ses_enum_chassis_t
*cp
;
3078 * Iterate over chassis looking for an internal enclosure. This
3079 * property is set via a vendor-specific plugin, and there should only
3080 * ever be a single internal chassis in a system.
3082 for (cp
= topo_list_next(&sdp
->sed_chassis
); cp
!= NULL
;
3083 cp
= topo_list_next(cp
)) {
3084 if (cp
->sec_internal
)
3089 topo_mod_dprintf(mod
, "failed to find internal chassis\n");
3093 if (ses_create_children(sdp
, pnode
, SES_ET_DEVICE
,
3094 BAY
, "BAY", cp
, B_FALSE
) != 0 ||
3095 ses_create_children(sdp
, pnode
, SES_ET_ARRAY_DEVICE
,
3096 BAY
, "BAY", cp
, B_FALSE
) != 0)
3103 * Initialize chassis or subchassis.
3106 ses_init_chassis(topo_mod_t
*mod
, ses_enum_data_t
*sdp
, ses_enum_chassis_t
*pcp
,
3107 ses_enum_chassis_t
*cp
, ses_node_t
*np
, nvlist_t
*props
,
3108 uint64_t subchassis
, ses_chassis_type_e flags
)
3110 boolean_t internal
, ident
;
3112 assert((flags
& (SES_NEW_CHASSIS
| SES_NEW_SUBCHASSIS
|
3113 SES_DUP_CHASSIS
| SES_DUP_SUBCHASSIS
)) != 0);
3117 assert(props
!= NULL
);
3119 if (flags
& (SES_NEW_SUBCHASSIS
| SES_DUP_SUBCHASSIS
))
3120 assert(pcp
!= NULL
);
3122 topo_mod_dprintf(mod
, "ses_init_chassis: %s: index %llu, flags (%d)",
3123 sdp
->sed_name
, subchassis
, flags
);
3125 if (flags
& (SES_NEW_CHASSIS
| SES_NEW_SUBCHASSIS
)) {
3127 topo_mod_dprintf(mod
, "new chassis/subchassis");
3128 if (nvlist_lookup_boolean_value(props
,
3129 LIBSES_EN_PROP_INTERNAL
, &internal
) == 0)
3130 cp
->sec_internal
= internal
;
3132 cp
->sec_enclosure
= np
;
3133 cp
->sec_target
= sdp
->sed_target
;
3135 if (flags
& SES_NEW_CHASSIS
) {
3136 if (!cp
->sec_internal
)
3137 cp
->sec_instance
= sdp
->sed_instance
++;
3138 topo_list_append(&sdp
->sed_chassis
, cp
);
3140 if (subchassis
!= NO_SUBCHASSIS
)
3141 cp
->sec_instance
= subchassis
;
3143 cp
->sec_instance
= pcp
->sec_scinstance
++;
3145 if (cp
->sec_instance
> pcp
->sec_maxinstance
)
3146 pcp
->sec_maxinstance
= cp
->sec_instance
;
3148 topo_list_append(&pcp
->sec_subchassis
, cp
);
3152 topo_mod_dprintf(mod
, "dup chassis/subchassis");
3153 if (nvlist_lookup_boolean_value(props
,
3154 SES_PROP_IDENT
, &ident
) == 0) {
3155 topo_mod_dprintf(mod
, "overriding enclosure node");
3157 cp
->sec_enclosure
= np
;
3158 cp
->sec_target
= sdp
->sed_target
;
3162 topo_list_append(&cp
->sec_targets
, sdp
->sed_target
);
3163 sdp
->sed_current
= cp
;
3169 * Gather nodes from the current SES target into our chassis list, merging the
3170 * results if necessary.
3172 static ses_walk_action_t
3173 ses_enum_gather(ses_node_t
*np
, void *data
)
3175 nvlist_t
*props
= ses_node_props(np
);
3176 ses_enum_data_t
*sdp
= data
;
3177 topo_mod_t
*mod
= sdp
->sed_mod
;
3178 ses_enum_chassis_t
*cp
, *scp
;
3179 ses_enum_node_t
*snp
;
3180 ses_alt_node_t
*sap
;
3182 uint64_t instance
, type
;
3183 uint64_t prevstatus
, status
;
3185 uint64_t subchassis
= NO_SUBCHASSIS
;
3187 if (ses_node_type(np
) == SES_NODE_ENCLOSURE
) {
3189 * If we have already identified the chassis for this target,
3190 * then this is a secondary enclosure and we should ignore it,
3191 * along with the rest of the tree (since this is depth-first).
3193 if (sdp
->sed_current
!= NULL
)
3194 return (SES_WALK_ACTION_TERMINATE
);
3197 * Go through the list of chassis we have seen so far and see
3198 * if this serial number matches one of the known values.
3199 * If so, check whether this enclosure is a subchassis.
3201 if (nvlist_lookup_string(props
, LIBSES_EN_PROP_CSN
,
3203 return (SES_WALK_ACTION_TERMINATE
);
3205 (void) nvlist_lookup_uint64(props
, LIBSES_EN_PROP_SUBCHASSIS_ID
,
3208 topo_mod_dprintf(mod
, "ses_enum_gather: Enclosure Node (%s) "
3209 "CSN (%s), subchassis (%llu)", sdp
->sed_name
, csn
,
3213 * We need to determine whether this enclosure node
3214 * represents a chassis or a subchassis. Since we may
3215 * receive the enclosure nodes in a non-deterministic
3216 * manner, we need to account for all possible combinations:
3217 * 1. Chassis for the current CSN has not yet been
3219 * 1.1 This is a new chassis:
3220 * allocate and instantiate the chassis
3221 * 1.2 This is a new subchassis:
3222 * allocate a placeholder chassis
3223 * allocate and instantiate the subchassis
3224 * link the subchassis to the chassis
3225 * 2. Chassis for the current CSN has been allocated
3226 * 2.1 This is a duplicate chassis enclosure
3227 * check whether to override old chassis
3228 * append to chassis' target list
3229 * 2.2 Only placeholder chassis exists
3230 * fill in the chassis fields
3231 * 2.3 This is a new subchassis
3232 * allocate and instantiate the subchassis
3233 * link the subchassis to the chassis
3234 * 2.4 This is a duplicate subchassis enclosure
3235 * check whether to override old chassis
3236 * append to chassis' target list
3239 for (cp
= topo_list_next(&sdp
->sed_chassis
); cp
!= NULL
;
3240 cp
= topo_list_next(cp
))
3241 if (strcmp(cp
->sec_csn
, csn
) == 0)
3245 /* 1. Haven't seen a chassis with this CSN before */
3247 if ((cp
= topo_mod_zalloc(mod
,
3248 sizeof (ses_enum_chassis_t
))) == NULL
)
3251 cp
->sec_scinstance
= SES_STARTING_SUBCHASSIS
;
3252 cp
->sec_maxinstance
= -1;
3255 if (subchassis
== NO_SUBCHASSIS
) {
3256 /* 1.1 This is a new chassis */
3258 topo_mod_dprintf(mod
, "%s: Initialize new "
3259 "chassis with CSN %s", sdp
->sed_name
, csn
);
3261 if (ses_init_chassis(mod
, sdp
, NULL
, cp
,
3262 np
, props
, NO_SUBCHASSIS
,
3263 SES_NEW_CHASSIS
) < 0)
3266 /* 1.2 This is a new subchassis */
3268 topo_mod_dprintf(mod
, "%s: Initialize new "
3269 "subchassis with CSN %s and index %llu",
3270 sdp
->sed_name
, csn
, subchassis
);
3272 if ((scp
= topo_mod_zalloc(mod
,
3273 sizeof (ses_enum_chassis_t
))) == NULL
)
3278 if (ses_init_chassis(mod
, sdp
, cp
, scp
, np
,
3279 props
, subchassis
, SES_NEW_SUBCHASSIS
) < 0)
3284 * We have a chassis or subchassis with this CSN. If
3285 * it's a chassis, we must check to see whether it is
3286 * a placeholder previously created because we found a
3287 * subchassis with this CSN. We will know that because
3288 * the sec_target value will not be set; it is set only
3289 * in ses_init_chassis(). In that case, initialise it
3290 * as a new chassis; otherwise, it's a duplicate and we
3291 * need to append only.
3293 if (subchassis
== NO_SUBCHASSIS
) {
3294 if (cp
->sec_target
!= NULL
) {
3295 /* 2.1 This is a duplicate chassis */
3297 topo_mod_dprintf(mod
, "%s: Append "
3298 "duplicate chassis with CSN (%s)",
3299 sdp
->sed_name
, csn
);
3301 if (ses_init_chassis(mod
, sdp
, NULL
, cp
,
3302 np
, props
, NO_SUBCHASSIS
,
3303 SES_DUP_CHASSIS
) < 0)
3306 /* Placeholder chassis - init it up */
3307 topo_mod_dprintf(mod
, "%s: Initialize"
3308 "placeholder chassis with CSN %s",
3309 sdp
->sed_name
, csn
);
3311 if (ses_init_chassis(mod
, sdp
, NULL
,
3312 cp
, np
, props
, NO_SUBCHASSIS
,
3313 SES_NEW_CHASSIS
) < 0)
3318 /* This is a subchassis */
3320 for (scp
= topo_list_next(&cp
->sec_subchassis
);
3321 scp
!= NULL
; scp
= topo_list_next(scp
))
3322 if (scp
->sec_instance
== subchassis
)
3326 /* 2.3 This is a new subchassis */
3328 topo_mod_dprintf(mod
, "%s: Initialize "
3329 "new subchassis with CSN (%s) "
3331 sdp
->sed_name
, csn
);
3333 if ((scp
= topo_mod_zalloc(mod
,
3334 sizeof (ses_enum_chassis_t
)))
3340 if (ses_init_chassis(mod
, sdp
, cp
, scp
,
3341 np
, props
, subchassis
,
3342 SES_NEW_SUBCHASSIS
) < 0)
3345 /* 2.4 This is a duplicate subchassis */
3347 topo_mod_dprintf(mod
, "%s: Append "
3348 "duplicate subchassis with "
3349 "CSN (%s)", sdp
->sed_name
, csn
);
3351 if (ses_init_chassis(mod
, sdp
, cp
, scp
,
3352 np
, props
, subchassis
,
3353 SES_DUP_SUBCHASSIS
) < 0)
3358 } else if (ses_node_type(np
) == SES_NODE_ELEMENT
) {
3360 * If we haven't yet seen an enclosure node and identified the
3361 * current chassis, something is very wrong; bail out.
3363 if (sdp
->sed_current
== NULL
)
3364 return (SES_WALK_ACTION_TERMINATE
);
3367 * If this isn't one of the element types we care about, then
3370 verify(nvlist_lookup_uint64(props
, SES_PROP_ELEMENT_TYPE
,
3372 if (type
!= SES_ET_DEVICE
&&
3373 type
!= SES_ET_ARRAY_DEVICE
&&
3374 type
!= SES_ET_SUNW_FANBOARD
&&
3375 type
!= SES_ET_SUNW_FANMODULE
&&
3376 type
!= SES_ET_COOLING
&&
3377 type
!= SES_ET_SUNW_POWERBOARD
&&
3378 type
!= SES_ET_SUNW_POWERMODULE
&&
3379 type
!= SES_ET_POWER_SUPPLY
&&
3380 type
!= SES_ET_ESC_ELECTRONICS
&&
3381 type
!= SES_ET_SAS_EXPANDER
&&
3382 type
!= SES_ET_SAS_CONNECTOR
)
3383 return (SES_WALK_ACTION_CONTINUE
);
3386 * Get the current instance number and see if we already know
3387 * about this element. If so, it means we have multiple paths
3388 * to the same elements, and we should ignore the current path.
3390 verify(nvlist_lookup_uint64(props
, SES_PROP_ELEMENT_CLASS_INDEX
,
3392 if (type
== SES_ET_DEVICE
|| type
== SES_ET_ARRAY_DEVICE
)
3393 (void) nvlist_lookup_uint64(props
, SES_PROP_BAY_NUMBER
,
3396 cp
= sdp
->sed_current
;
3398 for (snp
= topo_list_next(&cp
->sec_nodes
); snp
!= NULL
;
3399 snp
= topo_list_next(snp
)) {
3400 if (snp
->sen_type
== type
&&
3401 snp
->sen_instance
== instance
)
3406 * We prefer the new element under the following circumstances:
3408 * - The currently known element's status is unknown or not
3409 * available, but the new element has a known status. This
3410 * occurs if a given element is only available through a
3411 * particular target.
3413 * - This is an ESC_ELECTRONICS element, and the 'reported-via'
3414 * property is set. This allows us to get reliable firmware
3415 * revision information from the enclosure node.
3418 if (nvlist_lookup_uint64(
3419 ses_node_props(snp
->sen_node
),
3420 SES_PROP_STATUS_CODE
, &prevstatus
) != 0)
3421 prevstatus
= SES_ESC_UNSUPPORTED
;
3422 if (nvlist_lookup_uint64(
3423 props
, SES_PROP_STATUS_CODE
, &status
) != 0)
3424 status
= SES_ESC_UNSUPPORTED
;
3425 if (nvlist_lookup_boolean_value(
3426 props
, SES_PROP_REPORT
, &report
) != 0)
3429 if ((SES_STATUS_UNAVAIL(prevstatus
) &&
3430 !SES_STATUS_UNAVAIL(status
)) ||
3431 (type
== SES_ET_ESC_ELECTRONICS
&&
3434 snp
->sen_target
= sdp
->sed_target
;
3437 if ((sap
= topo_mod_zalloc(mod
,
3438 sizeof (ses_alt_node_t
))) == NULL
)
3442 topo_list_append(&snp
->sen_alt_nodes
, sap
);
3444 return (SES_WALK_ACTION_CONTINUE
);
3447 if ((snp
= topo_mod_zalloc(mod
,
3448 sizeof (ses_enum_node_t
))) == NULL
)
3451 if ((sap
= topo_mod_zalloc(mod
,
3452 sizeof (ses_alt_node_t
))) == NULL
) {
3453 topo_mod_free(mod
, snp
, sizeof (ses_enum_node_t
));
3457 topo_mod_dprintf(mod
, "%s: adding node (%llu, %llu)",
3458 sdp
->sed_name
, type
, instance
);
3460 snp
->sen_type
= type
;
3461 snp
->sen_instance
= instance
;
3462 snp
->sen_target
= sdp
->sed_target
;
3464 topo_list_append(&snp
->sen_alt_nodes
, sap
);
3465 topo_list_append(&cp
->sec_nodes
, snp
);
3467 if (type
== SES_ET_DEVICE
)
3468 cp
->sec_hasdev
= B_TRUE
;
3471 return (SES_WALK_ACTION_CONTINUE
);
3474 sdp
->sed_errno
= -1;
3475 return (SES_WALK_ACTION_TERMINATE
);
3479 ses_process_dir(const char *dirpath
, ses_enum_data_t
*sdp
)
3481 topo_mod_t
*mod
= sdp
->sed_mod
;
3484 char path
[PATH_MAX
];
3485 ses_enum_target_t
*stp
;
3489 * Open the SES target directory and iterate over any available
3492 if ((dir
= opendir(dirpath
)) == NULL
) {
3494 * If the SES target directory does not exist, then return as if
3495 * there are no active targets.
3497 topo_mod_dprintf(mod
, "failed to open ses "
3498 "directory '%s'", dirpath
);
3502 while ((dp
= readdir(dir
)) != NULL
) {
3503 if (strcmp(dp
->d_name
, ".") == 0 ||
3504 strcmp(dp
->d_name
, "..") == 0)
3508 * Create a new target instance and take a snapshot.
3510 if ((stp
= topo_mod_zalloc(mod
,
3511 sizeof (ses_enum_target_t
))) == NULL
)
3514 (void) pthread_mutex_init(&stp
->set_lock
, NULL
);
3516 (void) snprintf(path
, sizeof (path
), "%s/%s", dirpath
,
3520 * We keep track of the SES device path and export it on a
3521 * per-node basis to allow higher level software to get to the
3522 * corresponding SES state.
3524 if ((stp
->set_devpath
= topo_mod_strdup(mod
, path
)) == NULL
) {
3525 topo_mod_free(mod
, stp
, sizeof (ses_enum_target_t
));
3529 if ((stp
->set_target
=
3530 ses_open(LIBSES_VERSION
, path
)) == NULL
) {
3531 topo_mod_dprintf(mod
, "failed to open ses target "
3532 "'%s': %s", dp
->d_name
, ses_errmsg());
3533 ses_sof_alloc(mod
, stp
->set_devpath
);
3534 topo_mod_free(mod
, stp
, sizeof (ses_enum_target_t
));
3537 topo_mod_dprintf(mod
, "open contract");
3538 ses_ssl_alloc(mod
, stp
);
3539 ses_create_contract(mod
, stp
);
3541 stp
->set_refcount
= 1;
3542 sdp
->sed_target
= stp
;
3543 stp
->set_snap
= ses_snap_hold(stp
->set_target
);
3544 stp
->set_snaptime
= gethrtime();
3547 * Enumerate over all SES elements and merge them into the
3548 * correct ses_enum_chassis_t.
3550 sdp
->sed_current
= NULL
;
3552 sdp
->sed_name
= dp
->d_name
;
3553 (void) ses_walk(stp
->set_snap
, ses_enum_gather
, sdp
);
3555 if (sdp
->sed_errno
!= 0)
3561 (void) closedir(dir
);
3566 ses_release(topo_mod_t
*mod
, tnode_t
*tn
)
3568 ses_enum_target_t
*stp
;
3570 if ((stp
= topo_node_getspecific(tn
)) != NULL
) {
3571 topo_node_setspecific(tn
, NULL
);
3572 ses_target_free(mod
, stp
);
3578 ses_enum(topo_mod_t
*mod
, tnode_t
*rnode
, const char *name
,
3579 topo_instance_t min
, topo_instance_t max
, void *arg
, void *notused
)
3581 ses_enum_chassis_t
*cp
;
3582 ses_enum_data_t
*data
;
3585 * Check to make sure we're being invoked sensibly, and that we're not
3586 * being invoked as part of a post-processing step.
3588 if (strcmp(name
, SES_ENCLOSURE
) != 0 && strcmp(name
, BAY
) != 0)
3592 * If this is the first time we've called our enumeration method, then
3593 * gather information about any available enclosures.
3595 if ((data
= topo_mod_getspecific(mod
)) == NULL
) {
3596 ses_sof_freeall(mod
);
3597 if ((data
= topo_mod_zalloc(mod
, sizeof (ses_enum_data_t
))) ==
3601 data
->sed_mod
= mod
;
3602 topo_mod_setspecific(mod
, data
);
3604 if (dev_list_gather(mod
, &data
->sed_devs
) != 0)
3608 * We search both the ses(7D) and sgen(7D) locations, so we are
3609 * independent of any particular driver class bindings.
3611 if (ses_process_dir("/dev/es", data
) != 0 ||
3612 ses_process_dir("/dev/scsi/ses", data
) != 0)
3616 if (strcmp(name
, SES_ENCLOSURE
) == 0) {
3618 * This is a request to enumerate external enclosures. Go
3619 * through all the targets and create chassis nodes where
3622 for (cp
= topo_list_next(&data
->sed_chassis
); cp
!= NULL
;
3623 cp
= topo_list_next(cp
)) {
3624 if (ses_create_chassis(data
, rnode
, cp
) != 0)
3629 * This is a request to enumerate a specific bay underneath the
3630 * root chassis (for internal disks).
3632 if (ses_create_bays(data
, rnode
) != 0)
3637 * This is a bit of a kludge. In order to allow internal disks to be
3638 * enumerated and share snapshot-specific information with the external
3639 * enclosure enumeration, we rely on the fact that we will be invoked
3640 * for the 'ses-enclosure' node last.
3642 if (strcmp(name
, SES_ENCLOSURE
) == 0) {
3643 for (cp
= topo_list_next(&data
->sed_chassis
); cp
!= NULL
;
3644 cp
= topo_list_next(cp
))
3645 ses_data_free(data
, cp
);
3646 ses_data_free(data
, NULL
);
3647 topo_mod_setspecific(mod
, NULL
);
3652 for (cp
= topo_list_next(&data
->sed_chassis
); cp
!= NULL
;
3653 cp
= topo_list_next(cp
))
3654 ses_data_free(data
, cp
);
3655 ses_data_free(data
, NULL
);
3656 topo_mod_setspecific(mod
, NULL
);
3660 static const topo_modops_t ses_ops
=
3661 { ses_enum
, ses_release
};
3663 static topo_modinfo_t ses_info
=
3664 { SES_ENCLOSURE
, FM_FMRI_SCHEME_HC
, SES_VERSION
, &ses_ops
};
3668 _topo_init(topo_mod_t
*mod
, topo_version_t version
)
3672 if (getenv("TOPOSESDEBUG") != NULL
)
3673 topo_mod_setdebug(mod
);
3675 topo_mod_dprintf(mod
, "initializing %s enumerator\n",
3678 if ((rval
= topo_mod_register(mod
, &ses_info
, TOPO_VERSION
)) == 0)
3679 ses_thread_init(mod
);
3685 _topo_fini(topo_mod_t
*mod
)
3687 ses_thread_fini(mod
);
3688 ses_sof_freeall(mod
);
3689 topo_mod_unregister(mod
);