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 /* 23*44ed9dbbSStephen Hanson * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24940d71d2Seschrock */ 25940d71d2Seschrock 266efb64caSEric Schrock #include <alloca.h> 27940d71d2Seschrock #include <dirent.h> 28940d71d2Seschrock #include <devid.h> 29940d71d2Seschrock #include <fm/libdiskstatus.h> 30940d71d2Seschrock #include <inttypes.h> 31940d71d2Seschrock #include <pthread.h> 32940d71d2Seschrock #include <strings.h> 33940d71d2Seschrock #include <unistd.h> 34940d71d2Seschrock #include <sys/dkio.h> 35940d71d2Seschrock #include <sys/fm/protocol.h> 36*44ed9dbbSStephen Hanson #include <sys/libdevid.h> 37940d71d2Seschrock #include <sys/scsi/scsi_types.h> 38d91236feSeschrock 39940d71d2Seschrock #include "disk.h" 40d91236feSeschrock #include "ses.h" 41940d71d2Seschrock 42940d71d2Seschrock #define SES_VERSION 1 43940d71d2Seschrock 44525b85dbSEric Schrock static int ses_snap_freq = 250; /* 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; 7353dbcc59SSundeep Panicker topo_list_t sec_subchassis; 74940d71d2Seschrock topo_list_t sec_nodes; 75940d71d2Seschrock topo_list_t sec_targets; 76940d71d2Seschrock const char *sec_csn; 7753dbcc59SSundeep Panicker const char *sec_lid; 78940d71d2Seschrock ses_node_t *sec_enclosure; 79940d71d2Seschrock ses_enum_target_t *sec_target; 80940d71d2Seschrock topo_instance_t sec_instance; 8153dbcc59SSundeep 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 9753dbcc59SSundeep Panicker typedef enum { 9853dbcc59SSundeep Panicker SES_NEW_CHASSIS = 0x1, 9953dbcc59SSundeep Panicker SES_NEW_SUBCHASSIS = 0x2, 10053dbcc59SSundeep Panicker SES_DUP_CHASSIS = 0x4, 10153dbcc59SSundeep Panicker SES_DUP_SUBCHASSIS = 0x8 10253dbcc59SSundeep Panicker } ses_chassis_type_e; 10353dbcc59SSundeep 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 14653dbcc59SSundeep 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; 15353dbcc59SSundeep Panicker topo_list_t *cpl; 15453dbcc59SSundeep Panicker 15553dbcc59SSundeep Panicker 15653dbcc59SSundeep Panicker if (pcp != NULL) 15753dbcc59SSundeep Panicker cpl = &pcp->sec_subchassis; 15853dbcc59SSundeep Panicker else 15953dbcc59SSundeep Panicker cpl = &sdp->sed_chassis; 160940d71d2Seschrock 16153dbcc59SSundeep Panicker while ((cp = topo_list_next(cpl)) != NULL) { 16253dbcc59SSundeep 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 18253dbcc59SSundeep Panicker if (pcp == NULL) { 18353dbcc59SSundeep Panicker disk_list_free(mod, &sdp->sed_disks); 18453dbcc59SSundeep Panicker topo_mod_free(mod, sdp, sizeof (ses_enum_data_t)); 18553dbcc59SSundeep 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 ses_enum_target_t *tp = topo_node_getspecific(tn); 302525b85dbSEric Schrock hrtime_t now; 303940d71d2Seschrock ses_snap_t *snap; 304940d71d2Seschrock int err; 305d91236feSeschrock uint64_t nodeid; 306940d71d2Seschrock ses_node_t *np; 307940d71d2Seschrock 308d91236feSeschrock if (tp == NULL) { 309d91236feSeschrock (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 310d91236feSeschrock return (NULL); 311d91236feSeschrock } 312940d71d2Seschrock 3130b32bb8bSEric Schrock (void) pthread_mutex_lock(&tp->set_lock); 3140b32bb8bSEric Schrock 315940d71d2Seschrock /* 316940d71d2Seschrock * Determine if we need to take a new snapshot. 317940d71d2Seschrock */ 318525b85dbSEric Schrock now = gethrtime(); 319940d71d2Seschrock 320525b85dbSEric Schrock if (now - tp->set_snaptime > (ses_snap_freq * 1000 * 1000) && 321940d71d2Seschrock (snap = ses_snap_new(tp->set_target)) != NULL) { 322940d71d2Seschrock if (ses_snap_generation(snap) != 323940d71d2Seschrock ses_snap_generation(tp->set_snap)) { 324940d71d2Seschrock /* 325940d71d2Seschrock * If we find ourselves in this situation, we're in 326940d71d2Seschrock * trouble. The generation count has changed, which 327940d71d2Seschrock * indicates that our current topology is out of date. 328940d71d2Seschrock * But we need to consult the new topology in order to 329940d71d2Seschrock * determine presence at this moment in time. We can't 330940d71d2Seschrock * go back and change the topo snapshot in situ, so 331d91236feSeschrock * we'll just have to fail the call in this unlikely 332d91236feSeschrock * scenario. 333940d71d2Seschrock */ 334940d71d2Seschrock ses_snap_rele(snap); 335d91236feSeschrock (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 3360b32bb8bSEric Schrock (void) pthread_mutex_unlock(&tp->set_lock); 337d91236feSeschrock return (NULL); 338940d71d2Seschrock } else { 339940d71d2Seschrock ses_snap_rele(tp->set_snap); 340940d71d2Seschrock tp->set_snap = snap; 341940d71d2Seschrock } 342525b85dbSEric Schrock tp->set_snaptime = gethrtime(); 343940d71d2Seschrock } 344940d71d2Seschrock 345940d71d2Seschrock snap = tp->set_snap; 346940d71d2Seschrock 347940d71d2Seschrock verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES, 348940d71d2Seschrock TOPO_PROP_NODE_ID, &nodeid, &err) == 0); 349940d71d2Seschrock verify((np = ses_node_lookup(snap, nodeid)) != NULL); 350d91236feSeschrock 351d91236feSeschrock return (np); 352d91236feSeschrock } 353d91236feSeschrock 3540b32bb8bSEric Schrock /*ARGSUSED*/ 3550b32bb8bSEric Schrock void 3560b32bb8bSEric Schrock ses_node_unlock(topo_mod_t *mod, tnode_t *tn) 3570b32bb8bSEric Schrock { 3580b32bb8bSEric Schrock ses_enum_target_t *tp = topo_node_getspecific(tn); 3590b32bb8bSEric Schrock 3600b32bb8bSEric Schrock verify(tp != NULL); 3610b32bb8bSEric Schrock 3620b32bb8bSEric Schrock (void) pthread_mutex_unlock(&tp->set_lock); 3630b32bb8bSEric Schrock } 3640b32bb8bSEric Schrock 365d91236feSeschrock /* 366d91236feSeschrock * Determine if the element is present. 367d91236feSeschrock */ 368d91236feSeschrock /*ARGSUSED*/ 369d91236feSeschrock static int 370d91236feSeschrock ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 371d91236feSeschrock nvlist_t *in, nvlist_t **out) 372d91236feSeschrock { 373d91236feSeschrock boolean_t present; 374d91236feSeschrock ses_node_t *np; 375d91236feSeschrock nvlist_t *props, *nvl; 376d91236feSeschrock uint64_t status; 377d91236feSeschrock 3780b32bb8bSEric Schrock if ((np = ses_node_lock(mod, tn)) == NULL) 379d91236feSeschrock return (-1); 380d91236feSeschrock 381940d71d2Seschrock verify((props = ses_node_props(np)) != NULL); 382940d71d2Seschrock verify(nvlist_lookup_uint64(props, 383940d71d2Seschrock SES_PROP_STATUS_CODE, &status) == 0); 384940d71d2Seschrock 3850b32bb8bSEric Schrock ses_node_unlock(mod, tn); 3860b32bb8bSEric Schrock 387940d71d2Seschrock present = (status != SES_ESC_NOT_INSTALLED); 388940d71d2Seschrock 389940d71d2Seschrock if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) 390940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 391940d71d2Seschrock 392940d71d2Seschrock if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET, 393940d71d2Seschrock present) != 0) { 394940d71d2Seschrock nvlist_free(nvl); 395940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 396940d71d2Seschrock } 397940d71d2Seschrock 398940d71d2Seschrock *out = nvl; 399940d71d2Seschrock 400940d71d2Seschrock return (0); 401940d71d2Seschrock } 402940d71d2Seschrock 403940d71d2Seschrock /* 404940d71d2Seschrock * Sets standard properties for a ses node (enclosure or bay). This includes 405940d71d2Seschrock * setting the FRU to be the same as the resource, as well as setting the 406940d71d2Seschrock * authority information. 407940d71d2Seschrock */ 408940d71d2Seschrock static int 409940d71d2Seschrock ses_set_standard_props(topo_mod_t *mod, tnode_t *tn, nvlist_t *auth, 410940d71d2Seschrock uint64_t nodeid, const char *path) 411940d71d2Seschrock { 412940d71d2Seschrock int err; 413940d71d2Seschrock char *product, *chassis; 414940d71d2Seschrock nvlist_t *fmri; 415940d71d2Seschrock topo_pgroup_info_t pgi; 416940d71d2Seschrock 417940d71d2Seschrock /* 418940d71d2Seschrock * Set the authority explicitly if specified. 419940d71d2Seschrock */ 420940d71d2Seschrock if (auth) { 421940d71d2Seschrock verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, 422940d71d2Seschrock &product) == 0); 423940d71d2Seschrock verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, 424940d71d2Seschrock &chassis) == 0); 425940d71d2Seschrock if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 426940d71d2Seschrock FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product, 427940d71d2Seschrock &err) != 0 || 428940d71d2Seschrock topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 429940d71d2Seschrock FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis, 430940d71d2Seschrock &err) != 0 || 431940d71d2Seschrock topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 432940d71d2Seschrock FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "", 433940d71d2Seschrock &err) != 0) { 434940d71d2Seschrock topo_mod_dprintf(mod, "failed to add authority " 435d91236feSeschrock "properties: %s\n", topo_strerror(err)); 436940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 437940d71d2Seschrock } 438940d71d2Seschrock } 439940d71d2Seschrock 440940d71d2Seschrock /* 441940d71d2Seschrock * Copy the resource and set that as the FRU. 442940d71d2Seschrock */ 443940d71d2Seschrock if (topo_node_resource(tn, &fmri, &err) != 0) { 444940d71d2Seschrock topo_mod_dprintf(mod, 445940d71d2Seschrock "topo_node_resource() failed : %s\n", 446940d71d2Seschrock topo_strerror(err)); 447940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 448940d71d2Seschrock } 449940d71d2Seschrock 450940d71d2Seschrock if (topo_node_fru_set(tn, fmri, 0, &err) != 0) { 451940d71d2Seschrock topo_mod_dprintf(mod, 452940d71d2Seschrock "topo_node_fru_set() failed : %s\n", 453940d71d2Seschrock topo_strerror(err)); 454940d71d2Seschrock nvlist_free(fmri); 455940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 456940d71d2Seschrock } 457940d71d2Seschrock 458940d71d2Seschrock nvlist_free(fmri); 459940d71d2Seschrock 460940d71d2Seschrock /* 461940d71d2Seschrock * Set the SES-specific properties so that consumers can query 462940d71d2Seschrock * additional information about the particular SES element. 463940d71d2Seschrock */ 464940d71d2Seschrock pgi.tpi_name = TOPO_PGROUP_SES; 465940d71d2Seschrock pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 466940d71d2Seschrock pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 467940d71d2Seschrock pgi.tpi_version = TOPO_VERSION; 468940d71d2Seschrock if (topo_pgroup_create(tn, &pgi, &err) != 0) { 469940d71d2Seschrock topo_mod_dprintf(mod, "failed to create propgroup " 470940d71d2Seschrock "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err)); 471940d71d2Seschrock return (-1); 472940d71d2Seschrock } 473940d71d2Seschrock 474940d71d2Seschrock if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES, 475940d71d2Seschrock TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE, 476940d71d2Seschrock nodeid, &err) != 0) { 477940d71d2Seschrock topo_mod_dprintf(mod, 478940d71d2Seschrock "failed to create property %s: %s\n", 479940d71d2Seschrock TOPO_PROP_NODE_ID, topo_strerror(err)); 480940d71d2Seschrock return (-1); 481940d71d2Seschrock } 482940d71d2Seschrock 483940d71d2Seschrock if (topo_prop_set_string(tn, TOPO_PGROUP_SES, 484940d71d2Seschrock TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE, 485940d71d2Seschrock path, &err) != 0) { 486940d71d2Seschrock topo_mod_dprintf(mod, 487940d71d2Seschrock "failed to create property %s: %s\n", 488940d71d2Seschrock TOPO_PROP_TARGET_PATH, topo_strerror(err)); 489940d71d2Seschrock return (-1); 490940d71d2Seschrock } 491940d71d2Seschrock 492940d71d2Seschrock return (0); 493940d71d2Seschrock } 494940d71d2Seschrock 495940d71d2Seschrock /* 496940d71d2Seschrock * Callback to add a disk to a given bay. We first check the status-code to 497940d71d2Seschrock * determine if a disk is present, ignoring those that aren't in an appropriate 4980b32bb8bSEric Schrock * state. We then scan the parent bay node's SAS address array to determine 4990b32bb8bSEric Schrock * possible attached SAS addresses. We create a disk node if the disk is not 5000b32bb8bSEric Schrock * SAS or the SES target does not support the necessary pages for this; if we 5010b32bb8bSEric Schrock * find the SAS address, we create a disk node and also correlate it with 5020b32bb8bSEric Schrock * the corresponding Solaris device node to fill in the rest of the data. 503940d71d2Seschrock */ 504940d71d2Seschrock static int 505940d71d2Seschrock ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props) 506940d71d2Seschrock { 507940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 508940d71d2Seschrock uint64_t status; 509940d71d2Seschrock nvlist_t **sas; 510940d71d2Seschrock uint_t s, nsas; 5110b32bb8bSEric Schrock char **paths; 512*44ed9dbbSStephen Hanson int err, ret; 513*44ed9dbbSStephen Hanson tnode_t *child = NULL; 514940d71d2Seschrock 515940d71d2Seschrock /* 516940d71d2Seschrock * Skip devices that are not in a present (and possibly damaged) state. 517940d71d2Seschrock */ 518940d71d2Seschrock if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0) 519940d71d2Seschrock return (0); 520940d71d2Seschrock 521940d71d2Seschrock if (status != SES_ESC_OK && 522940d71d2Seschrock status != SES_ESC_CRITICAL && 523940d71d2Seschrock status != SES_ESC_NONCRITICAL && 524940d71d2Seschrock status != SES_ESC_UNRECOVERABLE && 525940d71d2Seschrock status != SES_ESC_NO_ACCESS) 526940d71d2Seschrock return (0); 527940d71d2Seschrock 528940d71d2Seschrock topo_mod_dprintf(mod, "found attached disk"); 529940d71d2Seschrock 530940d71d2Seschrock /* 531940d71d2Seschrock * Create the disk range. 532940d71d2Seschrock */ 5330b32bb8bSEric Schrock if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) { 534940d71d2Seschrock topo_mod_dprintf(mod, 535940d71d2Seschrock "topo_node_create_range() failed: %s", 536940d71d2Seschrock topo_mod_errmsg(mod)); 537940d71d2Seschrock return (-1); 538940d71d2Seschrock } 539940d71d2Seschrock 540940d71d2Seschrock /* 541940d71d2Seschrock * Look through all SAS addresses and attempt to correlate them to a 542940d71d2Seschrock * known Solaris device. If we don't find a matching node, then we 543940d71d2Seschrock * don't enumerate the disk node. 544940d71d2Seschrock */ 545940d71d2Seschrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 546940d71d2Seschrock &sas, &nsas) != 0) 547940d71d2Seschrock return (0); 548940d71d2Seschrock 5490b32bb8bSEric Schrock if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES, 5500b32bb8bSEric Schrock TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0) 5510b32bb8bSEric Schrock return (0); 5520b32bb8bSEric Schrock 5530b32bb8bSEric Schrock err = 0; 5540b32bb8bSEric Schrock 555940d71d2Seschrock for (s = 0; s < nsas; s++) { 556*44ed9dbbSStephen Hanson ret = disk_declare_addr(mod, pnode, &sdp->sed_disks, paths[s], 557*44ed9dbbSStephen Hanson &child); 558*44ed9dbbSStephen Hanson if (ret == 0) { 559*44ed9dbbSStephen Hanson break; 560*44ed9dbbSStephen Hanson } else if (ret < 0) { 5610b32bb8bSEric Schrock err = -1; 5620b32bb8bSEric Schrock break; 5630b32bb8bSEric Schrock } 5640b32bb8bSEric Schrock } 5650b32bb8bSEric Schrock 566*44ed9dbbSStephen Hanson if (s == nsas) 567*44ed9dbbSStephen Hanson disk_declare_non_enumerated(mod, pnode, &child); 568*44ed9dbbSStephen Hanson 569*44ed9dbbSStephen Hanson /* copy sas_addresses (target-ports) from parent (with 'w'added) */ 570*44ed9dbbSStephen Hanson if (child != NULL) { 571*44ed9dbbSStephen Hanson int i; 572*44ed9dbbSStephen Hanson char **tports; 573*44ed9dbbSStephen Hanson uint64_t wwn; 574*44ed9dbbSStephen Hanson 575*44ed9dbbSStephen Hanson tports = topo_mod_zalloc(mod, sizeof (char *) * nsas); 576*44ed9dbbSStephen Hanson if (tports != NULL) { 577*44ed9dbbSStephen Hanson for (i = 0; i < nsas; i++) { 578*44ed9dbbSStephen Hanson if (scsi_wwnstr_to_wwn(paths[i], &wwn) != 579*44ed9dbbSStephen Hanson DDI_SUCCESS) 580*44ed9dbbSStephen Hanson break; 581*44ed9dbbSStephen Hanson tports[i] = scsi_wwn_to_wwnstr(wwn, 1, NULL); 582*44ed9dbbSStephen Hanson if (tports[i] == NULL) 583*44ed9dbbSStephen Hanson break; 584*44ed9dbbSStephen Hanson } 585*44ed9dbbSStephen Hanson /* if they all worked then create the property */ 586*44ed9dbbSStephen Hanson if (i == nsas) 587*44ed9dbbSStephen Hanson (void) topo_prop_set_string_array(child, 588*44ed9dbbSStephen Hanson TOPO_PGROUP_STORAGE, 589*44ed9dbbSStephen Hanson TOPO_STORAGE_TARGET_PORT_L0IDS, 590*44ed9dbbSStephen Hanson TOPO_PROP_IMMUTABLE, (const char **)tports, 591*44ed9dbbSStephen Hanson nsas, &err); 592*44ed9dbbSStephen Hanson 593*44ed9dbbSStephen Hanson for (i = 0; i < nsas; i++) 594*44ed9dbbSStephen Hanson if (tports[i] != NULL) 595*44ed9dbbSStephen Hanson scsi_free_wwnstr(tports[i]); 596*44ed9dbbSStephen Hanson topo_mod_free(mod, tports, sizeof (char *) * nsas); 597*44ed9dbbSStephen Hanson } 598*44ed9dbbSStephen Hanson } 599*44ed9dbbSStephen Hanson 6000b32bb8bSEric Schrock for (s = 0; s < nsas; s++) 6010b32bb8bSEric Schrock topo_mod_free(mod, paths[s], strlen(paths[s]) + 1); 6020b32bb8bSEric Schrock topo_mod_free(mod, paths, nsas * sizeof (char *)); 6030b32bb8bSEric Schrock 6040b32bb8bSEric Schrock return (err); 6050b32bb8bSEric Schrock } 6060b32bb8bSEric Schrock 6070b32bb8bSEric Schrock static int 6080b32bb8bSEric Schrock ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp) 6090b32bb8bSEric Schrock { 6100b32bb8bSEric Schrock ses_alt_node_t *ap; 6110b32bb8bSEric Schrock ses_node_t *np; 6120b32bb8bSEric Schrock nvlist_t *props; 6130b32bb8bSEric Schrock 6140b32bb8bSEric Schrock nvlist_t **phys; 6150b32bb8bSEric Schrock uint_t i, j, n_phys, all_phys = 0; 6160b32bb8bSEric Schrock char **paths; 6170b32bb8bSEric Schrock uint64_t addr; 6180b32bb8bSEric Schrock size_t len; 6190b32bb8bSEric Schrock int terr, err = -1; 6200b32bb8bSEric Schrock 6210b32bb8bSEric Schrock for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 6220b32bb8bSEric Schrock ap = topo_list_next(ap)) { 6230b32bb8bSEric Schrock np = ap->san_node; 6240b32bb8bSEric Schrock props = ses_node_props(np); 6250b32bb8bSEric Schrock 6260b32bb8bSEric Schrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 6270b32bb8bSEric Schrock &phys, &n_phys) != 0) 6280b32bb8bSEric Schrock continue; 6290b32bb8bSEric Schrock 6300b32bb8bSEric Schrock all_phys += n_phys; 6310b32bb8bSEric Schrock } 6320b32bb8bSEric Schrock 6330b32bb8bSEric Schrock if (all_phys == 0) 6340b32bb8bSEric Schrock return (0); 6350b32bb8bSEric Schrock 6360b32bb8bSEric Schrock if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL) 6370b32bb8bSEric Schrock return (-1); 6380b32bb8bSEric Schrock 6390b32bb8bSEric Schrock for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 6400b32bb8bSEric Schrock ap = topo_list_next(ap)) { 6410b32bb8bSEric Schrock np = ap->san_node; 6420b32bb8bSEric Schrock props = ses_node_props(np); 6430b32bb8bSEric Schrock 6440b32bb8bSEric Schrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 6450b32bb8bSEric Schrock &phys, &n_phys) != 0) 646940d71d2Seschrock continue; 647940d71d2Seschrock 6480b32bb8bSEric Schrock for (j = 0; j < n_phys; j++) { 6490b32bb8bSEric Schrock if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR, 6500b32bb8bSEric Schrock &addr) != 0) 6510b32bb8bSEric Schrock continue; 652940d71d2Seschrock 6530b32bb8bSEric Schrock len = snprintf(NULL, 0, "%016llx", addr) + 1; 6540b32bb8bSEric Schrock if ((paths[i] = topo_mod_alloc(mod, len)) == NULL) 6550b32bb8bSEric Schrock goto error; 6560b32bb8bSEric Schrock 6570b32bb8bSEric Schrock (void) snprintf(paths[i], len, "%016llx", addr); 6580b32bb8bSEric Schrock 6590b32bb8bSEric Schrock ++i; 6600b32bb8bSEric Schrock } 661940d71d2Seschrock } 662940d71d2Seschrock 6630b32bb8bSEric Schrock err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 6640b32bb8bSEric Schrock TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, 6650b32bb8bSEric Schrock (const char **)paths, i, &terr); 6660b32bb8bSEric Schrock if (err != 0) 6670b32bb8bSEric Schrock err = topo_mod_seterrno(mod, terr); 6680b32bb8bSEric Schrock 6690b32bb8bSEric Schrock error: 6700b32bb8bSEric Schrock for (i = 0; i < all_phys && paths[i] != NULL; i++) 6710b32bb8bSEric Schrock topo_mod_free(mod, paths[i], strlen(paths[i]) + 1); 6720b32bb8bSEric Schrock topo_mod_free(mod, paths, all_phys * sizeof (char *)); 6730b32bb8bSEric Schrock 6740b32bb8bSEric Schrock return (err); 675940d71d2Seschrock } 676940d71d2Seschrock 677940d71d2Seschrock /* 678940d71d2Seschrock * Callback to create a basic node (bay, psu, fan, or controller). 679940d71d2Seschrock */ 680940d71d2Seschrock static int 681940d71d2Seschrock ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp, 682940d71d2Seschrock tnode_t *pnode, const char *nodename, const char *labelname) 683940d71d2Seschrock { 684940d71d2Seschrock ses_node_t *np = snp->sen_node; 685d91236feSeschrock ses_node_t *parent; 686940d71d2Seschrock uint64_t instance = snp->sen_instance; 687940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 688940d71d2Seschrock nvlist_t *props, *aprops; 689940d71d2Seschrock nvlist_t *auth = NULL, *fmri = NULL; 690940d71d2Seschrock tnode_t *tn; 691940d71d2Seschrock char label[128]; 692940d71d2Seschrock int err; 693d91236feSeschrock char *part = NULL, *serial = NULL, *revision = NULL; 694d91236feSeschrock char *desc; 695d91236feSeschrock boolean_t report; 696940d71d2Seschrock 697940d71d2Seschrock props = ses_node_props(np); 698940d71d2Seschrock 699940d71d2Seschrock (void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part); 700940d71d2Seschrock (void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial); 701940d71d2Seschrock 702940d71d2Seschrock topo_mod_dprintf(mod, "adding %s %llu", nodename, instance); 703940d71d2Seschrock 704940d71d2Seschrock /* 705940d71d2Seschrock * Create the node. The interesting information is all copied from the 706940d71d2Seschrock * parent enclosure node, so there is not much to do. 707940d71d2Seschrock */ 708940d71d2Seschrock if ((auth = topo_mod_auth(mod, pnode)) == NULL) 709940d71d2Seschrock goto error; 710940d71d2Seschrock 711d91236feSeschrock /* 712d91236feSeschrock * We want to report revision information for the controller nodes, but 713d91236feSeschrock * we do not get per-element revision information. However, we do have 714d91236feSeschrock * revision information for the entire enclosure, and we can use the 715d91236feSeschrock * 'reported-via' property to know that this controller corresponds to 716d91236feSeschrock * the given revision information. This means we cannot get revision 717d91236feSeschrock * information for targets we are not explicitly connected to, but 718d91236feSeschrock * there is little we can do about the situation. 719d91236feSeschrock */ 720d91236feSeschrock if (strcmp(nodename, CONTROLLER) == 0 && 721d91236feSeschrock nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 && 722d91236feSeschrock report) { 723d91236feSeschrock for (parent = ses_node_parent(np); parent != NULL; 724d91236feSeschrock parent = ses_node_parent(parent)) { 725d91236feSeschrock if (ses_node_type(parent) == SES_NODE_ENCLOSURE) { 726d91236feSeschrock (void) nvlist_lookup_string( 727d91236feSeschrock ses_node_props(parent), 728d91236feSeschrock SES_EN_PROP_REV, &revision); 729d91236feSeschrock break; 730d91236feSeschrock } 731d91236feSeschrock } 732d91236feSeschrock } 733d91236feSeschrock 734940d71d2Seschrock if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 735d91236feSeschrock nodename, (topo_instance_t)instance, NULL, auth, part, revision, 736940d71d2Seschrock serial)) == NULL) { 737940d71d2Seschrock topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 738940d71d2Seschrock topo_mod_errmsg(mod)); 739940d71d2Seschrock goto error; 740940d71d2Seschrock } 741940d71d2Seschrock 742940d71d2Seschrock if ((tn = topo_node_bind(mod, pnode, nodename, 743940d71d2Seschrock instance, fmri)) == NULL) { 744940d71d2Seschrock topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 745940d71d2Seschrock topo_mod_errmsg(mod)); 746940d71d2Seschrock goto error; 747940d71d2Seschrock } 748940d71d2Seschrock 749940d71d2Seschrock /* 750d91236feSeschrock * For the node label, we look for the following in order: 751d91236feSeschrock * 752d91236feSeschrock * <ses-description> 753d91236feSeschrock * <ses-class-description> <instance> 754d91236feSeschrock * <default-type-label> <instance> 755940d71d2Seschrock */ 756d91236feSeschrock if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 || 757d91236feSeschrock desc[0] == '\0') { 758d91236feSeschrock parent = ses_node_parent(np); 759d91236feSeschrock aprops = ses_node_props(parent); 760d91236feSeschrock if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION, 761d91236feSeschrock &desc) == 0 && desc[0] != '\0') 762d91236feSeschrock labelname = desc; 763d91236feSeschrock (void) snprintf(label, sizeof (label), "%s %llu", desc, 764d91236feSeschrock instance); 765d91236feSeschrock desc = label; 766d91236feSeschrock } 767d91236feSeschrock 768d91236feSeschrock if (topo_node_label_set(tn, desc, &err) != 0) 769940d71d2Seschrock goto error; 770940d71d2Seschrock 771940d71d2Seschrock if (ses_set_standard_props(mod, tn, NULL, ses_node_id(np), 772940d71d2Seschrock snp->sen_target->set_devpath) != 0) 773940d71d2Seschrock goto error; 774940d71d2Seschrock 775940d71d2Seschrock if (strcmp(nodename, "bay") == 0) { 7760b32bb8bSEric Schrock if (ses_add_bay_props(mod, tn, snp) != 0) 7770b32bb8bSEric Schrock goto error; 7780b32bb8bSEric Schrock 779940d71d2Seschrock if (ses_create_disk(sdp, tn, props) != 0) 780940d71d2Seschrock goto error; 781d91236feSeschrock 782d91236feSeschrock if (topo_method_register(mod, tn, ses_bay_methods) != 0) { 783d91236feSeschrock topo_mod_dprintf(mod, 784d91236feSeschrock "topo_method_register() failed: %s", 785d91236feSeschrock topo_mod_errmsg(mod)); 786d91236feSeschrock goto error; 787d91236feSeschrock } 788940d71d2Seschrock } else { 789940d71d2Seschrock /* 790d91236feSeschrock * Only fan, psu, and controller nodes have a 'present' method. 791d91236feSeschrock * Bay nodes are always present, and disk nodes are present by 792d91236feSeschrock * virtue of being enumerated. 793940d71d2Seschrock */ 794940d71d2Seschrock if (topo_method_register(mod, tn, ses_component_methods) != 0) { 795940d71d2Seschrock topo_mod_dprintf(mod, 796940d71d2Seschrock "topo_method_register() failed: %s", 797940d71d2Seschrock topo_mod_errmsg(mod)); 798940d71d2Seschrock goto error; 799940d71d2Seschrock } 800940d71d2Seschrock 801940d71d2Seschrock } 802940d71d2Seschrock 803d91236feSeschrock snp->sen_target->set_refcount++; 804d91236feSeschrock topo_node_setspecific(tn, snp->sen_target); 805d91236feSeschrock 806940d71d2Seschrock nvlist_free(auth); 807940d71d2Seschrock nvlist_free(fmri); 808940d71d2Seschrock return (0); 809940d71d2Seschrock 810940d71d2Seschrock error: 811940d71d2Seschrock nvlist_free(auth); 812940d71d2Seschrock nvlist_free(fmri); 813940d71d2Seschrock return (-1); 814940d71d2Seschrock } 815940d71d2Seschrock 816940d71d2Seschrock /* 817940d71d2Seschrock * Instantiate any children of a given type. 818940d71d2Seschrock */ 819940d71d2Seschrock static int 820940d71d2Seschrock ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type, 821d91236feSeschrock const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp, 822d91236feSeschrock boolean_t dorange) 823940d71d2Seschrock { 824940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 825940d71d2Seschrock boolean_t found; 826940d71d2Seschrock uint64_t max; 827940d71d2Seschrock ses_enum_node_t *snp; 828940d71d2Seschrock 829940d71d2Seschrock /* 830940d71d2Seschrock * First go through and count how many matching nodes we have. 831940d71d2Seschrock */ 832940d71d2Seschrock max = 0; 833940d71d2Seschrock found = B_FALSE; 834940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 835940d71d2Seschrock snp = topo_list_next(snp)) { 836940d71d2Seschrock if (snp->sen_type == type) { 837940d71d2Seschrock found = B_TRUE; 838940d71d2Seschrock if (snp->sen_instance > max) 839940d71d2Seschrock max = snp->sen_instance; 840940d71d2Seschrock } 841940d71d2Seschrock } 842940d71d2Seschrock 843940d71d2Seschrock /* 844940d71d2Seschrock * No enclosure should export both DEVICE and ARRAY_DEVICE elements. 845940d71d2Seschrock * Since we map both of these to 'disk', if an enclosure does this, we 846940d71d2Seschrock * just ignore the array elements. 847940d71d2Seschrock */ 848940d71d2Seschrock if (!found || 849940d71d2Seschrock (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev)) 850940d71d2Seschrock return (0); 851940d71d2Seschrock 852940d71d2Seschrock topo_mod_dprintf(mod, "%s: creating %llu %s nodes", 85353dbcc59SSundeep Panicker cp->sec_csn, max + 1, nodename); 854940d71d2Seschrock 855d91236feSeschrock if (dorange && topo_node_range_create(mod, pnode, 856940d71d2Seschrock nodename, 0, max) != 0) { 857940d71d2Seschrock topo_mod_dprintf(mod, 858940d71d2Seschrock "topo_node_create_range() failed: %s", 859940d71d2Seschrock topo_mod_errmsg(mod)); 860940d71d2Seschrock return (-1); 861940d71d2Seschrock } 862940d71d2Seschrock 863940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 864940d71d2Seschrock snp = topo_list_next(snp)) { 865940d71d2Seschrock if (snp->sen_type == type) { 866940d71d2Seschrock if (ses_create_generic(sdp, snp, pnode, 867940d71d2Seschrock nodename, defaultlabel) != 0) 868940d71d2Seschrock return (-1); 869940d71d2Seschrock } 870940d71d2Seschrock } 871940d71d2Seschrock 872940d71d2Seschrock return (0); 873940d71d2Seschrock } 874940d71d2Seschrock 87553dbcc59SSundeep Panicker /* 87653dbcc59SSundeep Panicker * Instantiate a new subchassis instance in the topology. 87753dbcc59SSundeep Panicker */ 87853dbcc59SSundeep Panicker static int 87953dbcc59SSundeep Panicker ses_create_subchassis(ses_enum_data_t *sdp, tnode_t *pnode, 88053dbcc59SSundeep Panicker ses_enum_chassis_t *scp) 88153dbcc59SSundeep Panicker { 88253dbcc59SSundeep Panicker topo_mod_t *mod = sdp->sed_mod; 88353dbcc59SSundeep Panicker tnode_t *tn; 88453dbcc59SSundeep Panicker nvlist_t *props; 88553dbcc59SSundeep Panicker nvlist_t *auth = NULL, *fmri = NULL; 88653dbcc59SSundeep Panicker char *part = NULL, *revision = NULL; 88753dbcc59SSundeep Panicker uint64_t instance = scp->sec_instance; 88853dbcc59SSundeep Panicker char *desc; 88953dbcc59SSundeep Panicker char label[128]; 89053dbcc59SSundeep Panicker char **paths; 89153dbcc59SSundeep Panicker int i, err; 89253dbcc59SSundeep Panicker ses_enum_target_t *stp; 89353dbcc59SSundeep Panicker int ret = -1; 89453dbcc59SSundeep Panicker 89553dbcc59SSundeep Panicker /* 89653dbcc59SSundeep Panicker * Copy authority information from parent enclosure node 89753dbcc59SSundeep Panicker */ 89853dbcc59SSundeep Panicker if ((auth = topo_mod_auth(mod, pnode)) == NULL) 89953dbcc59SSundeep Panicker goto error; 90053dbcc59SSundeep Panicker 90153dbcc59SSundeep Panicker /* 90253dbcc59SSundeep Panicker * Record the subchassis serial number in the FMRI. 90353dbcc59SSundeep Panicker * For now, we assume that logical id is the subchassis serial number. 90453dbcc59SSundeep Panicker * If this assumption changes in future, then the following 90553dbcc59SSundeep Panicker * piece of code will need to be updated via an RFE. 90653dbcc59SSundeep Panicker */ 90753dbcc59SSundeep Panicker if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 90853dbcc59SSundeep Panicker SUBCHASSIS, (topo_instance_t)instance, NULL, auth, part, revision, 90953dbcc59SSundeep Panicker scp->sec_lid)) == NULL) { 91053dbcc59SSundeep Panicker topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 91153dbcc59SSundeep Panicker topo_mod_errmsg(mod)); 91253dbcc59SSundeep Panicker goto error; 91353dbcc59SSundeep Panicker } 91453dbcc59SSundeep Panicker 91553dbcc59SSundeep Panicker if ((tn = topo_node_bind(mod, pnode, SUBCHASSIS, 91653dbcc59SSundeep Panicker instance, fmri)) == NULL) { 91753dbcc59SSundeep Panicker topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 91853dbcc59SSundeep Panicker topo_mod_errmsg(mod)); 91953dbcc59SSundeep Panicker goto error; 92053dbcc59SSundeep Panicker } 92153dbcc59SSundeep Panicker 92253dbcc59SSundeep Panicker props = ses_node_props(scp->sec_enclosure); 92353dbcc59SSundeep Panicker 92453dbcc59SSundeep Panicker /* 92553dbcc59SSundeep Panicker * Look for the subchassis label in the following order: 92653dbcc59SSundeep Panicker * <ses-description> 92753dbcc59SSundeep Panicker * <ses-class-description> <instance> 92853dbcc59SSundeep Panicker * <default-type-label> <instance> 92953dbcc59SSundeep Panicker * 93053dbcc59SSundeep Panicker * For subchassis, the default label is "SUBCHASSIS" 93153dbcc59SSundeep Panicker */ 93253dbcc59SSundeep Panicker if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 || 93353dbcc59SSundeep Panicker desc[0] == '\0') { 93453dbcc59SSundeep Panicker if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION, 93553dbcc59SSundeep Panicker &desc) == 0 && desc[0] != '\0') 93653dbcc59SSundeep Panicker (void) snprintf(label, sizeof (label), "%s %llu", desc, 93753dbcc59SSundeep Panicker instance); 93853dbcc59SSundeep Panicker else 93953dbcc59SSundeep Panicker (void) snprintf(label, sizeof (label), 94053dbcc59SSundeep Panicker "SUBCHASSIS %llu", instance); 94153dbcc59SSundeep Panicker desc = label; 94253dbcc59SSundeep Panicker } 94353dbcc59SSundeep Panicker 94453dbcc59SSundeep Panicker if (topo_node_label_set(tn, desc, &err) != 0) 94553dbcc59SSundeep Panicker goto error; 94653dbcc59SSundeep Panicker 94753dbcc59SSundeep Panicker if (ses_set_standard_props(mod, tn, NULL, 94853dbcc59SSundeep Panicker ses_node_id(scp->sec_enclosure), scp->sec_target->set_devpath) != 0) 94953dbcc59SSundeep Panicker goto error; 95053dbcc59SSundeep Panicker 95153dbcc59SSundeep Panicker /* 95253dbcc59SSundeep Panicker * For enclosures, we want to include all possible targets (for upgrade 95353dbcc59SSundeep Panicker * purposes). 95453dbcc59SSundeep Panicker */ 95553dbcc59SSundeep Panicker for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL; 95653dbcc59SSundeep Panicker stp = topo_list_next(stp), i++) 95753dbcc59SSundeep Panicker ; 95853dbcc59SSundeep Panicker 95953dbcc59SSundeep Panicker verify(i != 0); 96053dbcc59SSundeep Panicker paths = alloca(i * sizeof (char *)); 96153dbcc59SSundeep Panicker 96253dbcc59SSundeep Panicker for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL; 96353dbcc59SSundeep Panicker stp = topo_list_next(stp), i++) 96453dbcc59SSundeep Panicker paths[i] = stp->set_devpath; 96553dbcc59SSundeep Panicker 96653dbcc59SSundeep Panicker if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 96753dbcc59SSundeep Panicker TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths, 96853dbcc59SSundeep Panicker i, &err) != 0) { 96953dbcc59SSundeep Panicker topo_mod_dprintf(mod, "failed to create property %s: %s\n", 97053dbcc59SSundeep Panicker TOPO_PROP_PATHS, topo_strerror(err)); 97153dbcc59SSundeep Panicker goto error; 97253dbcc59SSundeep Panicker } 97353dbcc59SSundeep Panicker 97453dbcc59SSundeep Panicker if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 97553dbcc59SSundeep Panicker topo_mod_dprintf(mod, "topo_method_register() failed: %s", 97653dbcc59SSundeep Panicker topo_mod_errmsg(mod)); 97753dbcc59SSundeep Panicker goto error; 97853dbcc59SSundeep Panicker } 97953dbcc59SSundeep Panicker 98053dbcc59SSundeep Panicker /* 98153dbcc59SSundeep Panicker * Create the nodes for controllers and bays. 98253dbcc59SSundeep Panicker */ 98353dbcc59SSundeep Panicker if (ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 98453dbcc59SSundeep Panicker CONTROLLER, "CONTROLLER", scp, B_TRUE) != 0 || 98553dbcc59SSundeep Panicker ses_create_children(sdp, tn, SES_ET_DEVICE, 98653dbcc59SSundeep Panicker BAY, "BAY", scp, B_TRUE) != 0 || 98753dbcc59SSundeep Panicker ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 98853dbcc59SSundeep Panicker BAY, "BAY", scp, B_TRUE) != 0) 98953dbcc59SSundeep Panicker goto error; 99053dbcc59SSundeep Panicker 99153dbcc59SSundeep Panicker ret = 0; 99253dbcc59SSundeep Panicker 99353dbcc59SSundeep Panicker error: 99453dbcc59SSundeep Panicker nvlist_free(auth); 99553dbcc59SSundeep Panicker nvlist_free(fmri); 99653dbcc59SSundeep Panicker return (ret); 99753dbcc59SSundeep Panicker } 99853dbcc59SSundeep Panicker 999940d71d2Seschrock /* 1000940d71d2Seschrock * Instantiate a new chassis instance in the topology. 1001940d71d2Seschrock */ 1002940d71d2Seschrock static int 1003940d71d2Seschrock ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp) 1004940d71d2Seschrock { 1005940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 1006940d71d2Seschrock nvlist_t *props; 1007940d71d2Seschrock char *raw_manufacturer, *raw_model, *raw_revision; 1008940d71d2Seschrock char *manufacturer = NULL, *model = NULL, *product = NULL; 1009940d71d2Seschrock char *revision = NULL; 1010940d71d2Seschrock char *serial; 10116efb64caSEric Schrock char **paths; 1012940d71d2Seschrock size_t prodlen; 1013940d71d2Seschrock tnode_t *tn; 1014940d71d2Seschrock nvlist_t *fmri = NULL, *auth = NULL; 1015940d71d2Seschrock int ret = -1; 1016940d71d2Seschrock ses_enum_node_t *snp; 10176efb64caSEric Schrock ses_enum_target_t *stp; 101853dbcc59SSundeep Panicker ses_enum_chassis_t *scp; 10196efb64caSEric Schrock int i, err; 102053dbcc59SSundeep Panicker uint64_t sc_count = 0; 1021940d71d2Seschrock 1022d91236feSeschrock /* 1023d91236feSeschrock * Ignore any internal enclosures. 1024d91236feSeschrock */ 1025d91236feSeschrock if (cp->sec_internal) 1026d91236feSeschrock return (0); 1027d91236feSeschrock 1028940d71d2Seschrock /* 1029940d71d2Seschrock * Check to see if there are any devices presennt in the chassis. If 1030940d71d2Seschrock * not, ignore the chassis alltogether. This is most useful for 1031940d71d2Seschrock * ignoring internal HBAs that present a SES target but don't actually 1032940d71d2Seschrock * manage any of the devices. 1033940d71d2Seschrock */ 1034940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1035940d71d2Seschrock snp = topo_list_next(snp)) { 1036940d71d2Seschrock if (snp->sen_type == SES_ET_DEVICE || 1037940d71d2Seschrock snp->sen_type == SES_ET_ARRAY_DEVICE) 1038940d71d2Seschrock break; 1039940d71d2Seschrock } 1040940d71d2Seschrock 1041940d71d2Seschrock if (snp == NULL) 1042940d71d2Seschrock return (0); 1043940d71d2Seschrock 1044940d71d2Seschrock props = ses_node_props(cp->sec_enclosure); 1045940d71d2Seschrock 1046940d71d2Seschrock /* 1047940d71d2Seschrock * We use the following property mappings: 1048940d71d2Seschrock * 1049940d71d2Seschrock * manufacturer vendor-id 1050940d71d2Seschrock * model product-id 1051940d71d2Seschrock * serial-number libses-chassis-serial 1052940d71d2Seschrock */ 1053940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_VID, 1054940d71d2Seschrock &raw_manufacturer) == 0); 1055940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0); 1056940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_REV, 1057940d71d2Seschrock &raw_revision) == 0); 1058940d71d2Seschrock verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0); 1059940d71d2Seschrock 1060940d71d2Seschrock /* 1061940d71d2Seschrock * To construct the authority information, we 'clean' each string by 1062940d71d2Seschrock * removing any offensive characters and trimmming whitespace. For the 1063940d71d2Seschrock * 'product-id', we use a concatenation of 'manufacturer-model'. We 1064940d71d2Seschrock * also take the numerical serial number and convert it to a string. 1065940d71d2Seschrock */ 1066940d71d2Seschrock if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL || 1067940d71d2Seschrock (model = disk_auth_clean(mod, raw_model)) == NULL || 1068940d71d2Seschrock (revision = disk_auth_clean(mod, raw_revision)) == NULL) { 1069940d71d2Seschrock goto error; 1070940d71d2Seschrock } 1071940d71d2Seschrock 1072940d71d2Seschrock prodlen = strlen(manufacturer) + strlen(model) + 2; 1073940d71d2Seschrock if ((product = topo_mod_alloc(mod, prodlen)) == NULL) 1074940d71d2Seschrock goto error; 1075940d71d2Seschrock 1076940d71d2Seschrock (void) snprintf(product, prodlen, "%s-%s", manufacturer, model); 1077940d71d2Seschrock 1078940d71d2Seschrock /* 1079940d71d2Seschrock * Construct the topo node and bind it to our parent. 1080940d71d2Seschrock */ 1081940d71d2Seschrock if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0) 1082940d71d2Seschrock goto error; 1083940d71d2Seschrock 1084940d71d2Seschrock if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 || 1085940d71d2Seschrock nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) { 1086940d71d2Seschrock (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 1087940d71d2Seschrock goto error; 1088940d71d2Seschrock } 1089940d71d2Seschrock 1090940d71d2Seschrock /* 1091940d71d2Seschrock * We pass NULL for the parent FMRI because there is no resource 1092940d71d2Seschrock * associated with it. For the toplevel enclosure, we leave the 1093940d71d2Seschrock * serial/part/revision portions empty, which are reserved for 1094940d71d2Seschrock * individual components within the chassis. 1095940d71d2Seschrock */ 1096940d71d2Seschrock if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION, 1097940d71d2Seschrock SES_ENCLOSURE, cp->sec_instance, NULL, auth, 1098940d71d2Seschrock model, revision, serial)) == NULL) { 1099940d71d2Seschrock topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 1100940d71d2Seschrock topo_mod_errmsg(mod)); 1101940d71d2Seschrock goto error; 1102940d71d2Seschrock } 1103940d71d2Seschrock 1104940d71d2Seschrock if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE, 1105940d71d2Seschrock cp->sec_instance, fmri)) == NULL) { 1106940d71d2Seschrock topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 1107940d71d2Seschrock topo_mod_errmsg(mod)); 1108940d71d2Seschrock goto error; 1109940d71d2Seschrock } 1110940d71d2Seschrock 1111940d71d2Seschrock if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 1112940d71d2Seschrock topo_mod_dprintf(mod, 1113940d71d2Seschrock "topo_method_register() failed: %s", 1114940d71d2Seschrock topo_mod_errmsg(mod)); 1115940d71d2Seschrock goto error; 1116940d71d2Seschrock } 1117940d71d2Seschrock 1118940d71d2Seschrock if (ses_set_standard_props(mod, tn, auth, 1119940d71d2Seschrock ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0) 1120940d71d2Seschrock goto error; 1121940d71d2Seschrock 11226efb64caSEric Schrock /* 11236efb64caSEric Schrock * For enclosures, we want to include all possible targets (for upgrade 11246efb64caSEric Schrock * purposes). 11256efb64caSEric Schrock */ 11266efb64caSEric Schrock for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL; 11276efb64caSEric Schrock stp = topo_list_next(stp), i++) 11286efb64caSEric Schrock ; 11296efb64caSEric Schrock 11306efb64caSEric Schrock verify(i != 0); 11316efb64caSEric Schrock paths = alloca(i * sizeof (char *)); 11326efb64caSEric Schrock 11336efb64caSEric Schrock for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL; 11346efb64caSEric Schrock stp = topo_list_next(stp), i++) 11356efb64caSEric Schrock paths[i] = stp->set_devpath; 11366efb64caSEric Schrock 11376efb64caSEric Schrock 11386efb64caSEric Schrock if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 11396efb64caSEric Schrock TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths, 11406efb64caSEric Schrock i, &err) != 0) { 11416efb64caSEric Schrock topo_mod_dprintf(mod, 11426efb64caSEric Schrock "failed to create property %s: %s\n", 11436efb64caSEric Schrock TOPO_PROP_PATHS, topo_strerror(err)); 11446efb64caSEric Schrock goto error; 11456efb64caSEric Schrock } 11466efb64caSEric Schrock 1147940d71d2Seschrock /* 1148940d71d2Seschrock * Create the nodes for power supplies, fans, and devices. 1149940d71d2Seschrock */ 1150940d71d2Seschrock if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY, 1151d91236feSeschrock PSU, "PSU", cp, B_TRUE) != 0 || 1152940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_COOLING, 1153d91236feSeschrock FAN, "FAN", cp, B_TRUE) != 0 || 1154940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 1155d91236feSeschrock CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 || 1156940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_DEVICE, 1157d91236feSeschrock BAY, "BAY", cp, B_TRUE) != 0 || 1158940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 1159d91236feSeschrock BAY, "BAY", cp, B_TRUE) != 0) 1160940d71d2Seschrock goto error; 1161940d71d2Seschrock 116253dbcc59SSundeep Panicker if (cp->sec_scinstance > 0 && 116353dbcc59SSundeep Panicker topo_node_range_create(mod, tn, SUBCHASSIS, 0, 116453dbcc59SSundeep Panicker cp->sec_scinstance - 1) != 0) { 116553dbcc59SSundeep Panicker topo_mod_dprintf(mod, "topo_node_create_range() failed: %s", 116653dbcc59SSundeep Panicker topo_mod_errmsg(mod)); 116753dbcc59SSundeep Panicker goto error; 116853dbcc59SSundeep Panicker } 116953dbcc59SSundeep Panicker 117053dbcc59SSundeep Panicker for (scp = topo_list_next(&cp->sec_subchassis); scp != NULL; 117153dbcc59SSundeep Panicker scp = topo_list_next(scp)) { 117253dbcc59SSundeep Panicker 117353dbcc59SSundeep Panicker if (ses_create_subchassis(sdp, tn, scp) != 0) 117453dbcc59SSundeep Panicker goto error; 117553dbcc59SSundeep Panicker 117653dbcc59SSundeep Panicker topo_mod_dprintf(mod, "created Subchassis node with " 117753dbcc59SSundeep Panicker "LID (%s)\n and target (%s) under Chassis with CSN (%s)", 117853dbcc59SSundeep Panicker scp->sec_lid, scp->sec_target->set_devpath, cp->sec_csn); 117953dbcc59SSundeep Panicker 118053dbcc59SSundeep Panicker sc_count++; 118153dbcc59SSundeep Panicker } 118253dbcc59SSundeep Panicker 118353dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: created %llu %s nodes", 118453dbcc59SSundeep Panicker cp->sec_csn, sc_count, SUBCHASSIS); 118553dbcc59SSundeep Panicker 11866efb64caSEric Schrock cp->sec_target->set_refcount++; 11876efb64caSEric Schrock topo_node_setspecific(tn, cp->sec_target); 1188d91236feSeschrock 1189940d71d2Seschrock ret = 0; 1190940d71d2Seschrock error: 1191940d71d2Seschrock topo_mod_strfree(mod, manufacturer); 1192940d71d2Seschrock topo_mod_strfree(mod, model); 1193940d71d2Seschrock topo_mod_strfree(mod, revision); 1194940d71d2Seschrock topo_mod_strfree(mod, product); 1195940d71d2Seschrock 1196940d71d2Seschrock nvlist_free(fmri); 1197940d71d2Seschrock nvlist_free(auth); 1198940d71d2Seschrock return (ret); 1199940d71d2Seschrock } 1200940d71d2Seschrock 1201d91236feSeschrock /* 1202d91236feSeschrock * Create a bay node explicitly enumerated via XML. 1203d91236feSeschrock */ 1204d91236feSeschrock static int 1205d91236feSeschrock ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode) 1206d91236feSeschrock { 1207d91236feSeschrock topo_mod_t *mod = sdp->sed_mod; 1208d91236feSeschrock ses_enum_chassis_t *cp; 1209d91236feSeschrock 1210d91236feSeschrock /* 1211d91236feSeschrock * Iterate over chassis looking for an internal enclosure. This 1212d91236feSeschrock * property is set via a vendor-specific plugin, and there should only 1213d91236feSeschrock * ever be a single internal chassis in a system. 1214d91236feSeschrock */ 1215d91236feSeschrock for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 1216d91236feSeschrock cp = topo_list_next(cp)) { 1217d91236feSeschrock if (cp->sec_internal) 1218d91236feSeschrock break; 1219d91236feSeschrock } 1220d91236feSeschrock 1221d91236feSeschrock if (cp == NULL) { 1222d91236feSeschrock topo_mod_dprintf(mod, "failed to find internal chassis\n"); 1223d91236feSeschrock return (-1); 1224d91236feSeschrock } 1225d91236feSeschrock 1226d91236feSeschrock if (ses_create_children(sdp, pnode, SES_ET_DEVICE, 1227d91236feSeschrock BAY, "BAY", cp, B_FALSE) != 0 || 1228d91236feSeschrock ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE, 1229d91236feSeschrock BAY, "BAY", cp, B_FALSE) != 0) 1230d91236feSeschrock return (-1); 1231d91236feSeschrock 1232d91236feSeschrock return (0); 1233d91236feSeschrock } 123453dbcc59SSundeep Panicker 123553dbcc59SSundeep Panicker /* 123653dbcc59SSundeep Panicker * Initialize chassis or subchassis. 123753dbcc59SSundeep Panicker */ 123853dbcc59SSundeep Panicker static int 123953dbcc59SSundeep Panicker ses_init_chassis(topo_mod_t *mod, ses_enum_data_t *sdp, ses_enum_chassis_t *pcp, 124053dbcc59SSundeep Panicker ses_enum_chassis_t *cp, ses_node_t *np, nvlist_t *props, 124153dbcc59SSundeep Panicker char *lid, ses_chassis_type_e flags) 124253dbcc59SSundeep Panicker { 124353dbcc59SSundeep Panicker boolean_t internal, ident; 124453dbcc59SSundeep Panicker 124553dbcc59SSundeep Panicker assert((flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS | 124653dbcc59SSundeep Panicker SES_DUP_CHASSIS | SES_DUP_SUBCHASSIS)) != 0); 124753dbcc59SSundeep Panicker 124853dbcc59SSundeep Panicker assert((cp != NULL) && (np != NULL) && (props != NULL) && 124953dbcc59SSundeep Panicker (lid != NULL)); 125053dbcc59SSundeep Panicker 125153dbcc59SSundeep Panicker if (flags & (SES_NEW_SUBCHASSIS | SES_DUP_SUBCHASSIS)) 125253dbcc59SSundeep Panicker assert(pcp != NULL); 125353dbcc59SSundeep Panicker 125453dbcc59SSundeep Panicker topo_mod_dprintf(mod, "ses_init_chassis: %s: lid(%s), flags (%d)", 125553dbcc59SSundeep Panicker sdp->sed_name, lid, flags); 125653dbcc59SSundeep Panicker 125753dbcc59SSundeep Panicker if (flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS)) { 125853dbcc59SSundeep Panicker 125953dbcc59SSundeep Panicker topo_mod_dprintf(mod, "new chassis/subchassis"); 126053dbcc59SSundeep Panicker if (nvlist_lookup_boolean_value(props, 126153dbcc59SSundeep Panicker LIBSES_EN_PROP_INTERNAL, &internal) == 0) 126253dbcc59SSundeep Panicker cp->sec_internal = internal; 126353dbcc59SSundeep Panicker 126453dbcc59SSundeep Panicker cp->sec_lid = lid; 126553dbcc59SSundeep Panicker cp->sec_enclosure = np; 126653dbcc59SSundeep Panicker cp->sec_target = sdp->sed_target; 126753dbcc59SSundeep Panicker 126853dbcc59SSundeep Panicker if (flags & SES_NEW_CHASSIS) { 126953dbcc59SSundeep Panicker cp->sec_instance = sdp->sed_instance++; 127053dbcc59SSundeep Panicker topo_list_append(&sdp->sed_chassis, cp); 127153dbcc59SSundeep Panicker } else { 127253dbcc59SSundeep Panicker cp->sec_instance = pcp->sec_scinstance++; 127353dbcc59SSundeep Panicker topo_list_append(&pcp->sec_subchassis, cp); 127453dbcc59SSundeep Panicker } 127553dbcc59SSundeep Panicker 127653dbcc59SSundeep Panicker } else { 127753dbcc59SSundeep Panicker topo_mod_dprintf(mod, "dup chassis/subchassis"); 127853dbcc59SSundeep Panicker if (nvlist_lookup_boolean_value(props, 127953dbcc59SSundeep Panicker SES_PROP_IDENT, &ident) == 0) { 128053dbcc59SSundeep Panicker topo_mod_dprintf(mod, "overriding enclosure node"); 128153dbcc59SSundeep Panicker 128253dbcc59SSundeep Panicker cp->sec_enclosure = np; 128353dbcc59SSundeep Panicker cp->sec_target = sdp->sed_target; 128453dbcc59SSundeep Panicker } 128553dbcc59SSundeep Panicker } 128653dbcc59SSundeep Panicker 128753dbcc59SSundeep Panicker topo_list_append(&cp->sec_targets, sdp->sed_target); 128853dbcc59SSundeep Panicker sdp->sed_current = cp; 128953dbcc59SSundeep Panicker 129053dbcc59SSundeep Panicker return (0); 129153dbcc59SSundeep Panicker } 129253dbcc59SSundeep Panicker 1293940d71d2Seschrock /* 1294940d71d2Seschrock * Gather nodes from the current SES target into our chassis list, merging the 1295940d71d2Seschrock * results if necessary. 1296940d71d2Seschrock */ 1297940d71d2Seschrock static ses_walk_action_t 1298940d71d2Seschrock ses_enum_gather(ses_node_t *np, void *data) 1299940d71d2Seschrock { 1300940d71d2Seschrock nvlist_t *props = ses_node_props(np); 1301940d71d2Seschrock ses_enum_data_t *sdp = data; 1302940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 130353dbcc59SSundeep Panicker ses_enum_chassis_t *cp, *scp; 1304940d71d2Seschrock ses_enum_node_t *snp; 13050b32bb8bSEric Schrock ses_alt_node_t *sap; 1306940d71d2Seschrock char *csn; 1307940d71d2Seschrock uint64_t instance, type; 1308d91236feSeschrock uint64_t prevstatus, status; 130953dbcc59SSundeep Panicker boolean_t report; 131053dbcc59SSundeep Panicker boolean_t have_subchassis = B_TRUE; 131153dbcc59SSundeep Panicker char *lid; 1312940d71d2Seschrock 1313940d71d2Seschrock if (ses_node_type(np) == SES_NODE_ENCLOSURE) { 1314940d71d2Seschrock /* 1315940d71d2Seschrock * If we have already identified the chassis for this target, 1316940d71d2Seschrock * then this is a secondary enclosure and we should ignore it, 1317940d71d2Seschrock * along with the rest of the tree (since this is depth-first). 1318940d71d2Seschrock */ 1319940d71d2Seschrock if (sdp->sed_current != NULL) 1320940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1321940d71d2Seschrock 1322940d71d2Seschrock /* 1323940d71d2Seschrock * Go through the list of chassis we have seen so far and see 1324940d71d2Seschrock * if this serial number matches one of the known values. 132553dbcc59SSundeep Panicker * If so, check whether this enclosure is a subchassis. 1326940d71d2Seschrock */ 1327940d71d2Seschrock if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, 1328940d71d2Seschrock &csn) != 0) 1329940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1330940d71d2Seschrock 133153dbcc59SSundeep Panicker if (nvlist_lookup_string(props, LIBSES_EN_PROP_SUBCHASSIS_ID, 133253dbcc59SSundeep Panicker &lid) != 0) { 133353dbcc59SSundeep Panicker have_subchassis = B_FALSE; 1334480c3503SEric Schrock lid = ""; 133553dbcc59SSundeep Panicker } 133653dbcc59SSundeep Panicker 133753dbcc59SSundeep Panicker topo_mod_dprintf(mod, "ses_enum_gather: Enclosure Node (%s) " 133853dbcc59SSundeep Panicker "CSN (%s), LID (%s)", sdp->sed_name, csn, lid); 133953dbcc59SSundeep Panicker 134053dbcc59SSundeep Panicker /* 134153dbcc59SSundeep Panicker * We need to determine whether this enclosure node 134253dbcc59SSundeep Panicker * represents a chassis or a subchassis. Since we may 134353dbcc59SSundeep Panicker * receive the enclosure nodes in a non-deterministic 134453dbcc59SSundeep Panicker * manner, we need to account for all possible combinations: 134553dbcc59SSundeep Panicker * 1. Chassis for the current CSN has not yet been 134653dbcc59SSundeep Panicker * allocated 134753dbcc59SSundeep Panicker * 1.1 This is a new chassis: 134853dbcc59SSundeep Panicker * allocate and instantiate the chassis 134953dbcc59SSundeep Panicker * 1.2 This is a new subchassis: 135053dbcc59SSundeep Panicker * allocate a placeholder chassis 135153dbcc59SSundeep Panicker * allocate and instantiate the subchassis 135253dbcc59SSundeep Panicker * link the subchassis to the chassis 135353dbcc59SSundeep Panicker * 2. Chassis for the current CSN has been allocated 135453dbcc59SSundeep Panicker * 2.1 This is a duplicate chassis enclosure 135553dbcc59SSundeep Panicker * check whether to override old chassis 135653dbcc59SSundeep Panicker * append to chassis' target list 135753dbcc59SSundeep Panicker * 2.2 Only placeholder chassis exists 135853dbcc59SSundeep Panicker * fill in the chassis fields 135953dbcc59SSundeep Panicker * 2.3 This is a new subchassis 136053dbcc59SSundeep Panicker * allocate and instantiate the subchassis 136153dbcc59SSundeep Panicker * link the subchassis to the chassis 136253dbcc59SSundeep Panicker * 2.4 This is a duplicate subchassis enclosure 136353dbcc59SSundeep Panicker * check whether to override old chassis 136453dbcc59SSundeep Panicker * append to chassis' target list 136553dbcc59SSundeep Panicker */ 136653dbcc59SSundeep Panicker 1367940d71d2Seschrock for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 136853dbcc59SSundeep Panicker cp = topo_list_next(cp)) 136953dbcc59SSundeep Panicker if (strcmp(cp->sec_csn, csn) == 0) 1370940d71d2Seschrock break; 1371940d71d2Seschrock 1372940d71d2Seschrock if (cp == NULL) { 137353dbcc59SSundeep Panicker /* 1. Haven't seen a chassis with this CSN before */ 1374940d71d2Seschrock 1375940d71d2Seschrock if ((cp = topo_mod_zalloc(mod, 1376940d71d2Seschrock sizeof (ses_enum_chassis_t))) == NULL) 1377940d71d2Seschrock goto error; 1378940d71d2Seschrock 1379940d71d2Seschrock cp->sec_csn = csn; 138053dbcc59SSundeep Panicker 138153dbcc59SSundeep Panicker if (!have_subchassis || strcmp(csn, lid) == 0) { 138253dbcc59SSundeep Panicker /* 1.1 This is a new chassis */ 138353dbcc59SSundeep Panicker 138453dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Initialize new " 138553dbcc59SSundeep Panicker "chassis with CSN (%s) and LID (%s)", 138653dbcc59SSundeep Panicker sdp->sed_name, csn, lid); 138753dbcc59SSundeep Panicker 138853dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, NULL, cp, 138953dbcc59SSundeep Panicker np, props, lid, SES_NEW_CHASSIS) < 0) 139053dbcc59SSundeep Panicker goto error; 139153dbcc59SSundeep Panicker } else { 139253dbcc59SSundeep Panicker /* 1.2 This is a new subchassis */ 139353dbcc59SSundeep Panicker 139453dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Initialize new " 139553dbcc59SSundeep Panicker "subchassis with CSN (%s) and LID (%s)", 139653dbcc59SSundeep Panicker sdp->sed_name, csn, lid); 139753dbcc59SSundeep Panicker 139853dbcc59SSundeep Panicker if ((scp = topo_mod_zalloc(mod, 139953dbcc59SSundeep Panicker sizeof (ses_enum_chassis_t))) == NULL) 140053dbcc59SSundeep Panicker goto error; 140153dbcc59SSundeep Panicker 140253dbcc59SSundeep Panicker scp->sec_csn = csn; 140353dbcc59SSundeep Panicker 140453dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, cp, scp, 140553dbcc59SSundeep Panicker np, props, lid, SES_NEW_SUBCHASSIS) < 0) 140653dbcc59SSundeep Panicker goto error; 140753dbcc59SSundeep Panicker } 14086efb64caSEric Schrock } else { 140953dbcc59SSundeep Panicker /* 2. We have a chassis with this CSN */ 141053dbcc59SSundeep Panicker 141153dbcc59SSundeep Panicker if (!have_subchassis || strcmp(csn, lid) == 0) { 141253dbcc59SSundeep Panicker /* This is a chassis */ 141353dbcc59SSundeep Panicker 141453dbcc59SSundeep Panicker if (!have_subchassis || 141553dbcc59SSundeep Panicker strlen(cp->sec_lid) > 0) { 141653dbcc59SSundeep Panicker /* 2.1 This is a duplicate chassis */ 141753dbcc59SSundeep Panicker 141853dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Append " 141953dbcc59SSundeep Panicker "duplicate chassis with CSN (%s) " 142053dbcc59SSundeep Panicker "and LID (%s)", 142153dbcc59SSundeep Panicker sdp->sed_name, csn, lid); 142253dbcc59SSundeep Panicker 142353dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, NULL, cp, 142453dbcc59SSundeep Panicker np, props, lid, 142553dbcc59SSundeep Panicker SES_DUP_CHASSIS) < 0) 142653dbcc59SSundeep Panicker goto error; 142753dbcc59SSundeep Panicker } else { 142853dbcc59SSundeep Panicker /* 2.2 Init the placeholder chassis */ 142953dbcc59SSundeep Panicker 143053dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Initialize" 143153dbcc59SSundeep Panicker "placeholder chassis with CSN (%s) " 143253dbcc59SSundeep Panicker "and LID (%s)", 143353dbcc59SSundeep Panicker sdp->sed_name, csn, lid); 143453dbcc59SSundeep Panicker 143553dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, NULL, cp, 143653dbcc59SSundeep Panicker np, props, lid, 143753dbcc59SSundeep Panicker SES_NEW_CHASSIS) < 0) 143853dbcc59SSundeep Panicker goto error; 143953dbcc59SSundeep Panicker 144053dbcc59SSundeep Panicker } 144153dbcc59SSundeep Panicker } else { 144253dbcc59SSundeep Panicker /* This is a subchassis */ 144353dbcc59SSundeep Panicker 144453dbcc59SSundeep Panicker for (scp = topo_list_next(&cp->sec_subchassis); 144553dbcc59SSundeep Panicker scp != NULL; scp = topo_list_next(scp)) 144653dbcc59SSundeep Panicker if (strcmp(scp->sec_lid, lid) == 0) 144753dbcc59SSundeep Panicker break; 144853dbcc59SSundeep Panicker 144953dbcc59SSundeep Panicker if (scp == NULL) { 145053dbcc59SSundeep Panicker /* 2.3 This is a new subchassis */ 145153dbcc59SSundeep Panicker 145253dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Initialize " 145353dbcc59SSundeep Panicker "new subchassis with CSN (%s) " 145453dbcc59SSundeep Panicker "and LID (%s)", 145553dbcc59SSundeep Panicker sdp->sed_name, csn, lid); 145653dbcc59SSundeep Panicker 145753dbcc59SSundeep Panicker if ((scp = topo_mod_zalloc(mod, 145853dbcc59SSundeep Panicker sizeof (ses_enum_chassis_t))) 145953dbcc59SSundeep Panicker == NULL) 146053dbcc59SSundeep Panicker goto error; 146153dbcc59SSundeep Panicker 146253dbcc59SSundeep Panicker scp->sec_csn = csn; 146353dbcc59SSundeep Panicker 146453dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, cp, scp, 146553dbcc59SSundeep Panicker np, props, lid, 146653dbcc59SSundeep Panicker SES_NEW_SUBCHASSIS) < 0) 146753dbcc59SSundeep Panicker goto error; 146853dbcc59SSundeep Panicker } else { 146953dbcc59SSundeep Panicker /* 2.4 This is a duplicate subchassis */ 147053dbcc59SSundeep Panicker 147153dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Append " 147253dbcc59SSundeep Panicker "duplicate subchassis with " 147353dbcc59SSundeep Panicker "CSN (%s) and LID (%s)", 147453dbcc59SSundeep Panicker sdp->sed_name, csn, lid); 147553dbcc59SSundeep Panicker 147653dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, cp, scp, 147753dbcc59SSundeep Panicker np, props, lid, 147853dbcc59SSundeep Panicker SES_DUP_SUBCHASSIS) < 0) 147953dbcc59SSundeep Panicker goto error; 148053dbcc59SSundeep Panicker } 14816efb64caSEric Schrock } 1482940d71d2Seschrock } 1483940d71d2Seschrock } else if (ses_node_type(np) == SES_NODE_ELEMENT) { 1484940d71d2Seschrock /* 1485940d71d2Seschrock * If we haven't yet seen an enclosure node and identified the 1486940d71d2Seschrock * current chassis, something is very wrong; bail out. 1487940d71d2Seschrock */ 1488940d71d2Seschrock if (sdp->sed_current == NULL) 1489940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1490940d71d2Seschrock 1491940d71d2Seschrock /* 1492940d71d2Seschrock * If this isn't one of the element types we care about, then 1493940d71d2Seschrock * ignore it. 1494940d71d2Seschrock */ 1495940d71d2Seschrock verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, 1496940d71d2Seschrock &type) == 0); 1497940d71d2Seschrock if (type != SES_ET_DEVICE && 1498940d71d2Seschrock type != SES_ET_ARRAY_DEVICE && 1499940d71d2Seschrock type != SES_ET_COOLING && 1500940d71d2Seschrock type != SES_ET_POWER_SUPPLY && 1501940d71d2Seschrock type != SES_ET_ESC_ELECTRONICS) 1502940d71d2Seschrock return (SES_WALK_ACTION_CONTINUE); 1503940d71d2Seschrock 1504940d71d2Seschrock /* 1505940d71d2Seschrock * Get the current instance number and see if we already know 1506940d71d2Seschrock * about this element. If so, it means we have multiple paths 1507940d71d2Seschrock * to the same elements, and we should ignore the current path. 1508940d71d2Seschrock */ 1509940d71d2Seschrock verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX, 1510940d71d2Seschrock &instance) == 0); 1511940d71d2Seschrock if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE) 1512940d71d2Seschrock (void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER, 1513940d71d2Seschrock &instance); 1514940d71d2Seschrock 1515940d71d2Seschrock cp = sdp->sed_current; 1516940d71d2Seschrock 1517940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1518940d71d2Seschrock snp = topo_list_next(snp)) { 1519940d71d2Seschrock if (snp->sen_type == type && 1520940d71d2Seschrock snp->sen_instance == instance) 1521d91236feSeschrock break; 1522d91236feSeschrock } 1523d91236feSeschrock 1524d91236feSeschrock /* 1525d91236feSeschrock * We prefer the new element under the following circumstances: 1526d91236feSeschrock * 1527d91236feSeschrock * - The currently known element's status is unknown or not 1528d91236feSeschrock * available, but the new element has a known status. This 1529d91236feSeschrock * occurs if a given element is only available through a 1530d91236feSeschrock * particular target. 1531d91236feSeschrock * 1532d91236feSeschrock * - This is an ESC_ELECTRONICS element, and the 'reported-via' 1533d91236feSeschrock * property is set. This allows us to get reliable firmware 1534d91236feSeschrock * revision information from the enclosure node. 1535d91236feSeschrock */ 1536d91236feSeschrock if (snp != NULL) { 1537d91236feSeschrock if (nvlist_lookup_uint64( 1538d91236feSeschrock ses_node_props(snp->sen_node), 1539d91236feSeschrock SES_PROP_STATUS_CODE, &prevstatus) != 0) 1540d91236feSeschrock prevstatus = SES_ESC_UNSUPPORTED; 1541d91236feSeschrock if (nvlist_lookup_uint64( 1542d91236feSeschrock props, SES_PROP_STATUS_CODE, &status) != 0) 1543d91236feSeschrock status = SES_ESC_UNSUPPORTED; 1544d91236feSeschrock if (nvlist_lookup_boolean_value( 1545d91236feSeschrock props, SES_PROP_REPORT, &report) != 0) 1546d91236feSeschrock report = B_FALSE; 1547d91236feSeschrock 1548d91236feSeschrock if ((SES_STATUS_UNAVAIL(prevstatus) && 1549d91236feSeschrock !SES_STATUS_UNAVAIL(status)) || 1550d91236feSeschrock (type == SES_ET_ESC_ELECTRONICS && 1551d91236feSeschrock report)) { 1552d91236feSeschrock snp->sen_node = np; 1553d91236feSeschrock snp->sen_target = sdp->sed_target; 1554d91236feSeschrock } 1555d91236feSeschrock 15560b32bb8bSEric Schrock if ((sap = topo_mod_zalloc(mod, 15570b32bb8bSEric Schrock sizeof (ses_alt_node_t))) == NULL) 15580b32bb8bSEric Schrock goto error; 15590b32bb8bSEric Schrock 15600b32bb8bSEric Schrock sap->san_node = np; 15610b32bb8bSEric Schrock topo_list_append(&snp->sen_alt_nodes, sap); 15620b32bb8bSEric Schrock 1563d91236feSeschrock return (SES_WALK_ACTION_CONTINUE); 1564940d71d2Seschrock } 1565940d71d2Seschrock 1566940d71d2Seschrock if ((snp = topo_mod_zalloc(mod, 1567940d71d2Seschrock sizeof (ses_enum_node_t))) == NULL) 1568940d71d2Seschrock goto error; 1569940d71d2Seschrock 15700b32bb8bSEric Schrock if ((sap = topo_mod_zalloc(mod, 15710b32bb8bSEric Schrock sizeof (ses_alt_node_t))) == NULL) { 15720b32bb8bSEric Schrock topo_mod_free(mod, snp, sizeof (ses_enum_node_t)); 15730b32bb8bSEric Schrock goto error; 15740b32bb8bSEric Schrock } 15750b32bb8bSEric Schrock 1576940d71d2Seschrock topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)", 1577940d71d2Seschrock sdp->sed_name, type, instance); 1578940d71d2Seschrock snp->sen_node = np; 1579940d71d2Seschrock snp->sen_type = type; 1580940d71d2Seschrock snp->sen_instance = instance; 1581940d71d2Seschrock snp->sen_target = sdp->sed_target; 15820b32bb8bSEric Schrock sap->san_node = np; 15830b32bb8bSEric Schrock topo_list_append(&snp->sen_alt_nodes, sap); 1584940d71d2Seschrock topo_list_append(&cp->sec_nodes, snp); 1585940d71d2Seschrock 1586940d71d2Seschrock if (type == SES_ET_DEVICE) 1587940d71d2Seschrock cp->sec_hasdev = B_TRUE; 1588940d71d2Seschrock } 1589940d71d2Seschrock 1590940d71d2Seschrock return (SES_WALK_ACTION_CONTINUE); 1591940d71d2Seschrock 1592940d71d2Seschrock error: 1593940d71d2Seschrock sdp->sed_errno = -1; 1594940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1595940d71d2Seschrock } 1596940d71d2Seschrock 1597940d71d2Seschrock static int 1598940d71d2Seschrock ses_process_dir(const char *dirpath, ses_enum_data_t *sdp) 1599940d71d2Seschrock { 1600940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 1601940d71d2Seschrock DIR *dir; 1602940d71d2Seschrock struct dirent *dp; 1603940d71d2Seschrock char path[PATH_MAX]; 1604940d71d2Seschrock ses_enum_target_t *stp; 1605940d71d2Seschrock int err = -1; 1606940d71d2Seschrock 1607940d71d2Seschrock /* 1608940d71d2Seschrock * Open the SES target directory and iterate over any available 1609940d71d2Seschrock * targets. 1610940d71d2Seschrock */ 1611940d71d2Seschrock if ((dir = opendir(dirpath)) == NULL) { 1612940d71d2Seschrock /* 1613940d71d2Seschrock * If the SES target directory does not exist, then return as if 1614940d71d2Seschrock * there are no active targets. 1615940d71d2Seschrock */ 1616940d71d2Seschrock topo_mod_dprintf(mod, "failed to open ses " 1617940d71d2Seschrock "directory '%s'", dirpath); 1618940d71d2Seschrock return (0); 1619940d71d2Seschrock } 1620940d71d2Seschrock 1621940d71d2Seschrock while ((dp = readdir(dir)) != NULL) { 1622940d71d2Seschrock if (strcmp(dp->d_name, ".") == 0 || 1623940d71d2Seschrock strcmp(dp->d_name, "..") == 0) 1624940d71d2Seschrock continue; 1625940d71d2Seschrock 1626940d71d2Seschrock /* 1627940d71d2Seschrock * Create a new target instance and take a snapshot. 1628940d71d2Seschrock */ 1629940d71d2Seschrock if ((stp = topo_mod_zalloc(mod, 1630940d71d2Seschrock sizeof (ses_enum_target_t))) == NULL) 1631940d71d2Seschrock goto error; 1632940d71d2Seschrock 16330b32bb8bSEric Schrock (void) pthread_mutex_init(&stp->set_lock, NULL); 16340b32bb8bSEric Schrock 1635940d71d2Seschrock (void) snprintf(path, sizeof (path), "%s/%s", dirpath, 1636940d71d2Seschrock dp->d_name); 1637940d71d2Seschrock 1638940d71d2Seschrock /* 1639940d71d2Seschrock * We keep track of the SES device path and export it on a 1640940d71d2Seschrock * per-node basis to allow higher level software to get to the 1641940d71d2Seschrock * corresponding SES state. 1642940d71d2Seschrock */ 1643940d71d2Seschrock if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) { 1644940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 1645940d71d2Seschrock goto error; 1646940d71d2Seschrock } 1647940d71d2Seschrock 1648940d71d2Seschrock if ((stp->set_target = 1649940d71d2Seschrock ses_open(LIBSES_VERSION, path)) == NULL) { 1650940d71d2Seschrock topo_mod_dprintf(mod, "failed to open ses target " 1651940d71d2Seschrock "'%s': %s", dp->d_name, ses_errmsg()); 1652940d71d2Seschrock topo_mod_strfree(mod, stp->set_devpath); 1653940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 1654940d71d2Seschrock continue; 1655940d71d2Seschrock } 1656940d71d2Seschrock 1657940d71d2Seschrock stp->set_refcount = 1; 1658940d71d2Seschrock sdp->sed_target = stp; 1659940d71d2Seschrock stp->set_snap = ses_snap_hold(stp->set_target); 1660525b85dbSEric Schrock stp->set_snaptime = gethrtime(); 1661940d71d2Seschrock 1662940d71d2Seschrock /* 1663940d71d2Seschrock * Enumerate over all SES elements and merge them into the 1664940d71d2Seschrock * correct ses_enum_chassis_t. 1665940d71d2Seschrock */ 1666940d71d2Seschrock sdp->sed_current = NULL; 1667940d71d2Seschrock sdp->sed_errno = 0; 1668940d71d2Seschrock sdp->sed_name = dp->d_name; 1669940d71d2Seschrock (void) ses_walk(stp->set_snap, ses_enum_gather, sdp); 1670940d71d2Seschrock 1671940d71d2Seschrock if (sdp->sed_errno != 0) 1672940d71d2Seschrock goto error; 1673940d71d2Seschrock } 1674940d71d2Seschrock 1675940d71d2Seschrock err = 0; 1676940d71d2Seschrock error: 1677940d71d2Seschrock closedir(dir); 1678940d71d2Seschrock return (err); 1679940d71d2Seschrock } 1680940d71d2Seschrock 1681940d71d2Seschrock static void 1682940d71d2Seschrock ses_release(topo_mod_t *mod, tnode_t *tn) 1683940d71d2Seschrock { 1684940d71d2Seschrock ses_enum_target_t *stp; 1685940d71d2Seschrock 1686940d71d2Seschrock if ((stp = topo_node_getspecific(tn)) != NULL) 1687940d71d2Seschrock ses_target_free(mod, stp); 1688940d71d2Seschrock } 1689940d71d2Seschrock 1690940d71d2Seschrock /*ARGSUSED*/ 1691940d71d2Seschrock static int 1692940d71d2Seschrock ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 1693940d71d2Seschrock topo_instance_t min, topo_instance_t max, void *arg, void *notused) 1694940d71d2Seschrock { 1695940d71d2Seschrock ses_enum_chassis_t *cp; 1696d91236feSeschrock ses_enum_data_t *data; 1697940d71d2Seschrock 1698940d71d2Seschrock /* 1699940d71d2Seschrock * Check to make sure we're being invoked sensibly, and that we're not 1700940d71d2Seschrock * being invoked as part of a post-processing step. 1701940d71d2Seschrock */ 1702d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0) 1703940d71d2Seschrock return (0); 1704940d71d2Seschrock 1705940d71d2Seschrock /* 1706d91236feSeschrock * If this is the first time we've called our enumeration method, then 1707d91236feSeschrock * gather information about any available enclosures. 1708940d71d2Seschrock */ 1709d91236feSeschrock if ((data = topo_mod_getspecific(mod)) == NULL) { 1710d91236feSeschrock if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) == 1711d91236feSeschrock NULL) 1712d91236feSeschrock return (-1); 1713940d71d2Seschrock 1714d91236feSeschrock data->sed_mod = mod; 1715d91236feSeschrock topo_mod_setspecific(mod, data); 1716d91236feSeschrock 1717d91236feSeschrock if (disk_list_gather(mod, &data->sed_disks) != 0) 1718d91236feSeschrock goto error; 1719d91236feSeschrock 1720d91236feSeschrock /* 1721d91236feSeschrock * We search both the ses(7D) and sgen(7D) locations, so we are 1722d91236feSeschrock * independent of any particular driver class bindings. 1723d91236feSeschrock */ 1724d91236feSeschrock if (ses_process_dir("/dev/es", data) != 0 || 1725d91236feSeschrock ses_process_dir("/dev/scsi/ses", data) != 0) 1726d91236feSeschrock goto error; 1727d91236feSeschrock } 1728d91236feSeschrock 1729d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) == 0) { 1730d91236feSeschrock /* 1731d91236feSeschrock * This is a request to enumerate external enclosures. Go 1732d91236feSeschrock * through all the targets and create chassis nodes where 1733d91236feSeschrock * necessary. 1734d91236feSeschrock */ 1735d91236feSeschrock for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 1736d91236feSeschrock cp = topo_list_next(cp)) { 1737d91236feSeschrock if (ses_create_chassis(data, rnode, cp) != 0) 1738d91236feSeschrock goto error; 1739d91236feSeschrock } 1740d91236feSeschrock } else { 1741d91236feSeschrock /* 1742d91236feSeschrock * This is a request to enumerate a specific bay underneath the 1743d91236feSeschrock * root chassis (for internal disks). 1744d91236feSeschrock */ 1745d91236feSeschrock if (ses_create_bays(data, rnode) != 0) 1746940d71d2Seschrock goto error; 1747940d71d2Seschrock } 1748940d71d2Seschrock 1749d91236feSeschrock /* 1750d91236feSeschrock * This is a bit of a kludge. In order to allow internal disks to be 1751d91236feSeschrock * enumerated and share snapshot-specific information with the external 1752d91236feSeschrock * enclosure enumeration, we rely on the fact that we will be invoked 1753d91236feSeschrock * for the 'ses-enclosure' node last. 1754d91236feSeschrock */ 1755d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) == 0) { 175653dbcc59SSundeep Panicker for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 175753dbcc59SSundeep Panicker cp = topo_list_next(cp)) 175853dbcc59SSundeep Panicker ses_data_free(data, cp); 175953dbcc59SSundeep Panicker ses_data_free(data, NULL); 1760d91236feSeschrock topo_mod_setspecific(mod, NULL); 1761d91236feSeschrock } 1762940d71d2Seschrock return (0); 1763940d71d2Seschrock 1764940d71d2Seschrock error: 176553dbcc59SSundeep Panicker for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 176653dbcc59SSundeep Panicker cp = topo_list_next(cp)) 176753dbcc59SSundeep Panicker ses_data_free(data, cp); 176853dbcc59SSundeep Panicker ses_data_free(data, NULL); 1769d91236feSeschrock topo_mod_setspecific(mod, NULL); 1770940d71d2Seschrock return (-1); 1771940d71d2Seschrock } 1772940d71d2Seschrock 1773940d71d2Seschrock static const topo_modops_t ses_ops = 1774940d71d2Seschrock { ses_enum, ses_release }; 1775940d71d2Seschrock 1776940d71d2Seschrock static topo_modinfo_t ses_info = 1777940d71d2Seschrock { SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops }; 1778940d71d2Seschrock 1779940d71d2Seschrock /*ARGSUSED*/ 1780940d71d2Seschrock int 1781940d71d2Seschrock _topo_init(topo_mod_t *mod, topo_version_t version) 1782940d71d2Seschrock { 1783940d71d2Seschrock if (getenv("TOPOSESDEBUG") != NULL) 1784940d71d2Seschrock topo_mod_setdebug(mod); 1785940d71d2Seschrock 1786940d71d2Seschrock topo_mod_dprintf(mod, "initializing %s enumerator\n", 1787940d71d2Seschrock SES_ENCLOSURE); 1788940d71d2Seschrock 1789940d71d2Seschrock return (topo_mod_register(mod, &ses_info, TOPO_VERSION)); 1790940d71d2Seschrock } 1791940d71d2Seschrock 1792940d71d2Seschrock void 1793940d71d2Seschrock _topo_fini(topo_mod_t *mod) 1794940d71d2Seschrock { 1795940d71d2Seschrock topo_mod_unregister(mod); 1796940d71d2Seschrock } 1797