1940d71d2Seschrock /* 2940d71d2Seschrock * CDDL HEADER START 3940d71d2Seschrock * 4940d71d2Seschrock * The contents of this file are subject to the terms of the 5940d71d2Seschrock * Common Development and Distribution License (the "License"). 6940d71d2Seschrock * You may not use this file except in compliance with the License. 7940d71d2Seschrock * 8940d71d2Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9940d71d2Seschrock * or http://www.opensolaris.org/os/licensing. 10940d71d2Seschrock * See the License for the specific language governing permissions 11940d71d2Seschrock * and limitations under the License. 12940d71d2Seschrock * 13940d71d2Seschrock * When distributing Covered Code, include this CDDL HEADER in each 14940d71d2Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15940d71d2Seschrock * If applicable, add the following below this CDDL HEADER, with the 16940d71d2Seschrock * fields enclosed by brackets "[]" replaced with your own identifying 17940d71d2Seschrock * information: Portions Copyright [yyyy] [name of copyright owner] 18940d71d2Seschrock * 19940d71d2Seschrock * CDDL HEADER END 20940d71d2Seschrock */ 21940d71d2Seschrock 22940d71d2Seschrock /* 236efb64caSEric Schrock * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24940d71d2Seschrock * Use is subject to license terms. 25940d71d2Seschrock */ 26940d71d2Seschrock 276efb64caSEric Schrock #include <alloca.h> 28940d71d2Seschrock #include <dirent.h> 29940d71d2Seschrock #include <devid.h> 30940d71d2Seschrock #include <fm/libdiskstatus.h> 31940d71d2Seschrock #include <inttypes.h> 32940d71d2Seschrock #include <pthread.h> 33940d71d2Seschrock #include <strings.h> 34940d71d2Seschrock #include <unistd.h> 35940d71d2Seschrock #include <sys/dkio.h> 36940d71d2Seschrock #include <sys/fm/protocol.h> 37940d71d2Seschrock #include <sys/scsi/scsi_types.h> 38d91236feSeschrock 39940d71d2Seschrock #include "disk.h" 40d91236feSeschrock #include "ses.h" 41940d71d2Seschrock 42940d71d2Seschrock #define SES_VERSION 1 43940d71d2Seschrock 44940d71d2Seschrock #define SES_SNAP_FREQ 1000 /* in milliseconds */ 45940d71d2Seschrock 46d91236feSeschrock #define SES_STATUS_UNAVAIL(s) \ 470b32bb8bSEric Schrock ((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED) 48940d71d2Seschrock 49940d71d2Seschrock /* 50940d71d2Seschrock * Because multiple SES targets can be part of a single chassis, we construct 51940d71d2Seschrock * our own hierarchy that takes this into account. These SES targets may refer 52940d71d2Seschrock * to the same devices (multiple paths) or to different devices (managing 53940d71d2Seschrock * different portions of the space). We arrange things into a 54940d71d2Seschrock * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all 55940d71d2Seschrock * nodes found so far. 56940d71d2Seschrock */ 570b32bb8bSEric Schrock typedef struct ses_alt_node { 580b32bb8bSEric Schrock topo_list_t san_link; 590b32bb8bSEric Schrock ses_node_t *san_node; 600b32bb8bSEric Schrock } ses_alt_node_t; 61940d71d2Seschrock 62940d71d2Seschrock typedef struct ses_enum_node { 63940d71d2Seschrock topo_list_t sen_link; 64940d71d2Seschrock ses_node_t *sen_node; 650b32bb8bSEric Schrock topo_list_t sen_alt_nodes; 66940d71d2Seschrock uint64_t sen_type; 67940d71d2Seschrock uint64_t sen_instance; 68940d71d2Seschrock ses_enum_target_t *sen_target; 69940d71d2Seschrock } ses_enum_node_t; 70940d71d2Seschrock 71940d71d2Seschrock typedef struct ses_enum_chassis { 72940d71d2Seschrock topo_list_t sec_link; 73*53dbcc59SSundeep Panicker topo_list_t sec_subchassis; 74940d71d2Seschrock topo_list_t sec_nodes; 75940d71d2Seschrock topo_list_t sec_targets; 76940d71d2Seschrock const char *sec_csn; 77*53dbcc59SSundeep Panicker const char *sec_lid; 78940d71d2Seschrock ses_node_t *sec_enclosure; 79940d71d2Seschrock ses_enum_target_t *sec_target; 80940d71d2Seschrock topo_instance_t sec_instance; 81*53dbcc59SSundeep Panicker topo_instance_t sec_scinstance; 82940d71d2Seschrock boolean_t sec_hasdev; 83d91236feSeschrock boolean_t sec_internal; 84940d71d2Seschrock } ses_enum_chassis_t; 85940d71d2Seschrock 86940d71d2Seschrock typedef struct ses_enum_data { 87940d71d2Seschrock topo_list_t sed_disks; 88940d71d2Seschrock topo_list_t sed_chassis; 89940d71d2Seschrock ses_enum_chassis_t *sed_current; 90940d71d2Seschrock ses_enum_target_t *sed_target; 91940d71d2Seschrock int sed_errno; 92940d71d2Seschrock char *sed_name; 93940d71d2Seschrock topo_mod_t *sed_mod; 94940d71d2Seschrock topo_instance_t sed_instance; 95940d71d2Seschrock } ses_enum_data_t; 96940d71d2Seschrock 97*53dbcc59SSundeep Panicker typedef enum { 98*53dbcc59SSundeep Panicker SES_NEW_CHASSIS = 0x1, 99*53dbcc59SSundeep Panicker SES_NEW_SUBCHASSIS = 0x2, 100*53dbcc59SSundeep Panicker SES_DUP_CHASSIS = 0x4, 101*53dbcc59SSundeep Panicker SES_DUP_SUBCHASSIS = 0x8 102*53dbcc59SSundeep Panicker } ses_chassis_type_e; 103*53dbcc59SSundeep Panicker 104940d71d2Seschrock static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 105940d71d2Seschrock nvlist_t **); 106940d71d2Seschrock static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 107940d71d2Seschrock nvlist_t **); 108940d71d2Seschrock 109940d71d2Seschrock static const topo_method_t ses_component_methods[] = { 110940d71d2Seschrock { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, 111940d71d2Seschrock TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present }, 112d91236feSeschrock { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 113d91236feSeschrock TOPO_STABILITY_INTERNAL, ses_node_enum_facility }, 11488045cffSRobert Johnston { TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC, 11588045cffSRobert Johnston TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL, 11688045cffSRobert Johnston topo_method_sensor_failure }, 117d91236feSeschrock { NULL } 118d91236feSeschrock }; 119d91236feSeschrock 120d91236feSeschrock static const topo_method_t ses_bay_methods[] = { 121d91236feSeschrock { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 122d91236feSeschrock TOPO_STABILITY_INTERNAL, ses_node_enum_facility }, 123940d71d2Seschrock { NULL } 124940d71d2Seschrock }; 125940d71d2Seschrock 126940d71d2Seschrock static const topo_method_t ses_enclosure_methods[] = { 127940d71d2Seschrock { TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC, 128940d71d2Seschrock TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains }, 129d91236feSeschrock { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 130d91236feSeschrock TOPO_STABILITY_INTERNAL, ses_enc_enum_facility }, 131940d71d2Seschrock { NULL } 132940d71d2Seschrock }; 133940d71d2Seschrock 134940d71d2Seschrock static void 135940d71d2Seschrock ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp) 136940d71d2Seschrock { 137940d71d2Seschrock if (--stp->set_refcount == 0) { 138940d71d2Seschrock ses_snap_rele(stp->set_snap); 139940d71d2Seschrock ses_close(stp->set_target); 140940d71d2Seschrock topo_mod_strfree(mod, stp->set_devpath); 141940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 142940d71d2Seschrock } 143940d71d2Seschrock } 144940d71d2Seschrock 145940d71d2Seschrock static void 146*53dbcc59SSundeep Panicker ses_data_free(ses_enum_data_t *sdp, ses_enum_chassis_t *pcp) 147940d71d2Seschrock { 148940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 149940d71d2Seschrock ses_enum_chassis_t *cp; 150940d71d2Seschrock ses_enum_node_t *np; 151940d71d2Seschrock ses_enum_target_t *tp; 1520b32bb8bSEric Schrock ses_alt_node_t *ap; 153*53dbcc59SSundeep Panicker topo_list_t *cpl; 154*53dbcc59SSundeep Panicker 155*53dbcc59SSundeep Panicker 156*53dbcc59SSundeep Panicker if (pcp != NULL) 157*53dbcc59SSundeep Panicker cpl = &pcp->sec_subchassis; 158*53dbcc59SSundeep Panicker else 159*53dbcc59SSundeep Panicker cpl = &sdp->sed_chassis; 160940d71d2Seschrock 161*53dbcc59SSundeep Panicker while ((cp = topo_list_next(cpl)) != NULL) { 162*53dbcc59SSundeep Panicker topo_list_delete(cpl, cp); 163940d71d2Seschrock 164940d71d2Seschrock while ((np = topo_list_next(&cp->sec_nodes)) != NULL) { 1650b32bb8bSEric Schrock while ((ap = topo_list_next(&np->sen_alt_nodes)) != 1660b32bb8bSEric Schrock NULL) { 1670b32bb8bSEric Schrock topo_list_delete(&np->sen_alt_nodes, ap); 1680b32bb8bSEric Schrock topo_mod_free(mod, ap, sizeof (ses_alt_node_t)); 1690b32bb8bSEric Schrock } 170940d71d2Seschrock topo_list_delete(&cp->sec_nodes, np); 171940d71d2Seschrock topo_mod_free(mod, np, sizeof (ses_enum_node_t)); 172940d71d2Seschrock } 173940d71d2Seschrock 174940d71d2Seschrock while ((tp = topo_list_next(&cp->sec_targets)) != NULL) { 175940d71d2Seschrock topo_list_delete(&cp->sec_targets, tp); 176940d71d2Seschrock ses_target_free(mod, tp); 177940d71d2Seschrock } 178940d71d2Seschrock 179940d71d2Seschrock topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t)); 180940d71d2Seschrock } 181940d71d2Seschrock 182*53dbcc59SSundeep Panicker if (pcp == NULL) { 183*53dbcc59SSundeep Panicker disk_list_free(mod, &sdp->sed_disks); 184*53dbcc59SSundeep Panicker topo_mod_free(mod, sdp, sizeof (ses_enum_data_t)); 185*53dbcc59SSundeep Panicker } 186940d71d2Seschrock } 187940d71d2Seschrock 188940d71d2Seschrock /* 189940d71d2Seschrock * For enclosure nodes, we have a special contains method. By default, the hc 190940d71d2Seschrock * walker will compare the node name and instance number to determine if an 191940d71d2Seschrock * FMRI matches. For enclosures where the enumeration order is impossible to 192940d71d2Seschrock * predict, we instead use the chassis-id as a unique identifier, and ignore 193940d71d2Seschrock * the instance number. 194940d71d2Seschrock */ 195940d71d2Seschrock static int 196940d71d2Seschrock fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2) 197940d71d2Seschrock { 198940d71d2Seschrock uint8_t v1, v2; 199940d71d2Seschrock nvlist_t **hcp1, **hcp2; 200940d71d2Seschrock int err, i; 201940d71d2Seschrock uint_t nhcp1, nhcp2; 202940d71d2Seschrock nvlist_t *a1, *a2; 203940d71d2Seschrock char *c1, *c2; 204940d71d2Seschrock int mindepth; 205940d71d2Seschrock 206940d71d2Seschrock if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 || 207940d71d2Seschrock nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 || 208940d71d2Seschrock v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION) 209940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION)); 210940d71d2Seschrock 211940d71d2Seschrock err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1); 212940d71d2Seschrock err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2); 213940d71d2Seschrock if (err != 0) 214940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 215940d71d2Seschrock 216940d71d2Seschrock /* 217940d71d2Seschrock * If the chassis-id doesn't match, then these FMRIs are not 218940d71d2Seschrock * equivalent. If one of the FMRIs doesn't have a chassis ID, then we 219940d71d2Seschrock * have no choice but to fall back to the instance ID. 220940d71d2Seschrock */ 221940d71d2Seschrock if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 && 222940d71d2Seschrock nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 && 223940d71d2Seschrock nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 && 224940d71d2Seschrock nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) { 225940d71d2Seschrock if (strcmp(c1, c2) != 0) 226940d71d2Seschrock return (0); 227940d71d2Seschrock 228940d71d2Seschrock mindepth = 1; 229940d71d2Seschrock } else { 230940d71d2Seschrock mindepth = 0; 231940d71d2Seschrock } 232940d71d2Seschrock 233940d71d2Seschrock if (nhcp2 < nhcp1) 234940d71d2Seschrock return (0); 235940d71d2Seschrock 236940d71d2Seschrock for (i = 0; i < nhcp1; i++) { 237940d71d2Seschrock char *nm1 = NULL; 238940d71d2Seschrock char *nm2 = NULL; 239940d71d2Seschrock char *id1 = NULL; 240940d71d2Seschrock char *id2 = NULL; 241940d71d2Seschrock 242940d71d2Seschrock (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1); 243940d71d2Seschrock (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2); 244940d71d2Seschrock (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1); 245940d71d2Seschrock (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2); 246940d71d2Seschrock if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL) 247940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 248940d71d2Seschrock 249940d71d2Seschrock if (strcmp(nm1, nm2) == 0 && 250940d71d2Seschrock (i < mindepth || strcmp(id1, id2) == 0)) 251940d71d2Seschrock continue; 252940d71d2Seschrock 253940d71d2Seschrock return (0); 254940d71d2Seschrock } 255940d71d2Seschrock 256940d71d2Seschrock return (1); 257940d71d2Seschrock } 258940d71d2Seschrock 259940d71d2Seschrock /*ARGSUSED*/ 260940d71d2Seschrock static int 261940d71d2Seschrock ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 262940d71d2Seschrock nvlist_t *in, nvlist_t **out) 263940d71d2Seschrock { 264940d71d2Seschrock int ret; 265940d71d2Seschrock nvlist_t *nv1, *nv2; 266940d71d2Seschrock 267940d71d2Seschrock if (version > TOPO_METH_CONTAINS_VERSION) 268940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 269940d71d2Seschrock 270940d71d2Seschrock if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 || 271940d71d2Seschrock nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0) 272940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 273940d71d2Seschrock 274940d71d2Seschrock ret = fmri_contains(mod, nv1, nv2); 275940d71d2Seschrock if (ret < 0) 276940d71d2Seschrock return (-1); 277940d71d2Seschrock 278940d71d2Seschrock if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) { 27973e32a37SRobert Johnston if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET, 280940d71d2Seschrock ret) == 0) 281940d71d2Seschrock return (0); 282940d71d2Seschrock else 283940d71d2Seschrock nvlist_free(*out); 284940d71d2Seschrock } 285940d71d2Seschrock 286940d71d2Seschrock return (-1); 287940d71d2Seschrock 288940d71d2Seschrock } 289940d71d2Seschrock 290940d71d2Seschrock /* 291d91236feSeschrock * Return a current instance of the node. This is somewhat complicated because 292d91236feSeschrock * we need to take a new snapshot in order to get the new data, but we don't 293940d71d2Seschrock * want to be constantly taking SES snapshots if the consumer is going to do a 294940d71d2Seschrock * series of queries. So we adopt the strategy of assuming that the SES state 295d91236feSeschrock * is not going to be rapidly changing, and limit our snapshot frequency to 296d91236feSeschrock * some defined bounds. 297940d71d2Seschrock */ 298d91236feSeschrock ses_node_t * 2990b32bb8bSEric Schrock ses_node_lock(topo_mod_t *mod, tnode_t *tn) 300940d71d2Seschrock { 301940d71d2Seschrock struct timeval tv; 302940d71d2Seschrock ses_enum_target_t *tp = topo_node_getspecific(tn); 303940d71d2Seschrock uint64_t prev, now; 304940d71d2Seschrock ses_snap_t *snap; 305940d71d2Seschrock int err; 306d91236feSeschrock uint64_t nodeid; 307940d71d2Seschrock ses_node_t *np; 308940d71d2Seschrock 309d91236feSeschrock if (tp == NULL) { 310d91236feSeschrock (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 311d91236feSeschrock return (NULL); 312d91236feSeschrock } 313940d71d2Seschrock 3140b32bb8bSEric Schrock (void) pthread_mutex_lock(&tp->set_lock); 3150b32bb8bSEric Schrock 316940d71d2Seschrock /* 317940d71d2Seschrock * Determine if we need to take a new snapshot. 318940d71d2Seschrock */ 319940d71d2Seschrock if (gettimeofday(&tv, NULL) != 0) { 320940d71d2Seschrock tv.tv_sec = time(NULL); 321940d71d2Seschrock tv.tv_usec = 0; 322940d71d2Seschrock } 323940d71d2Seschrock 324940d71d2Seschrock now = tv.tv_sec * 1000 + tv.tv_usec / 1000; 325940d71d2Seschrock prev = tp->set_snaptime.tv_sec * 1000 + 326940d71d2Seschrock tp->set_snaptime.tv_usec / 1000; 327940d71d2Seschrock 328940d71d2Seschrock if (now - prev > SES_SNAP_FREQ && 329940d71d2Seschrock (snap = ses_snap_new(tp->set_target)) != NULL) { 330940d71d2Seschrock if (ses_snap_generation(snap) != 331940d71d2Seschrock ses_snap_generation(tp->set_snap)) { 332940d71d2Seschrock /* 333940d71d2Seschrock * If we find ourselves in this situation, we're in 334940d71d2Seschrock * trouble. The generation count has changed, which 335940d71d2Seschrock * indicates that our current topology is out of date. 336940d71d2Seschrock * But we need to consult the new topology in order to 337940d71d2Seschrock * determine presence at this moment in time. We can't 338940d71d2Seschrock * go back and change the topo snapshot in situ, so 339d91236feSeschrock * we'll just have to fail the call in this unlikely 340d91236feSeschrock * scenario. 341940d71d2Seschrock */ 342940d71d2Seschrock ses_snap_rele(snap); 343d91236feSeschrock (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 3440b32bb8bSEric Schrock (void) pthread_mutex_unlock(&tp->set_lock); 345d91236feSeschrock return (NULL); 346940d71d2Seschrock } else { 347940d71d2Seschrock ses_snap_rele(tp->set_snap); 348940d71d2Seschrock tp->set_snap = snap; 349940d71d2Seschrock } 350940d71d2Seschrock tp->set_snaptime = tv; 351940d71d2Seschrock } 352940d71d2Seschrock 353940d71d2Seschrock snap = tp->set_snap; 354940d71d2Seschrock 355940d71d2Seschrock verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES, 356940d71d2Seschrock TOPO_PROP_NODE_ID, &nodeid, &err) == 0); 357940d71d2Seschrock verify((np = ses_node_lookup(snap, nodeid)) != NULL); 358d91236feSeschrock 359d91236feSeschrock return (np); 360d91236feSeschrock } 361d91236feSeschrock 3620b32bb8bSEric Schrock /*ARGSUSED*/ 3630b32bb8bSEric Schrock void 3640b32bb8bSEric Schrock ses_node_unlock(topo_mod_t *mod, tnode_t *tn) 3650b32bb8bSEric Schrock { 3660b32bb8bSEric Schrock ses_enum_target_t *tp = topo_node_getspecific(tn); 3670b32bb8bSEric Schrock 3680b32bb8bSEric Schrock verify(tp != NULL); 3690b32bb8bSEric Schrock 3700b32bb8bSEric Schrock (void) pthread_mutex_unlock(&tp->set_lock); 3710b32bb8bSEric Schrock } 3720b32bb8bSEric Schrock 373d91236feSeschrock /* 374d91236feSeschrock * Determine if the element is present. 375d91236feSeschrock */ 376d91236feSeschrock /*ARGSUSED*/ 377d91236feSeschrock static int 378d91236feSeschrock ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 379d91236feSeschrock nvlist_t *in, nvlist_t **out) 380d91236feSeschrock { 381d91236feSeschrock boolean_t present; 382d91236feSeschrock ses_node_t *np; 383d91236feSeschrock nvlist_t *props, *nvl; 384d91236feSeschrock uint64_t status; 385d91236feSeschrock 3860b32bb8bSEric Schrock if ((np = ses_node_lock(mod, tn)) == NULL) 387d91236feSeschrock return (-1); 388d91236feSeschrock 389940d71d2Seschrock verify((props = ses_node_props(np)) != NULL); 390940d71d2Seschrock verify(nvlist_lookup_uint64(props, 391940d71d2Seschrock SES_PROP_STATUS_CODE, &status) == 0); 392940d71d2Seschrock 3930b32bb8bSEric Schrock ses_node_unlock(mod, tn); 3940b32bb8bSEric Schrock 395940d71d2Seschrock present = (status != SES_ESC_NOT_INSTALLED); 396940d71d2Seschrock 397940d71d2Seschrock if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) 398940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 399940d71d2Seschrock 400940d71d2Seschrock if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET, 401940d71d2Seschrock present) != 0) { 402940d71d2Seschrock nvlist_free(nvl); 403940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 404940d71d2Seschrock } 405940d71d2Seschrock 406940d71d2Seschrock *out = nvl; 407940d71d2Seschrock 408940d71d2Seschrock return (0); 409940d71d2Seschrock } 410940d71d2Seschrock 411940d71d2Seschrock /* 412940d71d2Seschrock * Sets standard properties for a ses node (enclosure or bay). This includes 413940d71d2Seschrock * setting the FRU to be the same as the resource, as well as setting the 414940d71d2Seschrock * authority information. 415940d71d2Seschrock */ 416940d71d2Seschrock static int 417940d71d2Seschrock ses_set_standard_props(topo_mod_t *mod, tnode_t *tn, nvlist_t *auth, 418940d71d2Seschrock uint64_t nodeid, const char *path) 419940d71d2Seschrock { 420940d71d2Seschrock int err; 421940d71d2Seschrock char *product, *chassis; 422940d71d2Seschrock nvlist_t *fmri; 423940d71d2Seschrock topo_pgroup_info_t pgi; 424940d71d2Seschrock 425940d71d2Seschrock /* 426940d71d2Seschrock * Set the authority explicitly if specified. 427940d71d2Seschrock */ 428940d71d2Seschrock if (auth) { 429940d71d2Seschrock verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, 430940d71d2Seschrock &product) == 0); 431940d71d2Seschrock verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, 432940d71d2Seschrock &chassis) == 0); 433940d71d2Seschrock if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 434940d71d2Seschrock FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product, 435940d71d2Seschrock &err) != 0 || 436940d71d2Seschrock topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 437940d71d2Seschrock FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis, 438940d71d2Seschrock &err) != 0 || 439940d71d2Seschrock topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 440940d71d2Seschrock FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "", 441940d71d2Seschrock &err) != 0) { 442940d71d2Seschrock topo_mod_dprintf(mod, "failed to add authority " 443d91236feSeschrock "properties: %s\n", topo_strerror(err)); 444940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 445940d71d2Seschrock } 446940d71d2Seschrock } 447940d71d2Seschrock 448940d71d2Seschrock /* 449940d71d2Seschrock * Copy the resource and set that as the FRU. 450940d71d2Seschrock */ 451940d71d2Seschrock if (topo_node_resource(tn, &fmri, &err) != 0) { 452940d71d2Seschrock topo_mod_dprintf(mod, 453940d71d2Seschrock "topo_node_resource() failed : %s\n", 454940d71d2Seschrock topo_strerror(err)); 455940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 456940d71d2Seschrock } 457940d71d2Seschrock 458940d71d2Seschrock if (topo_node_fru_set(tn, fmri, 0, &err) != 0) { 459940d71d2Seschrock topo_mod_dprintf(mod, 460940d71d2Seschrock "topo_node_fru_set() failed : %s\n", 461940d71d2Seschrock topo_strerror(err)); 462940d71d2Seschrock nvlist_free(fmri); 463940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 464940d71d2Seschrock } 465940d71d2Seschrock 466940d71d2Seschrock nvlist_free(fmri); 467940d71d2Seschrock 468940d71d2Seschrock /* 469940d71d2Seschrock * Set the SES-specific properties so that consumers can query 470940d71d2Seschrock * additional information about the particular SES element. 471940d71d2Seschrock */ 472940d71d2Seschrock pgi.tpi_name = TOPO_PGROUP_SES; 473940d71d2Seschrock pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 474940d71d2Seschrock pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 475940d71d2Seschrock pgi.tpi_version = TOPO_VERSION; 476940d71d2Seschrock if (topo_pgroup_create(tn, &pgi, &err) != 0) { 477940d71d2Seschrock topo_mod_dprintf(mod, "failed to create propgroup " 478940d71d2Seschrock "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err)); 479940d71d2Seschrock return (-1); 480940d71d2Seschrock } 481940d71d2Seschrock 482940d71d2Seschrock if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES, 483940d71d2Seschrock TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE, 484940d71d2Seschrock nodeid, &err) != 0) { 485940d71d2Seschrock topo_mod_dprintf(mod, 486940d71d2Seschrock "failed to create property %s: %s\n", 487940d71d2Seschrock TOPO_PROP_NODE_ID, topo_strerror(err)); 488940d71d2Seschrock return (-1); 489940d71d2Seschrock } 490940d71d2Seschrock 491940d71d2Seschrock if (topo_prop_set_string(tn, TOPO_PGROUP_SES, 492940d71d2Seschrock TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE, 493940d71d2Seschrock path, &err) != 0) { 494940d71d2Seschrock topo_mod_dprintf(mod, 495940d71d2Seschrock "failed to create property %s: %s\n", 496940d71d2Seschrock TOPO_PROP_TARGET_PATH, topo_strerror(err)); 497940d71d2Seschrock return (-1); 498940d71d2Seschrock } 499940d71d2Seschrock 500940d71d2Seschrock return (0); 501940d71d2Seschrock } 502940d71d2Seschrock 503940d71d2Seschrock /* 504940d71d2Seschrock * Callback to add a disk to a given bay. We first check the status-code to 505940d71d2Seschrock * determine if a disk is present, ignoring those that aren't in an appropriate 5060b32bb8bSEric Schrock * state. We then scan the parent bay node's SAS address array to determine 5070b32bb8bSEric Schrock * possible attached SAS addresses. We create a disk node if the disk is not 5080b32bb8bSEric Schrock * SAS or the SES target does not support the necessary pages for this; if we 5090b32bb8bSEric Schrock * find the SAS address, we create a disk node and also correlate it with 5100b32bb8bSEric Schrock * the corresponding Solaris device node to fill in the rest of the data. 511940d71d2Seschrock */ 512940d71d2Seschrock static int 513940d71d2Seschrock ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props) 514940d71d2Seschrock { 515940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 516940d71d2Seschrock uint64_t status; 517940d71d2Seschrock nvlist_t **sas; 518940d71d2Seschrock uint_t s, nsas; 5190b32bb8bSEric Schrock char **paths; 5200b32bb8bSEric Schrock int err; 521940d71d2Seschrock 522940d71d2Seschrock /* 523940d71d2Seschrock * Skip devices that are not in a present (and possibly damaged) state. 524940d71d2Seschrock */ 525940d71d2Seschrock if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0) 526940d71d2Seschrock return (0); 527940d71d2Seschrock 528940d71d2Seschrock if (status != SES_ESC_OK && 529940d71d2Seschrock status != SES_ESC_CRITICAL && 530940d71d2Seschrock status != SES_ESC_NONCRITICAL && 531940d71d2Seschrock status != SES_ESC_UNRECOVERABLE && 532940d71d2Seschrock status != SES_ESC_NO_ACCESS) 533940d71d2Seschrock return (0); 534940d71d2Seschrock 535940d71d2Seschrock topo_mod_dprintf(mod, "found attached disk"); 536940d71d2Seschrock 537940d71d2Seschrock /* 538940d71d2Seschrock * Create the disk range. 539940d71d2Seschrock */ 5400b32bb8bSEric Schrock if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) { 541940d71d2Seschrock topo_mod_dprintf(mod, 542940d71d2Seschrock "topo_node_create_range() failed: %s", 543940d71d2Seschrock topo_mod_errmsg(mod)); 544940d71d2Seschrock return (-1); 545940d71d2Seschrock } 546940d71d2Seschrock 547940d71d2Seschrock /* 548940d71d2Seschrock * Look through all SAS addresses and attempt to correlate them to a 549940d71d2Seschrock * known Solaris device. If we don't find a matching node, then we 550940d71d2Seschrock * don't enumerate the disk node. 551940d71d2Seschrock */ 552940d71d2Seschrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 553940d71d2Seschrock &sas, &nsas) != 0) 554940d71d2Seschrock return (0); 555940d71d2Seschrock 5560b32bb8bSEric Schrock if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES, 5570b32bb8bSEric Schrock TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0) 5580b32bb8bSEric Schrock return (0); 5590b32bb8bSEric Schrock 5600b32bb8bSEric Schrock err = 0; 5610b32bb8bSEric Schrock 562940d71d2Seschrock for (s = 0; s < nsas; s++) { 5630b32bb8bSEric Schrock if (disk_declare_addr(mod, pnode, 5640b32bb8bSEric Schrock &sdp->sed_disks, paths[s]) != 0 && 5650b32bb8bSEric Schrock topo_mod_errno(mod) != EMOD_NODE_BOUND) { 5660b32bb8bSEric Schrock err = -1; 5670b32bb8bSEric Schrock break; 5680b32bb8bSEric Schrock } 5690b32bb8bSEric Schrock } 5700b32bb8bSEric Schrock 5710b32bb8bSEric Schrock for (s = 0; s < nsas; s++) 5720b32bb8bSEric Schrock topo_mod_free(mod, paths[s], strlen(paths[s]) + 1); 5730b32bb8bSEric Schrock topo_mod_free(mod, paths, nsas * sizeof (char *)); 5740b32bb8bSEric Schrock 5750b32bb8bSEric Schrock return (err); 5760b32bb8bSEric Schrock } 5770b32bb8bSEric Schrock 5780b32bb8bSEric Schrock static int 5790b32bb8bSEric Schrock ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp) 5800b32bb8bSEric Schrock { 5810b32bb8bSEric Schrock ses_alt_node_t *ap; 5820b32bb8bSEric Schrock ses_node_t *np; 5830b32bb8bSEric Schrock nvlist_t *props; 5840b32bb8bSEric Schrock 5850b32bb8bSEric Schrock nvlist_t **phys; 5860b32bb8bSEric Schrock uint_t i, j, n_phys, all_phys = 0; 5870b32bb8bSEric Schrock char **paths; 5880b32bb8bSEric Schrock uint64_t addr; 5890b32bb8bSEric Schrock size_t len; 5900b32bb8bSEric Schrock int terr, err = -1; 5910b32bb8bSEric Schrock 5920b32bb8bSEric Schrock for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 5930b32bb8bSEric Schrock ap = topo_list_next(ap)) { 5940b32bb8bSEric Schrock np = ap->san_node; 5950b32bb8bSEric Schrock props = ses_node_props(np); 5960b32bb8bSEric Schrock 5970b32bb8bSEric Schrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 5980b32bb8bSEric Schrock &phys, &n_phys) != 0) 5990b32bb8bSEric Schrock continue; 6000b32bb8bSEric Schrock 6010b32bb8bSEric Schrock all_phys += n_phys; 6020b32bb8bSEric Schrock } 6030b32bb8bSEric Schrock 6040b32bb8bSEric Schrock if (all_phys == 0) 6050b32bb8bSEric Schrock return (0); 6060b32bb8bSEric Schrock 6070b32bb8bSEric Schrock if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL) 6080b32bb8bSEric Schrock return (-1); 6090b32bb8bSEric Schrock 6100b32bb8bSEric Schrock for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 6110b32bb8bSEric Schrock ap = topo_list_next(ap)) { 6120b32bb8bSEric Schrock np = ap->san_node; 6130b32bb8bSEric Schrock props = ses_node_props(np); 6140b32bb8bSEric Schrock 6150b32bb8bSEric Schrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 6160b32bb8bSEric Schrock &phys, &n_phys) != 0) 617940d71d2Seschrock continue; 618940d71d2Seschrock 6190b32bb8bSEric Schrock for (j = 0; j < n_phys; j++) { 6200b32bb8bSEric Schrock if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR, 6210b32bb8bSEric Schrock &addr) != 0) 6220b32bb8bSEric Schrock continue; 623940d71d2Seschrock 6240b32bb8bSEric Schrock len = snprintf(NULL, 0, "%016llx", addr) + 1; 6250b32bb8bSEric Schrock if ((paths[i] = topo_mod_alloc(mod, len)) == NULL) 6260b32bb8bSEric Schrock goto error; 6270b32bb8bSEric Schrock 6280b32bb8bSEric Schrock (void) snprintf(paths[i], len, "%016llx", addr); 6290b32bb8bSEric Schrock 6300b32bb8bSEric Schrock ++i; 6310b32bb8bSEric Schrock } 632940d71d2Seschrock } 633940d71d2Seschrock 6340b32bb8bSEric Schrock err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 6350b32bb8bSEric Schrock TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, 6360b32bb8bSEric Schrock (const char **)paths, i, &terr); 6370b32bb8bSEric Schrock if (err != 0) 6380b32bb8bSEric Schrock err = topo_mod_seterrno(mod, terr); 6390b32bb8bSEric Schrock 6400b32bb8bSEric Schrock error: 6410b32bb8bSEric Schrock for (i = 0; i < all_phys && paths[i] != NULL; i++) 6420b32bb8bSEric Schrock topo_mod_free(mod, paths[i], strlen(paths[i]) + 1); 6430b32bb8bSEric Schrock topo_mod_free(mod, paths, all_phys * sizeof (char *)); 6440b32bb8bSEric Schrock 6450b32bb8bSEric Schrock return (err); 646940d71d2Seschrock } 647940d71d2Seschrock 648940d71d2Seschrock /* 649940d71d2Seschrock * Callback to create a basic node (bay, psu, fan, or controller). 650940d71d2Seschrock */ 651940d71d2Seschrock static int 652940d71d2Seschrock ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp, 653940d71d2Seschrock tnode_t *pnode, const char *nodename, const char *labelname) 654940d71d2Seschrock { 655940d71d2Seschrock ses_node_t *np = snp->sen_node; 656d91236feSeschrock ses_node_t *parent; 657940d71d2Seschrock uint64_t instance = snp->sen_instance; 658940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 659940d71d2Seschrock nvlist_t *props, *aprops; 660940d71d2Seschrock nvlist_t *auth = NULL, *fmri = NULL; 661940d71d2Seschrock tnode_t *tn; 662940d71d2Seschrock char label[128]; 663940d71d2Seschrock int err; 664d91236feSeschrock char *part = NULL, *serial = NULL, *revision = NULL; 665d91236feSeschrock char *desc; 666d91236feSeschrock boolean_t report; 667940d71d2Seschrock 668940d71d2Seschrock props = ses_node_props(np); 669940d71d2Seschrock 670940d71d2Seschrock (void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part); 671940d71d2Seschrock (void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial); 672940d71d2Seschrock 673940d71d2Seschrock topo_mod_dprintf(mod, "adding %s %llu", nodename, instance); 674940d71d2Seschrock 675940d71d2Seschrock /* 676940d71d2Seschrock * Create the node. The interesting information is all copied from the 677940d71d2Seschrock * parent enclosure node, so there is not much to do. 678940d71d2Seschrock */ 679940d71d2Seschrock if ((auth = topo_mod_auth(mod, pnode)) == NULL) 680940d71d2Seschrock goto error; 681940d71d2Seschrock 682d91236feSeschrock /* 683d91236feSeschrock * We want to report revision information for the controller nodes, but 684d91236feSeschrock * we do not get per-element revision information. However, we do have 685d91236feSeschrock * revision information for the entire enclosure, and we can use the 686d91236feSeschrock * 'reported-via' property to know that this controller corresponds to 687d91236feSeschrock * the given revision information. This means we cannot get revision 688d91236feSeschrock * information for targets we are not explicitly connected to, but 689d91236feSeschrock * there is little we can do about the situation. 690d91236feSeschrock */ 691d91236feSeschrock if (strcmp(nodename, CONTROLLER) == 0 && 692d91236feSeschrock nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 && 693d91236feSeschrock report) { 694d91236feSeschrock for (parent = ses_node_parent(np); parent != NULL; 695d91236feSeschrock parent = ses_node_parent(parent)) { 696d91236feSeschrock if (ses_node_type(parent) == SES_NODE_ENCLOSURE) { 697d91236feSeschrock (void) nvlist_lookup_string( 698d91236feSeschrock ses_node_props(parent), 699d91236feSeschrock SES_EN_PROP_REV, &revision); 700d91236feSeschrock break; 701d91236feSeschrock } 702d91236feSeschrock } 703d91236feSeschrock } 704d91236feSeschrock 705940d71d2Seschrock if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 706d91236feSeschrock nodename, (topo_instance_t)instance, NULL, auth, part, revision, 707940d71d2Seschrock serial)) == NULL) { 708940d71d2Seschrock topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 709940d71d2Seschrock topo_mod_errmsg(mod)); 710940d71d2Seschrock goto error; 711940d71d2Seschrock } 712940d71d2Seschrock 713940d71d2Seschrock if ((tn = topo_node_bind(mod, pnode, nodename, 714940d71d2Seschrock instance, fmri)) == NULL) { 715940d71d2Seschrock topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 716940d71d2Seschrock topo_mod_errmsg(mod)); 717940d71d2Seschrock goto error; 718940d71d2Seschrock } 719940d71d2Seschrock 720940d71d2Seschrock /* 721d91236feSeschrock * For the node label, we look for the following in order: 722d91236feSeschrock * 723d91236feSeschrock * <ses-description> 724d91236feSeschrock * <ses-class-description> <instance> 725d91236feSeschrock * <default-type-label> <instance> 726940d71d2Seschrock */ 727d91236feSeschrock if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 || 728d91236feSeschrock desc[0] == '\0') { 729d91236feSeschrock parent = ses_node_parent(np); 730d91236feSeschrock aprops = ses_node_props(parent); 731d91236feSeschrock if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION, 732d91236feSeschrock &desc) == 0 && desc[0] != '\0') 733d91236feSeschrock labelname = desc; 734d91236feSeschrock (void) snprintf(label, sizeof (label), "%s %llu", desc, 735d91236feSeschrock instance); 736d91236feSeschrock desc = label; 737d91236feSeschrock } 738d91236feSeschrock 739d91236feSeschrock if (topo_node_label_set(tn, desc, &err) != 0) 740940d71d2Seschrock goto error; 741940d71d2Seschrock 742940d71d2Seschrock if (ses_set_standard_props(mod, tn, NULL, ses_node_id(np), 743940d71d2Seschrock snp->sen_target->set_devpath) != 0) 744940d71d2Seschrock goto error; 745940d71d2Seschrock 746940d71d2Seschrock if (strcmp(nodename, "bay") == 0) { 7470b32bb8bSEric Schrock if (ses_add_bay_props(mod, tn, snp) != 0) 7480b32bb8bSEric Schrock goto error; 7490b32bb8bSEric Schrock 750940d71d2Seschrock if (ses_create_disk(sdp, tn, props) != 0) 751940d71d2Seschrock goto error; 752d91236feSeschrock 753d91236feSeschrock if (topo_method_register(mod, tn, ses_bay_methods) != 0) { 754d91236feSeschrock topo_mod_dprintf(mod, 755d91236feSeschrock "topo_method_register() failed: %s", 756d91236feSeschrock topo_mod_errmsg(mod)); 757d91236feSeschrock goto error; 758d91236feSeschrock } 759940d71d2Seschrock } else { 760940d71d2Seschrock /* 761d91236feSeschrock * Only fan, psu, and controller nodes have a 'present' method. 762d91236feSeschrock * Bay nodes are always present, and disk nodes are present by 763d91236feSeschrock * virtue of being enumerated. 764940d71d2Seschrock */ 765940d71d2Seschrock if (topo_method_register(mod, tn, ses_component_methods) != 0) { 766940d71d2Seschrock topo_mod_dprintf(mod, 767940d71d2Seschrock "topo_method_register() failed: %s", 768940d71d2Seschrock topo_mod_errmsg(mod)); 769940d71d2Seschrock goto error; 770940d71d2Seschrock } 771940d71d2Seschrock 772940d71d2Seschrock } 773940d71d2Seschrock 774d91236feSeschrock snp->sen_target->set_refcount++; 775d91236feSeschrock topo_node_setspecific(tn, snp->sen_target); 776d91236feSeschrock 777940d71d2Seschrock nvlist_free(auth); 778940d71d2Seschrock nvlist_free(fmri); 779940d71d2Seschrock return (0); 780940d71d2Seschrock 781940d71d2Seschrock error: 782940d71d2Seschrock nvlist_free(auth); 783940d71d2Seschrock nvlist_free(fmri); 784940d71d2Seschrock return (-1); 785940d71d2Seschrock } 786940d71d2Seschrock 787940d71d2Seschrock /* 788940d71d2Seschrock * Instantiate any children of a given type. 789940d71d2Seschrock */ 790940d71d2Seschrock static int 791940d71d2Seschrock ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type, 792d91236feSeschrock const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp, 793d91236feSeschrock boolean_t dorange) 794940d71d2Seschrock { 795940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 796940d71d2Seschrock boolean_t found; 797940d71d2Seschrock uint64_t max; 798940d71d2Seschrock ses_enum_node_t *snp; 799940d71d2Seschrock 800940d71d2Seschrock /* 801940d71d2Seschrock * First go through and count how many matching nodes we have. 802940d71d2Seschrock */ 803940d71d2Seschrock max = 0; 804940d71d2Seschrock found = B_FALSE; 805940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 806940d71d2Seschrock snp = topo_list_next(snp)) { 807940d71d2Seschrock if (snp->sen_type == type) { 808940d71d2Seschrock found = B_TRUE; 809940d71d2Seschrock if (snp->sen_instance > max) 810940d71d2Seschrock max = snp->sen_instance; 811940d71d2Seschrock } 812940d71d2Seschrock } 813940d71d2Seschrock 814940d71d2Seschrock /* 815940d71d2Seschrock * No enclosure should export both DEVICE and ARRAY_DEVICE elements. 816940d71d2Seschrock * Since we map both of these to 'disk', if an enclosure does this, we 817940d71d2Seschrock * just ignore the array elements. 818940d71d2Seschrock */ 819940d71d2Seschrock if (!found || 820940d71d2Seschrock (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev)) 821940d71d2Seschrock return (0); 822940d71d2Seschrock 823940d71d2Seschrock topo_mod_dprintf(mod, "%s: creating %llu %s nodes", 824*53dbcc59SSundeep Panicker cp->sec_csn, max + 1, nodename); 825940d71d2Seschrock 826d91236feSeschrock if (dorange && topo_node_range_create(mod, pnode, 827940d71d2Seschrock nodename, 0, max) != 0) { 828940d71d2Seschrock topo_mod_dprintf(mod, 829940d71d2Seschrock "topo_node_create_range() failed: %s", 830940d71d2Seschrock topo_mod_errmsg(mod)); 831940d71d2Seschrock return (-1); 832940d71d2Seschrock } 833940d71d2Seschrock 834940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 835940d71d2Seschrock snp = topo_list_next(snp)) { 836940d71d2Seschrock if (snp->sen_type == type) { 837940d71d2Seschrock if (ses_create_generic(sdp, snp, pnode, 838940d71d2Seschrock nodename, defaultlabel) != 0) 839940d71d2Seschrock return (-1); 840940d71d2Seschrock } 841940d71d2Seschrock } 842940d71d2Seschrock 843940d71d2Seschrock return (0); 844940d71d2Seschrock } 845940d71d2Seschrock 846*53dbcc59SSundeep Panicker /* 847*53dbcc59SSundeep Panicker * Instantiate a new subchassis instance in the topology. 848*53dbcc59SSundeep Panicker */ 849*53dbcc59SSundeep Panicker static int 850*53dbcc59SSundeep Panicker ses_create_subchassis(ses_enum_data_t *sdp, tnode_t *pnode, 851*53dbcc59SSundeep Panicker ses_enum_chassis_t *scp) 852*53dbcc59SSundeep Panicker { 853*53dbcc59SSundeep Panicker topo_mod_t *mod = sdp->sed_mod; 854*53dbcc59SSundeep Panicker tnode_t *tn; 855*53dbcc59SSundeep Panicker nvlist_t *props; 856*53dbcc59SSundeep Panicker nvlist_t *auth = NULL, *fmri = NULL; 857*53dbcc59SSundeep Panicker char *part = NULL, *revision = NULL; 858*53dbcc59SSundeep Panicker uint64_t instance = scp->sec_instance; 859*53dbcc59SSundeep Panicker char *desc; 860*53dbcc59SSundeep Panicker char label[128]; 861*53dbcc59SSundeep Panicker char **paths; 862*53dbcc59SSundeep Panicker int i, err; 863*53dbcc59SSundeep Panicker ses_enum_target_t *stp; 864*53dbcc59SSundeep Panicker int ret = -1; 865*53dbcc59SSundeep Panicker 866*53dbcc59SSundeep Panicker /* 867*53dbcc59SSundeep Panicker * Copy authority information from parent enclosure node 868*53dbcc59SSundeep Panicker */ 869*53dbcc59SSundeep Panicker if ((auth = topo_mod_auth(mod, pnode)) == NULL) 870*53dbcc59SSundeep Panicker goto error; 871*53dbcc59SSundeep Panicker 872*53dbcc59SSundeep Panicker /* 873*53dbcc59SSundeep Panicker * Record the subchassis serial number in the FMRI. 874*53dbcc59SSundeep Panicker * For now, we assume that logical id is the subchassis serial number. 875*53dbcc59SSundeep Panicker * If this assumption changes in future, then the following 876*53dbcc59SSundeep Panicker * piece of code will need to be updated via an RFE. 877*53dbcc59SSundeep Panicker */ 878*53dbcc59SSundeep Panicker if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 879*53dbcc59SSundeep Panicker SUBCHASSIS, (topo_instance_t)instance, NULL, auth, part, revision, 880*53dbcc59SSundeep Panicker scp->sec_lid)) == NULL) { 881*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 882*53dbcc59SSundeep Panicker topo_mod_errmsg(mod)); 883*53dbcc59SSundeep Panicker goto error; 884*53dbcc59SSundeep Panicker } 885*53dbcc59SSundeep Panicker 886*53dbcc59SSundeep Panicker if ((tn = topo_node_bind(mod, pnode, SUBCHASSIS, 887*53dbcc59SSundeep Panicker instance, fmri)) == NULL) { 888*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 889*53dbcc59SSundeep Panicker topo_mod_errmsg(mod)); 890*53dbcc59SSundeep Panicker goto error; 891*53dbcc59SSundeep Panicker } 892*53dbcc59SSundeep Panicker 893*53dbcc59SSundeep Panicker props = ses_node_props(scp->sec_enclosure); 894*53dbcc59SSundeep Panicker 895*53dbcc59SSundeep Panicker /* 896*53dbcc59SSundeep Panicker * Look for the subchassis label in the following order: 897*53dbcc59SSundeep Panicker * <ses-description> 898*53dbcc59SSundeep Panicker * <ses-class-description> <instance> 899*53dbcc59SSundeep Panicker * <default-type-label> <instance> 900*53dbcc59SSundeep Panicker * 901*53dbcc59SSundeep Panicker * For subchassis, the default label is "SUBCHASSIS" 902*53dbcc59SSundeep Panicker */ 903*53dbcc59SSundeep Panicker if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 || 904*53dbcc59SSundeep Panicker desc[0] == '\0') { 905*53dbcc59SSundeep Panicker if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION, 906*53dbcc59SSundeep Panicker &desc) == 0 && desc[0] != '\0') 907*53dbcc59SSundeep Panicker (void) snprintf(label, sizeof (label), "%s %llu", desc, 908*53dbcc59SSundeep Panicker instance); 909*53dbcc59SSundeep Panicker else 910*53dbcc59SSundeep Panicker (void) snprintf(label, sizeof (label), 911*53dbcc59SSundeep Panicker "SUBCHASSIS %llu", instance); 912*53dbcc59SSundeep Panicker desc = label; 913*53dbcc59SSundeep Panicker } 914*53dbcc59SSundeep Panicker 915*53dbcc59SSundeep Panicker if (topo_node_label_set(tn, desc, &err) != 0) 916*53dbcc59SSundeep Panicker goto error; 917*53dbcc59SSundeep Panicker 918*53dbcc59SSundeep Panicker if (ses_set_standard_props(mod, tn, NULL, 919*53dbcc59SSundeep Panicker ses_node_id(scp->sec_enclosure), scp->sec_target->set_devpath) != 0) 920*53dbcc59SSundeep Panicker goto error; 921*53dbcc59SSundeep Panicker 922*53dbcc59SSundeep Panicker /* 923*53dbcc59SSundeep Panicker * For enclosures, we want to include all possible targets (for upgrade 924*53dbcc59SSundeep Panicker * purposes). 925*53dbcc59SSundeep Panicker */ 926*53dbcc59SSundeep Panicker for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL; 927*53dbcc59SSundeep Panicker stp = topo_list_next(stp), i++) 928*53dbcc59SSundeep Panicker ; 929*53dbcc59SSundeep Panicker 930*53dbcc59SSundeep Panicker verify(i != 0); 931*53dbcc59SSundeep Panicker paths = alloca(i * sizeof (char *)); 932*53dbcc59SSundeep Panicker 933*53dbcc59SSundeep Panicker for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL; 934*53dbcc59SSundeep Panicker stp = topo_list_next(stp), i++) 935*53dbcc59SSundeep Panicker paths[i] = stp->set_devpath; 936*53dbcc59SSundeep Panicker 937*53dbcc59SSundeep Panicker if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 938*53dbcc59SSundeep Panicker TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths, 939*53dbcc59SSundeep Panicker i, &err) != 0) { 940*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "failed to create property %s: %s\n", 941*53dbcc59SSundeep Panicker TOPO_PROP_PATHS, topo_strerror(err)); 942*53dbcc59SSundeep Panicker goto error; 943*53dbcc59SSundeep Panicker } 944*53dbcc59SSundeep Panicker 945*53dbcc59SSundeep Panicker if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 946*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "topo_method_register() failed: %s", 947*53dbcc59SSundeep Panicker topo_mod_errmsg(mod)); 948*53dbcc59SSundeep Panicker goto error; 949*53dbcc59SSundeep Panicker } 950*53dbcc59SSundeep Panicker 951*53dbcc59SSundeep Panicker /* 952*53dbcc59SSundeep Panicker * Create the nodes for controllers and bays. 953*53dbcc59SSundeep Panicker */ 954*53dbcc59SSundeep Panicker if (ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 955*53dbcc59SSundeep Panicker CONTROLLER, "CONTROLLER", scp, B_TRUE) != 0 || 956*53dbcc59SSundeep Panicker ses_create_children(sdp, tn, SES_ET_DEVICE, 957*53dbcc59SSundeep Panicker BAY, "BAY", scp, B_TRUE) != 0 || 958*53dbcc59SSundeep Panicker ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 959*53dbcc59SSundeep Panicker BAY, "BAY", scp, B_TRUE) != 0) 960*53dbcc59SSundeep Panicker goto error; 961*53dbcc59SSundeep Panicker 962*53dbcc59SSundeep Panicker ret = 0; 963*53dbcc59SSundeep Panicker 964*53dbcc59SSundeep Panicker error: 965*53dbcc59SSundeep Panicker nvlist_free(auth); 966*53dbcc59SSundeep Panicker nvlist_free(fmri); 967*53dbcc59SSundeep Panicker return (ret); 968*53dbcc59SSundeep Panicker } 969*53dbcc59SSundeep Panicker 970940d71d2Seschrock /* 971940d71d2Seschrock * Instantiate a new chassis instance in the topology. 972940d71d2Seschrock */ 973940d71d2Seschrock static int 974940d71d2Seschrock ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp) 975940d71d2Seschrock { 976940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 977940d71d2Seschrock nvlist_t *props; 978940d71d2Seschrock char *raw_manufacturer, *raw_model, *raw_revision; 979940d71d2Seschrock char *manufacturer = NULL, *model = NULL, *product = NULL; 980940d71d2Seschrock char *revision = NULL; 981940d71d2Seschrock char *serial; 9826efb64caSEric Schrock char **paths; 983940d71d2Seschrock size_t prodlen; 984940d71d2Seschrock tnode_t *tn; 985940d71d2Seschrock nvlist_t *fmri = NULL, *auth = NULL; 986940d71d2Seschrock int ret = -1; 987940d71d2Seschrock ses_enum_node_t *snp; 9886efb64caSEric Schrock ses_enum_target_t *stp; 989*53dbcc59SSundeep Panicker ses_enum_chassis_t *scp; 9906efb64caSEric Schrock int i, err; 991*53dbcc59SSundeep Panicker uint64_t sc_count = 0; 992940d71d2Seschrock 993d91236feSeschrock /* 994d91236feSeschrock * Ignore any internal enclosures. 995d91236feSeschrock */ 996d91236feSeschrock if (cp->sec_internal) 997d91236feSeschrock return (0); 998d91236feSeschrock 999940d71d2Seschrock /* 1000940d71d2Seschrock * Check to see if there are any devices presennt in the chassis. If 1001940d71d2Seschrock * not, ignore the chassis alltogether. This is most useful for 1002940d71d2Seschrock * ignoring internal HBAs that present a SES target but don't actually 1003940d71d2Seschrock * manage any of the devices. 1004940d71d2Seschrock */ 1005940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1006940d71d2Seschrock snp = topo_list_next(snp)) { 1007940d71d2Seschrock if (snp->sen_type == SES_ET_DEVICE || 1008940d71d2Seschrock snp->sen_type == SES_ET_ARRAY_DEVICE) 1009940d71d2Seschrock break; 1010940d71d2Seschrock } 1011940d71d2Seschrock 1012940d71d2Seschrock if (snp == NULL) 1013940d71d2Seschrock return (0); 1014940d71d2Seschrock 1015940d71d2Seschrock props = ses_node_props(cp->sec_enclosure); 1016940d71d2Seschrock 1017940d71d2Seschrock /* 1018940d71d2Seschrock * We use the following property mappings: 1019940d71d2Seschrock * 1020940d71d2Seschrock * manufacturer vendor-id 1021940d71d2Seschrock * model product-id 1022940d71d2Seschrock * serial-number libses-chassis-serial 1023940d71d2Seschrock */ 1024940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_VID, 1025940d71d2Seschrock &raw_manufacturer) == 0); 1026940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0); 1027940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_REV, 1028940d71d2Seschrock &raw_revision) == 0); 1029940d71d2Seschrock verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0); 1030940d71d2Seschrock 1031940d71d2Seschrock /* 1032940d71d2Seschrock * To construct the authority information, we 'clean' each string by 1033940d71d2Seschrock * removing any offensive characters and trimmming whitespace. For the 1034940d71d2Seschrock * 'product-id', we use a concatenation of 'manufacturer-model'. We 1035940d71d2Seschrock * also take the numerical serial number and convert it to a string. 1036940d71d2Seschrock */ 1037940d71d2Seschrock if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL || 1038940d71d2Seschrock (model = disk_auth_clean(mod, raw_model)) == NULL || 1039940d71d2Seschrock (revision = disk_auth_clean(mod, raw_revision)) == NULL) { 1040940d71d2Seschrock goto error; 1041940d71d2Seschrock } 1042940d71d2Seschrock 1043940d71d2Seschrock prodlen = strlen(manufacturer) + strlen(model) + 2; 1044940d71d2Seschrock if ((product = topo_mod_alloc(mod, prodlen)) == NULL) 1045940d71d2Seschrock goto error; 1046940d71d2Seschrock 1047940d71d2Seschrock (void) snprintf(product, prodlen, "%s-%s", manufacturer, model); 1048940d71d2Seschrock 1049940d71d2Seschrock /* 1050940d71d2Seschrock * Construct the topo node and bind it to our parent. 1051940d71d2Seschrock */ 1052940d71d2Seschrock if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0) 1053940d71d2Seschrock goto error; 1054940d71d2Seschrock 1055940d71d2Seschrock if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 || 1056940d71d2Seschrock nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) { 1057940d71d2Seschrock (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 1058940d71d2Seschrock goto error; 1059940d71d2Seschrock } 1060940d71d2Seschrock 1061940d71d2Seschrock /* 1062940d71d2Seschrock * We pass NULL for the parent FMRI because there is no resource 1063940d71d2Seschrock * associated with it. For the toplevel enclosure, we leave the 1064940d71d2Seschrock * serial/part/revision portions empty, which are reserved for 1065940d71d2Seschrock * individual components within the chassis. 1066940d71d2Seschrock */ 1067940d71d2Seschrock if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION, 1068940d71d2Seschrock SES_ENCLOSURE, cp->sec_instance, NULL, auth, 1069940d71d2Seschrock model, revision, serial)) == NULL) { 1070940d71d2Seschrock topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 1071940d71d2Seschrock topo_mod_errmsg(mod)); 1072940d71d2Seschrock goto error; 1073940d71d2Seschrock } 1074940d71d2Seschrock 1075940d71d2Seschrock if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE, 1076940d71d2Seschrock cp->sec_instance, fmri)) == NULL) { 1077940d71d2Seschrock topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 1078940d71d2Seschrock topo_mod_errmsg(mod)); 1079940d71d2Seschrock goto error; 1080940d71d2Seschrock } 1081940d71d2Seschrock 1082940d71d2Seschrock if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 1083940d71d2Seschrock topo_mod_dprintf(mod, 1084940d71d2Seschrock "topo_method_register() failed: %s", 1085940d71d2Seschrock topo_mod_errmsg(mod)); 1086940d71d2Seschrock goto error; 1087940d71d2Seschrock } 1088940d71d2Seschrock 1089940d71d2Seschrock if (ses_set_standard_props(mod, tn, auth, 1090940d71d2Seschrock ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0) 1091940d71d2Seschrock goto error; 1092940d71d2Seschrock 10936efb64caSEric Schrock /* 10946efb64caSEric Schrock * For enclosures, we want to include all possible targets (for upgrade 10956efb64caSEric Schrock * purposes). 10966efb64caSEric Schrock */ 10976efb64caSEric Schrock for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL; 10986efb64caSEric Schrock stp = topo_list_next(stp), i++) 10996efb64caSEric Schrock ; 11006efb64caSEric Schrock 11016efb64caSEric Schrock verify(i != 0); 11026efb64caSEric Schrock paths = alloca(i * sizeof (char *)); 11036efb64caSEric Schrock 11046efb64caSEric Schrock for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL; 11056efb64caSEric Schrock stp = topo_list_next(stp), i++) 11066efb64caSEric Schrock paths[i] = stp->set_devpath; 11076efb64caSEric Schrock 11086efb64caSEric Schrock 11096efb64caSEric Schrock if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 11106efb64caSEric Schrock TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths, 11116efb64caSEric Schrock i, &err) != 0) { 11126efb64caSEric Schrock topo_mod_dprintf(mod, 11136efb64caSEric Schrock "failed to create property %s: %s\n", 11146efb64caSEric Schrock TOPO_PROP_PATHS, topo_strerror(err)); 11156efb64caSEric Schrock goto error; 11166efb64caSEric Schrock } 11176efb64caSEric Schrock 1118940d71d2Seschrock /* 1119940d71d2Seschrock * Create the nodes for power supplies, fans, and devices. 1120940d71d2Seschrock */ 1121940d71d2Seschrock if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY, 1122d91236feSeschrock PSU, "PSU", cp, B_TRUE) != 0 || 1123940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_COOLING, 1124d91236feSeschrock FAN, "FAN", cp, B_TRUE) != 0 || 1125940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 1126d91236feSeschrock CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 || 1127940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_DEVICE, 1128d91236feSeschrock BAY, "BAY", cp, B_TRUE) != 0 || 1129940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 1130d91236feSeschrock BAY, "BAY", cp, B_TRUE) != 0) 1131940d71d2Seschrock goto error; 1132940d71d2Seschrock 1133*53dbcc59SSundeep Panicker if (cp->sec_scinstance > 0 && 1134*53dbcc59SSundeep Panicker topo_node_range_create(mod, tn, SUBCHASSIS, 0, 1135*53dbcc59SSundeep Panicker cp->sec_scinstance - 1) != 0) { 1136*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "topo_node_create_range() failed: %s", 1137*53dbcc59SSundeep Panicker topo_mod_errmsg(mod)); 1138*53dbcc59SSundeep Panicker goto error; 1139*53dbcc59SSundeep Panicker } 1140*53dbcc59SSundeep Panicker 1141*53dbcc59SSundeep Panicker for (scp = topo_list_next(&cp->sec_subchassis); scp != NULL; 1142*53dbcc59SSundeep Panicker scp = topo_list_next(scp)) { 1143*53dbcc59SSundeep Panicker 1144*53dbcc59SSundeep Panicker if (ses_create_subchassis(sdp, tn, scp) != 0) 1145*53dbcc59SSundeep Panicker goto error; 1146*53dbcc59SSundeep Panicker 1147*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "created Subchassis node with " 1148*53dbcc59SSundeep Panicker "LID (%s)\n and target (%s) under Chassis with CSN (%s)", 1149*53dbcc59SSundeep Panicker scp->sec_lid, scp->sec_target->set_devpath, cp->sec_csn); 1150*53dbcc59SSundeep Panicker 1151*53dbcc59SSundeep Panicker sc_count++; 1152*53dbcc59SSundeep Panicker } 1153*53dbcc59SSundeep Panicker 1154*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: created %llu %s nodes", 1155*53dbcc59SSundeep Panicker cp->sec_csn, sc_count, SUBCHASSIS); 1156*53dbcc59SSundeep Panicker 11576efb64caSEric Schrock cp->sec_target->set_refcount++; 11586efb64caSEric Schrock topo_node_setspecific(tn, cp->sec_target); 1159d91236feSeschrock 1160940d71d2Seschrock ret = 0; 1161940d71d2Seschrock error: 1162940d71d2Seschrock topo_mod_strfree(mod, manufacturer); 1163940d71d2Seschrock topo_mod_strfree(mod, model); 1164940d71d2Seschrock topo_mod_strfree(mod, revision); 1165940d71d2Seschrock topo_mod_strfree(mod, product); 1166940d71d2Seschrock 1167940d71d2Seschrock nvlist_free(fmri); 1168940d71d2Seschrock nvlist_free(auth); 1169940d71d2Seschrock return (ret); 1170940d71d2Seschrock } 1171940d71d2Seschrock 1172d91236feSeschrock /* 1173d91236feSeschrock * Create a bay node explicitly enumerated via XML. 1174d91236feSeschrock */ 1175d91236feSeschrock static int 1176d91236feSeschrock ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode) 1177d91236feSeschrock { 1178d91236feSeschrock topo_mod_t *mod = sdp->sed_mod; 1179d91236feSeschrock ses_enum_chassis_t *cp; 1180d91236feSeschrock 1181d91236feSeschrock /* 1182d91236feSeschrock * Iterate over chassis looking for an internal enclosure. This 1183d91236feSeschrock * property is set via a vendor-specific plugin, and there should only 1184d91236feSeschrock * ever be a single internal chassis in a system. 1185d91236feSeschrock */ 1186d91236feSeschrock for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 1187d91236feSeschrock cp = topo_list_next(cp)) { 1188d91236feSeschrock if (cp->sec_internal) 1189d91236feSeschrock break; 1190d91236feSeschrock } 1191d91236feSeschrock 1192d91236feSeschrock if (cp == NULL) { 1193d91236feSeschrock topo_mod_dprintf(mod, "failed to find internal chassis\n"); 1194d91236feSeschrock return (-1); 1195d91236feSeschrock } 1196d91236feSeschrock 1197d91236feSeschrock if (ses_create_children(sdp, pnode, SES_ET_DEVICE, 1198d91236feSeschrock BAY, "BAY", cp, B_FALSE) != 0 || 1199d91236feSeschrock ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE, 1200d91236feSeschrock BAY, "BAY", cp, B_FALSE) != 0) 1201d91236feSeschrock return (-1); 1202d91236feSeschrock 1203d91236feSeschrock return (0); 1204d91236feSeschrock } 1205*53dbcc59SSundeep Panicker 1206*53dbcc59SSundeep Panicker /* 1207*53dbcc59SSundeep Panicker * Initialize chassis or subchassis. 1208*53dbcc59SSundeep Panicker */ 1209*53dbcc59SSundeep Panicker static int 1210*53dbcc59SSundeep Panicker ses_init_chassis(topo_mod_t *mod, ses_enum_data_t *sdp, ses_enum_chassis_t *pcp, 1211*53dbcc59SSundeep Panicker ses_enum_chassis_t *cp, ses_node_t *np, nvlist_t *props, 1212*53dbcc59SSundeep Panicker char *lid, ses_chassis_type_e flags) 1213*53dbcc59SSundeep Panicker { 1214*53dbcc59SSundeep Panicker boolean_t internal, ident; 1215*53dbcc59SSundeep Panicker 1216*53dbcc59SSundeep Panicker assert((flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS | 1217*53dbcc59SSundeep Panicker SES_DUP_CHASSIS | SES_DUP_SUBCHASSIS)) != 0); 1218*53dbcc59SSundeep Panicker 1219*53dbcc59SSundeep Panicker assert((cp != NULL) && (np != NULL) && (props != NULL) && 1220*53dbcc59SSundeep Panicker (lid != NULL)); 1221*53dbcc59SSundeep Panicker 1222*53dbcc59SSundeep Panicker if (flags & (SES_NEW_SUBCHASSIS | SES_DUP_SUBCHASSIS)) 1223*53dbcc59SSundeep Panicker assert(pcp != NULL); 1224*53dbcc59SSundeep Panicker 1225*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "ses_init_chassis: %s: lid(%s), flags (%d)", 1226*53dbcc59SSundeep Panicker sdp->sed_name, lid, flags); 1227*53dbcc59SSundeep Panicker 1228*53dbcc59SSundeep Panicker if (flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS)) { 1229*53dbcc59SSundeep Panicker 1230*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "new chassis/subchassis"); 1231*53dbcc59SSundeep Panicker if (nvlist_lookup_boolean_value(props, 1232*53dbcc59SSundeep Panicker LIBSES_EN_PROP_INTERNAL, &internal) == 0) 1233*53dbcc59SSundeep Panicker cp->sec_internal = internal; 1234*53dbcc59SSundeep Panicker 1235*53dbcc59SSundeep Panicker cp->sec_lid = lid; 1236*53dbcc59SSundeep Panicker cp->sec_enclosure = np; 1237*53dbcc59SSundeep Panicker cp->sec_target = sdp->sed_target; 1238*53dbcc59SSundeep Panicker 1239*53dbcc59SSundeep Panicker if (flags & SES_NEW_CHASSIS) { 1240*53dbcc59SSundeep Panicker cp->sec_instance = sdp->sed_instance++; 1241*53dbcc59SSundeep Panicker topo_list_append(&sdp->sed_chassis, cp); 1242*53dbcc59SSundeep Panicker } else { 1243*53dbcc59SSundeep Panicker cp->sec_instance = pcp->sec_scinstance++; 1244*53dbcc59SSundeep Panicker topo_list_append(&pcp->sec_subchassis, cp); 1245*53dbcc59SSundeep Panicker } 1246*53dbcc59SSundeep Panicker 1247*53dbcc59SSundeep Panicker } else { 1248*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "dup chassis/subchassis"); 1249*53dbcc59SSundeep Panicker if (nvlist_lookup_boolean_value(props, 1250*53dbcc59SSundeep Panicker SES_PROP_IDENT, &ident) == 0) { 1251*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "overriding enclosure node"); 1252*53dbcc59SSundeep Panicker 1253*53dbcc59SSundeep Panicker cp->sec_enclosure = np; 1254*53dbcc59SSundeep Panicker cp->sec_target = sdp->sed_target; 1255*53dbcc59SSundeep Panicker } 1256*53dbcc59SSundeep Panicker } 1257*53dbcc59SSundeep Panicker 1258*53dbcc59SSundeep Panicker topo_list_append(&cp->sec_targets, sdp->sed_target); 1259*53dbcc59SSundeep Panicker sdp->sed_current = cp; 1260*53dbcc59SSundeep Panicker 1261*53dbcc59SSundeep Panicker return (0); 1262*53dbcc59SSundeep Panicker } 1263*53dbcc59SSundeep Panicker 1264940d71d2Seschrock /* 1265940d71d2Seschrock * Gather nodes from the current SES target into our chassis list, merging the 1266940d71d2Seschrock * results if necessary. 1267940d71d2Seschrock */ 1268940d71d2Seschrock static ses_walk_action_t 1269940d71d2Seschrock ses_enum_gather(ses_node_t *np, void *data) 1270940d71d2Seschrock { 1271940d71d2Seschrock nvlist_t *props = ses_node_props(np); 1272940d71d2Seschrock ses_enum_data_t *sdp = data; 1273940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 1274*53dbcc59SSundeep Panicker ses_enum_chassis_t *cp, *scp; 1275940d71d2Seschrock ses_enum_node_t *snp; 12760b32bb8bSEric Schrock ses_alt_node_t *sap; 1277940d71d2Seschrock char *csn; 1278940d71d2Seschrock uint64_t instance, type; 1279d91236feSeschrock uint64_t prevstatus, status; 1280*53dbcc59SSundeep Panicker boolean_t report; 1281*53dbcc59SSundeep Panicker boolean_t have_subchassis = B_TRUE; 1282*53dbcc59SSundeep Panicker char *lid; 1283940d71d2Seschrock 1284940d71d2Seschrock if (ses_node_type(np) == SES_NODE_ENCLOSURE) { 1285940d71d2Seschrock /* 1286940d71d2Seschrock * If we have already identified the chassis for this target, 1287940d71d2Seschrock * then this is a secondary enclosure and we should ignore it, 1288940d71d2Seschrock * along with the rest of the tree (since this is depth-first). 1289940d71d2Seschrock */ 1290940d71d2Seschrock if (sdp->sed_current != NULL) 1291940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1292940d71d2Seschrock 1293940d71d2Seschrock /* 1294940d71d2Seschrock * Go through the list of chassis we have seen so far and see 1295940d71d2Seschrock * if this serial number matches one of the known values. 1296*53dbcc59SSundeep Panicker * If so, check whether this enclosure is a subchassis. 1297940d71d2Seschrock */ 1298940d71d2Seschrock if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, 1299940d71d2Seschrock &csn) != 0) 1300940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1301940d71d2Seschrock 1302*53dbcc59SSundeep Panicker if (nvlist_lookup_string(props, LIBSES_EN_PROP_SUBCHASSIS_ID, 1303*53dbcc59SSundeep Panicker &lid) != 0) { 1304*53dbcc59SSundeep Panicker have_subchassis = B_FALSE; 1305*53dbcc59SSundeep Panicker if ((lid = topo_mod_zalloc(mod, sizeof (char))) == NULL) 1306*53dbcc59SSundeep Panicker goto error; 1307*53dbcc59SSundeep Panicker } 1308*53dbcc59SSundeep Panicker 1309*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "ses_enum_gather: Enclosure Node (%s) " 1310*53dbcc59SSundeep Panicker "CSN (%s), LID (%s)", sdp->sed_name, csn, lid); 1311*53dbcc59SSundeep Panicker 1312*53dbcc59SSundeep Panicker /* 1313*53dbcc59SSundeep Panicker * We need to determine whether this enclosure node 1314*53dbcc59SSundeep Panicker * represents a chassis or a subchassis. Since we may 1315*53dbcc59SSundeep Panicker * receive the enclosure nodes in a non-deterministic 1316*53dbcc59SSundeep Panicker * manner, we need to account for all possible combinations: 1317*53dbcc59SSundeep Panicker * 1. Chassis for the current CSN has not yet been 1318*53dbcc59SSundeep Panicker * allocated 1319*53dbcc59SSundeep Panicker * 1.1 This is a new chassis: 1320*53dbcc59SSundeep Panicker * allocate and instantiate the chassis 1321*53dbcc59SSundeep Panicker * 1.2 This is a new subchassis: 1322*53dbcc59SSundeep Panicker * allocate a placeholder chassis 1323*53dbcc59SSundeep Panicker * allocate and instantiate the subchassis 1324*53dbcc59SSundeep Panicker * link the subchassis to the chassis 1325*53dbcc59SSundeep Panicker * 2. Chassis for the current CSN has been allocated 1326*53dbcc59SSundeep Panicker * 2.1 This is a duplicate chassis enclosure 1327*53dbcc59SSundeep Panicker * check whether to override old chassis 1328*53dbcc59SSundeep Panicker * append to chassis' target list 1329*53dbcc59SSundeep Panicker * 2.2 Only placeholder chassis exists 1330*53dbcc59SSundeep Panicker * fill in the chassis fields 1331*53dbcc59SSundeep Panicker * 2.3 This is a new subchassis 1332*53dbcc59SSundeep Panicker * allocate and instantiate the subchassis 1333*53dbcc59SSundeep Panicker * link the subchassis to the chassis 1334*53dbcc59SSundeep Panicker * 2.4 This is a duplicate subchassis enclosure 1335*53dbcc59SSundeep Panicker * check whether to override old chassis 1336*53dbcc59SSundeep Panicker * append to chassis' target list 1337*53dbcc59SSundeep Panicker */ 1338*53dbcc59SSundeep Panicker 1339940d71d2Seschrock for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 1340*53dbcc59SSundeep Panicker cp = topo_list_next(cp)) 1341*53dbcc59SSundeep Panicker if (strcmp(cp->sec_csn, csn) == 0) 1342940d71d2Seschrock break; 1343940d71d2Seschrock 1344940d71d2Seschrock if (cp == NULL) { 1345*53dbcc59SSundeep Panicker /* 1. Haven't seen a chassis with this CSN before */ 1346940d71d2Seschrock 1347940d71d2Seschrock if ((cp = topo_mod_zalloc(mod, 1348940d71d2Seschrock sizeof (ses_enum_chassis_t))) == NULL) 1349940d71d2Seschrock goto error; 1350940d71d2Seschrock 1351940d71d2Seschrock cp->sec_csn = csn; 1352*53dbcc59SSundeep Panicker 1353*53dbcc59SSundeep Panicker if (!have_subchassis || strcmp(csn, lid) == 0) { 1354*53dbcc59SSundeep Panicker /* 1.1 This is a new chassis */ 1355*53dbcc59SSundeep Panicker 1356*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Initialize new " 1357*53dbcc59SSundeep Panicker "chassis with CSN (%s) and LID (%s)", 1358*53dbcc59SSundeep Panicker sdp->sed_name, csn, lid); 1359*53dbcc59SSundeep Panicker 1360*53dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, NULL, cp, 1361*53dbcc59SSundeep Panicker np, props, lid, SES_NEW_CHASSIS) < 0) 1362*53dbcc59SSundeep Panicker goto error; 1363*53dbcc59SSundeep Panicker } else { 1364*53dbcc59SSundeep Panicker /* 1.2 This is a new subchassis */ 1365*53dbcc59SSundeep Panicker 1366*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Initialize new " 1367*53dbcc59SSundeep Panicker "subchassis with CSN (%s) and LID (%s)", 1368*53dbcc59SSundeep Panicker sdp->sed_name, csn, lid); 1369*53dbcc59SSundeep Panicker 1370*53dbcc59SSundeep Panicker if ((scp = topo_mod_zalloc(mod, 1371*53dbcc59SSundeep Panicker sizeof (ses_enum_chassis_t))) == NULL) 1372*53dbcc59SSundeep Panicker goto error; 1373*53dbcc59SSundeep Panicker 1374*53dbcc59SSundeep Panicker scp->sec_csn = csn; 1375*53dbcc59SSundeep Panicker 1376*53dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, cp, scp, 1377*53dbcc59SSundeep Panicker np, props, lid, SES_NEW_SUBCHASSIS) < 0) 1378*53dbcc59SSundeep Panicker goto error; 1379*53dbcc59SSundeep Panicker } 13806efb64caSEric Schrock } else { 1381*53dbcc59SSundeep Panicker /* 2. We have a chassis with this CSN */ 1382*53dbcc59SSundeep Panicker 1383*53dbcc59SSundeep Panicker if (!have_subchassis || strcmp(csn, lid) == 0) { 1384*53dbcc59SSundeep Panicker /* This is a chassis */ 1385*53dbcc59SSundeep Panicker 1386*53dbcc59SSundeep Panicker if (!have_subchassis || 1387*53dbcc59SSundeep Panicker strlen(cp->sec_lid) > 0) { 1388*53dbcc59SSundeep Panicker /* 2.1 This is a duplicate chassis */ 1389*53dbcc59SSundeep Panicker 1390*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Append " 1391*53dbcc59SSundeep Panicker "duplicate chassis with CSN (%s) " 1392*53dbcc59SSundeep Panicker "and LID (%s)", 1393*53dbcc59SSundeep Panicker sdp->sed_name, csn, lid); 1394*53dbcc59SSundeep Panicker 1395*53dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, NULL, cp, 1396*53dbcc59SSundeep Panicker np, props, lid, 1397*53dbcc59SSundeep Panicker SES_DUP_CHASSIS) < 0) 1398*53dbcc59SSundeep Panicker goto error; 1399*53dbcc59SSundeep Panicker } else { 1400*53dbcc59SSundeep Panicker /* 2.2 Init the placeholder chassis */ 1401*53dbcc59SSundeep Panicker 1402*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Initialize" 1403*53dbcc59SSundeep Panicker "placeholder chassis with CSN (%s) " 1404*53dbcc59SSundeep Panicker "and LID (%s)", 1405*53dbcc59SSundeep Panicker sdp->sed_name, csn, lid); 1406*53dbcc59SSundeep Panicker 1407*53dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, NULL, cp, 1408*53dbcc59SSundeep Panicker np, props, lid, 1409*53dbcc59SSundeep Panicker SES_NEW_CHASSIS) < 0) 1410*53dbcc59SSundeep Panicker goto error; 1411*53dbcc59SSundeep Panicker 1412*53dbcc59SSundeep Panicker } 1413*53dbcc59SSundeep Panicker } else { 1414*53dbcc59SSundeep Panicker /* This is a subchassis */ 1415*53dbcc59SSundeep Panicker 1416*53dbcc59SSundeep Panicker for (scp = topo_list_next(&cp->sec_subchassis); 1417*53dbcc59SSundeep Panicker scp != NULL; scp = topo_list_next(scp)) 1418*53dbcc59SSundeep Panicker if (strcmp(scp->sec_lid, lid) == 0) 1419*53dbcc59SSundeep Panicker break; 1420*53dbcc59SSundeep Panicker 1421*53dbcc59SSundeep Panicker if (scp == NULL) { 1422*53dbcc59SSundeep Panicker /* 2.3 This is a new subchassis */ 1423*53dbcc59SSundeep Panicker 1424*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Initialize " 1425*53dbcc59SSundeep Panicker "new subchassis with CSN (%s) " 1426*53dbcc59SSundeep Panicker "and LID (%s)", 1427*53dbcc59SSundeep Panicker sdp->sed_name, csn, lid); 1428*53dbcc59SSundeep Panicker 1429*53dbcc59SSundeep Panicker if ((scp = topo_mod_zalloc(mod, 1430*53dbcc59SSundeep Panicker sizeof (ses_enum_chassis_t))) 1431*53dbcc59SSundeep Panicker == NULL) 1432*53dbcc59SSundeep Panicker goto error; 1433*53dbcc59SSundeep Panicker 1434*53dbcc59SSundeep Panicker scp->sec_csn = csn; 1435*53dbcc59SSundeep Panicker 1436*53dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, cp, scp, 1437*53dbcc59SSundeep Panicker np, props, lid, 1438*53dbcc59SSundeep Panicker SES_NEW_SUBCHASSIS) < 0) 1439*53dbcc59SSundeep Panicker goto error; 1440*53dbcc59SSundeep Panicker } else { 1441*53dbcc59SSundeep Panicker /* 2.4 This is a duplicate subchassis */ 1442*53dbcc59SSundeep Panicker 1443*53dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Append " 1444*53dbcc59SSundeep Panicker "duplicate subchassis with " 1445*53dbcc59SSundeep Panicker "CSN (%s) and LID (%s)", 1446*53dbcc59SSundeep Panicker sdp->sed_name, csn, lid); 1447*53dbcc59SSundeep Panicker 1448*53dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, cp, scp, 1449*53dbcc59SSundeep Panicker np, props, lid, 1450*53dbcc59SSundeep Panicker SES_DUP_SUBCHASSIS) < 0) 1451*53dbcc59SSundeep Panicker goto error; 1452*53dbcc59SSundeep Panicker } 14536efb64caSEric Schrock } 1454940d71d2Seschrock } 1455940d71d2Seschrock } else if (ses_node_type(np) == SES_NODE_ELEMENT) { 1456940d71d2Seschrock /* 1457940d71d2Seschrock * If we haven't yet seen an enclosure node and identified the 1458940d71d2Seschrock * current chassis, something is very wrong; bail out. 1459940d71d2Seschrock */ 1460940d71d2Seschrock if (sdp->sed_current == NULL) 1461940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1462940d71d2Seschrock 1463940d71d2Seschrock /* 1464940d71d2Seschrock * If this isn't one of the element types we care about, then 1465940d71d2Seschrock * ignore it. 1466940d71d2Seschrock */ 1467940d71d2Seschrock verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, 1468940d71d2Seschrock &type) == 0); 1469940d71d2Seschrock if (type != SES_ET_DEVICE && 1470940d71d2Seschrock type != SES_ET_ARRAY_DEVICE && 1471940d71d2Seschrock type != SES_ET_COOLING && 1472940d71d2Seschrock type != SES_ET_POWER_SUPPLY && 1473940d71d2Seschrock type != SES_ET_ESC_ELECTRONICS) 1474940d71d2Seschrock return (SES_WALK_ACTION_CONTINUE); 1475940d71d2Seschrock 1476940d71d2Seschrock /* 1477940d71d2Seschrock * Get the current instance number and see if we already know 1478940d71d2Seschrock * about this element. If so, it means we have multiple paths 1479940d71d2Seschrock * to the same elements, and we should ignore the current path. 1480940d71d2Seschrock */ 1481940d71d2Seschrock verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX, 1482940d71d2Seschrock &instance) == 0); 1483940d71d2Seschrock if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE) 1484940d71d2Seschrock (void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER, 1485940d71d2Seschrock &instance); 1486940d71d2Seschrock 1487940d71d2Seschrock cp = sdp->sed_current; 1488940d71d2Seschrock 1489940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1490940d71d2Seschrock snp = topo_list_next(snp)) { 1491940d71d2Seschrock if (snp->sen_type == type && 1492940d71d2Seschrock snp->sen_instance == instance) 1493d91236feSeschrock break; 1494d91236feSeschrock } 1495d91236feSeschrock 1496d91236feSeschrock /* 1497d91236feSeschrock * We prefer the new element under the following circumstances: 1498d91236feSeschrock * 1499d91236feSeschrock * - The currently known element's status is unknown or not 1500d91236feSeschrock * available, but the new element has a known status. This 1501d91236feSeschrock * occurs if a given element is only available through a 1502d91236feSeschrock * particular target. 1503d91236feSeschrock * 1504d91236feSeschrock * - This is an ESC_ELECTRONICS element, and the 'reported-via' 1505d91236feSeschrock * property is set. This allows us to get reliable firmware 1506d91236feSeschrock * revision information from the enclosure node. 1507d91236feSeschrock */ 1508d91236feSeschrock if (snp != NULL) { 1509d91236feSeschrock if (nvlist_lookup_uint64( 1510d91236feSeschrock ses_node_props(snp->sen_node), 1511d91236feSeschrock SES_PROP_STATUS_CODE, &prevstatus) != 0) 1512d91236feSeschrock prevstatus = SES_ESC_UNSUPPORTED; 1513d91236feSeschrock if (nvlist_lookup_uint64( 1514d91236feSeschrock props, SES_PROP_STATUS_CODE, &status) != 0) 1515d91236feSeschrock status = SES_ESC_UNSUPPORTED; 1516d91236feSeschrock if (nvlist_lookup_boolean_value( 1517d91236feSeschrock props, SES_PROP_REPORT, &report) != 0) 1518d91236feSeschrock report = B_FALSE; 1519d91236feSeschrock 1520d91236feSeschrock if ((SES_STATUS_UNAVAIL(prevstatus) && 1521d91236feSeschrock !SES_STATUS_UNAVAIL(status)) || 1522d91236feSeschrock (type == SES_ET_ESC_ELECTRONICS && 1523d91236feSeschrock report)) { 1524d91236feSeschrock snp->sen_node = np; 1525d91236feSeschrock snp->sen_target = sdp->sed_target; 1526d91236feSeschrock } 1527d91236feSeschrock 15280b32bb8bSEric Schrock if ((sap = topo_mod_zalloc(mod, 15290b32bb8bSEric Schrock sizeof (ses_alt_node_t))) == NULL) 15300b32bb8bSEric Schrock goto error; 15310b32bb8bSEric Schrock 15320b32bb8bSEric Schrock sap->san_node = np; 15330b32bb8bSEric Schrock topo_list_append(&snp->sen_alt_nodes, sap); 15340b32bb8bSEric Schrock 1535d91236feSeschrock return (SES_WALK_ACTION_CONTINUE); 1536940d71d2Seschrock } 1537940d71d2Seschrock 1538940d71d2Seschrock if ((snp = topo_mod_zalloc(mod, 1539940d71d2Seschrock sizeof (ses_enum_node_t))) == NULL) 1540940d71d2Seschrock goto error; 1541940d71d2Seschrock 15420b32bb8bSEric Schrock if ((sap = topo_mod_zalloc(mod, 15430b32bb8bSEric Schrock sizeof (ses_alt_node_t))) == NULL) { 15440b32bb8bSEric Schrock topo_mod_free(mod, snp, sizeof (ses_enum_node_t)); 15450b32bb8bSEric Schrock goto error; 15460b32bb8bSEric Schrock } 15470b32bb8bSEric Schrock 1548940d71d2Seschrock topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)", 1549940d71d2Seschrock sdp->sed_name, type, instance); 1550940d71d2Seschrock snp->sen_node = np; 1551940d71d2Seschrock snp->sen_type = type; 1552940d71d2Seschrock snp->sen_instance = instance; 1553940d71d2Seschrock snp->sen_target = sdp->sed_target; 15540b32bb8bSEric Schrock sap->san_node = np; 15550b32bb8bSEric Schrock topo_list_append(&snp->sen_alt_nodes, sap); 1556940d71d2Seschrock topo_list_append(&cp->sec_nodes, snp); 1557940d71d2Seschrock 1558940d71d2Seschrock if (type == SES_ET_DEVICE) 1559940d71d2Seschrock cp->sec_hasdev = B_TRUE; 1560940d71d2Seschrock } 1561940d71d2Seschrock 1562940d71d2Seschrock return (SES_WALK_ACTION_CONTINUE); 1563940d71d2Seschrock 1564940d71d2Seschrock error: 1565940d71d2Seschrock sdp->sed_errno = -1; 1566940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1567940d71d2Seschrock } 1568940d71d2Seschrock 1569940d71d2Seschrock static int 1570940d71d2Seschrock ses_process_dir(const char *dirpath, ses_enum_data_t *sdp) 1571940d71d2Seschrock { 1572940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 1573940d71d2Seschrock DIR *dir; 1574940d71d2Seschrock struct dirent *dp; 1575940d71d2Seschrock char path[PATH_MAX]; 1576940d71d2Seschrock ses_enum_target_t *stp; 1577940d71d2Seschrock int err = -1; 1578940d71d2Seschrock 1579940d71d2Seschrock /* 1580940d71d2Seschrock * Open the SES target directory and iterate over any available 1581940d71d2Seschrock * targets. 1582940d71d2Seschrock */ 1583940d71d2Seschrock if ((dir = opendir(dirpath)) == NULL) { 1584940d71d2Seschrock /* 1585940d71d2Seschrock * If the SES target directory does not exist, then return as if 1586940d71d2Seschrock * there are no active targets. 1587940d71d2Seschrock */ 1588940d71d2Seschrock topo_mod_dprintf(mod, "failed to open ses " 1589940d71d2Seschrock "directory '%s'", dirpath); 1590940d71d2Seschrock return (0); 1591940d71d2Seschrock } 1592940d71d2Seschrock 1593940d71d2Seschrock while ((dp = readdir(dir)) != NULL) { 1594940d71d2Seschrock if (strcmp(dp->d_name, ".") == 0 || 1595940d71d2Seschrock strcmp(dp->d_name, "..") == 0) 1596940d71d2Seschrock continue; 1597940d71d2Seschrock 1598940d71d2Seschrock /* 1599940d71d2Seschrock * Create a new target instance and take a snapshot. 1600940d71d2Seschrock */ 1601940d71d2Seschrock if ((stp = topo_mod_zalloc(mod, 1602940d71d2Seschrock sizeof (ses_enum_target_t))) == NULL) 1603940d71d2Seschrock goto error; 1604940d71d2Seschrock 16050b32bb8bSEric Schrock (void) pthread_mutex_init(&stp->set_lock, NULL); 16060b32bb8bSEric Schrock 1607940d71d2Seschrock (void) snprintf(path, sizeof (path), "%s/%s", dirpath, 1608940d71d2Seschrock dp->d_name); 1609940d71d2Seschrock 1610940d71d2Seschrock /* 1611940d71d2Seschrock * We keep track of the SES device path and export it on a 1612940d71d2Seschrock * per-node basis to allow higher level software to get to the 1613940d71d2Seschrock * corresponding SES state. 1614940d71d2Seschrock */ 1615940d71d2Seschrock if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) { 1616940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 1617940d71d2Seschrock goto error; 1618940d71d2Seschrock } 1619940d71d2Seschrock 1620940d71d2Seschrock if ((stp->set_target = 1621940d71d2Seschrock ses_open(LIBSES_VERSION, path)) == NULL) { 1622940d71d2Seschrock topo_mod_dprintf(mod, "failed to open ses target " 1623940d71d2Seschrock "'%s': %s", dp->d_name, ses_errmsg()); 1624940d71d2Seschrock topo_mod_strfree(mod, stp->set_devpath); 1625940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 1626940d71d2Seschrock continue; 1627940d71d2Seschrock } 1628940d71d2Seschrock 1629940d71d2Seschrock stp->set_refcount = 1; 1630940d71d2Seschrock sdp->sed_target = stp; 1631940d71d2Seschrock stp->set_snap = ses_snap_hold(stp->set_target); 1632940d71d2Seschrock if (gettimeofday(&stp->set_snaptime, NULL) != 0) 1633940d71d2Seschrock stp->set_snaptime.tv_sec = time(NULL); 1634940d71d2Seschrock 1635940d71d2Seschrock /* 1636940d71d2Seschrock * Enumerate over all SES elements and merge them into the 1637940d71d2Seschrock * correct ses_enum_chassis_t. 1638940d71d2Seschrock */ 1639940d71d2Seschrock sdp->sed_current = NULL; 1640940d71d2Seschrock sdp->sed_errno = 0; 1641940d71d2Seschrock sdp->sed_name = dp->d_name; 1642940d71d2Seschrock (void) ses_walk(stp->set_snap, ses_enum_gather, sdp); 1643940d71d2Seschrock 1644940d71d2Seschrock if (sdp->sed_errno != 0) 1645940d71d2Seschrock goto error; 1646940d71d2Seschrock } 1647940d71d2Seschrock 1648940d71d2Seschrock err = 0; 1649940d71d2Seschrock error: 1650940d71d2Seschrock closedir(dir); 1651940d71d2Seschrock return (err); 1652940d71d2Seschrock } 1653940d71d2Seschrock 1654940d71d2Seschrock static void 1655940d71d2Seschrock ses_release(topo_mod_t *mod, tnode_t *tn) 1656940d71d2Seschrock { 1657940d71d2Seschrock ses_enum_target_t *stp; 1658940d71d2Seschrock 1659940d71d2Seschrock if ((stp = topo_node_getspecific(tn)) != NULL) 1660940d71d2Seschrock ses_target_free(mod, stp); 1661940d71d2Seschrock } 1662940d71d2Seschrock 1663940d71d2Seschrock /*ARGSUSED*/ 1664940d71d2Seschrock static int 1665940d71d2Seschrock ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 1666940d71d2Seschrock topo_instance_t min, topo_instance_t max, void *arg, void *notused) 1667940d71d2Seschrock { 1668940d71d2Seschrock ses_enum_chassis_t *cp; 1669d91236feSeschrock ses_enum_data_t *data; 1670940d71d2Seschrock 1671940d71d2Seschrock /* 1672940d71d2Seschrock * Check to make sure we're being invoked sensibly, and that we're not 1673940d71d2Seschrock * being invoked as part of a post-processing step. 1674940d71d2Seschrock */ 1675d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0) 1676940d71d2Seschrock return (0); 1677940d71d2Seschrock 1678940d71d2Seschrock /* 1679d91236feSeschrock * If this is the first time we've called our enumeration method, then 1680d91236feSeschrock * gather information about any available enclosures. 1681940d71d2Seschrock */ 1682d91236feSeschrock if ((data = topo_mod_getspecific(mod)) == NULL) { 1683d91236feSeschrock if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) == 1684d91236feSeschrock NULL) 1685d91236feSeschrock return (-1); 1686940d71d2Seschrock 1687d91236feSeschrock data->sed_mod = mod; 1688d91236feSeschrock topo_mod_setspecific(mod, data); 1689d91236feSeschrock 1690d91236feSeschrock if (disk_list_gather(mod, &data->sed_disks) != 0) 1691d91236feSeschrock goto error; 1692d91236feSeschrock 1693d91236feSeschrock /* 1694d91236feSeschrock * We search both the ses(7D) and sgen(7D) locations, so we are 1695d91236feSeschrock * independent of any particular driver class bindings. 1696d91236feSeschrock */ 1697d91236feSeschrock if (ses_process_dir("/dev/es", data) != 0 || 1698d91236feSeschrock ses_process_dir("/dev/scsi/ses", data) != 0) 1699d91236feSeschrock goto error; 1700d91236feSeschrock } 1701d91236feSeschrock 1702d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) == 0) { 1703d91236feSeschrock /* 1704d91236feSeschrock * This is a request to enumerate external enclosures. Go 1705d91236feSeschrock * through all the targets and create chassis nodes where 1706d91236feSeschrock * necessary. 1707d91236feSeschrock */ 1708d91236feSeschrock for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 1709d91236feSeschrock cp = topo_list_next(cp)) { 1710d91236feSeschrock if (ses_create_chassis(data, rnode, cp) != 0) 1711d91236feSeschrock goto error; 1712d91236feSeschrock } 1713d91236feSeschrock } else { 1714d91236feSeschrock /* 1715d91236feSeschrock * This is a request to enumerate a specific bay underneath the 1716d91236feSeschrock * root chassis (for internal disks). 1717d91236feSeschrock */ 1718d91236feSeschrock if (ses_create_bays(data, rnode) != 0) 1719940d71d2Seschrock goto error; 1720940d71d2Seschrock } 1721940d71d2Seschrock 1722d91236feSeschrock /* 1723d91236feSeschrock * This is a bit of a kludge. In order to allow internal disks to be 1724d91236feSeschrock * enumerated and share snapshot-specific information with the external 1725d91236feSeschrock * enclosure enumeration, we rely on the fact that we will be invoked 1726d91236feSeschrock * for the 'ses-enclosure' node last. 1727d91236feSeschrock */ 1728d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) == 0) { 1729*53dbcc59SSundeep Panicker for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 1730*53dbcc59SSundeep Panicker cp = topo_list_next(cp)) 1731*53dbcc59SSundeep Panicker ses_data_free(data, cp); 1732*53dbcc59SSundeep Panicker ses_data_free(data, NULL); 1733d91236feSeschrock topo_mod_setspecific(mod, NULL); 1734d91236feSeschrock } 1735940d71d2Seschrock return (0); 1736940d71d2Seschrock 1737940d71d2Seschrock error: 1738*53dbcc59SSundeep Panicker for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 1739*53dbcc59SSundeep Panicker cp = topo_list_next(cp)) 1740*53dbcc59SSundeep Panicker ses_data_free(data, cp); 1741*53dbcc59SSundeep Panicker ses_data_free(data, NULL); 1742d91236feSeschrock topo_mod_setspecific(mod, NULL); 1743940d71d2Seschrock return (-1); 1744940d71d2Seschrock } 1745940d71d2Seschrock 1746940d71d2Seschrock static const topo_modops_t ses_ops = 1747940d71d2Seschrock { ses_enum, ses_release }; 1748940d71d2Seschrock 1749940d71d2Seschrock static topo_modinfo_t ses_info = 1750940d71d2Seschrock { SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops }; 1751940d71d2Seschrock 1752940d71d2Seschrock /*ARGSUSED*/ 1753940d71d2Seschrock int 1754940d71d2Seschrock _topo_init(topo_mod_t *mod, topo_version_t version) 1755940d71d2Seschrock { 1756940d71d2Seschrock if (getenv("TOPOSESDEBUG") != NULL) 1757940d71d2Seschrock topo_mod_setdebug(mod); 1758940d71d2Seschrock 1759940d71d2Seschrock topo_mod_dprintf(mod, "initializing %s enumerator\n", 1760940d71d2Seschrock SES_ENCLOSURE); 1761940d71d2Seschrock 1762940d71d2Seschrock return (topo_mod_register(mod, &ses_info, TOPO_VERSION)); 1763940d71d2Seschrock } 1764940d71d2Seschrock 1765940d71d2Seschrock void 1766940d71d2Seschrock _topo_fini(topo_mod_t *mod) 1767940d71d2Seschrock { 1768940d71d2Seschrock topo_mod_unregister(mod); 1769940d71d2Seschrock } 1770