1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <alloca.h>
27 #include <dirent.h>
28 #include <devid.h>
29 #include <fm/libdiskstatus.h>
30 #include <inttypes.h>
31 #include <pthread.h>
32 #include <strings.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <sys/dkio.h>
36 #include <sys/fm/protocol.h>
37 #include <sys/libdevid.h>
38 #include <sys/scsi/scsi_types.h>
39 #include <sys/byteorder.h>
40 #include <pthread.h>
41 #include <signal.h>
42 #include <fcntl.h>
43 #include <sys/ctfs.h>
44 #include <libcontract.h>
45 #include <poll.h>
46 #include <sys/contract/device.h>
47 #include <libsysevent.h>
48 #include <sys/sysevent/eventdefs.h>
49 #include <scsi/plugins/ses/vendor/sun.h>
50
51 #include "disk.h"
52 #include "ses.h"
53
54 #define SES_VERSION 1
55
56 #define SES_STARTING_SUBCHASSIS 256 /* valid subchassis IDs are uint8_t */
57 #define NO_SUBCHASSIS ((uint64_t)-1)
58
59 static int ses_snap_freq = 250; /* in milliseconds */
60
61 #define SES_STATUS_UNAVAIL(s) \
62 ((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED)
63
64 #define HR_SECOND 1000000000
65
66 /*
67 * Because multiple SES targets can be part of a single chassis, we construct
68 * our own hierarchy that takes this into account. These SES targets may refer
69 * to the same devices (multiple paths) or to different devices (managing
70 * different portions of the space). We arrange things into a
71 * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all
72 * nodes found so far.
73 */
74 typedef struct ses_alt_node {
75 topo_list_t san_link;
76 ses_node_t *san_node;
77 } ses_alt_node_t;
78
79 typedef struct ses_enum_node {
80 topo_list_t sen_link;
81 ses_node_t *sen_node;
82 topo_list_t sen_alt_nodes;
83 uint64_t sen_type;
84 uint64_t sen_instance;
85 ses_enum_target_t *sen_target;
86 } ses_enum_node_t;
87
88 typedef struct ses_enum_chassis {
89 topo_list_t sec_link;
90 topo_list_t sec_subchassis;
91 topo_list_t sec_nodes;
92 topo_list_t sec_targets;
93 const char *sec_csn;
94 ses_node_t *sec_enclosure;
95 ses_enum_target_t *sec_target;
96 topo_instance_t sec_instance;
97 topo_instance_t sec_scinstance;
98 topo_instance_t sec_maxinstance;
99 boolean_t sec_hasdev;
100 boolean_t sec_internal;
101 } ses_enum_chassis_t;
102
103 typedef struct ses_enum_data {
104 topo_list_t sed_devs;
105 topo_list_t sed_chassis;
106 ses_enum_chassis_t *sed_current;
107 ses_enum_target_t *sed_target;
108 int sed_errno;
109 char *sed_name;
110 topo_mod_t *sed_mod;
111 topo_instance_t sed_instance;
112 } ses_enum_data_t;
113
114 typedef struct sas_connector_phy_data {
115 uint64_t scpd_index;
116 uint64_t scpd_pm;
117 } sas_connector_phy_data_t;
118
119 typedef struct sas_connector_type {
120 uint64_t sct_type;
121 char *sct_name;
122 } sas_connector_type_t;
123
124 static const sas_connector_type_t sas_connector_type_list[] = {
125 { 0x0, "Information unknown" },
126 { 0x1, "External SAS 4x receptacle (see SAS-2 and SFF-8470)" },
127 { 0x2, "Exteranl Mini SAS 4x receptacle (see SAS-2 and SFF-8088)" },
128 { 0xF, "Vendor-specific external connector" },
129 { 0x10, "Internal wide SAS 4i plug (see SAS-2 and SFF-8484)" },
130 { 0x11,
131 "Internal wide Mini SAS 4i receptacle (see SAS-2 and SFF-8087)" },
132 { 0x20, "Internal SAS Drive receptacle (see SAS-2 and SFF-8482)" },
133 { 0x21, "Internal SATA host plug (see SAS-2 and SATA-2)" },
134 { 0x22, "Internal SAS Drive plug (see SAS-2 and SFF-8482)" },
135 { 0x23, "Internal SATA device plug (see SAS-2 and SATA-2)" },
136 { 0x2F, "Internal SAS virtual connector" },
137 { 0x3F, "Vendor-specific internal connector" },
138 { 0x70, "Other Vendor-specific connector" },
139 { 0x71, "Other Vendor-specific connector" },
140 { 0x72, "Other Vendor-specific connector" },
141 { 0x73, "Other Vendor-specific connector" },
142 { 0x74, "Other Vendor-specific connector" },
143 { 0x75, "Other Vendor-specific connector" },
144 { 0x76, "Other Vendor-specific connector" },
145 { 0x77, "Other Vendor-specific connector" },
146 { 0x78, "Other Vendor-specific connector" },
147 { 0x79, "Other Vendor-specific connector" },
148 { 0x7A, "Other Vendor-specific connector" },
149 { 0x7B, "Other Vendor-specific connector" },
150 { 0x7C, "Other Vendor-specific connector" },
151 { 0x7D, "Other Vendor-specific connector" },
152 { 0x7E, "Other Vendor-specific connector" },
153 { 0x7F, "Other Vendor-specific connector" },
154 { 0x80, "Not Defined" }
155 };
156
157 #define SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED 0x80
158 #define SAS_CONNECTOR_TYPE_NOT_DEFINED \
159 "Connector type not definedi by SES-2 standard"
160 #define SAS_CONNECTOR_TYPE_RESERVED \
161 "Connector type reserved by SES-2 standard"
162
163 typedef struct phys_enum_type {
164 uint64_t pet_type;
165 char *pet_nodename;
166 char *pet_defaultlabel;
167 boolean_t pet_dorange;
168 } phys_enum_type_t;
169
170 static const phys_enum_type_t phys_enum_type_list[] = {
171 { SES_ET_ARRAY_DEVICE, BAY, "BAY", B_TRUE },
172 { SES_ET_COOLING, FAN, "FAN", B_TRUE },
173 { SES_ET_DEVICE, BAY, "BAY", B_TRUE },
174 { SES_ET_ESC_ELECTRONICS, CONTROLLER, "CONTROLLER", B_TRUE },
175 { SES_ET_POWER_SUPPLY, PSU, "PSU", B_TRUE },
176 { SES_ET_SUNW_FANBOARD, FANBOARD, "FANBOARD", B_TRUE },
177 { SES_ET_SUNW_FANMODULE, FANMODULE, "FANMODULE", B_TRUE },
178 { SES_ET_SUNW_POWERBOARD, POWERBOARD, "POWERBOARD", B_TRUE },
179 { SES_ET_SUNW_POWERMODULE, POWERMODULE, "POWERMODULE", B_TRUE }
180 };
181
182 #define N_PHYS_ENUM_TYPES (sizeof (phys_enum_type_list) / \
183 sizeof (phys_enum_type_list[0]))
184
185 /*
186 * Structure for the hierarchical tree for element nodes.
187 */
188 typedef struct ses_phys_tree {
189 ses_node_t *spt_snode;
190 ses_enum_node_t *spt_senumnode;
191 boolean_t spt_isfru;
192 uint64_t spt_eonlyindex;
193 uint64_t spt_cindex;
194 uint64_t spt_pindex;
195 uint64_t spt_maxinst;
196 struct ses_phys_tree *spt_parent;
197 struct ses_phys_tree *spt_child;
198 struct ses_phys_tree *spt_sibling;
199 tnode_t *spt_tnode;
200 } ses_phys_tree_t;
201
202 typedef enum {
203 SES_NEW_CHASSIS = 0x1,
204 SES_NEW_SUBCHASSIS = 0x2,
205 SES_DUP_CHASSIS = 0x4,
206 SES_DUP_SUBCHASSIS = 0x8
207 } ses_chassis_type_e;
208
209
210 static const topo_pgroup_info_t storage_pgroup = {
211 TOPO_PGROUP_STORAGE,
212 TOPO_STABILITY_PRIVATE,
213 TOPO_STABILITY_PRIVATE,
214 1
215 };
216
217 static const topo_pgroup_info_t smp_pgroup = {
218 TOPO_PGROUP_SMP,
219 TOPO_STABILITY_PRIVATE,
220 TOPO_STABILITY_PRIVATE,
221 1
222 };
223
224 static const topo_pgroup_info_t ses_pgroup = {
225 TOPO_PGROUP_SES,
226 TOPO_STABILITY_PRIVATE,
227 TOPO_STABILITY_PRIVATE,
228 1
229 };
230
231 static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
232 nvlist_t **);
233 static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
234 nvlist_t **);
235
236 static const topo_method_t ses_component_methods[] = {
237 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
238 TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present },
239 { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
240 TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
241 { TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC,
242 TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL,
243 topo_method_sensor_failure },
244 { NULL }
245 };
246
247 static const topo_method_t ses_bay_methods[] = {
248 { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
249 TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
250 { NULL }
251 };
252
253 static const topo_method_t ses_enclosure_methods[] = {
254 { TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC,
255 TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains },
256 { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
257 TOPO_STABILITY_INTERNAL, ses_enc_enum_facility },
258 { NULL }
259 };
260
261 /*
262 * Functions for tracking ses devices which we were unable to open. We retry
263 * these at regular intervals using ses_recheck_dir() and if we find that we
264 * can now open any of them then we send a sysevent to indicate that a new topo
265 * snapshot should be taken.
266 */
267 typedef struct ses_open_fail_list {
268 struct ses_open_fail_list *sof_next;
269 char *sof_path;
270 } ses_open_fail_list_t;
271
272 static ses_open_fail_list_t *ses_sofh;
273 static pthread_mutex_t ses_sofmt;
274 static void ses_ct_print(char *ptr);
275
276 static void
ses_recheck_dir()277 ses_recheck_dir()
278 {
279 ses_target_t *target;
280 sysevent_id_t eid;
281 char buf[80];
282 ses_open_fail_list_t *sof;
283
284 /*
285 * check list of "unable to open" devices
286 */
287 (void) pthread_mutex_lock(&ses_sofmt);
288 for (sof = ses_sofh; sof != NULL; sof = sof->sof_next) {
289 /*
290 * see if we can open it now
291 */
292 if ((target = ses_open(LIBSES_VERSION,
293 sof->sof_path)) == NULL) {
294 (void) snprintf(buf, sizeof (buf),
295 "recheck_dir - still can't open %s", sof->sof_path);
296 ses_ct_print(buf);
297 continue;
298 }
299
300 /*
301 * ok - better force a new snapshot
302 */
303 (void) snprintf(buf, sizeof (buf),
304 "recheck_dir - can now open %s", sof->sof_path);
305 ses_ct_print(buf);
306 (void) sysevent_post_event(EC_PLATFORM, ESC_PLATFORM_SP_RESET,
307 SUNW_VENDOR, "fmd", NULL, &eid);
308 ses_close(target);
309 break;
310 }
311 (void) pthread_mutex_unlock(&ses_sofmt);
312 }
313
314 static void
ses_sof_alloc(topo_mod_t * mod,char * path)315 ses_sof_alloc(topo_mod_t *mod, char *path)
316 {
317 ses_open_fail_list_t *sof;
318
319 (void) pthread_mutex_lock(&ses_sofmt);
320 sof = topo_mod_zalloc(mod, sizeof (*sof));
321 topo_mod_dprintf(mod, "sof_alloc %s", path);
322 sof->sof_path = path;
323 sof->sof_next = ses_sofh;
324 ses_sofh = sof;
325 (void) pthread_mutex_unlock(&ses_sofmt);
326 }
327
328 static void
ses_sof_freeall(topo_mod_t * mod)329 ses_sof_freeall(topo_mod_t *mod)
330 {
331 ses_open_fail_list_t *sof, *next_sof;
332
333 (void) pthread_mutex_lock(&ses_sofmt);
334 for (sof = ses_sofh; sof != NULL; sof = next_sof) {
335 next_sof = sof->sof_next;
336 topo_mod_dprintf(mod, "sof_freeall %s", sof->sof_path);
337 topo_mod_strfree(mod, sof->sof_path);
338 topo_mod_free(mod, sof, sizeof (*sof));
339 }
340 ses_sofh = NULL;
341 (void) pthread_mutex_unlock(&ses_sofmt);
342 }
343
344 /*
345 * functions for verifying that the ses_enum_target_t held in a device
346 * contract's cookie field is still valid (it may have been freed by
347 * ses_release()).
348 */
349 typedef struct ses_stp_list {
350 struct ses_stp_list *ssl_next;
351 ses_enum_target_t *ssl_tgt;
352 } ses_stp_list_t;
353
354 static ses_stp_list_t *ses_sslh;
355 static pthread_mutex_t ses_sslmt;
356
357 static void
ses_ssl_alloc(topo_mod_t * mod,ses_enum_target_t * stp)358 ses_ssl_alloc(topo_mod_t *mod, ses_enum_target_t *stp)
359 {
360 ses_stp_list_t *ssl;
361
362 (void) pthread_mutex_lock(&ses_sslmt);
363 ssl = topo_mod_zalloc(mod, sizeof (*ssl));
364 topo_mod_dprintf(mod, "ssl_alloc %p", stp);
365 ssl->ssl_tgt = stp;
366 ssl->ssl_next = ses_sslh;
367 ses_sslh = ssl;
368 (void) pthread_mutex_unlock(&ses_sslmt);
369 }
370
371 static void
ses_ssl_free(topo_mod_t * mod,ses_enum_target_t * stp)372 ses_ssl_free(topo_mod_t *mod, ses_enum_target_t *stp)
373 {
374 ses_stp_list_t *ssl, *prev_ssl;
375
376 (void) pthread_mutex_lock(&ses_sslmt);
377 prev_ssl = NULL;
378 for (ssl = ses_sslh; ssl != NULL; ssl = ssl->ssl_next) {
379 if (ssl->ssl_tgt == stp) {
380 topo_mod_dprintf(mod, "ssl_free %p", ssl->ssl_tgt);
381 if (prev_ssl == NULL)
382 ses_sslh = ssl->ssl_next;
383 else
384 prev_ssl->ssl_next = ssl->ssl_next;
385 topo_mod_free(mod, ssl, sizeof (*ssl));
386 break;
387 }
388 prev_ssl = ssl;
389 }
390 (void) pthread_mutex_unlock(&ses_sslmt);
391 }
392
393 static int
ses_ssl_valid(ses_enum_target_t * stp)394 ses_ssl_valid(ses_enum_target_t *stp)
395 {
396 ses_stp_list_t *ssl;
397
398 for (ssl = ses_sslh; ssl != NULL; ssl = ssl->ssl_next)
399 if (ssl->ssl_tgt == stp)
400 return (1);
401 return (0);
402 }
403
404 /*
405 * Functions for creating and destroying a background thread
406 * (ses_contract_thread) used for detecting when ses devices have been
407 * retired/unretired.
408 */
409 static struct ses_thread_s {
410 pthread_mutex_t mt;
411 pthread_t tid;
412 int thr_sig;
413 int doexit;
414 int count;
415 } sesthread = {
416 PTHREAD_MUTEX_INITIALIZER,
417 0,
418 SIGTERM,
419 0,
420 0
421 };
422
423 typedef struct ses_mod_list {
424 struct ses_mod_list *smod_next;
425 topo_mod_t *smod_mod;
426 } ses_mod_list_t;
427
428 static ses_mod_list_t *ses_smod;
429
430 static void
ses_ct_print(char * ptr)431 ses_ct_print(char *ptr)
432 {
433 (void) pthread_mutex_lock(&sesthread.mt);
434 if (ses_smod != NULL && ses_smod->smod_mod != NULL)
435 topo_mod_dprintf(ses_smod->smod_mod, ptr);
436 (void) pthread_mutex_unlock(&sesthread.mt);
437 }
438
439 /*ARGSUSED*/
440 static void *
ses_contract_thread(void * arg)441 ses_contract_thread(void *arg)
442 {
443 int efd, ctlfd, statfd;
444 ct_evthdl_t ev;
445 ctevid_t evid;
446 uint_t event;
447 char path[PATH_MAX];
448 char buf[80];
449 ses_enum_target_t *stp;
450 ct_stathdl_t stathdl;
451 ctid_t ctid;
452 struct pollfd fds;
453 int pollret;
454
455 ses_ct_print("start contract event thread");
456 efd = open64(CTFS_ROOT "/device/pbundle", O_RDONLY);
457 fds.fd = efd;
458 fds.events = POLLIN;
459 fds.revents = 0;
460 for (;;) {
461 /* check if we've been asked to exit */
462 (void) pthread_mutex_lock(&sesthread.mt);
463 if (sesthread.doexit) {
464 (void) pthread_mutex_unlock(&sesthread.mt);
465 break;
466 }
467 (void) pthread_mutex_unlock(&sesthread.mt);
468
469 /* poll until an event arrives */
470 if ((pollret = poll(&fds, 1, 10000)) <= 0) {
471 if (pollret == 0)
472 ses_recheck_dir();
473 continue;
474 }
475
476 /* read the event */
477 (void) pthread_mutex_lock(&ses_sslmt);
478 ses_ct_print("read contract event");
479 if (ct_event_read(efd, &ev) != 0) {
480 (void) pthread_mutex_unlock(&ses_sslmt);
481 continue;
482 }
483
484 /* see if it is an event we are expecting */
485 ctid = ct_event_get_ctid(ev);
486 (void) snprintf(buf, sizeof (buf),
487 "got contract event ctid=%d", ctid);
488 ses_ct_print(buf);
489 event = ct_event_get_type(ev);
490 if (event != CT_DEV_EV_OFFLINE && event != CT_EV_NEGEND) {
491 snprintf(buf, sizeof (buf),
492 "bad contract event %x", event);
493 ses_ct_print(buf);
494 ct_event_free(ev);
495 (void) pthread_mutex_unlock(&ses_sslmt);
496 continue;
497 }
498
499 /* find target pointer saved in cookie */
500 evid = ct_event_get_evid(ev);
501 (void) snprintf(path, PATH_MAX, CTFS_ROOT "/device/%ld/status",
502 ctid);
503 statfd = open64(path, O_RDONLY);
504 ct_status_read(statfd, CTD_COMMON, &stathdl);
505 stp = (ses_enum_target_t *)(uintptr_t)
506 ct_status_get_cookie(stathdl);
507 ct_status_free(stathdl);
508 close(statfd);
509
510 /* check if target pointer is still valid */
511 if (ses_ssl_valid(stp) == 0) {
512 snprintf(buf, sizeof (buf),
513 "contract already abandoned %x", event);
514 ses_ct_print(buf);
515 (void) snprintf(path, PATH_MAX,
516 CTFS_ROOT "/device/%ld/ctl", ctid);
517 ctlfd = open64(path, O_WRONLY);
518 if (event != CT_EV_NEGEND)
519 ct_ctl_ack(ctlfd, evid);
520 else
521 ct_ctl_abandon(ctlfd);
522 close(ctlfd);
523 ct_event_free(ev);
524 (void) pthread_mutex_unlock(&ses_sslmt);
525 continue;
526 }
527
528 /* find control device for ack/abandon */
529 (void) pthread_mutex_lock(&stp->set_lock);
530 (void) snprintf(path, PATH_MAX, CTFS_ROOT "/device/%ld/ctl",
531 ctid);
532 ctlfd = open64(path, O_WRONLY);
533 if (event != CT_EV_NEGEND) {
534 /* if this is an offline event, do the offline */
535 ses_ct_print("got contract offline event");
536 if (stp->set_target) {
537 ses_ct_print("contract thread rele");
538 ses_snap_rele(stp->set_snap);
539 ses_close(stp->set_target);
540 stp->set_target = NULL;
541 }
542 ct_ctl_ack(ctlfd, evid);
543 } else {
544 /* if this is the negend, then abandon the contract */
545 ses_ct_print("got contract negend");
546 if (stp->set_ctid) {
547 snprintf(buf, sizeof (buf),
548 "abandon old contract %d", stp->set_ctid);
549 ses_ct_print(buf);
550 stp->set_ctid = NULL;
551 }
552 ct_ctl_abandon(ctlfd);
553 }
554 close(ctlfd);
555 (void) pthread_mutex_unlock(&stp->set_lock);
556 ct_event_free(ev);
557 (void) pthread_mutex_unlock(&ses_sslmt);
558 }
559 close(efd);
560 return (NULL);
561 }
562
563 int
find_thr_sig(void)564 find_thr_sig(void)
565 {
566 int i;
567 sigset_t oset, rset;
568 int sig[] = {SIGTERM, SIGUSR1, SIGUSR2};
569 int sig_sz = sizeof (sig) / sizeof (int);
570 int rc = SIGTERM;
571
572 /* prefered set of signals that are likely used to terminate threads */
573 (void) sigemptyset(&oset);
574 (void) pthread_sigmask(SIG_SETMASK, NULL, &oset);
575 for (i = 0; i < sig_sz; i++) {
576 if (sigismember(&oset, sig[i]) == 0) {
577 return (sig[i]);
578 }
579 }
580
581 /* reserved set of signals that are not allowed to terminate thread */
582 (void) sigemptyset(&rset);
583 (void) sigaddset(&rset, SIGABRT);
584 (void) sigaddset(&rset, SIGKILL);
585 (void) sigaddset(&rset, SIGSTOP);
586 (void) sigaddset(&rset, SIGCANCEL);
587
588 /* Find signal that is not masked and not in the reserved list. */
589 for (i = 1; i < MAXSIG; i++) {
590 if (sigismember(&rset, i) == 1) {
591 continue;
592 }
593 if (sigismember(&oset, i) == 0) {
594 return (i);
595 }
596 }
597
598 return (rc);
599 }
600
601 /*ARGSUSED*/
602 static void
ses_handler(int sig)603 ses_handler(int sig)
604 {
605 }
606
607 static void
ses_thread_init(topo_mod_t * mod)608 ses_thread_init(topo_mod_t *mod)
609 {
610 pthread_attr_t *attr = NULL;
611 struct sigaction act;
612 ses_mod_list_t *smod;
613
614 (void) pthread_mutex_lock(&sesthread.mt);
615 sesthread.count++;
616 smod = topo_mod_zalloc(mod, sizeof (*smod));
617 smod->smod_mod = mod;
618 smod->smod_next = ses_smod;
619 ses_smod = smod;
620 if (sesthread.tid == 0) {
621 /* find a suitable signal to use for killing the thread below */
622 sesthread.thr_sig = find_thr_sig();
623
624 /* if don't have a handler for this signal, create one */
625 (void) sigaction(sesthread.thr_sig, NULL, &act);
626 if (act.sa_handler == SIG_DFL || act.sa_handler == SIG_IGN)
627 act.sa_handler = ses_handler;
628 (void) sigaction(sesthread.thr_sig, &act, NULL);
629
630 /* create a thread to listen for offline events */
631 (void) pthread_create(&sesthread.tid,
632 attr, ses_contract_thread, NULL);
633 }
634 (void) pthread_mutex_unlock(&sesthread.mt);
635 }
636
637 static void
ses_thread_fini(topo_mod_t * mod)638 ses_thread_fini(topo_mod_t *mod)
639 {
640 ses_mod_list_t *smod, *prev_smod;
641
642 (void) pthread_mutex_lock(&sesthread.mt);
643 prev_smod = NULL;
644 for (smod = ses_smod; smod != NULL; smod = smod->smod_next) {
645 if (smod->smod_mod == mod) {
646 if (prev_smod == NULL)
647 ses_smod = smod->smod_next;
648 else
649 prev_smod->smod_next = smod->smod_next;
650 topo_mod_free(mod, smod, sizeof (*smod));
651 break;
652 }
653 prev_smod = smod;
654 }
655 if (--sesthread.count > 0) {
656 (void) pthread_mutex_unlock(&sesthread.mt);
657 return;
658 }
659 sesthread.doexit = 1;
660 (void) pthread_mutex_unlock(&sesthread.mt);
661 (void) pthread_kill(sesthread.tid, sesthread.thr_sig);
662 (void) pthread_join(sesthread.tid, NULL);
663 sesthread.tid = 0;
664 }
665
666 static void
ses_create_contract(topo_mod_t * mod,ses_enum_target_t * stp)667 ses_create_contract(topo_mod_t *mod, ses_enum_target_t *stp)
668 {
669 int tfd, len, rval;
670 char link_path[PATH_MAX];
671
672 stp->set_ctid = NULL;
673
674 /* convert "/dev" path into "/devices" path */
675 if ((len = readlink(stp->set_devpath, link_path, PATH_MAX)) < 0) {
676 topo_mod_dprintf(mod, "readlink failed");
677 return;
678 }
679 link_path[len] = '\0';
680
681 /* set up template to create new contract */
682 tfd = open64(CTFS_ROOT "/device/template", O_RDWR);
683 ct_tmpl_set_critical(tfd, CT_DEV_EV_OFFLINE);
684 ct_tmpl_set_cookie(tfd, (uint64_t)(uintptr_t)stp);
685
686 /* strip "../../devices" off the front and create the contract */
687 if ((rval = ct_dev_tmpl_set_minor(tfd, &link_path[13])) != 0)
688 topo_mod_dprintf(mod, "failed to set minor %s rval = %d",
689 &link_path[13], rval);
690 else if ((rval = ct_tmpl_create(tfd, &stp->set_ctid)) != 0)
691 topo_mod_dprintf(mod, "failed to create ctid rval = %d", rval);
692 else
693 topo_mod_dprintf(mod, "created ctid=%d", stp->set_ctid);
694 close(tfd);
695 }
696
697 static void
ses_target_free(topo_mod_t * mod,ses_enum_target_t * stp)698 ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp)
699 {
700 if (--stp->set_refcount == 0) {
701 /* check if already closed due to contract offline request */
702 (void) pthread_mutex_lock(&stp->set_lock);
703 if (stp->set_target) {
704 ses_snap_rele(stp->set_snap);
705 ses_close(stp->set_target);
706 stp->set_target = NULL;
707 }
708 if (stp->set_ctid) {
709 int ctlfd;
710 char path[PATH_MAX];
711
712 topo_mod_dprintf(mod, "abandon old contract %d",
713 stp->set_ctid);
714 (void) snprintf(path, PATH_MAX,
715 CTFS_ROOT "/device/%ld/ctl", stp->set_ctid);
716 ctlfd = open64(path, O_WRONLY);
717 ct_ctl_abandon(ctlfd);
718 close(ctlfd);
719 stp->set_ctid = NULL;
720 }
721 (void) pthread_mutex_unlock(&stp->set_lock);
722 ses_ssl_free(mod, stp);
723 topo_mod_strfree(mod, stp->set_devpath);
724 topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
725 }
726 }
727
728 static void
ses_data_free(ses_enum_data_t * sdp,ses_enum_chassis_t * pcp)729 ses_data_free(ses_enum_data_t *sdp, ses_enum_chassis_t *pcp)
730 {
731 topo_mod_t *mod = sdp->sed_mod;
732 ses_enum_chassis_t *cp;
733 ses_enum_node_t *np;
734 ses_enum_target_t *tp;
735 ses_alt_node_t *ap;
736 topo_list_t *cpl;
737
738
739 if (pcp != NULL)
740 cpl = &pcp->sec_subchassis;
741 else
742 cpl = &sdp->sed_chassis;
743
744 while ((cp = topo_list_next(cpl)) != NULL) {
745 topo_list_delete(cpl, cp);
746
747 while ((np = topo_list_next(&cp->sec_nodes)) != NULL) {
748 while ((ap = topo_list_next(&np->sen_alt_nodes)) !=
749 NULL) {
750 topo_list_delete(&np->sen_alt_nodes, ap);
751 topo_mod_free(mod, ap, sizeof (ses_alt_node_t));
752 }
753 topo_list_delete(&cp->sec_nodes, np);
754 topo_mod_free(mod, np, sizeof (ses_enum_node_t));
755 }
756
757 while ((tp = topo_list_next(&cp->sec_targets)) != NULL) {
758 topo_list_delete(&cp->sec_targets, tp);
759 ses_target_free(mod, tp);
760 }
761
762 topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t));
763 }
764
765 if (pcp == NULL) {
766 dev_list_free(mod, &sdp->sed_devs);
767 topo_mod_free(mod, sdp, sizeof (ses_enum_data_t));
768 }
769 }
770
771 /*
772 * For enclosure nodes, we have a special contains method. By default, the hc
773 * walker will compare the node name and instance number to determine if an
774 * FMRI matches. For enclosures where the enumeration order is impossible to
775 * predict, we instead use the chassis-id as a unique identifier, and ignore
776 * the instance number.
777 */
778 static int
fmri_contains(topo_mod_t * mod,nvlist_t * nv1,nvlist_t * nv2)779 fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2)
780 {
781 uint8_t v1, v2;
782 nvlist_t **hcp1, **hcp2;
783 int err, i;
784 uint_t nhcp1, nhcp2;
785 nvlist_t *a1, *a2;
786 char *c1, *c2;
787 int mindepth;
788
789 if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 ||
790 nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 ||
791 v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION)
792 return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
793
794 err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1);
795 err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2);
796 if (err != 0)
797 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
798
799 /*
800 * If the chassis-id doesn't match, then these FMRIs are not
801 * equivalent. If one of the FMRIs doesn't have a chassis ID, then we
802 * have no choice but to fall back to the instance ID.
803 */
804 if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 &&
805 nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 &&
806 nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 &&
807 nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) {
808 if (strcmp(c1, c2) != 0)
809 return (0);
810
811 mindepth = 1;
812 } else {
813 mindepth = 0;
814 }
815
816 if (nhcp2 < nhcp1)
817 return (0);
818
819 for (i = 0; i < nhcp1; i++) {
820 char *nm1 = NULL;
821 char *nm2 = NULL;
822 char *id1 = NULL;
823 char *id2 = NULL;
824
825 (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1);
826 (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2);
827 (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1);
828 (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2);
829 if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL)
830 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
831
832 if (strcmp(nm1, nm2) == 0 &&
833 (i < mindepth || strcmp(id1, id2) == 0))
834 continue;
835
836 return (0);
837 }
838
839 return (1);
840 }
841
842 /*ARGSUSED*/
843 static int
ses_contains(topo_mod_t * mod,tnode_t * tn,topo_version_t version,nvlist_t * in,nvlist_t ** out)844 ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
845 nvlist_t *in, nvlist_t **out)
846 {
847 int ret;
848 nvlist_t *nv1, *nv2;
849
850 if (version > TOPO_METH_CONTAINS_VERSION)
851 return (topo_mod_seterrno(mod, EMOD_VER_NEW));
852
853 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 ||
854 nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0)
855 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
856
857 ret = fmri_contains(mod, nv1, nv2);
858 if (ret < 0)
859 return (-1);
860
861 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) {
862 if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET,
863 ret) == 0)
864 return (0);
865 else
866 nvlist_free(*out);
867 }
868
869 return (-1);
870
871 }
872
873 /*
874 * Return a current instance of the node. This is somewhat complicated because
875 * we need to take a new snapshot in order to get the new data, but we don't
876 * want to be constantly taking SES snapshots if the consumer is going to do a
877 * series of queries. So we adopt the strategy of assuming that the SES state
878 * is not going to be rapidly changing, and limit our snapshot frequency to
879 * some defined bounds.
880 */
881 ses_node_t *
ses_node_lock(topo_mod_t * mod,tnode_t * tn)882 ses_node_lock(topo_mod_t *mod, tnode_t *tn)
883 {
884 ses_enum_target_t *tp = topo_node_getspecific(tn);
885 hrtime_t now;
886 ses_snap_t *snap;
887 int err;
888 uint64_t nodeid;
889 ses_node_t *np;
890
891 if (tp == NULL) {
892 (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
893 return (NULL);
894 }
895
896 (void) pthread_mutex_lock(&tp->set_lock);
897
898 /*
899 * Determine if we need to take a new snapshot.
900 */
901 now = gethrtime();
902
903 if (tp->set_target == NULL) {
904 /*
905 * We may have closed the device but not yet abandoned the
906 * contract (ie we've had the offline event but not yet the
907 * negend). If so, just return failure.
908 */
909 if (tp->set_ctid != NULL) {
910 (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
911 (void) pthread_mutex_unlock(&tp->set_lock);
912 return (NULL);
913 }
914
915 /*
916 * The device has been closed due to a contract offline
917 * request, then we need to reopen it and create a new contract.
918 */
919 if ((tp->set_target =
920 ses_open(LIBSES_VERSION, tp->set_devpath)) == NULL) {
921 sysevent_id_t eid;
922
923 (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
924 (void) pthread_mutex_unlock(&tp->set_lock);
925 topo_mod_dprintf(mod, "recheck_dir - "
926 "can no longer open %s", tp->set_devpath);
927 (void) sysevent_post_event(EC_PLATFORM,
928 ESC_PLATFORM_SP_RESET, SUNW_VENDOR, "fmd", NULL,
929 &eid);
930 return (NULL);
931 }
932 topo_mod_dprintf(mod, "reopen contract");
933 ses_create_contract(mod, tp);
934 tp->set_snap = ses_snap_hold(tp->set_target);
935 tp->set_snaptime = gethrtime();
936 } else if (now - tp->set_snaptime > (ses_snap_freq * 1000 * 1000) &&
937 (snap = ses_snap_new(tp->set_target)) != NULL) {
938 if (ses_snap_generation(snap) !=
939 ses_snap_generation(tp->set_snap)) {
940 /*
941 * If we find ourselves in this situation, we're in
942 * trouble. The generation count has changed, which
943 * indicates that our current topology is out of date.
944 * But we need to consult the new topology in order to
945 * determine presence at this moment in time. We can't
946 * go back and change the topo snapshot in situ, so
947 * we'll just have to fail the call in this unlikely
948 * scenario.
949 */
950 ses_snap_rele(snap);
951 (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
952 (void) pthread_mutex_unlock(&tp->set_lock);
953 return (NULL);
954 } else {
955 ses_snap_rele(tp->set_snap);
956 tp->set_snap = snap;
957 }
958 tp->set_snaptime = gethrtime();
959 }
960
961 snap = tp->set_snap;
962
963 verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES,
964 TOPO_PROP_NODE_ID, &nodeid, &err) == 0);
965 verify((np = ses_node_lookup(snap, nodeid)) != NULL);
966
967 return (np);
968 }
969
970 /*ARGSUSED*/
971 void
ses_node_unlock(topo_mod_t * mod,tnode_t * tn)972 ses_node_unlock(topo_mod_t *mod, tnode_t *tn)
973 {
974 ses_enum_target_t *tp = topo_node_getspecific(tn);
975
976 verify(tp != NULL);
977
978 (void) pthread_mutex_unlock(&tp->set_lock);
979 }
980
981 /*
982 * Determine if the element is present.
983 */
984 /*ARGSUSED*/
985 static int
ses_present(topo_mod_t * mod,tnode_t * tn,topo_version_t version,nvlist_t * in,nvlist_t ** out)986 ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
987 nvlist_t *in, nvlist_t **out)
988 {
989 boolean_t present;
990 ses_node_t *np;
991 nvlist_t *props, *nvl;
992 uint64_t status;
993
994 if ((np = ses_node_lock(mod, tn)) == NULL)
995 return (-1);
996
997 verify((props = ses_node_props(np)) != NULL);
998 verify(nvlist_lookup_uint64(props,
999 SES_PROP_STATUS_CODE, &status) == 0);
1000
1001 ses_node_unlock(mod, tn);
1002
1003 present = (status != SES_ESC_NOT_INSTALLED);
1004
1005 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
1006 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
1007
1008 if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET,
1009 present) != 0) {
1010 nvlist_free(nvl);
1011 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
1012 }
1013
1014 *out = nvl;
1015
1016 return (0);
1017 }
1018
1019 /*
1020 * Sets standard properties for a ses node (enclosure, bay, controller
1021 * or expander).
1022 * This includes setting the FRU, as well as setting the
1023 * authority information. When the fru topo node(frutn) is not NULL
1024 * its resouce should be used as FRU.
1025 */
1026 static int
ses_set_standard_props(topo_mod_t * mod,tnode_t * frutn,tnode_t * tn,nvlist_t * auth,uint64_t nodeid,const char * path)1027 ses_set_standard_props(topo_mod_t *mod, tnode_t *frutn, tnode_t *tn,
1028 nvlist_t *auth, uint64_t nodeid, const char *path)
1029 {
1030 int err;
1031 char *product, *chassis;
1032 nvlist_t *fmri;
1033
1034 /*
1035 * Set the authority explicitly if specified.
1036 */
1037 if (auth) {
1038 verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT,
1039 &product) == 0);
1040 verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS,
1041 &chassis) == 0);
1042 if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
1043 FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product,
1044 &err) != 0 ||
1045 topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
1046 FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis,
1047 &err) != 0 ||
1048 topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
1049 FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "",
1050 &err) != 0) {
1051 topo_mod_dprintf(mod, "failed to add authority "
1052 "properties: %s\n", topo_strerror(err));
1053 return (topo_mod_seterrno(mod, err));
1054 }
1055 }
1056
1057 /*
1058 * Copy the resource and set that as the FRU.
1059 */
1060 if (frutn != NULL) {
1061 if (topo_node_resource(frutn, &fmri, &err) != 0) {
1062 topo_mod_dprintf(mod,
1063 "topo_node_resource() failed : %s\n",
1064 topo_strerror(err));
1065 return (topo_mod_seterrno(mod, err));
1066 }
1067 } else {
1068 if (topo_node_resource(tn, &fmri, &err) != 0) {
1069 topo_mod_dprintf(mod,
1070 "topo_node_resource() failed : %s\n",
1071 topo_strerror(err));
1072 return (topo_mod_seterrno(mod, err));
1073 }
1074 }
1075
1076 if (topo_node_fru_set(tn, fmri, 0, &err) != 0) {
1077 topo_mod_dprintf(mod,
1078 "topo_node_fru_set() failed : %s\n",
1079 topo_strerror(err));
1080 nvlist_free(fmri);
1081 return (topo_mod_seterrno(mod, err));
1082 }
1083
1084 nvlist_free(fmri);
1085
1086 /*
1087 * Set the SES-specific properties so that consumers can query
1088 * additional information about the particular SES element.
1089 */
1090 if (topo_pgroup_create(tn, &ses_pgroup, &err) != 0) {
1091 topo_mod_dprintf(mod, "failed to create propgroup "
1092 "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err));
1093 return (-1);
1094 }
1095
1096 if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES,
1097 TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE,
1098 nodeid, &err) != 0) {
1099 topo_mod_dprintf(mod,
1100 "failed to create property %s: %s\n",
1101 TOPO_PROP_NODE_ID, topo_strerror(err));
1102 return (-1);
1103 }
1104
1105 if (topo_prop_set_string(tn, TOPO_PGROUP_SES,
1106 TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE,
1107 path, &err) != 0) {
1108 topo_mod_dprintf(mod,
1109 "failed to create property %s: %s\n",
1110 TOPO_PROP_TARGET_PATH, topo_strerror(err));
1111 return (-1);
1112 }
1113
1114 return (0);
1115 }
1116
1117 /*
1118 * Callback to add a disk to a given bay. We first check the status-code to
1119 * determine if a disk is present, ignoring those that aren't in an appropriate
1120 * state. We then scan the parent bay node's SAS address array to determine
1121 * possible attached SAS addresses. We create a disk node if the disk is not
1122 * SAS or the SES target does not support the necessary pages for this; if we
1123 * find the SAS address, we create a disk node and also correlate it with
1124 * the corresponding Solaris device node to fill in the rest of the data.
1125 */
1126 static int
ses_create_disk(ses_enum_data_t * sdp,tnode_t * pnode,nvlist_t * props)1127 ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props)
1128 {
1129 topo_mod_t *mod = sdp->sed_mod;
1130 uint64_t status;
1131 uint_t s, nsas;
1132 char **paths;
1133 int err, ret;
1134 tnode_t *child = NULL;
1135
1136 /*
1137 * Skip devices that are not in a present (and possibly damaged) state.
1138 */
1139 if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0)
1140 return (0);
1141
1142 if (status != SES_ESC_UNSUPPORTED &&
1143 status != SES_ESC_OK &&
1144 status != SES_ESC_CRITICAL &&
1145 status != SES_ESC_NONCRITICAL &&
1146 status != SES_ESC_UNRECOVERABLE &&
1147 status != SES_ESC_NO_ACCESS)
1148 return (0);
1149
1150 topo_mod_dprintf(mod, "found attached disk");
1151
1152 /*
1153 * Create the disk range.
1154 */
1155 if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) {
1156 topo_mod_dprintf(mod,
1157 "topo_node_create_range() failed: %s",
1158 topo_mod_errmsg(mod));
1159 return (-1);
1160 }
1161
1162 /*
1163 * Look through all SAS addresses and attempt to correlate them to a
1164 * known Solaris device. If we don't find a matching node, then we
1165 * don't enumerate the disk node.
1166 * Note that TOPO_PROP_SAS_ADDR prop includes SAS address from
1167 * alternate elements that represent the same device.
1168 */
1169 if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES,
1170 TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0)
1171 return (0);
1172
1173 err = 0;
1174
1175 for (s = 0; s < nsas; s++) {
1176 ret = disk_declare_addr(mod, pnode, &sdp->sed_devs, paths[s],
1177 &child);
1178 if (ret == 0) {
1179 break;
1180 } else if (ret < 0) {
1181 err = -1;
1182 break;
1183 }
1184 }
1185
1186 if (s == nsas)
1187 disk_declare_non_enumerated(mod, pnode, &child);
1188
1189 /* copy sas_addresses (target-ports) from parent (with 'w'added) */
1190 if (child != NULL) {
1191 int i;
1192 char **tports;
1193 uint64_t wwn;
1194
1195 tports = topo_mod_zalloc(mod, sizeof (char *) * nsas);
1196 if (tports != NULL) {
1197 for (i = 0; i < nsas; i++) {
1198 if (scsi_wwnstr_to_wwn(paths[i], &wwn) !=
1199 DDI_SUCCESS)
1200 break;
1201 tports[i] = scsi_wwn_to_wwnstr(wwn, 1, NULL);
1202 if (tports[i] == NULL)
1203 break;
1204 }
1205 /* if they all worked then create the property */
1206 if (i == nsas)
1207 (void) topo_prop_set_string_array(child,
1208 TOPO_PGROUP_STORAGE,
1209 TOPO_STORAGE_TARGET_PORT_L0IDS,
1210 TOPO_PROP_IMMUTABLE, (const char **)tports,
1211 nsas, &err);
1212
1213 for (i = 0; i < nsas; i++)
1214 if (tports[i] != NULL)
1215 scsi_free_wwnstr(tports[i]);
1216 topo_mod_free(mod, tports, sizeof (char *) * nsas);
1217 }
1218 }
1219
1220 for (s = 0; s < nsas; s++)
1221 topo_mod_free(mod, paths[s], strlen(paths[s]) + 1);
1222 topo_mod_free(mod, paths, nsas * sizeof (char *));
1223
1224 return (err);
1225 }
1226
1227 static int
ses_add_bay_props(topo_mod_t * mod,tnode_t * tn,ses_enum_node_t * snp)1228 ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp)
1229 {
1230 ses_alt_node_t *ap;
1231 ses_node_t *np;
1232 nvlist_t *props;
1233
1234 nvlist_t **phys;
1235 uint_t i, j, n_phys, all_phys = 0;
1236 char **paths;
1237 uint64_t addr;
1238 size_t len;
1239 int terr, err = -1;
1240
1241 for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
1242 ap = topo_list_next(ap)) {
1243 np = ap->san_node;
1244 props = ses_node_props(np);
1245
1246 if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
1247 &phys, &n_phys) != 0)
1248 continue;
1249
1250 all_phys += n_phys;
1251 }
1252
1253 if (all_phys == 0)
1254 return (0);
1255
1256 if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL)
1257 return (-1);
1258
1259 for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
1260 ap = topo_list_next(ap)) {
1261 np = ap->san_node;
1262 props = ses_node_props(np);
1263
1264 if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
1265 &phys, &n_phys) != 0)
1266 continue;
1267
1268 for (j = 0; j < n_phys; j++) {
1269 if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR,
1270 &addr) != 0)
1271 continue;
1272
1273 len = snprintf(NULL, 0, "%016llx", addr) + 1;
1274 if ((paths[i] = topo_mod_alloc(mod, len)) == NULL)
1275 goto error;
1276
1277 (void) snprintf(paths[i], len, "%016llx", addr);
1278
1279 ++i;
1280 }
1281 }
1282
1283 err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
1284 TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE,
1285 (const char **)paths, i, &terr);
1286 if (err != 0)
1287 err = topo_mod_seterrno(mod, terr);
1288
1289 error:
1290 for (i = 0; i < all_phys && paths[i] != NULL; i++)
1291 topo_mod_free(mod, paths[i], strlen(paths[i]) + 1);
1292 topo_mod_free(mod, paths, all_phys * sizeof (char *));
1293
1294 return (err);
1295 }
1296
1297 /*
1298 * Callback to create a basic node (bay, psu, fan, or controller and expander).
1299 */
1300 static int
ses_create_generic(ses_enum_data_t * sdp,ses_enum_node_t * snp,tnode_t * pnode,tnode_t * frutn,const char * nodename,const char * labelname,tnode_t ** node)1301 ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp, tnode_t *pnode,
1302 tnode_t *frutn, const char *nodename, const char *labelname,
1303 tnode_t **node)
1304 {
1305 ses_node_t *np = snp->sen_node;
1306 ses_node_t *parent;
1307 uint64_t instance = snp->sen_instance;
1308 topo_mod_t *mod = sdp->sed_mod;
1309 nvlist_t *props, *aprops;
1310 nvlist_t *auth = NULL, *fmri = NULL;
1311 tnode_t *tn = NULL;
1312 char label[128];
1313 int err;
1314 char *part = NULL, *serial = NULL, *revision = NULL;
1315 char *desc;
1316 boolean_t report;
1317
1318 props = ses_node_props(np);
1319
1320 (void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part);
1321 (void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial);
1322
1323 topo_mod_dprintf(mod, "adding %s %llu", nodename, instance);
1324
1325 /*
1326 * Create the node. The interesting information is all copied from the
1327 * parent enclosure node, so there is not much to do.
1328 */
1329 if ((auth = topo_mod_auth(mod, pnode)) == NULL)
1330 goto error;
1331
1332 /*
1333 * We want to report revision information for the controller nodes, but
1334 * we do not get per-element revision information. However, we do have
1335 * revision information for the entire enclosure, and we can use the
1336 * 'reported-via' property to know that this controller corresponds to
1337 * the given revision information. This means we cannot get revision
1338 * information for targets we are not explicitly connected to, but
1339 * there is little we can do about the situation.
1340 */
1341 if (strcmp(nodename, CONTROLLER) == 0 &&
1342 nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 &&
1343 report) {
1344 for (parent = ses_node_parent(np); parent != NULL;
1345 parent = ses_node_parent(parent)) {
1346 if (ses_node_type(parent) == SES_NODE_ENCLOSURE) {
1347 (void) nvlist_lookup_string(
1348 ses_node_props(parent),
1349 SES_EN_PROP_REV, &revision);
1350 break;
1351 }
1352 }
1353 }
1354
1355 if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
1356 nodename, (topo_instance_t)instance, NULL, auth, part, revision,
1357 serial)) == NULL) {
1358 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
1359 topo_mod_errmsg(mod));
1360 goto error;
1361 }
1362
1363 if ((tn = topo_node_bind(mod, pnode, nodename,
1364 instance, fmri)) == NULL) {
1365 topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
1366 topo_mod_errmsg(mod));
1367 goto error;
1368 }
1369
1370 /*
1371 * For the node label, we look for the following in order:
1372 *
1373 * <ses-description>
1374 * <ses-class-description> <instance>
1375 * <default-type-label> <instance>
1376 */
1377 if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
1378 desc[0] == '\0') {
1379 parent = ses_node_parent(np);
1380 aprops = ses_node_props(parent);
1381 if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION,
1382 &desc) != 0 || desc[0] == '\0')
1383 desc = (char *)labelname;
1384 (void) snprintf(label, sizeof (label), "%s %llu", desc,
1385 instance);
1386 desc = label;
1387 }
1388
1389 if (topo_node_label_set(tn, desc, &err) != 0)
1390 goto error;
1391
1392 if (ses_set_standard_props(mod, frutn, tn, NULL, ses_node_id(np),
1393 snp->sen_target->set_devpath) != 0)
1394 goto error;
1395
1396 if (strcmp(nodename, BAY) == 0) {
1397 if (ses_add_bay_props(mod, tn, snp) != 0)
1398 goto error;
1399
1400 if (ses_create_disk(sdp, tn, props) != 0)
1401 goto error;
1402
1403 if (topo_method_register(mod, tn, ses_bay_methods) != 0) {
1404 topo_mod_dprintf(mod,
1405 "topo_method_register() failed: %s",
1406 topo_mod_errmsg(mod));
1407 goto error;
1408 }
1409 } else if ((strcmp(nodename, FAN) == 0) ||
1410 (strcmp(nodename, PSU) == 0) ||
1411 (strcmp(nodename, CONTROLLER) == 0)) {
1412 /*
1413 * Only fan, psu, and controller nodes have a 'present' method.
1414 * Bay nodes are always present, and disk nodes are present by
1415 * virtue of being enumerated and SAS expander nodes and
1416 * SAS connector nodes are also always present once
1417 * the parent controller is found.
1418 */
1419 if (topo_method_register(mod, tn, ses_component_methods) != 0) {
1420 topo_mod_dprintf(mod,
1421 "topo_method_register() failed: %s",
1422 topo_mod_errmsg(mod));
1423 goto error;
1424 }
1425
1426 }
1427
1428 snp->sen_target->set_refcount++;
1429 topo_node_setspecific(tn, snp->sen_target);
1430
1431 nvlist_free(auth);
1432 nvlist_free(fmri);
1433 if (node != NULL) *node = tn;
1434 return (0);
1435
1436 error:
1437 nvlist_free(auth);
1438 nvlist_free(fmri);
1439 return (-1);
1440 }
1441
1442 /*
1443 * Create SAS expander specific props.
1444 */
1445 /*ARGSUSED*/
1446 static int
ses_set_expander_props(ses_enum_data_t * sdp,ses_enum_node_t * snp,tnode_t * ptnode,tnode_t * tnode,int * phycount,int64_t * connlist)1447 ses_set_expander_props(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1448 tnode_t *ptnode, tnode_t *tnode, int *phycount, int64_t *connlist)
1449 {
1450 ses_node_t *np = snp->sen_node;
1451 topo_mod_t *mod = sdp->sed_mod;
1452 nvlist_t *auth = NULL, *fmri = NULL;
1453 nvlist_t *props, **phylist;
1454 int err, i;
1455 uint_t pcount;
1456 uint64_t sasaddr, connidx;
1457 char sasaddr_str[17];
1458 boolean_t found = B_FALSE, ses_found = B_FALSE;
1459 dev_di_node_t *dnode, *sesdnode;
1460
1461 props = ses_node_props(np);
1462
1463 /*
1464 * the uninstalled expander is not enumerated by checking
1465 * the element status code. No present present' method provided.
1466 */
1467 /*
1468 * Get the Expander SAS address. It should exist.
1469 */
1470 if (nvlist_lookup_uint64(props, SES_EXP_PROP_SAS_ADDR,
1471 &sasaddr) != 0) {
1472 topo_mod_dprintf(mod,
1473 "Failed to get prop %s.", SES_EXP_PROP_SAS_ADDR);
1474 goto error;
1475 }
1476
1477 (void) sprintf(sasaddr_str, "%llx", sasaddr);
1478
1479 /* search matching dev_di_node. */
1480 for (dnode = topo_list_next(&sdp->sed_devs); dnode != NULL;
1481 dnode = topo_list_next(dnode)) {
1482 for (i = 0; i < dnode->ddn_ppath_count; i++) {
1483 if ((dnode->ddn_target_port[i] != NULL) &&
1484 (strstr(dnode->ddn_target_port[i],
1485 sasaddr_str) != NULL)) {
1486 found = B_TRUE;
1487 break;
1488 }
1489 }
1490 if (found)
1491 break;
1492 }
1493
1494 if (!found) {
1495 topo_mod_dprintf(mod,
1496 "ses_set_expander_props: Failed to find matching "
1497 "devinfo node for Exapnder SAS address %s",
1498 SES_EXP_PROP_SAS_ADDR);
1499 /* continue on to get storage group props. */
1500 } else {
1501 /* create/set the devfs-path and devid in the smp group */
1502 if (topo_pgroup_create(tnode, &smp_pgroup, &err) != 0) {
1503 topo_mod_dprintf(mod, "ses_set_expander_props: "
1504 "failed to create smp property group %s\n",
1505 topo_strerror(err));
1506 goto error;
1507 } else {
1508 if (topo_prop_set_string(tnode, TOPO_PGROUP_SMP,
1509 TOPO_PROP_SMP_TARGET_PORT, TOPO_PROP_IMMUTABLE,
1510 dnode->ddn_target_port[i], &err) != 0) {
1511 topo_mod_dprintf(mod, "ses_set_expander_props: "
1512 "set %S error %s\n", TOPO_PROP_SAS_ADDR,
1513 topo_strerror(err));
1514 }
1515 if (topo_prop_set_string(tnode, TOPO_PGROUP_SMP,
1516 TOPO_PROP_SMP_DEV_PATH, TOPO_PROP_IMMUTABLE,
1517 dnode->ddn_dpath, &err) != 0) {
1518 topo_mod_dprintf(mod, "ses_set_expander_props: "
1519 "set dev error %s\n", topo_strerror(err));
1520 }
1521 if (topo_prop_set_string(tnode, TOPO_PGROUP_SMP,
1522 TOPO_PROP_SMP_DEVID, TOPO_PROP_IMMUTABLE,
1523 dnode->ddn_devid, &err) != 0) {
1524 topo_mod_dprintf(mod, "ses_set_expander_props: "
1525 "set devid error %s\n", topo_strerror(err));
1526 }
1527 if (dnode->ddn_ppath_count != 0 &&
1528 topo_prop_set_string_array(tnode, TOPO_PGROUP_SMP,
1529 TOPO_PROP_SMP_PHYS_PATH, TOPO_PROP_IMMUTABLE,
1530 (const char **)dnode->ddn_ppath,
1531 dnode->ddn_ppath_count, &err) != 0) {
1532 topo_mod_dprintf(mod, "ses_set_expander_props: "
1533 "set phys-path error %s\n",
1534 topo_strerror(err));
1535 }
1536 }
1537 }
1538
1539 /* update the ses property group with SES target info */
1540 if ((topo_pgroup_create(tnode, &ses_pgroup, &err) != 0) &&
1541 (err != ETOPO_PROP_DEFD)) {
1542 /* SES prop group doesn't exist but failed to be created. */
1543 topo_mod_dprintf(mod, "ses_set_expander_props: "
1544 "ses pgroup create error %s\n", topo_strerror(err));
1545 goto error;
1546 } else {
1547 /* locate assciated enclosure dev_di_node. */
1548 for (sesdnode = topo_list_next(&sdp->sed_devs);
1549 sesdnode != NULL; sesdnode = topo_list_next(sesdnode)) {
1550 for (i = 0; i < sesdnode->ddn_ppath_count; i++) {
1551 /*
1552 * check if attached port exists and
1553 * its node type is enclosure and
1554 * attached port is same as sas address of
1555 * the expander and
1556 * bridge port for virtual phy indication
1557 * exist.
1558 */
1559 if ((sesdnode->ddn_attached_port[i] != NULL) &&
1560 (sesdnode->ddn_dtype == DTYPE_ESI) &&
1561 (strstr(sesdnode->ddn_attached_port[i],
1562 sasaddr_str) != NULL) &&
1563 (sesdnode->ddn_bridge_port[i] != NULL)) {
1564 ses_found = B_TRUE;
1565 break;
1566 }
1567 }
1568 if (ses_found) break;
1569 }
1570
1571 if (ses_found) {
1572 if (topo_prop_set_string(tnode, TOPO_PGROUP_SES,
1573 TOPO_PROP_SES_TARGET_PORT, TOPO_PROP_IMMUTABLE,
1574 sesdnode->ddn_target_port[i], &err) != 0) {
1575 topo_mod_dprintf(mod, "ses_set_expander_props: "
1576 "set ses %S error %s\n", TOPO_PROP_SAS_ADDR,
1577 topo_strerror(err));
1578 }
1579 if (topo_prop_set_string(tnode, TOPO_PGROUP_SES,
1580 TOPO_PROP_SES_DEV_PATH, TOPO_PROP_IMMUTABLE,
1581 sesdnode->ddn_dpath, &err) != 0) {
1582 topo_mod_dprintf(mod, "ses_set_expander_props: "
1583 "set ses dev error %s\n",
1584 topo_strerror(err));
1585 }
1586 if (topo_prop_set_string(tnode, TOPO_PGROUP_SES,
1587 TOPO_PROP_SES_DEVID, TOPO_PROP_IMMUTABLE,
1588 sesdnode->ddn_devid, &err) != 0) {
1589 topo_mod_dprintf(mod, "ses_set_expander_props: "
1590 "set ses devid error %s\n",
1591 topo_strerror(err));
1592 }
1593 if (sesdnode->ddn_ppath_count != 0 &&
1594 topo_prop_set_string_array(tnode, TOPO_PGROUP_SES,
1595 TOPO_PROP_SES_PHYS_PATH, TOPO_PROP_IMMUTABLE,
1596 (const char **)sesdnode->ddn_ppath,
1597 sesdnode->ddn_ppath_count, &err) != 0) {
1598 topo_mod_dprintf(mod, "ses_set_expander_props: "
1599 "set ses phys-path error %s\n",
1600 topo_strerror(err));
1601 }
1602
1603 }
1604 }
1605
1606 /* create the storage group */
1607 if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) {
1608 topo_mod_dprintf(mod, "ses_set_expander_props: "
1609 "create storage error %s\n", topo_strerror(err));
1610 goto error;
1611 } else {
1612 /* set the SAS address prop out of expander element status. */
1613 if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1614 TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, sasaddr_str,
1615 &err) != 0) {
1616 topo_mod_dprintf(mod, "ses_set_expander_props: "
1617 "set %S error %s\n", TOPO_PROP_SAS_ADDR,
1618 topo_strerror(err));
1619 }
1620
1621 /* Get the phy information for the expander */
1622 if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
1623 &phylist, &pcount) != 0) {
1624 topo_mod_dprintf(mod,
1625 "Failed to get prop %s.", SES_SAS_PROP_PHYS);
1626 } else {
1627 /*
1628 * For each phy, get the connector element index and
1629 * stores into connector element index array.
1630 */
1631 *phycount = pcount;
1632 for (i = 0; i < pcount; i++) {
1633 if (nvlist_lookup_uint64(phylist[i],
1634 SES_PROP_CE_IDX, &connidx) == 0) {
1635 if (connidx != 0xff) {
1636 connlist[i] = connidx;
1637 } else {
1638 connlist[i] = -1;
1639 }
1640 } else {
1641 /* Fail to get the index. set to -1. */
1642 connlist[i] = -1;
1643 }
1644 }
1645
1646 /* set the phy count prop of the expander. */
1647 if (topo_prop_set_uint64(tnode, TOPO_PGROUP_STORAGE,
1648 TOPO_PROP_PHY_COUNT, TOPO_PROP_IMMUTABLE, pcount,
1649 &err) != 0) {
1650 topo_mod_dprintf(mod, "ses_set_expander_props: "
1651 "set %S error %s\n", TOPO_PROP_PHY_COUNT,
1652 topo_strerror(err));
1653 }
1654
1655 /*
1656 * set the connector element index of
1657 * the expander phys.
1658 */
1659 }
1660
1661 /* populate other misc storage group properties */
1662 if (found) {
1663 if (dnode->ddn_mfg && (topo_prop_set_string(tnode,
1664 TOPO_PGROUP_STORAGE, TOPO_STORAGE_MANUFACTURER,
1665 TOPO_PROP_IMMUTABLE, dnode->ddn_mfg, &err) != 0)) {
1666 topo_mod_dprintf(mod, "ses_set_expander_props: "
1667 "set mfg error %s\n", topo_strerror(err));
1668 }
1669
1670 if (dnode->ddn_model && (topo_prop_set_string(tnode,
1671 TOPO_PGROUP_STORAGE, TOPO_STORAGE_MODEL,
1672 TOPO_PROP_IMMUTABLE,
1673 dnode->ddn_model, &err) != 0)) {
1674 topo_mod_dprintf(mod, "ses_set_expander_props: "
1675 "set model error %s\n", topo_strerror(err));
1676 }
1677
1678 if (dnode->ddn_serial && (topo_prop_set_string(tnode,
1679 TOPO_PGROUP_STORAGE, TOPO_STORAGE_SERIAL_NUM,
1680 TOPO_PROP_IMMUTABLE,
1681 dnode->ddn_serial, &err) != 0)) {
1682 topo_mod_dprintf(mod, "ses_set_expander_props: "
1683 "set serial error %s\n",
1684 topo_strerror(err));
1685 }
1686
1687 if (dnode->ddn_firm && (topo_prop_set_string(tnode,
1688 TOPO_PGROUP_STORAGE,
1689 TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE,
1690 dnode->ddn_firm, &err) != 0)) {
1691 topo_mod_dprintf(mod, "ses_set_expander_props: "
1692 "set firm error %s\n", topo_strerror(err));
1693 }
1694 }
1695 }
1696
1697 return (0);
1698
1699 error:
1700 nvlist_free(auth);
1701 nvlist_free(fmri);
1702 return (-1);
1703 }
1704
1705 /*
1706 * Create SAS expander specific props.
1707 */
1708 /*ARGSUSED*/
1709 static int
ses_set_connector_props(ses_enum_data_t * sdp,ses_enum_node_t * snp,tnode_t * tnode,int64_t phy_mask)1710 ses_set_connector_props(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1711 tnode_t *tnode, int64_t phy_mask)
1712 {
1713 ses_node_t *np = snp->sen_node;
1714 topo_mod_t *mod = sdp->sed_mod;
1715 nvlist_t *props;
1716 int err, i;
1717 uint64_t conntype;
1718 char phymask_str[17], *conntype_str;
1719 boolean_t found;
1720
1721 props = ses_node_props(np);
1722
1723 /*
1724 * convert phy mask to string.
1725 */
1726 (void) snprintf(phymask_str, 17, "%llx", phy_mask);
1727
1728 /* create the storage group */
1729 if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) {
1730 topo_mod_dprintf(mod, "ses_set_expander_props: "
1731 "create storage error %s\n", topo_strerror(err));
1732 return (-1);
1733 } else {
1734 /* set the SAS address prop of the expander. */
1735 if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1736 TOPO_STORAGE_SAS_PHY_MASK, TOPO_PROP_IMMUTABLE,
1737 phymask_str, &err) != 0) {
1738 topo_mod_dprintf(mod, "ses_set_expander_props: "
1739 "set %S error %s\n", TOPO_STORAGE_SAS_PHY_MASK,
1740 topo_strerror(err));
1741 }
1742
1743 /* Get the connector type information for the expander */
1744 if (nvlist_lookup_uint64(props,
1745 SES_SC_PROP_CONNECTOR_TYPE, &conntype) != 0) {
1746 topo_mod_dprintf(mod, "Failed to get prop %s.",
1747 TOPO_STORAGE_SAS_PHY_MASK);
1748 } else {
1749 found = B_FALSE;
1750 for (i = 0; ; i++) {
1751 if (sas_connector_type_list[i].sct_type ==
1752 SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) {
1753 break;
1754 }
1755 if (sas_connector_type_list[i].sct_type ==
1756 conntype) {
1757 conntype_str =
1758 sas_connector_type_list[i].sct_name;
1759 found = B_TRUE;
1760 break;
1761 }
1762 }
1763
1764 if (!found) {
1765 if (conntype <
1766 SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) {
1767 conntype_str =
1768 SAS_CONNECTOR_TYPE_RESERVED;
1769 } else {
1770 conntype_str =
1771 SAS_CONNECTOR_TYPE_NOT_DEFINED;
1772 }
1773 }
1774
1775 /* set the phy count prop of the expander. */
1776 if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1777 TOPO_STORAGE_SAS_CONNECTOR_TYPE,
1778 TOPO_PROP_IMMUTABLE, conntype_str, &err) != 0) {
1779 topo_mod_dprintf(mod, "ses_set_expander_props: "
1780 "set %S error %s\n", TOPO_PROP_PHY_COUNT,
1781 topo_strerror(err));
1782 }
1783 }
1784 }
1785
1786 return (0);
1787 }
1788
1789 /*
1790 * Instantiate SAS expander nodes for a given ESC Electronics node(controller)
1791 * nodes.
1792 */
1793 /*ARGSUSED*/
1794 static int
ses_create_esc_sasspecific(ses_enum_data_t * sdp,ses_enum_node_t * snp,tnode_t * pnode,ses_enum_chassis_t * cp,boolean_t dorange)1795 ses_create_esc_sasspecific(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1796 tnode_t *pnode, ses_enum_chassis_t *cp,
1797 boolean_t dorange)
1798 {
1799 topo_mod_t *mod = sdp->sed_mod;
1800 tnode_t *exptn, *contn;
1801 boolean_t found;
1802 sas_connector_phy_data_t connectors[64] = {NULL};
1803 uint64_t max;
1804 ses_enum_node_t *ctlsnp, *xsnp, *consnp;
1805 ses_node_t *np = snp->sen_node;
1806 nvlist_t *props, *psprops;
1807 uint64_t index, psindex, conindex, psstatus, i, j, count;
1808 int64_t cidxlist[256] = {NULL};
1809 int phycount;
1810
1811 props = ses_node_props(np);
1812
1813 if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_ONLY_INDEX,
1814 &index) != 0)
1815 return (-1);
1816
1817 /*
1818 * For SES constroller node, check to see if there are
1819 * associated SAS expanders.
1820 */
1821 found = B_FALSE;
1822 max = 0;
1823 for (ctlsnp = topo_list_next(&cp->sec_nodes); ctlsnp != NULL;
1824 ctlsnp = topo_list_next(ctlsnp)) {
1825 if (ctlsnp->sen_type == SES_ET_SAS_EXPANDER) {
1826 found = B_TRUE;
1827 if (ctlsnp->sen_instance > max)
1828 max = ctlsnp->sen_instance;
1829 }
1830 }
1831
1832 /*
1833 * No SAS expander found notthing to process.
1834 */
1835 if (!found)
1836 return (0);
1837
1838 topo_mod_dprintf(mod, "%s Controller %d: creating "
1839 "%llu %s nodes", cp->sec_csn, index, max + 1, SASEXPANDER);
1840
1841 /*
1842 * The max number represent the number of elements
1843 * deducted from the highest SES_PROP_ELEMENT_CLASS_INDEX
1844 * of SET_ET_SAS_EXPANDER type element.
1845 *
1846 * There may be multiple ESC Electronics element(controllers)
1847 * within JBOD(typicall two for redundancy) and SAS expander
1848 * elements are associated with only one of them. We are
1849 * still creating the range based max number here.
1850 * That will cover the case that all expanders are associated
1851 * with one SES controller.
1852 */
1853 if (dorange && topo_node_range_create(mod, pnode,
1854 SASEXPANDER, 0, max) != 0) {
1855 topo_mod_dprintf(mod,
1856 "topo_node_create_range() failed: %s",
1857 topo_mod_errmsg(mod));
1858 return (-1);
1859 }
1860
1861 /*
1862 * Search exapnders with the parent index matching with
1863 * ESC Electronics element index.
1864 * Note the index used here is a global index across
1865 * SES elements.
1866 */
1867 for (xsnp = topo_list_next(&cp->sec_nodes); xsnp != NULL;
1868 xsnp = topo_list_next(xsnp)) {
1869 if (xsnp->sen_type == SES_ET_SAS_EXPANDER) {
1870 /*
1871 * get the parent ESC controller.
1872 */
1873 psprops = ses_node_props(xsnp->sen_node);
1874 if (nvlist_lookup_uint64(psprops,
1875 SES_PROP_STATUS_CODE, &psstatus) == 0) {
1876 if (psstatus == SES_ESC_NOT_INSTALLED) {
1877 /*
1878 * Not installed.
1879 * Don't create a ndoe.
1880 */
1881 continue;
1882 }
1883 } else {
1884 /*
1885 * The element should have status code.
1886 * If not there is no way to find
1887 * out if the expander element exist or
1888 * not.
1889 */
1890 continue;
1891 }
1892
1893 /* Get the physical parent index to compare. */
1894 if (nvlist_lookup_uint64(psprops,
1895 LIBSES_PROP_PHYS_PARENT, &psindex) == 0) {
1896 if (index == psindex) {
1897 /* indentation moved forward */
1898 /*
1899 * Handle basic node information of SAS expander
1900 * element - binding to parent node and
1901 * allocating FMRI...
1902 */
1903 if (ses_create_generic(sdp, xsnp, pnode, pnode, SASEXPANDER,
1904 "SAS-EXPANDER", &exptn) != 0)
1905 continue;
1906 /*
1907 * Now handle SAS expander unique portion of node creation.
1908 * The max nubmer of the phy count is 256 since SES-2
1909 * defines as 1 byte field. The cidxlist has the same
1910 * number of elements.
1911 *
1912 * We use size 64 array to store the connectors.
1913 * Typically a connectors associated with 4 phys so that
1914 * matches with the max number of connecters associated
1915 * with an expander.
1916 * The phy count goes up to 38 for Sun supported
1917 * JBOD.
1918 */
1919 memset(cidxlist, 0, sizeof (int64_t) * 64);
1920 if (ses_set_expander_props(sdp, xsnp, pnode, exptn, &phycount,
1921 cidxlist) != 0) {
1922 /*
1923 * error on getting specific prop failed.
1924 * continue on. Note that the node is
1925 * left bound.
1926 */
1927 continue;
1928 }
1929
1930 /*
1931 * count represetns the number of connectors discovered so far.
1932 */
1933 count = 0;
1934 memset(connectors, 0, sizeof (sas_connector_phy_data_t) * 64);
1935 for (i = 0; i < phycount; i++) {
1936 if (cidxlist[i] != -1) {
1937 /* connector index is valid. */
1938 for (j = 0; j < count; j++) {
1939 if (connectors[j].scpd_index ==
1940 cidxlist[i]) {
1941 /*
1942 * Just update phy mask.
1943 * The postion for connector
1944 * index lists(cidxlist index)
1945 * is set.
1946 */
1947 connectors[j].scpd_pm =
1948 connectors[j].scpd_pm |
1949 (1ULL << i);
1950 break;
1951 }
1952 }
1953 /*
1954 * If j and count matche a new connector
1955 * index is found.
1956 */
1957 if (j == count) {
1958 /* add a new index and phy mask. */
1959 connectors[count].scpd_index =
1960 cidxlist[i];
1961 connectors[count].scpd_pm =
1962 connectors[count].scpd_pm |
1963 (1ULL << i);
1964 count++;
1965 }
1966 }
1967 }
1968
1969 /*
1970 * create range for the connector nodes.
1971 * The class index of the ses connector element
1972 * is set as the instance nubmer for the node.
1973 * Even though one expander may not have all connectors
1974 * are associated with we are creating the range with
1975 * max possible instance number.
1976 */
1977 found = B_FALSE;
1978 max = 0;
1979 for (consnp = topo_list_next(&cp->sec_nodes);
1980 consnp != NULL; consnp = topo_list_next(consnp)) {
1981 if (consnp->sen_type == SES_ET_SAS_CONNECTOR) {
1982 psprops = ses_node_props(consnp->sen_node);
1983 found = B_TRUE;
1984 if (consnp->sen_instance > max)
1985 max = consnp->sen_instance;
1986 }
1987 }
1988
1989 /*
1990 * No SAS connector found nothing to process.
1991 */
1992 if (!found)
1993 return (0);
1994
1995 if (dorange && topo_node_range_create(mod, exptn,
1996 RECEPTACLE, 0, max) != 0) {
1997 topo_mod_dprintf(mod,
1998 "topo_node_create_range() failed: %s",
1999 topo_mod_errmsg(mod));
2000 return (-1);
2001 }
2002
2003 /* search matching connector element using the index. */
2004 for (i = 0; i < count; i++) {
2005 found = B_FALSE;
2006 for (consnp = topo_list_next(&cp->sec_nodes);
2007 consnp != NULL; consnp = topo_list_next(consnp)) {
2008 if (consnp->sen_type == SES_ET_SAS_CONNECTOR) {
2009 psprops = ses_node_props(
2010 consnp->sen_node);
2011 /*
2012 * Get the physical parent index to
2013 * compare.
2014 * The connector elements are children
2015 * of ESC Electronics element even
2016 * though we enumerate them under
2017 * an expander in libtopo.
2018 */
2019 if (nvlist_lookup_uint64(psprops,
2020 SES_PROP_ELEMENT_ONLY_INDEX,
2021 &conindex) == 0) {
2022 if (conindex ==
2023 connectors[i].scpd_index) {
2024 found = B_TRUE;
2025 break;
2026 }
2027 }
2028 }
2029 }
2030
2031 /* now create a libtopo node. */
2032 if (found) {
2033 /* Create generic props. */
2034 if (ses_create_generic(sdp, consnp, exptn,
2035 topo_node_parent(exptn),
2036 RECEPTACLE, "RECEPTACLE", &contn) !=
2037 0) {
2038 continue;
2039 }
2040 /* Create connector specific props. */
2041 if (ses_set_connector_props(sdp, consnp,
2042 contn, connectors[i].scpd_pm) != 0) {
2043 continue;
2044 }
2045 }
2046 }
2047 /* end indentation change */
2048 }
2049 }
2050 }
2051 }
2052
2053 return (0);
2054 }
2055
2056 /*
2057 * Instantiate any protocol specific portion of a node.
2058 */
2059 /*ARGSUSED*/
2060 static int
ses_create_protocol_specific(ses_enum_data_t * sdp,ses_enum_node_t * snp,tnode_t * pnode,uint64_t type,ses_enum_chassis_t * cp,boolean_t dorange)2061 ses_create_protocol_specific(ses_enum_data_t *sdp, ses_enum_node_t *snp,
2062 tnode_t *pnode, uint64_t type, ses_enum_chassis_t *cp,
2063 boolean_t dorange)
2064 {
2065
2066 if (type == SES_ET_ESC_ELECTRONICS) {
2067 /* create SAS specific children(expanders and connectors. */
2068 return (ses_create_esc_sasspecific(sdp, snp, pnode, cp,
2069 dorange));
2070 }
2071
2072 return (0);
2073 }
2074
2075 /*
2076 * Instantiate any children of a given type.
2077 */
2078 static int
ses_create_children(ses_enum_data_t * sdp,tnode_t * pnode,uint64_t type,const char * nodename,const char * defaultlabel,ses_enum_chassis_t * cp,boolean_t dorange)2079 ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type,
2080 const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp,
2081 boolean_t dorange)
2082 {
2083 topo_mod_t *mod = sdp->sed_mod;
2084 boolean_t found;
2085 uint64_t max;
2086 ses_enum_node_t *snp;
2087 tnode_t *tn;
2088
2089 /*
2090 * First go through and count how many matching nodes we have.
2091 */
2092 max = 0;
2093 found = B_FALSE;
2094 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2095 snp = topo_list_next(snp)) {
2096 if (snp->sen_type == type) {
2097 found = B_TRUE;
2098 if (snp->sen_instance > max)
2099 max = snp->sen_instance;
2100 }
2101 }
2102
2103 /*
2104 * No enclosure should export both DEVICE and ARRAY_DEVICE elements.
2105 * Since we map both of these to 'disk', if an enclosure does this, we
2106 * just ignore the array elements.
2107 */
2108 if (!found ||
2109 (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev))
2110 return (0);
2111
2112 topo_mod_dprintf(mod, "%s: creating %llu %s nodes",
2113 cp->sec_csn, max + 1, nodename);
2114
2115 if (dorange && topo_node_range_create(mod, pnode,
2116 nodename, 0, max) != 0) {
2117 topo_mod_dprintf(mod,
2118 "topo_node_create_range() failed: %s",
2119 topo_mod_errmsg(mod));
2120 return (-1);
2121 }
2122
2123 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2124 snp = topo_list_next(snp)) {
2125 if (snp->sen_type == type) {
2126 /*
2127 * With flat layout of ses nodes there is no
2128 * way to find out the direct FRU for a node.
2129 * Passing NULL for fru topo node. Note that
2130 * ses_create_children_from_phys_tree() provides
2131 * the actual direct FRU for a node.
2132 */
2133 if (ses_create_generic(sdp, snp, pnode, NULL,
2134 nodename, defaultlabel, &tn) != 0)
2135 return (-1);
2136 /*
2137 * For some SES element there may be protocol specific
2138 * information to process. Here we are processing
2139 * the association between enclosure controller and
2140 * SAS expanders.
2141 */
2142 if (type == SES_ET_ESC_ELECTRONICS) {
2143 /* create SAS expander node */
2144 if (ses_create_protocol_specific(sdp, snp,
2145 tn, type, cp, dorange) != 0) {
2146 return (-1);
2147 }
2148 }
2149
2150 }
2151 }
2152
2153 return (0);
2154 }
2155
2156 /*
2157 * Instantiate a new subchassis instance in the topology.
2158 */
2159 static int
ses_create_subchassis(ses_enum_data_t * sdp,tnode_t * pnode,ses_enum_chassis_t * scp)2160 ses_create_subchassis(ses_enum_data_t *sdp, tnode_t *pnode,
2161 ses_enum_chassis_t *scp)
2162 {
2163 topo_mod_t *mod = sdp->sed_mod;
2164 tnode_t *tn;
2165 nvlist_t *props;
2166 nvlist_t *auth = NULL, *fmri = NULL;
2167 uint64_t instance = scp->sec_instance;
2168 char *desc;
2169 char label[128];
2170 char **paths;
2171 int i, err;
2172 ses_enum_target_t *stp;
2173 int ret = -1;
2174
2175 /*
2176 * Copy authority information from parent enclosure node
2177 */
2178 if ((auth = topo_mod_auth(mod, pnode)) == NULL)
2179 goto error;
2180
2181 /*
2182 * Record the subchassis serial number in the FMRI.
2183 * For now, we assume that logical id is the subchassis serial number.
2184 * If this assumption changes in future, then the following
2185 * piece of code will need to be updated via an RFE.
2186 */
2187 if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
2188 SUBCHASSIS, (topo_instance_t)instance, NULL, auth, NULL, NULL,
2189 NULL)) == NULL) {
2190 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
2191 topo_mod_errmsg(mod));
2192 goto error;
2193 }
2194
2195 if ((tn = topo_node_bind(mod, pnode, SUBCHASSIS,
2196 instance, fmri)) == NULL) {
2197 topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
2198 topo_mod_errmsg(mod));
2199 goto error;
2200 }
2201
2202 props = ses_node_props(scp->sec_enclosure);
2203
2204 /*
2205 * Look for the subchassis label in the following order:
2206 * <ses-description>
2207 * <ses-class-description> <instance>
2208 * <default-type-label> <instance>
2209 *
2210 * For subchassis, the default label is "SUBCHASSIS"
2211 */
2212 if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
2213 desc[0] == '\0') {
2214 if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION,
2215 &desc) == 0 && desc[0] != '\0')
2216 (void) snprintf(label, sizeof (label), "%s %llu", desc,
2217 instance);
2218 else
2219 (void) snprintf(label, sizeof (label),
2220 "SUBCHASSIS %llu", instance);
2221 desc = label;
2222 }
2223
2224 if (topo_node_label_set(tn, desc, &err) != 0)
2225 goto error;
2226
2227 if (ses_set_standard_props(mod, NULL, tn, NULL,
2228 ses_node_id(scp->sec_enclosure), scp->sec_target->set_devpath) != 0)
2229 goto error;
2230
2231 /*
2232 * Set the 'chassis-type' property for this subchassis. This is either
2233 * 'ses-class-description' or 'subchassis'.
2234 */
2235 if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION, &desc) != 0)
2236 desc = "subchassis";
2237
2238 if (topo_prop_set_string(tn, TOPO_PGROUP_SES,
2239 TOPO_PROP_CHASSIS_TYPE, TOPO_PROP_IMMUTABLE, desc, &err) != 0) {
2240 topo_mod_dprintf(mod, "failed to create property %s: %s\n",
2241 TOPO_PROP_CHASSIS_TYPE, topo_strerror(err));
2242 goto error;
2243 }
2244
2245 /*
2246 * For enclosures, we want to include all possible targets (for upgrade
2247 * purposes).
2248 */
2249 for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
2250 stp = topo_list_next(stp), i++)
2251 ;
2252
2253 verify(i != 0);
2254 paths = alloca(i * sizeof (char *));
2255
2256 for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
2257 stp = topo_list_next(stp), i++)
2258 paths[i] = stp->set_devpath;
2259
2260 if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
2261 TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
2262 i, &err) != 0) {
2263 topo_mod_dprintf(mod, "failed to create property %s: %s\n",
2264 TOPO_PROP_PATHS, topo_strerror(err));
2265 goto error;
2266 }
2267
2268 if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
2269 topo_mod_dprintf(mod, "topo_method_register() failed: %s",
2270 topo_mod_errmsg(mod));
2271 goto error;
2272 }
2273
2274 /*
2275 * Create the nodes for controllers and bays.
2276 */
2277 if (ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
2278 CONTROLLER, "CONTROLLER", scp, B_TRUE) != 0 ||
2279 ses_create_children(sdp, tn, SES_ET_DEVICE,
2280 BAY, "BAY", scp, B_TRUE) != 0 ||
2281 ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
2282 BAY, "BAY", scp, B_TRUE) != 0)
2283 goto error;
2284
2285 ret = 0;
2286
2287 error:
2288 nvlist_free(auth);
2289 nvlist_free(fmri);
2290 return (ret);
2291 }
2292
2293 /*
2294 * Function we use to insert a node.
2295 */
2296 static int
ses_phys_tree_insert(topo_mod_t * mod,ses_phys_tree_t ** sproot,ses_phys_tree_t * child)2297 ses_phys_tree_insert(topo_mod_t *mod, ses_phys_tree_t **sproot,
2298 ses_phys_tree_t *child)
2299 {
2300 uint64_t ppindex, eindex, pindex;
2301 ses_phys_tree_t *node_ptr;
2302 int ret = 0;
2303
2304 assert(sproot != NULL);
2305 assert(child != NULL);
2306
2307 if (*sproot == NULL) {
2308 *sproot = child;
2309 return (0);
2310 }
2311
2312 pindex = child->spt_pindex;
2313 ppindex = (*sproot)->spt_pindex;
2314 eindex = (*sproot)->spt_eonlyindex;
2315
2316 /*
2317 * If the element only index of the root is same as the physical
2318 * parent index of a node to be added, add the node as a child of
2319 * the current root.
2320 */
2321 if (eindex == pindex) {
2322 (void) ses_phys_tree_insert(mod, &(*sproot)->spt_child, child);
2323 child->spt_parent = *sproot;
2324 } else if (ppindex == pindex) {
2325 /*
2326 * if the physical parent of the current root and the child
2327 * is same, then this should be a sibling node.
2328 * Siblings can be different element types and arrange
2329 * them by group.
2330 */
2331 if ((*sproot)->spt_senumnode->sen_type ==
2332 child->spt_senumnode->sen_type) {
2333 child->spt_sibling = *sproot;
2334 *sproot = child;
2335 } else {
2336 /* add a node in front of matching element type. */
2337 node_ptr = *sproot;
2338 while (node_ptr->spt_sibling != NULL) {
2339 if (node_ptr->spt_sibling->
2340 spt_senumnode->sen_type ==
2341 child->spt_senumnode->sen_type) {
2342 child->spt_sibling =
2343 node_ptr->spt_sibling;
2344 node_ptr->spt_sibling = child;
2345 break;
2346 }
2347 node_ptr = node_ptr->spt_sibling;
2348 }
2349 /* no matching. Add the child at the end. */
2350 if (node_ptr->spt_sibling == NULL) {
2351 node_ptr->spt_sibling = child;
2352 }
2353 }
2354 child->spt_parent = (*sproot)->spt_parent;
2355 } else {
2356 /*
2357 * The root and the node is not directly related.
2358 * Try to insert to the child sub-tree first and then try to
2359 * insert to the sibling sub-trees. If fails for both
2360 * the caller will retry insertion later.
2361 */
2362 if ((*sproot)->spt_child) {
2363 ret = ses_phys_tree_insert(mod, &(*sproot)->spt_child,
2364 child);
2365 }
2366 if ((*sproot)->spt_child == NULL || ret != 0) {
2367 if ((*sproot)->spt_sibling) {
2368 ret = ses_phys_tree_insert(mod,
2369 &(*sproot)->spt_sibling, child);
2370 } else {
2371 ret = 1;
2372 }
2373 }
2374 return (ret);
2375 }
2376 return (0);
2377 }
2378
2379 /*
2380 * Construct tree view of ses elements through parent phyiscal element index.
2381 * The root of tree is already constructed using the enclosure element.
2382 */
2383 static int
ses_construct_phys_tree(ses_enum_data_t * sdp,ses_enum_chassis_t * cp,ses_phys_tree_t * sproot)2384 ses_construct_phys_tree(ses_enum_data_t *sdp, ses_enum_chassis_t *cp,
2385 ses_phys_tree_t *sproot)
2386 {
2387 ses_enum_node_t *snp;
2388 ses_phys_tree_t *child;
2389 ses_phys_tree_t *u_watch = NULL;
2390 ses_phys_tree_t *u_head = NULL;
2391 ses_phys_tree_t *u_tail = NULL;
2392 int u_inserted = 0, u_left = 0;
2393 nvlist_t *props;
2394 topo_mod_t *mod = sdp->sed_mod;
2395
2396 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2397 snp = topo_list_next(snp)) {
2398 if ((child = topo_mod_zalloc(mod,
2399 sizeof (ses_phys_tree_t))) == NULL) {
2400 topo_mod_dprintf(mod,
2401 "failed to allocate root.");
2402 return (-1);
2403 }
2404 child->spt_snode = snp->sen_node;
2405 props = ses_node_props(snp->sen_node);
2406 if (nvlist_lookup_uint64(props,
2407 LIBSES_PROP_PHYS_PARENT, &child->spt_pindex) != 0) {
2408 /*
2409 * the prop should exist. continue to see if
2410 * we can build a partial tree with other elements.
2411 */
2412 topo_mod_dprintf(mod,
2413 "ses_construct_phys_tree(): Failed to find prop %s "
2414 "on ses element type %d and instance %d "
2415 "(CSN %s).", LIBSES_PROP_PHYS_PARENT,
2416 snp->sen_type, snp->sen_instance, cp->sec_csn);
2417 topo_mod_free(mod, child, sizeof (ses_phys_tree_t));
2418 continue;
2419 } else {
2420 if (nvlist_lookup_boolean_value(props,
2421 LIBSES_PROP_FRU, &child->spt_isfru) != 0) {
2422 topo_mod_dprintf(mod,
2423 "ses_construct_phys_tree(): Failed to "
2424 "find prop %s on ses element type %d "
2425 "and instance %d (CSN %s).",
2426 LIBSES_PROP_FRU,
2427 snp->sen_type, snp->sen_instance,
2428 cp->sec_csn);
2429 /*
2430 * Ignore if the prop doesn't exist.
2431 * Note that the enclosure itself should be
2432 * a FRU so if no FRU found the enclosure FRU
2433 * can be a direct FRU.
2434 */
2435 }
2436 verify(nvlist_lookup_uint64(props,
2437 SES_PROP_ELEMENT_ONLY_INDEX,
2438 &child->spt_eonlyindex) == 0);
2439 verify(nvlist_lookup_uint64(props,
2440 SES_PROP_ELEMENT_CLASS_INDEX,
2441 &child->spt_cindex) == 0);
2442 }
2443 child->spt_senumnode = snp;
2444 if (ses_phys_tree_insert(mod, &sproot, child) != 0) {
2445 /* collect unresolved element to process later. */
2446 if (u_head == NULL) {
2447 u_head = child;
2448 u_tail = child;
2449 } else {
2450 child->spt_sibling = u_head;
2451 u_head = child;
2452 }
2453 }
2454 }
2455
2456 /*
2457 * The parent of a child node may not be inserted yet.
2458 * Trying to insert the child until no child is left or
2459 * no child is not added further. For the latter
2460 * the hierarchical relationship between elements
2461 * should be checked through SUNW,FRUID page.
2462 * u_watch is a watch dog to check the prgress of unresolved
2463 * node.
2464 */
2465 u_watch = u_tail;
2466 while (u_head) {
2467 child = u_head;
2468 u_head = u_head->spt_sibling;
2469 if (u_head == NULL)
2470 u_tail = NULL;
2471 child->spt_sibling = NULL;
2472 if (ses_phys_tree_insert(mod, &sproot, child) != 0) {
2473 u_tail->spt_sibling = child;
2474 u_tail = child;
2475 if (child == u_watch) {
2476 /*
2477 * We just scanned one round for the
2478 * unresolved list. Check to see whether we
2479 * have nodes inserted, if none, we should
2480 * break in case of an indefinite loop.
2481 */
2482 if (u_inserted == 0) {
2483 /*
2484 * Indicate there is unhandled node.
2485 * Chain free the whole unsolved
2486 * list here.
2487 */
2488 u_left++;
2489 break;
2490 } else {
2491 u_inserted = 0;
2492 u_watch = u_tail;
2493 }
2494 }
2495 } else {
2496 /*
2497 * We just inserted one rpnode, increment the
2498 * unsolved_inserted counter. We will utilize this
2499 * counter to detect an indefinite insertion loop.
2500 */
2501 u_inserted++;
2502 if (child == u_watch) {
2503 /*
2504 * watch dog node itself is inserted.
2505 * Set it to the tail and refresh the watching.
2506 */
2507 u_watch = u_tail;
2508 u_inserted = 0;
2509 u_left = 0;
2510 }
2511 }
2512 }
2513
2514 /* check if there is left out unresolved nodes. */
2515 if (u_left) {
2516 topo_mod_dprintf(mod, "ses_construct_phys_tree(): "
2517 "Failed to construct physical view of the following "
2518 "ses elements of Chassis CSN %s.", cp->sec_csn);
2519 while (u_head) {
2520 u_tail = u_head->spt_sibling;
2521 topo_mod_dprintf(mod,
2522 "\telement type (%d) and instance (%d)",
2523 u_head->spt_senumnode->sen_type,
2524 u_head->spt_senumnode->sen_instance);
2525 topo_mod_free(mod, u_head, sizeof (ses_phys_tree_t));
2526 u_head = u_tail;
2527 }
2528 return (-1);
2529 }
2530
2531 return (0);
2532 }
2533
2534 /*
2535 * Free the whole phys tree.
2536 */
ses_phys_tree_free(topo_mod_t * mod,ses_phys_tree_t * sproot)2537 static void ses_phys_tree_free(topo_mod_t *mod, ses_phys_tree_t *sproot)
2538 {
2539 if (sproot == NULL)
2540 return;
2541
2542 /* Free child tree. */
2543 if (sproot->spt_child) {
2544 ses_phys_tree_free(mod, sproot->spt_child);
2545 }
2546
2547 /* Free sibling trees. */
2548 if (sproot->spt_sibling) {
2549 ses_phys_tree_free(mod, sproot->spt_sibling);
2550 }
2551
2552 /* Free root node itself. */
2553 topo_mod_free(mod, sproot, sizeof (ses_phys_tree_t));
2554 }
2555
2556 /*
2557 * Parses phys_enum_type table to get the index of the given type.
2558 */
2559 static boolean_t
is_type_enumerated(ses_phys_tree_t * node,int * index)2560 is_type_enumerated(ses_phys_tree_t *node, int *index)
2561 {
2562 int i;
2563
2564 for (i = 0; i < N_PHYS_ENUM_TYPES; i++) {
2565 if (node->spt_senumnode->sen_type ==
2566 phys_enum_type_list[i].pet_type) {
2567 *index = i;
2568 return (B_TRUE);
2569 }
2570 }
2571 return (B_FALSE);
2572 }
2573
2574 /*
2575 * Recusrive routine for top-down enumeration of the tree.
2576 */
2577 static int
ses_enumerate_node(ses_enum_data_t * sdp,tnode_t * pnode,ses_enum_chassis_t * cp,ses_phys_tree_t * parent,int mrange[])2578 ses_enumerate_node(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp,
2579 ses_phys_tree_t *parent, int mrange[])
2580 {
2581 topo_mod_t *mod = sdp->sed_mod;
2582 ses_phys_tree_t *child = NULL;
2583 int i, ret = 0, ret_ch;
2584 uint64_t prevtype = SES_ET_UNSPECIFIED;
2585 ses_phys_tree_t *dirfru = NULL;
2586 tnode_t *tn = NULL, *frutn = NULL;
2587
2588 if (parent == NULL) {
2589 return (0);
2590 }
2591
2592 for (child = parent->spt_child; child != NULL;
2593 child = child->spt_sibling) {
2594 if (is_type_enumerated(child, &i)) {
2595 if (prevtype != phys_enum_type_list[i].pet_type) {
2596 /* check if range needs to be created. */
2597 if (phys_enum_type_list[i].pet_dorange &&
2598 topo_node_range_create(mod, pnode,
2599 phys_enum_type_list[i].pet_nodename, 0,
2600 mrange[i]) != 0) {
2601 topo_mod_dprintf(mod,
2602 "topo_node_create_range() failed: "
2603 "%s", topo_mod_errmsg(mod));
2604 return (-1);
2605 }
2606 prevtype = phys_enum_type_list[i].pet_type;
2607 }
2608
2609 if (!(child->spt_isfru)) {
2610 for (dirfru = parent; dirfru != NULL;
2611 dirfru = dirfru->spt_parent) {
2612 if (dirfru->spt_isfru) {
2613 break;
2614 }
2615 }
2616 /* found direct FRU node. */
2617 if (dirfru) {
2618 frutn = dirfru->spt_tnode;
2619 } else {
2620 frutn = NULL;
2621 }
2622 } else {
2623 frutn = NULL;
2624 }
2625
2626 if (ses_create_generic(sdp, child->spt_senumnode,
2627 pnode, frutn, phys_enum_type_list[i].pet_nodename,
2628 phys_enum_type_list[i].pet_defaultlabel, &tn) != 0)
2629 return (-1);
2630
2631 child->spt_tnode = tn;
2632 /*
2633 * For some SES element there may be protocol specific
2634 * information to process. Here we are processing
2635 * the association between enclosure controller and
2636 * SAS expanders.
2637 */
2638 if (phys_enum_type_list[i].pet_type ==
2639 SES_ET_ESC_ELECTRONICS) {
2640 /* create SAS expander node */
2641 if (ses_create_protocol_specific(sdp,
2642 child->spt_senumnode, tn,
2643 phys_enum_type_list[i].pet_type,
2644 cp, phys_enum_type_list[i].pet_dorange) !=
2645 0) {
2646 return (-1);
2647 }
2648 }
2649 } else {
2650 continue;
2651 }
2652 ret_ch = ses_enumerate_node(sdp, tn, cp, child, mrange);
2653 if (ret_ch)
2654 ret = ret_ch; /* there was an error and set the ret. */
2655 }
2656
2657 return (ret);
2658 }
2659
2660 /*
2661 * Instantiate types of nodes that are specified in the hierarchy
2662 * element type list.
2663 */
2664 static int
ses_create_children_from_phys_tree(ses_enum_data_t * sdp,tnode_t * pnode,ses_enum_chassis_t * cp,ses_phys_tree_t * phys_tree)2665 ses_create_children_from_phys_tree(ses_enum_data_t *sdp, tnode_t *pnode,
2666 ses_enum_chassis_t *cp, ses_phys_tree_t *phys_tree)
2667 {
2668 topo_mod_t *mod = sdp->sed_mod;
2669 int mrange[N_PHYS_ENUM_TYPES] = { 0 };
2670 ses_enum_node_t *snp;
2671 int i, ret;
2672
2673 /*
2674 * First get max range for each type of element to be enumerated.
2675 */
2676 for (i = 0; i < N_PHYS_ENUM_TYPES; i++) {
2677 if (phys_enum_type_list[i].pet_dorange) {
2678 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2679 snp = topo_list_next(snp)) {
2680 if (snp->sen_type ==
2681 phys_enum_type_list[i].pet_type) {
2682 if (snp->sen_instance > mrange[i])
2683 mrange[i] =
2684 snp->sen_instance;
2685 }
2686 }
2687 }
2688 }
2689
2690 topo_mod_dprintf(mod, "%s: creating nodes from FRU hierarchy tree.",
2691 cp->sec_csn);
2692
2693 if ((ret = ses_enumerate_node(sdp, pnode, cp, phys_tree, mrange)) !=
2694 0) {
2695 topo_mod_dprintf(mod,
2696 "ses_create_children_from_phys_tree() failed: ");
2697 return (ret);
2698 }
2699
2700 return (0);
2701 }
2702
2703 /*
2704 * Instantiate a new chassis instance in the topology.
2705 */
2706 static int
ses_create_chassis(ses_enum_data_t * sdp,tnode_t * pnode,ses_enum_chassis_t * cp)2707 ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp)
2708 {
2709 topo_mod_t *mod = sdp->sed_mod;
2710 nvlist_t *props;
2711 char *raw_manufacturer, *raw_model, *raw_revision;
2712 char *manufacturer = NULL, *model = NULL, *product = NULL;
2713 char *revision = NULL;
2714 char *serial;
2715 char **paths;
2716 size_t prodlen;
2717 tnode_t *tn;
2718 nvlist_t *fmri = NULL, *auth = NULL;
2719 int ret = -1;
2720 ses_enum_node_t *snp;
2721 ses_enum_target_t *stp;
2722 ses_enum_chassis_t *scp;
2723 int i, err;
2724 uint64_t sc_count = 0, pindex;
2725 ses_phys_tree_t *sproot = NULL;
2726 hrtime_t start;
2727 hrtime_t end;
2728 double duration;
2729
2730 /*
2731 * Ignore any internal enclosures.
2732 */
2733 if (cp->sec_internal)
2734 return (0);
2735
2736 /*
2737 * Check to see if there are any devices presennt in the chassis. If
2738 * not, ignore the chassis alltogether. This is most useful for
2739 * ignoring internal HBAs that present a SES target but don't actually
2740 * manage any of the devices.
2741 */
2742 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2743 snp = topo_list_next(snp)) {
2744 if (snp->sen_type == SES_ET_DEVICE ||
2745 snp->sen_type == SES_ET_ARRAY_DEVICE)
2746 break;
2747 }
2748
2749 if (snp == NULL)
2750 return (0);
2751
2752 props = ses_node_props(cp->sec_enclosure);
2753
2754 /*
2755 * We use the following property mappings:
2756 *
2757 * manufacturer vendor-id
2758 * model product-id
2759 * serial-number libses-chassis-serial
2760 */
2761 verify(nvlist_lookup_string(props, SES_EN_PROP_VID,
2762 &raw_manufacturer) == 0);
2763 verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0);
2764 verify(nvlist_lookup_string(props, SES_EN_PROP_REV,
2765 &raw_revision) == 0);
2766 verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0);
2767
2768 /*
2769 * To construct the authority information, we 'clean' each string by
2770 * removing any offensive characters and trimmming whitespace. For the
2771 * 'product-id', we use a concatenation of 'manufacturer-model'. We
2772 * also take the numerical serial number and convert it to a string.
2773 */
2774 if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL ||
2775 (model = disk_auth_clean(mod, raw_model)) == NULL ||
2776 (revision = disk_auth_clean(mod, raw_revision)) == NULL) {
2777 goto error;
2778 }
2779
2780 prodlen = strlen(manufacturer) + strlen(model) + 2;
2781 if ((product = topo_mod_alloc(mod, prodlen)) == NULL)
2782 goto error;
2783
2784 (void) snprintf(product, prodlen, "%s-%s", manufacturer, model);
2785
2786 /*
2787 * Construct the topo node and bind it to our parent.
2788 */
2789 if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0)
2790 goto error;
2791
2792 if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 ||
2793 nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) {
2794 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
2795 goto error;
2796 }
2797
2798 /*
2799 * We pass NULL for the parent FMRI because there is no resource
2800 * associated with it. For the toplevel enclosure, we leave the
2801 * serial/part/revision portions empty, which are reserved for
2802 * individual components within the chassis.
2803 */
2804 if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION,
2805 SES_ENCLOSURE, cp->sec_instance, NULL, auth,
2806 model, revision, serial)) == NULL) {
2807 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
2808 topo_mod_errmsg(mod));
2809 goto error;
2810 }
2811
2812 if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE,
2813 cp->sec_instance, fmri)) == NULL) {
2814 topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
2815 topo_mod_errmsg(mod));
2816 goto error;
2817 }
2818
2819 if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
2820 topo_mod_dprintf(mod,
2821 "topo_method_register() failed: %s",
2822 topo_mod_errmsg(mod));
2823 goto error;
2824 }
2825
2826 if (ses_set_standard_props(mod, NULL, tn, auth,
2827 ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0)
2828 goto error;
2829
2830 /*
2831 * For enclosures, we want to include all possible targets (for upgrade
2832 * purposes).
2833 */
2834 for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
2835 stp = topo_list_next(stp), i++)
2836 ;
2837
2838 verify(i != 0);
2839 paths = alloca(i * sizeof (char *));
2840
2841 for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
2842 stp = topo_list_next(stp), i++)
2843 paths[i] = stp->set_devpath;
2844
2845
2846 if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
2847 TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
2848 i, &err) != 0) {
2849 topo_mod_dprintf(mod,
2850 "failed to create property %s: %s\n",
2851 TOPO_PROP_PATHS, topo_strerror(err));
2852 goto error;
2853 }
2854
2855 if (nvlist_lookup_uint64(props,
2856 LIBSES_PROP_PHYS_PARENT, &pindex) == 0) {
2857 start = gethrtime(); /* to mearusre performance */
2858 /*
2859 * The enclosure is supported through SUNW,FRUID.
2860 * Need to enumerate the nodes through hierarchical order.
2861 */
2862 if ((sproot = topo_mod_zalloc(mod,
2863 sizeof (ses_phys_tree_t))) == NULL) {
2864 topo_mod_dprintf(mod,
2865 "failed to allocate root: %s\n",
2866 topo_strerror(err));
2867 goto error;
2868 }
2869 sproot->spt_pindex = pindex;
2870 if (nvlist_lookup_boolean_value(props,
2871 LIBSES_PROP_FRU, &sproot->spt_isfru) != 0) {
2872 topo_mod_dprintf(mod,
2873 "ses_create_chassis(): Failed to find prop %s "
2874 "on enclosure element (CSN %s).",
2875 LIBSES_PROP_FRU, cp->sec_csn);
2876 /* an enclosure should be a FRU. continue to process. */
2877 sproot->spt_isfru = B_TRUE;
2878 }
2879 if (nvlist_lookup_uint64(props,
2880 SES_PROP_ELEMENT_ONLY_INDEX,
2881 &sproot->spt_eonlyindex) != 0) {
2882 topo_mod_dprintf(mod,
2883 "ses_create_chassis(): Failed to find prop %s "
2884 "on enclosure element (CSN %s).",
2885 LIBSES_PROP_PHYS_PARENT, cp->sec_csn);
2886 topo_mod_free(mod, sproot, sizeof (ses_phys_tree_t));
2887 goto error;
2888 }
2889 if (sproot->spt_pindex != sproot->spt_eonlyindex) {
2890 topo_mod_dprintf(mod, "ses_create_chassis(): "
2891 "Enclosure element(CSN %s) should have "
2892 "itself as the parent to be the root node "
2893 "of FRU hierarchical tree.)", cp->sec_csn);
2894 topo_mod_free(mod, sproot, sizeof (ses_phys_tree_t));
2895 goto error;
2896 } else {
2897 sproot->spt_snode = cp->sec_enclosure;
2898 sproot->spt_tnode = tn;
2899 /* construct a tree. */
2900 if (ses_construct_phys_tree(sdp, cp, sproot) != 0) {
2901 topo_mod_dprintf(mod, "ses_create_chassis(): "
2902 "Failed to construct FRU hierarchical "
2903 "tree on enclosure (CSN %s.)",
2904 cp->sec_csn);
2905 }
2906
2907 /* enumerate elements from the tree. */
2908 if (ses_create_children_from_phys_tree(sdp, tn, cp,
2909 sproot) != 0) {
2910 topo_mod_dprintf(mod, "ses_create_chassis(): "
2911 "Failed to create children topo nodes out "
2912 "of FRU hierarchical tree on enclosure "
2913 "(CSN %s).", cp->sec_csn);
2914 }
2915 /* destroy the phys tree. */
2916 ses_phys_tree_free(mod, sproot);
2917 }
2918
2919 end = gethrtime();
2920 duration = end - start;
2921 duration /= HR_SECOND;
2922 topo_mod_dprintf(mod,
2923 "FRU boundary tree based enumeration: %.6f seconds",
2924 duration);
2925 } else {
2926 /*
2927 * Create the nodes for power supplies, fans, controllers and
2928 * devices. Note that SAS exopander nodes and connector nodes
2929 * are handled through protocol specific processing of
2930 * controllers.
2931 */
2932 if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY,
2933 PSU, "PSU", cp, B_TRUE) != 0 ||
2934 ses_create_children(sdp, tn, SES_ET_COOLING,
2935 FAN, "FAN", cp, B_TRUE) != 0 ||
2936 ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
2937 CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 ||
2938 ses_create_children(sdp, tn, SES_ET_DEVICE,
2939 BAY, "BAY", cp, B_TRUE) != 0 ||
2940 ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
2941 BAY, "BAY", cp, B_TRUE) != 0)
2942 goto error;
2943 }
2944
2945 if (cp->sec_maxinstance >= 0 &&
2946 (topo_node_range_create(mod, tn, SUBCHASSIS, 0,
2947 cp->sec_maxinstance) != 0)) {
2948 topo_mod_dprintf(mod, "topo_node_create_range() failed: %s",
2949 topo_mod_errmsg(mod));
2950 goto error;
2951 }
2952
2953 for (scp = topo_list_next(&cp->sec_subchassis); scp != NULL;
2954 scp = topo_list_next(scp)) {
2955
2956 if (ses_create_subchassis(sdp, tn, scp) != 0)
2957 goto error;
2958
2959 topo_mod_dprintf(mod, "created Subchassis node with "
2960 "instance %u\nand target (%s) under Chassis with CSN %s",
2961 scp->sec_instance, scp->sec_target->set_devpath,
2962 cp->sec_csn);
2963
2964 sc_count++;
2965 }
2966
2967 topo_mod_dprintf(mod, "%s: created %llu %s nodes",
2968 cp->sec_csn, sc_count, SUBCHASSIS);
2969
2970 cp->sec_target->set_refcount++;
2971 topo_node_setspecific(tn, cp->sec_target);
2972
2973 ret = 0;
2974 error:
2975 topo_mod_strfree(mod, manufacturer);
2976 topo_mod_strfree(mod, model);
2977 topo_mod_strfree(mod, revision);
2978 topo_mod_strfree(mod, product);
2979
2980 nvlist_free(fmri);
2981 nvlist_free(auth);
2982 return (ret);
2983 }
2984
2985 /*
2986 * Create a bay node explicitly enumerated via XML.
2987 */
2988 static int
ses_create_bays(ses_enum_data_t * sdp,tnode_t * pnode)2989 ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode)
2990 {
2991 topo_mod_t *mod = sdp->sed_mod;
2992 ses_enum_chassis_t *cp;
2993
2994 /*
2995 * Iterate over chassis looking for an internal enclosure. This
2996 * property is set via a vendor-specific plugin, and there should only
2997 * ever be a single internal chassis in a system.
2998 */
2999 for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
3000 cp = topo_list_next(cp)) {
3001 if (cp->sec_internal)
3002 break;
3003 }
3004
3005 if (cp == NULL) {
3006 topo_mod_dprintf(mod, "failed to find internal chassis\n");
3007 return (-1);
3008 }
3009
3010 if (ses_create_children(sdp, pnode, SES_ET_DEVICE,
3011 BAY, "BAY", cp, B_FALSE) != 0 ||
3012 ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE,
3013 BAY, "BAY", cp, B_FALSE) != 0)
3014 return (-1);
3015
3016 return (0);
3017 }
3018
3019 /*
3020 * Initialize chassis or subchassis.
3021 */
3022 static int
ses_init_chassis(topo_mod_t * mod,ses_enum_data_t * sdp,ses_enum_chassis_t * pcp,ses_enum_chassis_t * cp,ses_node_t * np,nvlist_t * props,uint64_t subchassis,ses_chassis_type_e flags)3023 ses_init_chassis(topo_mod_t *mod, ses_enum_data_t *sdp, ses_enum_chassis_t *pcp,
3024 ses_enum_chassis_t *cp, ses_node_t *np, nvlist_t *props,
3025 uint64_t subchassis, ses_chassis_type_e flags)
3026 {
3027 boolean_t internal, ident;
3028
3029 assert((flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS |
3030 SES_DUP_CHASSIS | SES_DUP_SUBCHASSIS)) != 0);
3031
3032 assert(cp != NULL);
3033 assert(np != NULL);
3034 assert(props != NULL);
3035
3036 if (flags & (SES_NEW_SUBCHASSIS | SES_DUP_SUBCHASSIS))
3037 assert(pcp != NULL);
3038
3039 topo_mod_dprintf(mod, "ses_init_chassis: %s: index %llu, flags (%d)",
3040 sdp->sed_name, subchassis, flags);
3041
3042 if (flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS)) {
3043
3044 topo_mod_dprintf(mod, "new chassis/subchassis");
3045 if (nvlist_lookup_boolean_value(props,
3046 LIBSES_EN_PROP_INTERNAL, &internal) == 0)
3047 cp->sec_internal = internal;
3048
3049 cp->sec_enclosure = np;
3050 cp->sec_target = sdp->sed_target;
3051
3052 if (flags & SES_NEW_CHASSIS) {
3053 if (!cp->sec_internal)
3054 cp->sec_instance = sdp->sed_instance++;
3055 topo_list_append(&sdp->sed_chassis, cp);
3056 } else {
3057 if (subchassis != NO_SUBCHASSIS)
3058 cp->sec_instance = subchassis;
3059 else
3060 cp->sec_instance = pcp->sec_scinstance++;
3061
3062 if (cp->sec_instance > pcp->sec_maxinstance)
3063 pcp->sec_maxinstance = cp->sec_instance;
3064
3065 topo_list_append(&pcp->sec_subchassis, cp);
3066 }
3067
3068 } else {
3069 topo_mod_dprintf(mod, "dup chassis/subchassis");
3070 if (nvlist_lookup_boolean_value(props,
3071 SES_PROP_IDENT, &ident) == 0) {
3072 topo_mod_dprintf(mod, "overriding enclosure node");
3073
3074 cp->sec_enclosure = np;
3075 cp->sec_target = sdp->sed_target;
3076 }
3077 }
3078
3079 topo_list_append(&cp->sec_targets, sdp->sed_target);
3080 sdp->sed_current = cp;
3081
3082 return (0);
3083 }
3084
3085 /*
3086 * Gather nodes from the current SES target into our chassis list, merging the
3087 * results if necessary.
3088 */
3089 static ses_walk_action_t
ses_enum_gather(ses_node_t * np,void * data)3090 ses_enum_gather(ses_node_t *np, void *data)
3091 {
3092 nvlist_t *props = ses_node_props(np);
3093 ses_enum_data_t *sdp = data;
3094 topo_mod_t *mod = sdp->sed_mod;
3095 ses_enum_chassis_t *cp, *scp;
3096 ses_enum_node_t *snp;
3097 ses_alt_node_t *sap;
3098 char *csn;
3099 uint64_t instance, type;
3100 uint64_t prevstatus, status;
3101 boolean_t report;
3102 uint64_t subchassis = NO_SUBCHASSIS;
3103
3104 if (ses_node_type(np) == SES_NODE_ENCLOSURE) {
3105 /*
3106 * If we have already identified the chassis for this target,
3107 * then this is a secondary enclosure and we should ignore it,
3108 * along with the rest of the tree (since this is depth-first).
3109 */
3110 if (sdp->sed_current != NULL)
3111 return (SES_WALK_ACTION_TERMINATE);
3112
3113 /*
3114 * Go through the list of chassis we have seen so far and see
3115 * if this serial number matches one of the known values.
3116 * If so, check whether this enclosure is a subchassis.
3117 */
3118 if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN,
3119 &csn) != 0)
3120 return (SES_WALK_ACTION_TERMINATE);
3121
3122 (void) nvlist_lookup_uint64(props, LIBSES_EN_PROP_SUBCHASSIS_ID,
3123 &subchassis);
3124
3125 topo_mod_dprintf(mod, "ses_enum_gather: Enclosure Node (%s) "
3126 "CSN (%s), subchassis (%llu)", sdp->sed_name, csn,
3127 subchassis);
3128
3129 /*
3130 * We need to determine whether this enclosure node
3131 * represents a chassis or a subchassis. Since we may
3132 * receive the enclosure nodes in a non-deterministic
3133 * manner, we need to account for all possible combinations:
3134 * 1. Chassis for the current CSN has not yet been
3135 * allocated
3136 * 1.1 This is a new chassis:
3137 * allocate and instantiate the chassis
3138 * 1.2 This is a new subchassis:
3139 * allocate a placeholder chassis
3140 * allocate and instantiate the subchassis
3141 * link the subchassis to the chassis
3142 * 2. Chassis for the current CSN has been allocated
3143 * 2.1 This is a duplicate chassis enclosure
3144 * check whether to override old chassis
3145 * append to chassis' target list
3146 * 2.2 Only placeholder chassis exists
3147 * fill in the chassis fields
3148 * 2.3 This is a new subchassis
3149 * allocate and instantiate the subchassis
3150 * link the subchassis to the chassis
3151 * 2.4 This is a duplicate subchassis enclosure
3152 * check whether to override old chassis
3153 * append to chassis' target list
3154 */
3155
3156 for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
3157 cp = topo_list_next(cp))
3158 if (strcmp(cp->sec_csn, csn) == 0)
3159 break;
3160
3161 if (cp == NULL) {
3162 /* 1. Haven't seen a chassis with this CSN before */
3163
3164 if ((cp = topo_mod_zalloc(mod,
3165 sizeof (ses_enum_chassis_t))) == NULL)
3166 goto error;
3167
3168 cp->sec_scinstance = SES_STARTING_SUBCHASSIS;
3169 cp->sec_maxinstance = -1;
3170 cp->sec_csn = csn;
3171
3172 if (subchassis == NO_SUBCHASSIS) {
3173 /* 1.1 This is a new chassis */
3174
3175 topo_mod_dprintf(mod, "%s: Initialize new "
3176 "chassis with CSN %s", sdp->sed_name, csn);
3177
3178 if (ses_init_chassis(mod, sdp, NULL, cp,
3179 np, props, NO_SUBCHASSIS,
3180 SES_NEW_CHASSIS) < 0)
3181 goto error;
3182 } else {
3183 /* 1.2 This is a new subchassis */
3184
3185 topo_mod_dprintf(mod, "%s: Initialize new "
3186 "subchassis with CSN %s and index %llu",
3187 sdp->sed_name, csn, subchassis);
3188
3189 if ((scp = topo_mod_zalloc(mod,
3190 sizeof (ses_enum_chassis_t))) == NULL)
3191 goto error;
3192
3193 scp->sec_csn = csn;
3194
3195 if (ses_init_chassis(mod, sdp, cp, scp, np,
3196 props, subchassis, SES_NEW_SUBCHASSIS) < 0)
3197 goto error;
3198 }
3199 } else {
3200 /*
3201 * We have a chassis or subchassis with this CSN. If
3202 * it's a chassis, we must check to see whether it is
3203 * a placeholder previously created because we found a
3204 * subchassis with this CSN. We will know that because
3205 * the sec_target value will not be set; it is set only
3206 * in ses_init_chassis(). In that case, initialise it
3207 * as a new chassis; otherwise, it's a duplicate and we
3208 * need to append only.
3209 */
3210 if (subchassis == NO_SUBCHASSIS) {
3211 if (cp->sec_target != NULL) {
3212 /* 2.1 This is a duplicate chassis */
3213
3214 topo_mod_dprintf(mod, "%s: Append "
3215 "duplicate chassis with CSN (%s)",
3216 sdp->sed_name, csn);
3217
3218 if (ses_init_chassis(mod, sdp, NULL, cp,
3219 np, props, NO_SUBCHASSIS,
3220 SES_DUP_CHASSIS) < 0)
3221 goto error;
3222 } else {
3223 /* Placeholder chassis - init it up */
3224 topo_mod_dprintf(mod, "%s: Initialize"
3225 "placeholder chassis with CSN %s",
3226 sdp->sed_name, csn);
3227
3228 if (ses_init_chassis(mod, sdp, NULL,
3229 cp, np, props, NO_SUBCHASSIS,
3230 SES_NEW_CHASSIS) < 0)
3231 goto error;
3232
3233 }
3234 } else {
3235 /* This is a subchassis */
3236
3237 for (scp = topo_list_next(&cp->sec_subchassis);
3238 scp != NULL; scp = topo_list_next(scp))
3239 if (scp->sec_instance == subchassis)
3240 break;
3241
3242 if (scp == NULL) {
3243 /* 2.3 This is a new subchassis */
3244
3245 topo_mod_dprintf(mod, "%s: Initialize "
3246 "new subchassis with CSN (%s) "
3247 "and LID (%s)",
3248 sdp->sed_name, csn);
3249
3250 if ((scp = topo_mod_zalloc(mod,
3251 sizeof (ses_enum_chassis_t)))
3252 == NULL)
3253 goto error;
3254
3255 scp->sec_csn = csn;
3256
3257 if (ses_init_chassis(mod, sdp, cp, scp,
3258 np, props, subchassis,
3259 SES_NEW_SUBCHASSIS) < 0)
3260 goto error;
3261 } else {
3262 /* 2.4 This is a duplicate subchassis */
3263
3264 topo_mod_dprintf(mod, "%s: Append "
3265 "duplicate subchassis with "
3266 "CSN (%s)", sdp->sed_name, csn);
3267
3268 if (ses_init_chassis(mod, sdp, cp, scp,
3269 np, props, subchassis,
3270 SES_DUP_SUBCHASSIS) < 0)
3271 goto error;
3272 }
3273 }
3274 }
3275 } else if (ses_node_type(np) == SES_NODE_ELEMENT) {
3276 /*
3277 * If we haven't yet seen an enclosure node and identified the
3278 * current chassis, something is very wrong; bail out.
3279 */
3280 if (sdp->sed_current == NULL)
3281 return (SES_WALK_ACTION_TERMINATE);
3282
3283 /*
3284 * If this isn't one of the element types we care about, then
3285 * ignore it.
3286 */
3287 verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE,
3288 &type) == 0);
3289 if (type != SES_ET_DEVICE &&
3290 type != SES_ET_ARRAY_DEVICE &&
3291 type != SES_ET_SUNW_FANBOARD &&
3292 type != SES_ET_SUNW_FANMODULE &&
3293 type != SES_ET_COOLING &&
3294 type != SES_ET_SUNW_POWERBOARD &&
3295 type != SES_ET_SUNW_POWERMODULE &&
3296 type != SES_ET_POWER_SUPPLY &&
3297 type != SES_ET_ESC_ELECTRONICS &&
3298 type != SES_ET_SAS_EXPANDER &&
3299 type != SES_ET_SAS_CONNECTOR)
3300 return (SES_WALK_ACTION_CONTINUE);
3301
3302 /*
3303 * Get the current instance number and see if we already know
3304 * about this element. If so, it means we have multiple paths
3305 * to the same elements, and we should ignore the current path.
3306 */
3307 verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX,
3308 &instance) == 0);
3309 if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE)
3310 (void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER,
3311 &instance);
3312
3313 cp = sdp->sed_current;
3314
3315 for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
3316 snp = topo_list_next(snp)) {
3317 if (snp->sen_type == type &&
3318 snp->sen_instance == instance)
3319 break;
3320 }
3321
3322 /*
3323 * We prefer the new element under the following circumstances:
3324 *
3325 * - The currently known element's status is unknown or not
3326 * available, but the new element has a known status. This
3327 * occurs if a given element is only available through a
3328 * particular target.
3329 *
3330 * - This is an ESC_ELECTRONICS element, and the 'reported-via'
3331 * property is set. This allows us to get reliable firmware
3332 * revision information from the enclosure node.
3333 */
3334 if (snp != NULL) {
3335 if (nvlist_lookup_uint64(
3336 ses_node_props(snp->sen_node),
3337 SES_PROP_STATUS_CODE, &prevstatus) != 0)
3338 prevstatus = SES_ESC_UNSUPPORTED;
3339 if (nvlist_lookup_uint64(
3340 props, SES_PROP_STATUS_CODE, &status) != 0)
3341 status = SES_ESC_UNSUPPORTED;
3342 if (nvlist_lookup_boolean_value(
3343 props, SES_PROP_REPORT, &report) != 0)
3344 report = B_FALSE;
3345
3346 if ((SES_STATUS_UNAVAIL(prevstatus) &&
3347 !SES_STATUS_UNAVAIL(status)) ||
3348 (type == SES_ET_ESC_ELECTRONICS &&
3349 report)) {
3350 snp->sen_node = np;
3351 snp->sen_target = sdp->sed_target;
3352 }
3353
3354 if ((sap = topo_mod_zalloc(mod,
3355 sizeof (ses_alt_node_t))) == NULL)
3356 goto error;
3357
3358 sap->san_node = np;
3359 topo_list_append(&snp->sen_alt_nodes, sap);
3360
3361 return (SES_WALK_ACTION_CONTINUE);
3362 }
3363
3364 if ((snp = topo_mod_zalloc(mod,
3365 sizeof (ses_enum_node_t))) == NULL)
3366 goto error;
3367
3368 if ((sap = topo_mod_zalloc(mod,
3369 sizeof (ses_alt_node_t))) == NULL) {
3370 topo_mod_free(mod, snp, sizeof (ses_enum_node_t));
3371 goto error;
3372 }
3373
3374 topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)",
3375 sdp->sed_name, type, instance);
3376 snp->sen_node = np;
3377 snp->sen_type = type;
3378 snp->sen_instance = instance;
3379 snp->sen_target = sdp->sed_target;
3380 sap->san_node = np;
3381 topo_list_append(&snp->sen_alt_nodes, sap);
3382 topo_list_append(&cp->sec_nodes, snp);
3383
3384 if (type == SES_ET_DEVICE)
3385 cp->sec_hasdev = B_TRUE;
3386 }
3387
3388 return (SES_WALK_ACTION_CONTINUE);
3389
3390 error:
3391 sdp->sed_errno = -1;
3392 return (SES_WALK_ACTION_TERMINATE);
3393 }
3394
3395 static int
ses_process_dir(const char * dirpath,ses_enum_data_t * sdp)3396 ses_process_dir(const char *dirpath, ses_enum_data_t *sdp)
3397 {
3398 topo_mod_t *mod = sdp->sed_mod;
3399 DIR *dir;
3400 struct dirent *dp;
3401 char path[PATH_MAX];
3402 ses_enum_target_t *stp;
3403 int err = -1;
3404
3405 /*
3406 * Open the SES target directory and iterate over any available
3407 * targets.
3408 */
3409 if ((dir = opendir(dirpath)) == NULL) {
3410 /*
3411 * If the SES target directory does not exist, then return as if
3412 * there are no active targets.
3413 */
3414 topo_mod_dprintf(mod, "failed to open ses "
3415 "directory '%s'", dirpath);
3416 return (0);
3417 }
3418
3419 while ((dp = readdir(dir)) != NULL) {
3420 if (strcmp(dp->d_name, ".") == 0 ||
3421 strcmp(dp->d_name, "..") == 0)
3422 continue;
3423
3424 /*
3425 * Create a new target instance and take a snapshot.
3426 */
3427 if ((stp = topo_mod_zalloc(mod,
3428 sizeof (ses_enum_target_t))) == NULL)
3429 goto error;
3430
3431 (void) pthread_mutex_init(&stp->set_lock, NULL);
3432
3433 (void) snprintf(path, sizeof (path), "%s/%s", dirpath,
3434 dp->d_name);
3435
3436 /*
3437 * We keep track of the SES device path and export it on a
3438 * per-node basis to allow higher level software to get to the
3439 * corresponding SES state.
3440 */
3441 if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) {
3442 topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
3443 goto error;
3444 }
3445
3446 if ((stp->set_target =
3447 ses_open(LIBSES_VERSION, path)) == NULL) {
3448 topo_mod_dprintf(mod, "failed to open ses target "
3449 "'%s': %s", dp->d_name, ses_errmsg());
3450 ses_sof_alloc(mod, stp->set_devpath);
3451 topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
3452 continue;
3453 }
3454 topo_mod_dprintf(mod, "open contract");
3455 ses_ssl_alloc(mod, stp);
3456 ses_create_contract(mod, stp);
3457
3458 stp->set_refcount = 1;
3459 sdp->sed_target = stp;
3460 stp->set_snap = ses_snap_hold(stp->set_target);
3461 stp->set_snaptime = gethrtime();
3462
3463 /*
3464 * Enumerate over all SES elements and merge them into the
3465 * correct ses_enum_chassis_t.
3466 */
3467 sdp->sed_current = NULL;
3468 sdp->sed_errno = 0;
3469 sdp->sed_name = dp->d_name;
3470 (void) ses_walk(stp->set_snap, ses_enum_gather, sdp);
3471
3472 if (sdp->sed_errno != 0)
3473 goto error;
3474 }
3475
3476 err = 0;
3477 error:
3478 closedir(dir);
3479 return (err);
3480 }
3481
3482 static void
ses_release(topo_mod_t * mod,tnode_t * tn)3483 ses_release(topo_mod_t *mod, tnode_t *tn)
3484 {
3485 ses_enum_target_t *stp;
3486
3487 if ((stp = topo_node_getspecific(tn)) != NULL) {
3488 topo_node_setspecific(tn, NULL);
3489 ses_target_free(mod, stp);
3490 }
3491 }
3492
3493 /*ARGSUSED*/
3494 static int
ses_enum(topo_mod_t * mod,tnode_t * rnode,const char * name,topo_instance_t min,topo_instance_t max,void * arg,void * notused)3495 ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
3496 topo_instance_t min, topo_instance_t max, void *arg, void *notused)
3497 {
3498 ses_enum_chassis_t *cp;
3499 ses_enum_data_t *data;
3500
3501 /*
3502 * Check to make sure we're being invoked sensibly, and that we're not
3503 * being invoked as part of a post-processing step.
3504 */
3505 if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0)
3506 return (0);
3507
3508 /*
3509 * If this is the first time we've called our enumeration method, then
3510 * gather information about any available enclosures.
3511 */
3512 if ((data = topo_mod_getspecific(mod)) == NULL) {
3513 ses_sof_freeall(mod);
3514 if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) ==
3515 NULL)
3516 return (-1);
3517
3518 data->sed_mod = mod;
3519 topo_mod_setspecific(mod, data);
3520
3521 if (dev_list_gather(mod, &data->sed_devs) != 0)
3522 goto error;
3523
3524 /*
3525 * We search both the ses(7D) and sgen(7D) locations, so we are
3526 * independent of any particular driver class bindings.
3527 */
3528 if (ses_process_dir("/dev/es", data) != 0 ||
3529 ses_process_dir("/dev/scsi/ses", data) != 0)
3530 goto error;
3531 }
3532
3533 if (strcmp(name, SES_ENCLOSURE) == 0) {
3534 /*
3535 * This is a request to enumerate external enclosures. Go
3536 * through all the targets and create chassis nodes where
3537 * necessary.
3538 */
3539 for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
3540 cp = topo_list_next(cp)) {
3541 if (ses_create_chassis(data, rnode, cp) != 0)
3542 goto error;
3543 }
3544 } else {
3545 /*
3546 * This is a request to enumerate a specific bay underneath the
3547 * root chassis (for internal disks).
3548 */
3549 if (ses_create_bays(data, rnode) != 0)
3550 goto error;
3551 }
3552
3553 /*
3554 * This is a bit of a kludge. In order to allow internal disks to be
3555 * enumerated and share snapshot-specific information with the external
3556 * enclosure enumeration, we rely on the fact that we will be invoked
3557 * for the 'ses-enclosure' node last.
3558 */
3559 if (strcmp(name, SES_ENCLOSURE) == 0) {
3560 for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
3561 cp = topo_list_next(cp))
3562 ses_data_free(data, cp);
3563 ses_data_free(data, NULL);
3564 topo_mod_setspecific(mod, NULL);
3565 }
3566 return (0);
3567
3568 error:
3569 for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
3570 cp = topo_list_next(cp))
3571 ses_data_free(data, cp);
3572 ses_data_free(data, NULL);
3573 topo_mod_setspecific(mod, NULL);
3574 return (-1);
3575 }
3576
3577 static const topo_modops_t ses_ops =
3578 { ses_enum, ses_release };
3579
3580 static topo_modinfo_t ses_info =
3581 { SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops };
3582
3583 /*ARGSUSED*/
3584 int
_topo_init(topo_mod_t * mod,topo_version_t version)3585 _topo_init(topo_mod_t *mod, topo_version_t version)
3586 {
3587 int rval;
3588
3589 if (getenv("TOPOSESDEBUG") != NULL)
3590 topo_mod_setdebug(mod);
3591
3592 topo_mod_dprintf(mod, "initializing %s enumerator\n",
3593 SES_ENCLOSURE);
3594
3595 if ((rval = topo_mod_register(mod, &ses_info, TOPO_VERSION)) == 0)
3596 ses_thread_init(mod);
3597
3598 return (rval);
3599 }
3600
3601 void
_topo_fini(topo_mod_t * mod)3602 _topo_fini(topo_mod_t *mod)
3603 {
3604 ses_thread_fini(mod);
3605 ses_sof_freeall(mod);
3606 topo_mod_unregister(mod);
3607 }
3608