1*940d71d2Seschrock /* 2*940d71d2Seschrock * CDDL HEADER START 3*940d71d2Seschrock * 4*940d71d2Seschrock * The contents of this file are subject to the terms of the 5*940d71d2Seschrock * Common Development and Distribution License (the "License"). 6*940d71d2Seschrock * You may not use this file except in compliance with the License. 7*940d71d2Seschrock * 8*940d71d2Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*940d71d2Seschrock * or http://www.opensolaris.org/os/licensing. 10*940d71d2Seschrock * See the License for the specific language governing permissions 11*940d71d2Seschrock * and limitations under the License. 12*940d71d2Seschrock * 13*940d71d2Seschrock * When distributing Covered Code, include this CDDL HEADER in each 14*940d71d2Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*940d71d2Seschrock * If applicable, add the following below this CDDL HEADER, with the 16*940d71d2Seschrock * fields enclosed by brackets "[]" replaced with your own identifying 17*940d71d2Seschrock * information: Portions Copyright [yyyy] [name of copyright owner] 18*940d71d2Seschrock * 19*940d71d2Seschrock * CDDL HEADER END 20*940d71d2Seschrock */ 21*940d71d2Seschrock 22*940d71d2Seschrock /* 23*940d71d2Seschrock * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24*940d71d2Seschrock * Use is subject to license terms. 25*940d71d2Seschrock */ 26*940d71d2Seschrock 27*940d71d2Seschrock #pragma ident "%Z%%M% %I% %E% SMI" 28*940d71d2Seschrock 29*940d71d2Seschrock #include <assert.h> 30*940d71d2Seschrock #include <dirent.h> 31*940d71d2Seschrock #include <devid.h> 32*940d71d2Seschrock #include <fm/topo_mod.h> 33*940d71d2Seschrock #include <fm/topo_list.h> 34*940d71d2Seschrock #include <fm/topo_method.h> 35*940d71d2Seschrock #include <fm/libdiskstatus.h> 36*940d71d2Seschrock #include <inttypes.h> 37*940d71d2Seschrock #include <scsi/libses.h> 38*940d71d2Seschrock #include <pthread.h> 39*940d71d2Seschrock #include <strings.h> 40*940d71d2Seschrock #include <unistd.h> 41*940d71d2Seschrock #include <sys/dkio.h> 42*940d71d2Seschrock #include <sys/fm/protocol.h> 43*940d71d2Seschrock #include <sys/scsi/scsi_types.h> 44*940d71d2Seschrock #include "disk.h" 45*940d71d2Seschrock 46*940d71d2Seschrock #define SES_VERSION 1 47*940d71d2Seschrock 48*940d71d2Seschrock #define TOPO_PGROUP_SES "ses" 49*940d71d2Seschrock #define TOPO_PROP_NODE_ID "node-id" 50*940d71d2Seschrock #define TOPO_PROP_TARGET_PATH "target-path" 51*940d71d2Seschrock 52*940d71d2Seschrock #define SES_SNAP_FREQ 1000 /* in milliseconds */ 53*940d71d2Seschrock 54*940d71d2Seschrock #ifndef NDEBUG 55*940d71d2Seschrock #define verify(x) assert(x) 56*940d71d2Seschrock #else 57*940d71d2Seschrock #define verify(x) ((void)(x)) 58*940d71d2Seschrock #endif 59*940d71d2Seschrock 60*940d71d2Seschrock /* 61*940d71d2Seschrock * Because multiple SES targets can be part of a single chassis, we construct 62*940d71d2Seschrock * our own hierarchy that takes this into account. These SES targets may refer 63*940d71d2Seschrock * to the same devices (multiple paths) or to different devices (managing 64*940d71d2Seschrock * different portions of the space). We arrange things into a 65*940d71d2Seschrock * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all 66*940d71d2Seschrock * nodes found so far. 67*940d71d2Seschrock */ 68*940d71d2Seschrock 69*940d71d2Seschrock typedef struct ses_enum_target { 70*940d71d2Seschrock topo_list_t set_link; 71*940d71d2Seschrock ses_target_t *set_target; 72*940d71d2Seschrock ses_snap_t *set_snap; 73*940d71d2Seschrock struct timeval set_snaptime; 74*940d71d2Seschrock char *set_devpath; 75*940d71d2Seschrock int set_refcount; 76*940d71d2Seschrock } ses_enum_target_t; 77*940d71d2Seschrock 78*940d71d2Seschrock typedef struct ses_enum_node { 79*940d71d2Seschrock topo_list_t sen_link; 80*940d71d2Seschrock ses_node_t *sen_node; 81*940d71d2Seschrock uint64_t sen_type; 82*940d71d2Seschrock uint64_t sen_instance; 83*940d71d2Seschrock ses_enum_target_t *sen_target; 84*940d71d2Seschrock } ses_enum_node_t; 85*940d71d2Seschrock 86*940d71d2Seschrock typedef struct ses_enum_chassis { 87*940d71d2Seschrock topo_list_t sec_link; 88*940d71d2Seschrock topo_list_t sec_nodes; 89*940d71d2Seschrock topo_list_t sec_targets; 90*940d71d2Seschrock const char *sec_csn; 91*940d71d2Seschrock ses_node_t *sec_enclosure; 92*940d71d2Seschrock ses_enum_target_t *sec_target; 93*940d71d2Seschrock topo_instance_t sec_instance; 94*940d71d2Seschrock boolean_t sec_hasdev; 95*940d71d2Seschrock } ses_enum_chassis_t; 96*940d71d2Seschrock 97*940d71d2Seschrock typedef struct ses_enum_data { 98*940d71d2Seschrock topo_list_t sed_disks; 99*940d71d2Seschrock topo_list_t sed_chassis; 100*940d71d2Seschrock ses_enum_chassis_t *sed_current; 101*940d71d2Seschrock ses_enum_target_t *sed_target; 102*940d71d2Seschrock int sed_errno; 103*940d71d2Seschrock char *sed_name; 104*940d71d2Seschrock topo_mod_t *sed_mod; 105*940d71d2Seschrock topo_instance_t sed_instance; 106*940d71d2Seschrock } ses_enum_data_t; 107*940d71d2Seschrock 108*940d71d2Seschrock static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 109*940d71d2Seschrock nvlist_t **); 110*940d71d2Seschrock static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 111*940d71d2Seschrock nvlist_t **); 112*940d71d2Seschrock 113*940d71d2Seschrock static const topo_method_t ses_component_methods[] = { 114*940d71d2Seschrock { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, 115*940d71d2Seschrock TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present }, 116*940d71d2Seschrock { NULL } 117*940d71d2Seschrock }; 118*940d71d2Seschrock 119*940d71d2Seschrock static const topo_method_t ses_enclosure_methods[] = { 120*940d71d2Seschrock { TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC, 121*940d71d2Seschrock TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains }, 122*940d71d2Seschrock { NULL } 123*940d71d2Seschrock }; 124*940d71d2Seschrock 125*940d71d2Seschrock static void 126*940d71d2Seschrock ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp) 127*940d71d2Seschrock { 128*940d71d2Seschrock if (--stp->set_refcount == 0) { 129*940d71d2Seschrock ses_snap_rele(stp->set_snap); 130*940d71d2Seschrock ses_close(stp->set_target); 131*940d71d2Seschrock topo_mod_strfree(mod, stp->set_devpath); 132*940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 133*940d71d2Seschrock } 134*940d71d2Seschrock } 135*940d71d2Seschrock 136*940d71d2Seschrock static void 137*940d71d2Seschrock ses_data_free(ses_enum_data_t *sdp) 138*940d71d2Seschrock { 139*940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 140*940d71d2Seschrock ses_enum_chassis_t *cp; 141*940d71d2Seschrock ses_enum_node_t *np; 142*940d71d2Seschrock ses_enum_target_t *tp; 143*940d71d2Seschrock 144*940d71d2Seschrock while ((cp = topo_list_next(&sdp->sed_chassis)) != NULL) { 145*940d71d2Seschrock topo_list_delete(&sdp->sed_chassis, cp); 146*940d71d2Seschrock 147*940d71d2Seschrock while ((np = topo_list_next(&cp->sec_nodes)) != NULL) { 148*940d71d2Seschrock topo_list_delete(&cp->sec_nodes, np); 149*940d71d2Seschrock topo_mod_free(mod, np, sizeof (ses_enum_node_t)); 150*940d71d2Seschrock } 151*940d71d2Seschrock 152*940d71d2Seschrock while ((tp = topo_list_next(&cp->sec_targets)) != NULL) { 153*940d71d2Seschrock topo_list_delete(&cp->sec_targets, tp); 154*940d71d2Seschrock ses_target_free(mod, tp); 155*940d71d2Seschrock } 156*940d71d2Seschrock 157*940d71d2Seschrock topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t)); 158*940d71d2Seschrock } 159*940d71d2Seschrock 160*940d71d2Seschrock disk_list_free(mod, &sdp->sed_disks); 161*940d71d2Seschrock } 162*940d71d2Seschrock 163*940d71d2Seschrock /* 164*940d71d2Seschrock * For enclosure nodes, we have a special contains method. By default, the hc 165*940d71d2Seschrock * walker will compare the node name and instance number to determine if an 166*940d71d2Seschrock * FMRI matches. For enclosures where the enumeration order is impossible to 167*940d71d2Seschrock * predict, we instead use the chassis-id as a unique identifier, and ignore 168*940d71d2Seschrock * the instance number. 169*940d71d2Seschrock */ 170*940d71d2Seschrock static int 171*940d71d2Seschrock fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2) 172*940d71d2Seschrock { 173*940d71d2Seschrock uint8_t v1, v2; 174*940d71d2Seschrock nvlist_t **hcp1, **hcp2; 175*940d71d2Seschrock int err, i; 176*940d71d2Seschrock uint_t nhcp1, nhcp2; 177*940d71d2Seschrock nvlist_t *a1, *a2; 178*940d71d2Seschrock char *c1, *c2; 179*940d71d2Seschrock int mindepth; 180*940d71d2Seschrock 181*940d71d2Seschrock if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 || 182*940d71d2Seschrock nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 || 183*940d71d2Seschrock v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION) 184*940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION)); 185*940d71d2Seschrock 186*940d71d2Seschrock err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1); 187*940d71d2Seschrock err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2); 188*940d71d2Seschrock if (err != 0) 189*940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 190*940d71d2Seschrock 191*940d71d2Seschrock /* 192*940d71d2Seschrock * If the chassis-id doesn't match, then these FMRIs are not 193*940d71d2Seschrock * equivalent. If one of the FMRIs doesn't have a chassis ID, then we 194*940d71d2Seschrock * have no choice but to fall back to the instance ID. 195*940d71d2Seschrock */ 196*940d71d2Seschrock if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 && 197*940d71d2Seschrock nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 && 198*940d71d2Seschrock nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 && 199*940d71d2Seschrock nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) { 200*940d71d2Seschrock if (strcmp(c1, c2) != 0) 201*940d71d2Seschrock return (0); 202*940d71d2Seschrock 203*940d71d2Seschrock mindepth = 1; 204*940d71d2Seschrock } else { 205*940d71d2Seschrock mindepth = 0; 206*940d71d2Seschrock } 207*940d71d2Seschrock 208*940d71d2Seschrock if (nhcp2 < nhcp1) 209*940d71d2Seschrock return (0); 210*940d71d2Seschrock 211*940d71d2Seschrock for (i = 0; i < nhcp1; i++) { 212*940d71d2Seschrock char *nm1 = NULL; 213*940d71d2Seschrock char *nm2 = NULL; 214*940d71d2Seschrock char *id1 = NULL; 215*940d71d2Seschrock char *id2 = NULL; 216*940d71d2Seschrock 217*940d71d2Seschrock (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1); 218*940d71d2Seschrock (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2); 219*940d71d2Seschrock (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1); 220*940d71d2Seschrock (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2); 221*940d71d2Seschrock if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL) 222*940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 223*940d71d2Seschrock 224*940d71d2Seschrock if (strcmp(nm1, nm2) == 0 && 225*940d71d2Seschrock (i < mindepth || strcmp(id1, id2) == 0)) 226*940d71d2Seschrock continue; 227*940d71d2Seschrock 228*940d71d2Seschrock return (0); 229*940d71d2Seschrock } 230*940d71d2Seschrock 231*940d71d2Seschrock return (1); 232*940d71d2Seschrock } 233*940d71d2Seschrock 234*940d71d2Seschrock /*ARGSUSED*/ 235*940d71d2Seschrock static int 236*940d71d2Seschrock ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 237*940d71d2Seschrock nvlist_t *in, nvlist_t **out) 238*940d71d2Seschrock { 239*940d71d2Seschrock int ret; 240*940d71d2Seschrock nvlist_t *nv1, *nv2; 241*940d71d2Seschrock 242*940d71d2Seschrock if (version > TOPO_METH_CONTAINS_VERSION) 243*940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 244*940d71d2Seschrock 245*940d71d2Seschrock if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 || 246*940d71d2Seschrock nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0) 247*940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 248*940d71d2Seschrock 249*940d71d2Seschrock ret = fmri_contains(mod, nv1, nv2); 250*940d71d2Seschrock if (ret < 0) 251*940d71d2Seschrock return (-1); 252*940d71d2Seschrock 253*940d71d2Seschrock if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) { 254*940d71d2Seschrock if (nvlist_add_uint32(*out, TOPO_METH_COMPARE_RET, 255*940d71d2Seschrock ret) == 0) 256*940d71d2Seschrock return (0); 257*940d71d2Seschrock else 258*940d71d2Seschrock nvlist_free(*out); 259*940d71d2Seschrock } 260*940d71d2Seschrock 261*940d71d2Seschrock return (-1); 262*940d71d2Seschrock 263*940d71d2Seschrock } 264*940d71d2Seschrock 265*940d71d2Seschrock /* 266*940d71d2Seschrock * Determine if the element is present. This is somewhat complicated because 267*940d71d2Seschrock * we need to take a new snapshot in order to determine presence, but we don't 268*940d71d2Seschrock * want to be constantly taking SES snapshots if the consumer is going to do a 269*940d71d2Seschrock * series of queries. So we adopt the strategy of assuming that the SES state 270*940d71d2Seschrock * is not going to be rapidly changing, and limit our snapshot frequency to some 271*940d71d2Seschrock * defined bounds. 272*940d71d2Seschrock */ 273*940d71d2Seschrock /*ARGSUSED*/ 274*940d71d2Seschrock static int 275*940d71d2Seschrock ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 276*940d71d2Seschrock nvlist_t *in, nvlist_t **out) 277*940d71d2Seschrock { 278*940d71d2Seschrock nvlist_t *nvl; 279*940d71d2Seschrock struct timeval tv; 280*940d71d2Seschrock ses_enum_target_t *tp = topo_node_getspecific(tn); 281*940d71d2Seschrock uint64_t prev, now; 282*940d71d2Seschrock ses_snap_t *snap; 283*940d71d2Seschrock int err; 284*940d71d2Seschrock uint64_t nodeid, status; 285*940d71d2Seschrock ses_node_t *np; 286*940d71d2Seschrock nvlist_t *props; 287*940d71d2Seschrock boolean_t present; 288*940d71d2Seschrock 289*940d71d2Seschrock if (tp == NULL) 290*940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP)); 291*940d71d2Seschrock 292*940d71d2Seschrock /* 293*940d71d2Seschrock * Determine if we need to take a new snapshot. 294*940d71d2Seschrock */ 295*940d71d2Seschrock if (gettimeofday(&tv, NULL) != 0) { 296*940d71d2Seschrock tv.tv_sec = time(NULL); 297*940d71d2Seschrock tv.tv_usec = 0; 298*940d71d2Seschrock } 299*940d71d2Seschrock 300*940d71d2Seschrock now = tv.tv_sec * 1000 + tv.tv_usec / 1000; 301*940d71d2Seschrock prev = tp->set_snaptime.tv_sec * 1000 + 302*940d71d2Seschrock tp->set_snaptime.tv_usec / 1000; 303*940d71d2Seschrock 304*940d71d2Seschrock if (now - prev > SES_SNAP_FREQ && 305*940d71d2Seschrock (snap = ses_snap_new(tp->set_target)) != NULL) { 306*940d71d2Seschrock if (ses_snap_generation(snap) != 307*940d71d2Seschrock ses_snap_generation(tp->set_snap)) { 308*940d71d2Seschrock /* 309*940d71d2Seschrock * If we find ourselves in this situation, we're in 310*940d71d2Seschrock * trouble. The generation count has changed, which 311*940d71d2Seschrock * indicates that our current topology is out of date. 312*940d71d2Seschrock * But we need to consult the new topology in order to 313*940d71d2Seschrock * determine presence at this moment in time. We can't 314*940d71d2Seschrock * go back and change the topo snapshot in situ, so 315*940d71d2Seschrock * we'll just pretend like the device is present in 316*940d71d2Seschrock * this scenario. 317*940d71d2Seschrock */ 318*940d71d2Seschrock ses_snap_rele(snap); 319*940d71d2Seschrock present = B_TRUE; 320*940d71d2Seschrock goto out; 321*940d71d2Seschrock } else { 322*940d71d2Seschrock ses_snap_rele(tp->set_snap); 323*940d71d2Seschrock tp->set_snap = snap; 324*940d71d2Seschrock } 325*940d71d2Seschrock tp->set_snaptime = tv; 326*940d71d2Seschrock } 327*940d71d2Seschrock 328*940d71d2Seschrock snap = tp->set_snap; 329*940d71d2Seschrock 330*940d71d2Seschrock verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES, 331*940d71d2Seschrock TOPO_PROP_NODE_ID, &nodeid, &err) == 0); 332*940d71d2Seschrock verify((np = ses_node_lookup(snap, nodeid)) != NULL); 333*940d71d2Seschrock verify((props = ses_node_props(np)) != NULL); 334*940d71d2Seschrock verify(nvlist_lookup_uint64(props, 335*940d71d2Seschrock SES_PROP_STATUS_CODE, &status) == 0); 336*940d71d2Seschrock 337*940d71d2Seschrock present = (status != SES_ESC_NOT_INSTALLED); 338*940d71d2Seschrock 339*940d71d2Seschrock out: 340*940d71d2Seschrock if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) 341*940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 342*940d71d2Seschrock 343*940d71d2Seschrock if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET, 344*940d71d2Seschrock present) != 0) { 345*940d71d2Seschrock nvlist_free(nvl); 346*940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 347*940d71d2Seschrock } 348*940d71d2Seschrock 349*940d71d2Seschrock *out = nvl; 350*940d71d2Seschrock 351*940d71d2Seschrock return (0); 352*940d71d2Seschrock } 353*940d71d2Seschrock 354*940d71d2Seschrock /* 355*940d71d2Seschrock * Sets standard properties for a ses node (enclosure or bay). This includes 356*940d71d2Seschrock * setting the FRU to be the same as the resource, as well as setting the 357*940d71d2Seschrock * authority information. 358*940d71d2Seschrock */ 359*940d71d2Seschrock static int 360*940d71d2Seschrock ses_set_standard_props(topo_mod_t *mod, tnode_t *tn, nvlist_t *auth, 361*940d71d2Seschrock uint64_t nodeid, const char *path) 362*940d71d2Seschrock { 363*940d71d2Seschrock int err; 364*940d71d2Seschrock char *product, *chassis; 365*940d71d2Seschrock nvlist_t *fmri; 366*940d71d2Seschrock topo_pgroup_info_t pgi; 367*940d71d2Seschrock 368*940d71d2Seschrock /* 369*940d71d2Seschrock * Set the authority explicitly if specified. 370*940d71d2Seschrock */ 371*940d71d2Seschrock if (auth) { 372*940d71d2Seschrock verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, 373*940d71d2Seschrock &product) == 0); 374*940d71d2Seschrock verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, 375*940d71d2Seschrock &chassis) == 0); 376*940d71d2Seschrock if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 377*940d71d2Seschrock FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product, 378*940d71d2Seschrock &err) != 0 || 379*940d71d2Seschrock topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 380*940d71d2Seschrock FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis, 381*940d71d2Seschrock &err) != 0 || 382*940d71d2Seschrock topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 383*940d71d2Seschrock FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "", 384*940d71d2Seschrock &err) != 0) { 385*940d71d2Seschrock topo_mod_dprintf(mod, "failed to add authority " 386*940d71d2Seschrock "properties: %s", topo_strerror(err)); 387*940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 388*940d71d2Seschrock } 389*940d71d2Seschrock } 390*940d71d2Seschrock 391*940d71d2Seschrock /* 392*940d71d2Seschrock * Copy the resource and set that as the FRU. 393*940d71d2Seschrock */ 394*940d71d2Seschrock if (topo_node_resource(tn, &fmri, &err) != 0) { 395*940d71d2Seschrock topo_mod_dprintf(mod, 396*940d71d2Seschrock "topo_node_resource() failed : %s\n", 397*940d71d2Seschrock topo_strerror(err)); 398*940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 399*940d71d2Seschrock } 400*940d71d2Seschrock 401*940d71d2Seschrock if (topo_node_fru_set(tn, fmri, 0, &err) != 0) { 402*940d71d2Seschrock topo_mod_dprintf(mod, 403*940d71d2Seschrock "topo_node_fru_set() failed : %s\n", 404*940d71d2Seschrock topo_strerror(err)); 405*940d71d2Seschrock nvlist_free(fmri); 406*940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 407*940d71d2Seschrock } 408*940d71d2Seschrock 409*940d71d2Seschrock nvlist_free(fmri); 410*940d71d2Seschrock 411*940d71d2Seschrock /* 412*940d71d2Seschrock * Set the SES-specific properties so that consumers can query 413*940d71d2Seschrock * additional information about the particular SES element. 414*940d71d2Seschrock */ 415*940d71d2Seschrock pgi.tpi_name = TOPO_PGROUP_SES; 416*940d71d2Seschrock pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 417*940d71d2Seschrock pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 418*940d71d2Seschrock pgi.tpi_version = TOPO_VERSION; 419*940d71d2Seschrock if (topo_pgroup_create(tn, &pgi, &err) != 0) { 420*940d71d2Seschrock topo_mod_dprintf(mod, "failed to create propgroup " 421*940d71d2Seschrock "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err)); 422*940d71d2Seschrock return (-1); 423*940d71d2Seschrock } 424*940d71d2Seschrock 425*940d71d2Seschrock if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES, 426*940d71d2Seschrock TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE, 427*940d71d2Seschrock nodeid, &err) != 0) { 428*940d71d2Seschrock topo_mod_dprintf(mod, 429*940d71d2Seschrock "failed to create property %s: %s\n", 430*940d71d2Seschrock TOPO_PROP_NODE_ID, topo_strerror(err)); 431*940d71d2Seschrock return (-1); 432*940d71d2Seschrock } 433*940d71d2Seschrock 434*940d71d2Seschrock if (topo_prop_set_string(tn, TOPO_PGROUP_SES, 435*940d71d2Seschrock TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE, 436*940d71d2Seschrock path, &err) != 0) { 437*940d71d2Seschrock topo_mod_dprintf(mod, 438*940d71d2Seschrock "failed to create property %s: %s\n", 439*940d71d2Seschrock TOPO_PROP_TARGET_PATH, topo_strerror(err)); 440*940d71d2Seschrock return (-1); 441*940d71d2Seschrock } 442*940d71d2Seschrock 443*940d71d2Seschrock return (0); 444*940d71d2Seschrock } 445*940d71d2Seschrock 446*940d71d2Seschrock /* 447*940d71d2Seschrock * Callback to add a disk to a given bay. We first check the status-code to 448*940d71d2Seschrock * determine if a disk is present, ignoring those that aren't in an appropriate 449*940d71d2Seschrock * state. We then scan the sas-phys array to determine the attached SAS 450*940d71d2Seschrock * address. We create a disk node regardless of whether the SES target is SAS 451*940d71d2Seschrock * and supports the necessary pages. If we do find a SAS address, we correlate 452*940d71d2Seschrock * this to the corresponding Solaris device node to fill in the rest of the 453*940d71d2Seschrock * data. 454*940d71d2Seschrock */ 455*940d71d2Seschrock static int 456*940d71d2Seschrock ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props) 457*940d71d2Seschrock { 458*940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 459*940d71d2Seschrock uint64_t status; 460*940d71d2Seschrock nvlist_t **sas; 461*940d71d2Seschrock uint_t s, nsas; 462*940d71d2Seschrock uint64_t addr; 463*940d71d2Seschrock char buf[17]; 464*940d71d2Seschrock 465*940d71d2Seschrock /* 466*940d71d2Seschrock * Skip devices that are not in a present (and possibly damaged) state. 467*940d71d2Seschrock */ 468*940d71d2Seschrock if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0) 469*940d71d2Seschrock return (0); 470*940d71d2Seschrock 471*940d71d2Seschrock if (status != SES_ESC_OK && 472*940d71d2Seschrock status != SES_ESC_CRITICAL && 473*940d71d2Seschrock status != SES_ESC_NONCRITICAL && 474*940d71d2Seschrock status != SES_ESC_UNRECOVERABLE && 475*940d71d2Seschrock status != SES_ESC_NO_ACCESS) 476*940d71d2Seschrock return (0); 477*940d71d2Seschrock 478*940d71d2Seschrock topo_mod_dprintf(mod, "found attached disk"); 479*940d71d2Seschrock 480*940d71d2Seschrock /* 481*940d71d2Seschrock * Create the disk range. 482*940d71d2Seschrock */ 483*940d71d2Seschrock if (topo_node_range_create(mod, pnode, DISK, 0, 1) != 0) { 484*940d71d2Seschrock topo_mod_dprintf(mod, 485*940d71d2Seschrock "topo_node_create_range() failed: %s", 486*940d71d2Seschrock topo_mod_errmsg(mod)); 487*940d71d2Seschrock return (-1); 488*940d71d2Seschrock } 489*940d71d2Seschrock 490*940d71d2Seschrock /* 491*940d71d2Seschrock * Look through all SAS addresses and attempt to correlate them to a 492*940d71d2Seschrock * known Solaris device. If we don't find a matching node, then we 493*940d71d2Seschrock * don't enumerate the disk node. 494*940d71d2Seschrock */ 495*940d71d2Seschrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 496*940d71d2Seschrock &sas, &nsas) != 0) 497*940d71d2Seschrock return (0); 498*940d71d2Seschrock 499*940d71d2Seschrock for (s = 0; s < nsas; s++) { 500*940d71d2Seschrock verify(nvlist_lookup_uint64(sas[s], 501*940d71d2Seschrock SES_SAS_PROP_ADDR, &addr) == 0); 502*940d71d2Seschrock if (addr == 0) 503*940d71d2Seschrock continue; 504*940d71d2Seschrock 505*940d71d2Seschrock (void) snprintf(buf, sizeof (buf), "%llx", addr); 506*940d71d2Seschrock 507*940d71d2Seschrock if (disk_declare_addr(mod, pnode, &sdp->sed_disks, 508*940d71d2Seschrock buf) != 0) 509*940d71d2Seschrock return (-1); 510*940d71d2Seschrock } 511*940d71d2Seschrock 512*940d71d2Seschrock return (0); 513*940d71d2Seschrock } 514*940d71d2Seschrock 515*940d71d2Seschrock /* 516*940d71d2Seschrock * Callback to create a basic node (bay, psu, fan, or controller). 517*940d71d2Seschrock */ 518*940d71d2Seschrock static int 519*940d71d2Seschrock ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp, 520*940d71d2Seschrock tnode_t *pnode, const char *nodename, const char *labelname) 521*940d71d2Seschrock { 522*940d71d2Seschrock ses_node_t *np = snp->sen_node; 523*940d71d2Seschrock ses_node_t *agg; 524*940d71d2Seschrock uint64_t instance = snp->sen_instance; 525*940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 526*940d71d2Seschrock nvlist_t *props, *aprops; 527*940d71d2Seschrock nvlist_t *auth = NULL, *fmri = NULL; 528*940d71d2Seschrock tnode_t *tn; 529*940d71d2Seschrock char label[128]; 530*940d71d2Seschrock int err; 531*940d71d2Seschrock char *part = NULL, *serial = NULL; 532*940d71d2Seschrock char *classdesc; 533*940d71d2Seschrock 534*940d71d2Seschrock props = ses_node_props(np); 535*940d71d2Seschrock 536*940d71d2Seschrock (void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part); 537*940d71d2Seschrock (void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial); 538*940d71d2Seschrock 539*940d71d2Seschrock topo_mod_dprintf(mod, "adding %s %llu", nodename, instance); 540*940d71d2Seschrock 541*940d71d2Seschrock /* 542*940d71d2Seschrock * Create the node. The interesting information is all copied from the 543*940d71d2Seschrock * parent enclosure node, so there is not much to do. 544*940d71d2Seschrock */ 545*940d71d2Seschrock if ((auth = topo_mod_auth(mod, pnode)) == NULL) 546*940d71d2Seschrock goto error; 547*940d71d2Seschrock 548*940d71d2Seschrock if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 549*940d71d2Seschrock nodename, (topo_instance_t)instance, NULL, auth, part, NULL, 550*940d71d2Seschrock serial)) == NULL) { 551*940d71d2Seschrock topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 552*940d71d2Seschrock topo_mod_errmsg(mod)); 553*940d71d2Seschrock goto error; 554*940d71d2Seschrock } 555*940d71d2Seschrock 556*940d71d2Seschrock if ((tn = topo_node_bind(mod, pnode, nodename, 557*940d71d2Seschrock instance, fmri)) == NULL) { 558*940d71d2Seschrock topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 559*940d71d2Seschrock topo_mod_errmsg(mod)); 560*940d71d2Seschrock goto error; 561*940d71d2Seschrock } 562*940d71d2Seschrock 563*940d71d2Seschrock /* 564*940d71d2Seschrock * If the aggregate gives us class description, then use that instead 565*940d71d2Seschrock * of the default label name. 566*940d71d2Seschrock */ 567*940d71d2Seschrock agg = ses_node_parent(np); 568*940d71d2Seschrock aprops = ses_node_props(agg); 569*940d71d2Seschrock if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION, 570*940d71d2Seschrock &classdesc) == 0 && classdesc[0] != '\0') 571*940d71d2Seschrock labelname = classdesc; 572*940d71d2Seschrock 573*940d71d2Seschrock (void) snprintf(label, sizeof (label), "%s %llu", labelname, instance); 574*940d71d2Seschrock if (topo_node_label_set(tn, label, &err) != 0) 575*940d71d2Seschrock goto error; 576*940d71d2Seschrock 577*940d71d2Seschrock if (ses_set_standard_props(mod, tn, NULL, ses_node_id(np), 578*940d71d2Seschrock snp->sen_target->set_devpath) != 0) 579*940d71d2Seschrock goto error; 580*940d71d2Seschrock 581*940d71d2Seschrock if (strcmp(nodename, "bay") == 0) { 582*940d71d2Seschrock if (ses_create_disk(sdp, tn, props) != 0) 583*940d71d2Seschrock goto error; 584*940d71d2Seschrock } else { 585*940d71d2Seschrock /* 586*940d71d2Seschrock * Only fan/psu nodes have a 'present' method. Bay nodes are 587*940d71d2Seschrock * always present, and disk nodes are present by virtue of being 588*940d71d2Seschrock * enumerated. 589*940d71d2Seschrock */ 590*940d71d2Seschrock if (topo_method_register(mod, tn, ses_component_methods) != 0) { 591*940d71d2Seschrock topo_mod_dprintf(mod, 592*940d71d2Seschrock "topo_method_register() failed: %s", 593*940d71d2Seschrock topo_mod_errmsg(mod)); 594*940d71d2Seschrock goto error; 595*940d71d2Seschrock } 596*940d71d2Seschrock 597*940d71d2Seschrock snp->sen_target->set_refcount++; 598*940d71d2Seschrock topo_node_setspecific(tn, snp->sen_target); 599*940d71d2Seschrock } 600*940d71d2Seschrock 601*940d71d2Seschrock nvlist_free(auth); 602*940d71d2Seschrock nvlist_free(fmri); 603*940d71d2Seschrock return (0); 604*940d71d2Seschrock 605*940d71d2Seschrock error: 606*940d71d2Seschrock nvlist_free(auth); 607*940d71d2Seschrock nvlist_free(fmri); 608*940d71d2Seschrock return (-1); 609*940d71d2Seschrock } 610*940d71d2Seschrock 611*940d71d2Seschrock /* 612*940d71d2Seschrock * Instantiate any children of a given type. 613*940d71d2Seschrock */ 614*940d71d2Seschrock static int 615*940d71d2Seschrock ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type, 616*940d71d2Seschrock const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp) 617*940d71d2Seschrock { 618*940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 619*940d71d2Seschrock boolean_t found; 620*940d71d2Seschrock uint64_t max; 621*940d71d2Seschrock ses_enum_node_t *snp; 622*940d71d2Seschrock 623*940d71d2Seschrock /* 624*940d71d2Seschrock * First go through and count how many matching nodes we have. 625*940d71d2Seschrock */ 626*940d71d2Seschrock max = 0; 627*940d71d2Seschrock found = B_FALSE; 628*940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 629*940d71d2Seschrock snp = topo_list_next(snp)) { 630*940d71d2Seschrock if (snp->sen_type == type) { 631*940d71d2Seschrock found = B_TRUE; 632*940d71d2Seschrock if (snp->sen_instance > max) 633*940d71d2Seschrock max = snp->sen_instance; 634*940d71d2Seschrock } 635*940d71d2Seschrock } 636*940d71d2Seschrock 637*940d71d2Seschrock /* 638*940d71d2Seschrock * No enclosure should export both DEVICE and ARRAY_DEVICE elements. 639*940d71d2Seschrock * Since we map both of these to 'disk', if an enclosure does this, we 640*940d71d2Seschrock * just ignore the array elements. 641*940d71d2Seschrock */ 642*940d71d2Seschrock if (!found || 643*940d71d2Seschrock (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev)) 644*940d71d2Seschrock return (0); 645*940d71d2Seschrock 646*940d71d2Seschrock topo_mod_dprintf(mod, "%s: creating %llu %s nodes", 647*940d71d2Seschrock cp->sec_csn, max, nodename); 648*940d71d2Seschrock 649*940d71d2Seschrock if (topo_node_range_create(mod, pnode, 650*940d71d2Seschrock nodename, 0, max) != 0) { 651*940d71d2Seschrock topo_mod_dprintf(mod, 652*940d71d2Seschrock "topo_node_create_range() failed: %s", 653*940d71d2Seschrock topo_mod_errmsg(mod)); 654*940d71d2Seschrock return (-1); 655*940d71d2Seschrock } 656*940d71d2Seschrock 657*940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 658*940d71d2Seschrock snp = topo_list_next(snp)) { 659*940d71d2Seschrock if (snp->sen_type == type) { 660*940d71d2Seschrock if (ses_create_generic(sdp, snp, pnode, 661*940d71d2Seschrock nodename, defaultlabel) != 0) 662*940d71d2Seschrock return (-1); 663*940d71d2Seschrock } 664*940d71d2Seschrock } 665*940d71d2Seschrock 666*940d71d2Seschrock return (0); 667*940d71d2Seschrock } 668*940d71d2Seschrock 669*940d71d2Seschrock /* 670*940d71d2Seschrock * Instantiate a new chassis instance in the topology. 671*940d71d2Seschrock */ 672*940d71d2Seschrock static int 673*940d71d2Seschrock ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp) 674*940d71d2Seschrock { 675*940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 676*940d71d2Seschrock nvlist_t *props; 677*940d71d2Seschrock char *raw_manufacturer, *raw_model, *raw_revision; 678*940d71d2Seschrock char *manufacturer = NULL, *model = NULL, *product = NULL; 679*940d71d2Seschrock char *revision = NULL; 680*940d71d2Seschrock char *serial; 681*940d71d2Seschrock size_t prodlen; 682*940d71d2Seschrock tnode_t *tn; 683*940d71d2Seschrock nvlist_t *fmri = NULL, *auth = NULL; 684*940d71d2Seschrock int ret = -1; 685*940d71d2Seschrock ses_enum_node_t *snp; 686*940d71d2Seschrock 687*940d71d2Seschrock /* 688*940d71d2Seschrock * Check to see if there are any devices presennt in the chassis. If 689*940d71d2Seschrock * not, ignore the chassis alltogether. This is most useful for 690*940d71d2Seschrock * ignoring internal HBAs that present a SES target but don't actually 691*940d71d2Seschrock * manage any of the devices. 692*940d71d2Seschrock */ 693*940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 694*940d71d2Seschrock snp = topo_list_next(snp)) { 695*940d71d2Seschrock if (snp->sen_type == SES_ET_DEVICE || 696*940d71d2Seschrock snp->sen_type == SES_ET_ARRAY_DEVICE) 697*940d71d2Seschrock break; 698*940d71d2Seschrock } 699*940d71d2Seschrock 700*940d71d2Seschrock if (snp == NULL) 701*940d71d2Seschrock return (0); 702*940d71d2Seschrock 703*940d71d2Seschrock props = ses_node_props(cp->sec_enclosure); 704*940d71d2Seschrock 705*940d71d2Seschrock /* 706*940d71d2Seschrock * We use the following property mappings: 707*940d71d2Seschrock * 708*940d71d2Seschrock * manufacturer vendor-id 709*940d71d2Seschrock * model product-id 710*940d71d2Seschrock * serial-number libses-chassis-serial 711*940d71d2Seschrock */ 712*940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_VID, 713*940d71d2Seschrock &raw_manufacturer) == 0); 714*940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0); 715*940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_REV, 716*940d71d2Seschrock &raw_revision) == 0); 717*940d71d2Seschrock verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0); 718*940d71d2Seschrock 719*940d71d2Seschrock /* 720*940d71d2Seschrock * To construct the authority information, we 'clean' each string by 721*940d71d2Seschrock * removing any offensive characters and trimmming whitespace. For the 722*940d71d2Seschrock * 'product-id', we use a concatenation of 'manufacturer-model'. We 723*940d71d2Seschrock * also take the numerical serial number and convert it to a string. 724*940d71d2Seschrock */ 725*940d71d2Seschrock if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL || 726*940d71d2Seschrock (model = disk_auth_clean(mod, raw_model)) == NULL || 727*940d71d2Seschrock (revision = disk_auth_clean(mod, raw_revision)) == NULL) { 728*940d71d2Seschrock goto error; 729*940d71d2Seschrock } 730*940d71d2Seschrock 731*940d71d2Seschrock prodlen = strlen(manufacturer) + strlen(model) + 2; 732*940d71d2Seschrock if ((product = topo_mod_alloc(mod, prodlen)) == NULL) 733*940d71d2Seschrock goto error; 734*940d71d2Seschrock 735*940d71d2Seschrock (void) snprintf(product, prodlen, "%s-%s", manufacturer, model); 736*940d71d2Seschrock 737*940d71d2Seschrock /* 738*940d71d2Seschrock * Construct the topo node and bind it to our parent. 739*940d71d2Seschrock */ 740*940d71d2Seschrock if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0) 741*940d71d2Seschrock goto error; 742*940d71d2Seschrock 743*940d71d2Seschrock if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 || 744*940d71d2Seschrock nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) { 745*940d71d2Seschrock (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 746*940d71d2Seschrock goto error; 747*940d71d2Seschrock } 748*940d71d2Seschrock 749*940d71d2Seschrock /* 750*940d71d2Seschrock * We pass NULL for the parent FMRI because there is no resource 751*940d71d2Seschrock * associated with it. For the toplevel enclosure, we leave the 752*940d71d2Seschrock * serial/part/revision portions empty, which are reserved for 753*940d71d2Seschrock * individual components within the chassis. 754*940d71d2Seschrock */ 755*940d71d2Seschrock if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION, 756*940d71d2Seschrock SES_ENCLOSURE, cp->sec_instance, NULL, auth, 757*940d71d2Seschrock model, revision, serial)) == NULL) { 758*940d71d2Seschrock topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 759*940d71d2Seschrock topo_mod_errmsg(mod)); 760*940d71d2Seschrock goto error; 761*940d71d2Seschrock } 762*940d71d2Seschrock 763*940d71d2Seschrock if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE, 764*940d71d2Seschrock cp->sec_instance, fmri)) == NULL) { 765*940d71d2Seschrock topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 766*940d71d2Seschrock topo_mod_errmsg(mod)); 767*940d71d2Seschrock goto error; 768*940d71d2Seschrock } 769*940d71d2Seschrock 770*940d71d2Seschrock if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 771*940d71d2Seschrock topo_mod_dprintf(mod, 772*940d71d2Seschrock "topo_method_register() failed: %s", 773*940d71d2Seschrock topo_mod_errmsg(mod)); 774*940d71d2Seschrock goto error; 775*940d71d2Seschrock } 776*940d71d2Seschrock 777*940d71d2Seschrock if (ses_set_standard_props(mod, tn, auth, 778*940d71d2Seschrock ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0) 779*940d71d2Seschrock goto error; 780*940d71d2Seschrock 781*940d71d2Seschrock /* 782*940d71d2Seschrock * Create the nodes for power supplies, fans, and devices. 783*940d71d2Seschrock */ 784*940d71d2Seschrock if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY, 785*940d71d2Seschrock PSU, "PSU", cp) != 0 || 786*940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_COOLING, 787*940d71d2Seschrock FAN, "FAN", cp) != 0 || 788*940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 789*940d71d2Seschrock CONTROLLER, "CONTROLLER", cp) != 0 || 790*940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_DEVICE, 791*940d71d2Seschrock BAY, "BAY", cp) != 0 || 792*940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 793*940d71d2Seschrock BAY, "BAY", cp) != 0) 794*940d71d2Seschrock goto error; 795*940d71d2Seschrock 796*940d71d2Seschrock ret = 0; 797*940d71d2Seschrock error: 798*940d71d2Seschrock topo_mod_strfree(mod, manufacturer); 799*940d71d2Seschrock topo_mod_strfree(mod, model); 800*940d71d2Seschrock topo_mod_strfree(mod, revision); 801*940d71d2Seschrock topo_mod_strfree(mod, product); 802*940d71d2Seschrock 803*940d71d2Seschrock nvlist_free(fmri); 804*940d71d2Seschrock nvlist_free(auth); 805*940d71d2Seschrock return (ret); 806*940d71d2Seschrock } 807*940d71d2Seschrock 808*940d71d2Seschrock /* 809*940d71d2Seschrock * Gather nodes from the current SES target into our chassis list, merging the 810*940d71d2Seschrock * results if necessary. 811*940d71d2Seschrock */ 812*940d71d2Seschrock static ses_walk_action_t 813*940d71d2Seschrock ses_enum_gather(ses_node_t *np, void *data) 814*940d71d2Seschrock { 815*940d71d2Seschrock nvlist_t *props = ses_node_props(np); 816*940d71d2Seschrock ses_enum_data_t *sdp = data; 817*940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 818*940d71d2Seschrock ses_enum_chassis_t *cp; 819*940d71d2Seschrock ses_enum_node_t *snp; 820*940d71d2Seschrock char *csn; 821*940d71d2Seschrock uint64_t instance, type; 822*940d71d2Seschrock 823*940d71d2Seschrock if (ses_node_type(np) == SES_NODE_ENCLOSURE) { 824*940d71d2Seschrock /* 825*940d71d2Seschrock * If we have already identified the chassis for this target, 826*940d71d2Seschrock * then this is a secondary enclosure and we should ignore it, 827*940d71d2Seschrock * along with the rest of the tree (since this is depth-first). 828*940d71d2Seschrock */ 829*940d71d2Seschrock if (sdp->sed_current != NULL) 830*940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 831*940d71d2Seschrock 832*940d71d2Seschrock /* 833*940d71d2Seschrock * Go through the list of chassis we have seen so far and see 834*940d71d2Seschrock * if this serial number matches one of the known values. 835*940d71d2Seschrock */ 836*940d71d2Seschrock if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, 837*940d71d2Seschrock &csn) != 0) 838*940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 839*940d71d2Seschrock 840*940d71d2Seschrock for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 841*940d71d2Seschrock cp = topo_list_next(cp)) { 842*940d71d2Seschrock if (strcmp(cp->sec_csn, csn) == 0) { 843*940d71d2Seschrock topo_mod_dprintf(mod, "%s: part of already " 844*940d71d2Seschrock "known chassis %s", sdp->sed_name, csn); 845*940d71d2Seschrock break; 846*940d71d2Seschrock } 847*940d71d2Seschrock } 848*940d71d2Seschrock 849*940d71d2Seschrock if (cp == NULL) { 850*940d71d2Seschrock topo_mod_dprintf(mod, "%s: creating chassis %s", 851*940d71d2Seschrock sdp->sed_name, csn); 852*940d71d2Seschrock 853*940d71d2Seschrock if ((cp = topo_mod_zalloc(mod, 854*940d71d2Seschrock sizeof (ses_enum_chassis_t))) == NULL) 855*940d71d2Seschrock goto error; 856*940d71d2Seschrock 857*940d71d2Seschrock cp->sec_csn = csn; 858*940d71d2Seschrock cp->sec_enclosure = np; 859*940d71d2Seschrock cp->sec_target = sdp->sed_target; 860*940d71d2Seschrock cp->sec_instance = sdp->sed_instance++; 861*940d71d2Seschrock topo_list_append(&sdp->sed_chassis, cp); 862*940d71d2Seschrock } 863*940d71d2Seschrock 864*940d71d2Seschrock topo_list_append(&cp->sec_targets, sdp->sed_target); 865*940d71d2Seschrock sdp->sed_current = cp; 866*940d71d2Seschrock 867*940d71d2Seschrock } else if (ses_node_type(np) == SES_NODE_ELEMENT) { 868*940d71d2Seschrock /* 869*940d71d2Seschrock * If we haven't yet seen an enclosure node and identified the 870*940d71d2Seschrock * current chassis, something is very wrong; bail out. 871*940d71d2Seschrock */ 872*940d71d2Seschrock if (sdp->sed_current == NULL) 873*940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 874*940d71d2Seschrock 875*940d71d2Seschrock /* 876*940d71d2Seschrock * If this isn't one of the element types we care about, then 877*940d71d2Seschrock * ignore it. 878*940d71d2Seschrock */ 879*940d71d2Seschrock verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, 880*940d71d2Seschrock &type) == 0); 881*940d71d2Seschrock if (type != SES_ET_DEVICE && 882*940d71d2Seschrock type != SES_ET_ARRAY_DEVICE && 883*940d71d2Seschrock type != SES_ET_COOLING && 884*940d71d2Seschrock type != SES_ET_POWER_SUPPLY && 885*940d71d2Seschrock type != SES_ET_ESC_ELECTRONICS) 886*940d71d2Seschrock return (SES_WALK_ACTION_CONTINUE); 887*940d71d2Seschrock 888*940d71d2Seschrock /* 889*940d71d2Seschrock * Get the current instance number and see if we already know 890*940d71d2Seschrock * about this element. If so, it means we have multiple paths 891*940d71d2Seschrock * to the same elements, and we should ignore the current path. 892*940d71d2Seschrock */ 893*940d71d2Seschrock verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX, 894*940d71d2Seschrock &instance) == 0); 895*940d71d2Seschrock if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE) 896*940d71d2Seschrock (void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER, 897*940d71d2Seschrock &instance); 898*940d71d2Seschrock 899*940d71d2Seschrock cp = sdp->sed_current; 900*940d71d2Seschrock 901*940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 902*940d71d2Seschrock snp = topo_list_next(snp)) { 903*940d71d2Seschrock if (snp->sen_type == type && 904*940d71d2Seschrock snp->sen_instance == instance) 905*940d71d2Seschrock return (SES_WALK_ACTION_CONTINUE); 906*940d71d2Seschrock } 907*940d71d2Seschrock 908*940d71d2Seschrock if ((snp = topo_mod_zalloc(mod, 909*940d71d2Seschrock sizeof (ses_enum_node_t))) == NULL) 910*940d71d2Seschrock goto error; 911*940d71d2Seschrock 912*940d71d2Seschrock topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)", 913*940d71d2Seschrock sdp->sed_name, type, instance); 914*940d71d2Seschrock snp->sen_node = np; 915*940d71d2Seschrock snp->sen_type = type; 916*940d71d2Seschrock snp->sen_instance = instance; 917*940d71d2Seschrock snp->sen_target = sdp->sed_target; 918*940d71d2Seschrock topo_list_append(&cp->sec_nodes, snp); 919*940d71d2Seschrock 920*940d71d2Seschrock if (type == SES_ET_DEVICE) 921*940d71d2Seschrock cp->sec_hasdev = B_TRUE; 922*940d71d2Seschrock } 923*940d71d2Seschrock 924*940d71d2Seschrock return (SES_WALK_ACTION_CONTINUE); 925*940d71d2Seschrock 926*940d71d2Seschrock error: 927*940d71d2Seschrock sdp->sed_errno = -1; 928*940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 929*940d71d2Seschrock } 930*940d71d2Seschrock 931*940d71d2Seschrock static int 932*940d71d2Seschrock ses_process_dir(const char *dirpath, ses_enum_data_t *sdp) 933*940d71d2Seschrock { 934*940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 935*940d71d2Seschrock DIR *dir; 936*940d71d2Seschrock struct dirent *dp; 937*940d71d2Seschrock char path[PATH_MAX]; 938*940d71d2Seschrock ses_enum_target_t *stp; 939*940d71d2Seschrock int err = -1; 940*940d71d2Seschrock 941*940d71d2Seschrock /* 942*940d71d2Seschrock * Open the SES target directory and iterate over any available 943*940d71d2Seschrock * targets. 944*940d71d2Seschrock */ 945*940d71d2Seschrock if ((dir = opendir(dirpath)) == NULL) { 946*940d71d2Seschrock /* 947*940d71d2Seschrock * If the SES target directory does not exist, then return as if 948*940d71d2Seschrock * there are no active targets. 949*940d71d2Seschrock */ 950*940d71d2Seschrock topo_mod_dprintf(mod, "failed to open ses " 951*940d71d2Seschrock "directory '%s'", dirpath); 952*940d71d2Seschrock return (0); 953*940d71d2Seschrock } 954*940d71d2Seschrock 955*940d71d2Seschrock while ((dp = readdir(dir)) != NULL) { 956*940d71d2Seschrock if (strcmp(dp->d_name, ".") == 0 || 957*940d71d2Seschrock strcmp(dp->d_name, "..") == 0) 958*940d71d2Seschrock continue; 959*940d71d2Seschrock 960*940d71d2Seschrock /* 961*940d71d2Seschrock * Create a new target instance and take a snapshot. 962*940d71d2Seschrock */ 963*940d71d2Seschrock if ((stp = topo_mod_zalloc(mod, 964*940d71d2Seschrock sizeof (ses_enum_target_t))) == NULL) 965*940d71d2Seschrock goto error; 966*940d71d2Seschrock 967*940d71d2Seschrock (void) snprintf(path, sizeof (path), "%s/%s", dirpath, 968*940d71d2Seschrock dp->d_name); 969*940d71d2Seschrock 970*940d71d2Seschrock /* 971*940d71d2Seschrock * We keep track of the SES device path and export it on a 972*940d71d2Seschrock * per-node basis to allow higher level software to get to the 973*940d71d2Seschrock * corresponding SES state. 974*940d71d2Seschrock */ 975*940d71d2Seschrock if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) { 976*940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 977*940d71d2Seschrock goto error; 978*940d71d2Seschrock } 979*940d71d2Seschrock 980*940d71d2Seschrock if ((stp->set_target = 981*940d71d2Seschrock ses_open(LIBSES_VERSION, path)) == NULL) { 982*940d71d2Seschrock topo_mod_dprintf(mod, "failed to open ses target " 983*940d71d2Seschrock "'%s': %s", dp->d_name, ses_errmsg()); 984*940d71d2Seschrock topo_mod_strfree(mod, stp->set_devpath); 985*940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 986*940d71d2Seschrock continue; 987*940d71d2Seschrock } 988*940d71d2Seschrock 989*940d71d2Seschrock stp->set_refcount = 1; 990*940d71d2Seschrock sdp->sed_target = stp; 991*940d71d2Seschrock stp->set_snap = ses_snap_hold(stp->set_target); 992*940d71d2Seschrock if (gettimeofday(&stp->set_snaptime, NULL) != 0) 993*940d71d2Seschrock stp->set_snaptime.tv_sec = time(NULL); 994*940d71d2Seschrock 995*940d71d2Seschrock /* 996*940d71d2Seschrock * Enumerate over all SES elements and merge them into the 997*940d71d2Seschrock * correct ses_enum_chassis_t. 998*940d71d2Seschrock */ 999*940d71d2Seschrock sdp->sed_current = NULL; 1000*940d71d2Seschrock sdp->sed_errno = 0; 1001*940d71d2Seschrock sdp->sed_name = dp->d_name; 1002*940d71d2Seschrock (void) ses_walk(stp->set_snap, ses_enum_gather, sdp); 1003*940d71d2Seschrock 1004*940d71d2Seschrock if (sdp->sed_errno != 0) 1005*940d71d2Seschrock goto error; 1006*940d71d2Seschrock } 1007*940d71d2Seschrock 1008*940d71d2Seschrock err = 0; 1009*940d71d2Seschrock error: 1010*940d71d2Seschrock closedir(dir); 1011*940d71d2Seschrock return (err); 1012*940d71d2Seschrock } 1013*940d71d2Seschrock 1014*940d71d2Seschrock static void 1015*940d71d2Seschrock ses_release(topo_mod_t *mod, tnode_t *tn) 1016*940d71d2Seschrock { 1017*940d71d2Seschrock ses_enum_target_t *stp; 1018*940d71d2Seschrock 1019*940d71d2Seschrock if ((stp = topo_node_getspecific(tn)) != NULL) 1020*940d71d2Seschrock ses_target_free(mod, stp); 1021*940d71d2Seschrock } 1022*940d71d2Seschrock 1023*940d71d2Seschrock /*ARGSUSED*/ 1024*940d71d2Seschrock static int 1025*940d71d2Seschrock ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 1026*940d71d2Seschrock topo_instance_t min, topo_instance_t max, void *arg, void *notused) 1027*940d71d2Seschrock { 1028*940d71d2Seschrock ses_enum_chassis_t *cp; 1029*940d71d2Seschrock ses_enum_data_t data; 1030*940d71d2Seschrock 1031*940d71d2Seschrock /* 1032*940d71d2Seschrock * Check to make sure we're being invoked sensibly, and that we're not 1033*940d71d2Seschrock * being invoked as part of a post-processing step. 1034*940d71d2Seschrock */ 1035*940d71d2Seschrock if (strcmp(name, SES_ENCLOSURE) != 0 || 1036*940d71d2Seschrock strcmp(topo_node_name(rnode), FM_FMRI_SCHEME_HC) != 0) 1037*940d71d2Seschrock return (0); 1038*940d71d2Seschrock 1039*940d71d2Seschrock (void) memset(&data, 0, sizeof (data)); 1040*940d71d2Seschrock data.sed_mod = mod; 1041*940d71d2Seschrock 1042*940d71d2Seschrock if (disk_list_gather(mod, &data.sed_disks) != 0) 1043*940d71d2Seschrock return (-1); 1044*940d71d2Seschrock 1045*940d71d2Seschrock /* 1046*940d71d2Seschrock * We search both the ses(7D) and sgen(7D) locations, so we are 1047*940d71d2Seschrock * independent of any particular driver class bindings. 1048*940d71d2Seschrock */ 1049*940d71d2Seschrock if (ses_process_dir("/dev/es", &data) != 0 || 1050*940d71d2Seschrock ses_process_dir("/dev/scsi/ses", &data) != 0) 1051*940d71d2Seschrock goto error; 1052*940d71d2Seschrock 1053*940d71d2Seschrock /* 1054*940d71d2Seschrock * Iterate over known chassis and create the necessary nodes. 1055*940d71d2Seschrock */ 1056*940d71d2Seschrock for (cp = topo_list_next(&data.sed_chassis); cp != NULL; 1057*940d71d2Seschrock cp = topo_list_next(cp)) { 1058*940d71d2Seschrock if (ses_create_chassis(&data, rnode, cp) != 0) 1059*940d71d2Seschrock goto error; 1060*940d71d2Seschrock } 1061*940d71d2Seschrock 1062*940d71d2Seschrock ses_data_free(&data); 1063*940d71d2Seschrock return (0); 1064*940d71d2Seschrock 1065*940d71d2Seschrock error: 1066*940d71d2Seschrock ses_data_free(&data); 1067*940d71d2Seschrock return (-1); 1068*940d71d2Seschrock } 1069*940d71d2Seschrock 1070*940d71d2Seschrock static const topo_modops_t ses_ops = 1071*940d71d2Seschrock { ses_enum, ses_release }; 1072*940d71d2Seschrock 1073*940d71d2Seschrock static topo_modinfo_t ses_info = 1074*940d71d2Seschrock { SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops }; 1075*940d71d2Seschrock 1076*940d71d2Seschrock /*ARGSUSED*/ 1077*940d71d2Seschrock int 1078*940d71d2Seschrock _topo_init(topo_mod_t *mod, topo_version_t version) 1079*940d71d2Seschrock { 1080*940d71d2Seschrock if (getenv("TOPOSESDEBUG") != NULL) 1081*940d71d2Seschrock topo_mod_setdebug(mod); 1082*940d71d2Seschrock 1083*940d71d2Seschrock topo_mod_dprintf(mod, "initializing %s enumerator\n", 1084*940d71d2Seschrock SES_ENCLOSURE); 1085*940d71d2Seschrock 1086*940d71d2Seschrock return (topo_mod_register(mod, &ses_info, TOPO_VERSION)); 1087*940d71d2Seschrock } 1088*940d71d2Seschrock 1089*940d71d2Seschrock void 1090*940d71d2Seschrock _topo_fini(topo_mod_t *mod) 1091*940d71d2Seschrock { 1092*940d71d2Seschrock topo_mod_unregister(mod); 1093*940d71d2Seschrock } 1094