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 /* 23940d71d2Seschrock * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24940d71d2Seschrock * Use is subject to license terms. 25940d71d2Seschrock */ 26940d71d2Seschrock 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> 36940d71d2Seschrock #include <sys/scsi/scsi_types.h> 37d91236feSeschrock 38940d71d2Seschrock #include "disk.h" 39d91236feSeschrock #include "ses.h" 40940d71d2Seschrock 41940d71d2Seschrock #define SES_VERSION 1 42940d71d2Seschrock 43940d71d2Seschrock #define SES_SNAP_FREQ 1000 /* in milliseconds */ 44940d71d2Seschrock 45d91236feSeschrock #define SES_STATUS_UNAVAIL(s) \ 46*0b32bb8bSEric Schrock ((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED) 47940d71d2Seschrock 48940d71d2Seschrock /* 49940d71d2Seschrock * Because multiple SES targets can be part of a single chassis, we construct 50940d71d2Seschrock * our own hierarchy that takes this into account. These SES targets may refer 51940d71d2Seschrock * to the same devices (multiple paths) or to different devices (managing 52940d71d2Seschrock * different portions of the space). We arrange things into a 53940d71d2Seschrock * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all 54940d71d2Seschrock * nodes found so far. 55940d71d2Seschrock */ 56*0b32bb8bSEric Schrock typedef struct ses_alt_node { 57*0b32bb8bSEric Schrock topo_list_t san_link; 58*0b32bb8bSEric Schrock ses_node_t *san_node; 59*0b32bb8bSEric Schrock } ses_alt_node_t; 60940d71d2Seschrock 61940d71d2Seschrock typedef struct ses_enum_node { 62940d71d2Seschrock topo_list_t sen_link; 63940d71d2Seschrock ses_node_t *sen_node; 64*0b32bb8bSEric Schrock topo_list_t sen_alt_nodes; 65940d71d2Seschrock uint64_t sen_type; 66940d71d2Seschrock uint64_t sen_instance; 67940d71d2Seschrock ses_enum_target_t *sen_target; 68940d71d2Seschrock } ses_enum_node_t; 69940d71d2Seschrock 70940d71d2Seschrock typedef struct ses_enum_chassis { 71940d71d2Seschrock topo_list_t sec_link; 72940d71d2Seschrock topo_list_t sec_nodes; 73940d71d2Seschrock topo_list_t sec_targets; 74940d71d2Seschrock const char *sec_csn; 75940d71d2Seschrock ses_node_t *sec_enclosure; 76940d71d2Seschrock ses_enum_target_t *sec_target; 77940d71d2Seschrock topo_instance_t sec_instance; 78940d71d2Seschrock boolean_t sec_hasdev; 79d91236feSeschrock boolean_t sec_internal; 80940d71d2Seschrock } ses_enum_chassis_t; 81940d71d2Seschrock 82940d71d2Seschrock typedef struct ses_enum_data { 83940d71d2Seschrock topo_list_t sed_disks; 84940d71d2Seschrock topo_list_t sed_chassis; 85940d71d2Seschrock ses_enum_chassis_t *sed_current; 86940d71d2Seschrock ses_enum_target_t *sed_target; 87940d71d2Seschrock int sed_errno; 88940d71d2Seschrock char *sed_name; 89940d71d2Seschrock topo_mod_t *sed_mod; 90940d71d2Seschrock topo_instance_t sed_instance; 91940d71d2Seschrock } ses_enum_data_t; 92940d71d2Seschrock 93940d71d2Seschrock static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 94940d71d2Seschrock nvlist_t **); 95940d71d2Seschrock static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 96940d71d2Seschrock nvlist_t **); 97940d71d2Seschrock 98940d71d2Seschrock static const topo_method_t ses_component_methods[] = { 99940d71d2Seschrock { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, 100940d71d2Seschrock TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present }, 101d91236feSeschrock { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 102d91236feSeschrock TOPO_STABILITY_INTERNAL, ses_node_enum_facility }, 103d91236feSeschrock { NULL } 104d91236feSeschrock }; 105d91236feSeschrock 106d91236feSeschrock static const topo_method_t ses_bay_methods[] = { 107d91236feSeschrock { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 108d91236feSeschrock TOPO_STABILITY_INTERNAL, ses_node_enum_facility }, 109940d71d2Seschrock { NULL } 110940d71d2Seschrock }; 111940d71d2Seschrock 112940d71d2Seschrock static const topo_method_t ses_enclosure_methods[] = { 113940d71d2Seschrock { TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC, 114940d71d2Seschrock TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains }, 115d91236feSeschrock { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 116d91236feSeschrock TOPO_STABILITY_INTERNAL, ses_enc_enum_facility }, 117940d71d2Seschrock { NULL } 118940d71d2Seschrock }; 119940d71d2Seschrock 120940d71d2Seschrock static void 121940d71d2Seschrock ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp) 122940d71d2Seschrock { 123940d71d2Seschrock if (--stp->set_refcount == 0) { 124940d71d2Seschrock ses_snap_rele(stp->set_snap); 125940d71d2Seschrock ses_close(stp->set_target); 126940d71d2Seschrock topo_mod_strfree(mod, stp->set_devpath); 127940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 128940d71d2Seschrock } 129940d71d2Seschrock } 130940d71d2Seschrock 131940d71d2Seschrock static void 132940d71d2Seschrock ses_data_free(ses_enum_data_t *sdp) 133940d71d2Seschrock { 134940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 135940d71d2Seschrock ses_enum_chassis_t *cp; 136940d71d2Seschrock ses_enum_node_t *np; 137940d71d2Seschrock ses_enum_target_t *tp; 138*0b32bb8bSEric Schrock ses_alt_node_t *ap; 139940d71d2Seschrock 140940d71d2Seschrock while ((cp = topo_list_next(&sdp->sed_chassis)) != NULL) { 141940d71d2Seschrock topo_list_delete(&sdp->sed_chassis, cp); 142940d71d2Seschrock 143940d71d2Seschrock while ((np = topo_list_next(&cp->sec_nodes)) != NULL) { 144*0b32bb8bSEric Schrock while ((ap = topo_list_next(&np->sen_alt_nodes)) != 145*0b32bb8bSEric Schrock NULL) { 146*0b32bb8bSEric Schrock topo_list_delete(&np->sen_alt_nodes, ap); 147*0b32bb8bSEric Schrock topo_mod_free(mod, ap, sizeof (ses_alt_node_t)); 148*0b32bb8bSEric Schrock } 149940d71d2Seschrock topo_list_delete(&cp->sec_nodes, np); 150940d71d2Seschrock topo_mod_free(mod, np, sizeof (ses_enum_node_t)); 151940d71d2Seschrock } 152940d71d2Seschrock 153940d71d2Seschrock while ((tp = topo_list_next(&cp->sec_targets)) != NULL) { 154940d71d2Seschrock topo_list_delete(&cp->sec_targets, tp); 155940d71d2Seschrock ses_target_free(mod, tp); 156940d71d2Seschrock } 157940d71d2Seschrock 158940d71d2Seschrock topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t)); 159940d71d2Seschrock } 160940d71d2Seschrock 161940d71d2Seschrock disk_list_free(mod, &sdp->sed_disks); 162d91236feSeschrock topo_mod_free(mod, sdp, sizeof (ses_enum_data_t)); 163940d71d2Seschrock } 164940d71d2Seschrock 165940d71d2Seschrock /* 166940d71d2Seschrock * For enclosure nodes, we have a special contains method. By default, the hc 167940d71d2Seschrock * walker will compare the node name and instance number to determine if an 168940d71d2Seschrock * FMRI matches. For enclosures where the enumeration order is impossible to 169940d71d2Seschrock * predict, we instead use the chassis-id as a unique identifier, and ignore 170940d71d2Seschrock * the instance number. 171940d71d2Seschrock */ 172940d71d2Seschrock static int 173940d71d2Seschrock fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2) 174940d71d2Seschrock { 175940d71d2Seschrock uint8_t v1, v2; 176940d71d2Seschrock nvlist_t **hcp1, **hcp2; 177940d71d2Seschrock int err, i; 178940d71d2Seschrock uint_t nhcp1, nhcp2; 179940d71d2Seschrock nvlist_t *a1, *a2; 180940d71d2Seschrock char *c1, *c2; 181940d71d2Seschrock int mindepth; 182940d71d2Seschrock 183940d71d2Seschrock if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 || 184940d71d2Seschrock nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 || 185940d71d2Seschrock v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION) 186940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION)); 187940d71d2Seschrock 188940d71d2Seschrock err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1); 189940d71d2Seschrock err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2); 190940d71d2Seschrock if (err != 0) 191940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 192940d71d2Seschrock 193940d71d2Seschrock /* 194940d71d2Seschrock * If the chassis-id doesn't match, then these FMRIs are not 195940d71d2Seschrock * equivalent. If one of the FMRIs doesn't have a chassis ID, then we 196940d71d2Seschrock * have no choice but to fall back to the instance ID. 197940d71d2Seschrock */ 198940d71d2Seschrock if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 && 199940d71d2Seschrock nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 && 200940d71d2Seschrock nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 && 201940d71d2Seschrock nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) { 202940d71d2Seschrock if (strcmp(c1, c2) != 0) 203940d71d2Seschrock return (0); 204940d71d2Seschrock 205940d71d2Seschrock mindepth = 1; 206940d71d2Seschrock } else { 207940d71d2Seschrock mindepth = 0; 208940d71d2Seschrock } 209940d71d2Seschrock 210940d71d2Seschrock if (nhcp2 < nhcp1) 211940d71d2Seschrock return (0); 212940d71d2Seschrock 213940d71d2Seschrock for (i = 0; i < nhcp1; i++) { 214940d71d2Seschrock char *nm1 = NULL; 215940d71d2Seschrock char *nm2 = NULL; 216940d71d2Seschrock char *id1 = NULL; 217940d71d2Seschrock char *id2 = NULL; 218940d71d2Seschrock 219940d71d2Seschrock (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1); 220940d71d2Seschrock (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2); 221940d71d2Seschrock (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1); 222940d71d2Seschrock (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2); 223940d71d2Seschrock if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL) 224940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 225940d71d2Seschrock 226940d71d2Seschrock if (strcmp(nm1, nm2) == 0 && 227940d71d2Seschrock (i < mindepth || strcmp(id1, id2) == 0)) 228940d71d2Seschrock continue; 229940d71d2Seschrock 230940d71d2Seschrock return (0); 231940d71d2Seschrock } 232940d71d2Seschrock 233940d71d2Seschrock return (1); 234940d71d2Seschrock } 235940d71d2Seschrock 236940d71d2Seschrock /*ARGSUSED*/ 237940d71d2Seschrock static int 238940d71d2Seschrock ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 239940d71d2Seschrock nvlist_t *in, nvlist_t **out) 240940d71d2Seschrock { 241940d71d2Seschrock int ret; 242940d71d2Seschrock nvlist_t *nv1, *nv2; 243940d71d2Seschrock 244940d71d2Seschrock if (version > TOPO_METH_CONTAINS_VERSION) 245940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 246940d71d2Seschrock 247940d71d2Seschrock if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 || 248940d71d2Seschrock nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0) 249940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 250940d71d2Seschrock 251940d71d2Seschrock ret = fmri_contains(mod, nv1, nv2); 252940d71d2Seschrock if (ret < 0) 253940d71d2Seschrock return (-1); 254940d71d2Seschrock 255940d71d2Seschrock if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) { 25673e32a37SRobert Johnston if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET, 257940d71d2Seschrock ret) == 0) 258940d71d2Seschrock return (0); 259940d71d2Seschrock else 260940d71d2Seschrock nvlist_free(*out); 261940d71d2Seschrock } 262940d71d2Seschrock 263940d71d2Seschrock return (-1); 264940d71d2Seschrock 265940d71d2Seschrock } 266940d71d2Seschrock 267940d71d2Seschrock /* 268d91236feSeschrock * Return a current instance of the node. This is somewhat complicated because 269d91236feSeschrock * we need to take a new snapshot in order to get the new data, but we don't 270940d71d2Seschrock * want to be constantly taking SES snapshots if the consumer is going to do a 271940d71d2Seschrock * series of queries. So we adopt the strategy of assuming that the SES state 272d91236feSeschrock * is not going to be rapidly changing, and limit our snapshot frequency to 273d91236feSeschrock * some defined bounds. 274940d71d2Seschrock */ 275d91236feSeschrock ses_node_t * 276*0b32bb8bSEric Schrock ses_node_lock(topo_mod_t *mod, tnode_t *tn) 277940d71d2Seschrock { 278940d71d2Seschrock struct timeval tv; 279940d71d2Seschrock ses_enum_target_t *tp = topo_node_getspecific(tn); 280940d71d2Seschrock uint64_t prev, now; 281940d71d2Seschrock ses_snap_t *snap; 282940d71d2Seschrock int err; 283d91236feSeschrock uint64_t nodeid; 284940d71d2Seschrock ses_node_t *np; 285940d71d2Seschrock 286d91236feSeschrock if (tp == NULL) { 287d91236feSeschrock (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 288d91236feSeschrock return (NULL); 289d91236feSeschrock } 290940d71d2Seschrock 291*0b32bb8bSEric Schrock (void) pthread_mutex_lock(&tp->set_lock); 292*0b32bb8bSEric Schrock 293940d71d2Seschrock /* 294940d71d2Seschrock * Determine if we need to take a new snapshot. 295940d71d2Seschrock */ 296940d71d2Seschrock if (gettimeofday(&tv, NULL) != 0) { 297940d71d2Seschrock tv.tv_sec = time(NULL); 298940d71d2Seschrock tv.tv_usec = 0; 299940d71d2Seschrock } 300940d71d2Seschrock 301940d71d2Seschrock now = tv.tv_sec * 1000 + tv.tv_usec / 1000; 302940d71d2Seschrock prev = tp->set_snaptime.tv_sec * 1000 + 303940d71d2Seschrock tp->set_snaptime.tv_usec / 1000; 304940d71d2Seschrock 305940d71d2Seschrock if (now - prev > SES_SNAP_FREQ && 306940d71d2Seschrock (snap = ses_snap_new(tp->set_target)) != NULL) { 307940d71d2Seschrock if (ses_snap_generation(snap) != 308940d71d2Seschrock ses_snap_generation(tp->set_snap)) { 309940d71d2Seschrock /* 310940d71d2Seschrock * If we find ourselves in this situation, we're in 311940d71d2Seschrock * trouble. The generation count has changed, which 312940d71d2Seschrock * indicates that our current topology is out of date. 313940d71d2Seschrock * But we need to consult the new topology in order to 314940d71d2Seschrock * determine presence at this moment in time. We can't 315940d71d2Seschrock * go back and change the topo snapshot in situ, so 316d91236feSeschrock * we'll just have to fail the call in this unlikely 317d91236feSeschrock * scenario. 318940d71d2Seschrock */ 319940d71d2Seschrock ses_snap_rele(snap); 320d91236feSeschrock (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 321*0b32bb8bSEric Schrock (void) pthread_mutex_unlock(&tp->set_lock); 322d91236feSeschrock return (NULL); 323940d71d2Seschrock } else { 324940d71d2Seschrock ses_snap_rele(tp->set_snap); 325940d71d2Seschrock tp->set_snap = snap; 326940d71d2Seschrock } 327940d71d2Seschrock tp->set_snaptime = tv; 328940d71d2Seschrock } 329940d71d2Seschrock 330940d71d2Seschrock snap = tp->set_snap; 331940d71d2Seschrock 332940d71d2Seschrock verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES, 333940d71d2Seschrock TOPO_PROP_NODE_ID, &nodeid, &err) == 0); 334940d71d2Seschrock verify((np = ses_node_lookup(snap, nodeid)) != NULL); 335d91236feSeschrock 336d91236feSeschrock return (np); 337d91236feSeschrock } 338d91236feSeschrock 339*0b32bb8bSEric Schrock /*ARGSUSED*/ 340*0b32bb8bSEric Schrock void 341*0b32bb8bSEric Schrock ses_node_unlock(topo_mod_t *mod, tnode_t *tn) 342*0b32bb8bSEric Schrock { 343*0b32bb8bSEric Schrock ses_enum_target_t *tp = topo_node_getspecific(tn); 344*0b32bb8bSEric Schrock 345*0b32bb8bSEric Schrock verify(tp != NULL); 346*0b32bb8bSEric Schrock 347*0b32bb8bSEric Schrock (void) pthread_mutex_unlock(&tp->set_lock); 348*0b32bb8bSEric Schrock } 349*0b32bb8bSEric Schrock 350d91236feSeschrock /* 351d91236feSeschrock * Determine if the element is present. 352d91236feSeschrock */ 353d91236feSeschrock /*ARGSUSED*/ 354d91236feSeschrock static int 355d91236feSeschrock ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 356d91236feSeschrock nvlist_t *in, nvlist_t **out) 357d91236feSeschrock { 358d91236feSeschrock boolean_t present; 359d91236feSeschrock ses_node_t *np; 360d91236feSeschrock nvlist_t *props, *nvl; 361d91236feSeschrock uint64_t status; 362d91236feSeschrock 363*0b32bb8bSEric Schrock if ((np = ses_node_lock(mod, tn)) == NULL) 364d91236feSeschrock return (-1); 365d91236feSeschrock 366940d71d2Seschrock verify((props = ses_node_props(np)) != NULL); 367940d71d2Seschrock verify(nvlist_lookup_uint64(props, 368940d71d2Seschrock SES_PROP_STATUS_CODE, &status) == 0); 369940d71d2Seschrock 370*0b32bb8bSEric Schrock ses_node_unlock(mod, tn); 371*0b32bb8bSEric Schrock 372940d71d2Seschrock present = (status != SES_ESC_NOT_INSTALLED); 373940d71d2Seschrock 374940d71d2Seschrock if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) 375940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 376940d71d2Seschrock 377940d71d2Seschrock if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET, 378940d71d2Seschrock present) != 0) { 379940d71d2Seschrock nvlist_free(nvl); 380940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 381940d71d2Seschrock } 382940d71d2Seschrock 383940d71d2Seschrock *out = nvl; 384940d71d2Seschrock 385940d71d2Seschrock return (0); 386940d71d2Seschrock } 387940d71d2Seschrock 388940d71d2Seschrock /* 389940d71d2Seschrock * Sets standard properties for a ses node (enclosure or bay). This includes 390940d71d2Seschrock * setting the FRU to be the same as the resource, as well as setting the 391940d71d2Seschrock * authority information. 392940d71d2Seschrock */ 393940d71d2Seschrock static int 394940d71d2Seschrock ses_set_standard_props(topo_mod_t *mod, tnode_t *tn, nvlist_t *auth, 395940d71d2Seschrock uint64_t nodeid, const char *path) 396940d71d2Seschrock { 397940d71d2Seschrock int err; 398940d71d2Seschrock char *product, *chassis; 399940d71d2Seschrock nvlist_t *fmri; 400940d71d2Seschrock topo_pgroup_info_t pgi; 401940d71d2Seschrock 402940d71d2Seschrock /* 403940d71d2Seschrock * Set the authority explicitly if specified. 404940d71d2Seschrock */ 405940d71d2Seschrock if (auth) { 406940d71d2Seschrock verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, 407940d71d2Seschrock &product) == 0); 408940d71d2Seschrock verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, 409940d71d2Seschrock &chassis) == 0); 410940d71d2Seschrock if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 411940d71d2Seschrock FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product, 412940d71d2Seschrock &err) != 0 || 413940d71d2Seschrock topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 414940d71d2Seschrock FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis, 415940d71d2Seschrock &err) != 0 || 416940d71d2Seschrock topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 417940d71d2Seschrock FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "", 418940d71d2Seschrock &err) != 0) { 419940d71d2Seschrock topo_mod_dprintf(mod, "failed to add authority " 420d91236feSeschrock "properties: %s\n", topo_strerror(err)); 421940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 422940d71d2Seschrock } 423940d71d2Seschrock } 424940d71d2Seschrock 425940d71d2Seschrock /* 426940d71d2Seschrock * Copy the resource and set that as the FRU. 427940d71d2Seschrock */ 428940d71d2Seschrock if (topo_node_resource(tn, &fmri, &err) != 0) { 429940d71d2Seschrock topo_mod_dprintf(mod, 430940d71d2Seschrock "topo_node_resource() failed : %s\n", 431940d71d2Seschrock topo_strerror(err)); 432940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 433940d71d2Seschrock } 434940d71d2Seschrock 435940d71d2Seschrock if (topo_node_fru_set(tn, fmri, 0, &err) != 0) { 436940d71d2Seschrock topo_mod_dprintf(mod, 437940d71d2Seschrock "topo_node_fru_set() failed : %s\n", 438940d71d2Seschrock topo_strerror(err)); 439940d71d2Seschrock nvlist_free(fmri); 440940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 441940d71d2Seschrock } 442940d71d2Seschrock 443940d71d2Seschrock nvlist_free(fmri); 444940d71d2Seschrock 445940d71d2Seschrock /* 446940d71d2Seschrock * Set the SES-specific properties so that consumers can query 447940d71d2Seschrock * additional information about the particular SES element. 448940d71d2Seschrock */ 449940d71d2Seschrock pgi.tpi_name = TOPO_PGROUP_SES; 450940d71d2Seschrock pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 451940d71d2Seschrock pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 452940d71d2Seschrock pgi.tpi_version = TOPO_VERSION; 453940d71d2Seschrock if (topo_pgroup_create(tn, &pgi, &err) != 0) { 454940d71d2Seschrock topo_mod_dprintf(mod, "failed to create propgroup " 455940d71d2Seschrock "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err)); 456940d71d2Seschrock return (-1); 457940d71d2Seschrock } 458940d71d2Seschrock 459940d71d2Seschrock if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES, 460940d71d2Seschrock TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE, 461940d71d2Seschrock nodeid, &err) != 0) { 462940d71d2Seschrock topo_mod_dprintf(mod, 463940d71d2Seschrock "failed to create property %s: %s\n", 464940d71d2Seschrock TOPO_PROP_NODE_ID, topo_strerror(err)); 465940d71d2Seschrock return (-1); 466940d71d2Seschrock } 467940d71d2Seschrock 468940d71d2Seschrock if (topo_prop_set_string(tn, TOPO_PGROUP_SES, 469940d71d2Seschrock TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE, 470940d71d2Seschrock path, &err) != 0) { 471940d71d2Seschrock topo_mod_dprintf(mod, 472940d71d2Seschrock "failed to create property %s: %s\n", 473940d71d2Seschrock TOPO_PROP_TARGET_PATH, topo_strerror(err)); 474940d71d2Seschrock return (-1); 475940d71d2Seschrock } 476940d71d2Seschrock 477940d71d2Seschrock return (0); 478940d71d2Seschrock } 479940d71d2Seschrock 480940d71d2Seschrock /* 481940d71d2Seschrock * Callback to add a disk to a given bay. We first check the status-code to 482940d71d2Seschrock * determine if a disk is present, ignoring those that aren't in an appropriate 483*0b32bb8bSEric Schrock * state. We then scan the parent bay node's SAS address array to determine 484*0b32bb8bSEric Schrock * possible attached SAS addresses. We create a disk node if the disk is not 485*0b32bb8bSEric Schrock * SAS or the SES target does not support the necessary pages for this; if we 486*0b32bb8bSEric Schrock * find the SAS address, we create a disk node and also correlate it with 487*0b32bb8bSEric Schrock * the corresponding Solaris device node to fill in the rest of the data. 488940d71d2Seschrock */ 489940d71d2Seschrock static int 490940d71d2Seschrock ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props) 491940d71d2Seschrock { 492940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 493940d71d2Seschrock uint64_t status; 494940d71d2Seschrock nvlist_t **sas; 495940d71d2Seschrock uint_t s, nsas; 496*0b32bb8bSEric Schrock char **paths; 497*0b32bb8bSEric Schrock int err; 498940d71d2Seschrock 499940d71d2Seschrock /* 500940d71d2Seschrock * Skip devices that are not in a present (and possibly damaged) state. 501940d71d2Seschrock */ 502940d71d2Seschrock if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0) 503940d71d2Seschrock return (0); 504940d71d2Seschrock 505940d71d2Seschrock if (status != SES_ESC_OK && 506940d71d2Seschrock status != SES_ESC_CRITICAL && 507940d71d2Seschrock status != SES_ESC_NONCRITICAL && 508940d71d2Seschrock status != SES_ESC_UNRECOVERABLE && 509940d71d2Seschrock status != SES_ESC_NO_ACCESS) 510940d71d2Seschrock return (0); 511940d71d2Seschrock 512940d71d2Seschrock topo_mod_dprintf(mod, "found attached disk"); 513940d71d2Seschrock 514940d71d2Seschrock /* 515940d71d2Seschrock * Create the disk range. 516940d71d2Seschrock */ 517*0b32bb8bSEric Schrock if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) { 518940d71d2Seschrock topo_mod_dprintf(mod, 519940d71d2Seschrock "topo_node_create_range() failed: %s", 520940d71d2Seschrock topo_mod_errmsg(mod)); 521940d71d2Seschrock return (-1); 522940d71d2Seschrock } 523940d71d2Seschrock 524940d71d2Seschrock /* 525940d71d2Seschrock * Look through all SAS addresses and attempt to correlate them to a 526940d71d2Seschrock * known Solaris device. If we don't find a matching node, then we 527940d71d2Seschrock * don't enumerate the disk node. 528940d71d2Seschrock */ 529940d71d2Seschrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 530940d71d2Seschrock &sas, &nsas) != 0) 531940d71d2Seschrock return (0); 532940d71d2Seschrock 533*0b32bb8bSEric Schrock if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES, 534*0b32bb8bSEric Schrock TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0) 535*0b32bb8bSEric Schrock return (0); 536*0b32bb8bSEric Schrock 537*0b32bb8bSEric Schrock err = 0; 538*0b32bb8bSEric Schrock 539940d71d2Seschrock for (s = 0; s < nsas; s++) { 540*0b32bb8bSEric Schrock if (disk_declare_addr(mod, pnode, 541*0b32bb8bSEric Schrock &sdp->sed_disks, paths[s]) != 0 && 542*0b32bb8bSEric Schrock topo_mod_errno(mod) != EMOD_NODE_BOUND) { 543*0b32bb8bSEric Schrock err = -1; 544*0b32bb8bSEric Schrock break; 545*0b32bb8bSEric Schrock } 546*0b32bb8bSEric Schrock } 547*0b32bb8bSEric Schrock 548*0b32bb8bSEric Schrock for (s = 0; s < nsas; s++) 549*0b32bb8bSEric Schrock topo_mod_free(mod, paths[s], strlen(paths[s]) + 1); 550*0b32bb8bSEric Schrock topo_mod_free(mod, paths, nsas * sizeof (char *)); 551*0b32bb8bSEric Schrock 552*0b32bb8bSEric Schrock return (err); 553*0b32bb8bSEric Schrock } 554*0b32bb8bSEric Schrock 555*0b32bb8bSEric Schrock static int 556*0b32bb8bSEric Schrock ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp) 557*0b32bb8bSEric Schrock { 558*0b32bb8bSEric Schrock ses_alt_node_t *ap; 559*0b32bb8bSEric Schrock ses_node_t *np; 560*0b32bb8bSEric Schrock nvlist_t *props; 561*0b32bb8bSEric Schrock 562*0b32bb8bSEric Schrock nvlist_t **phys; 563*0b32bb8bSEric Schrock uint_t i, j, n_phys, all_phys = 0; 564*0b32bb8bSEric Schrock char **paths; 565*0b32bb8bSEric Schrock uint64_t addr; 566*0b32bb8bSEric Schrock size_t len; 567*0b32bb8bSEric Schrock int terr, err = -1; 568*0b32bb8bSEric Schrock 569*0b32bb8bSEric Schrock for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 570*0b32bb8bSEric Schrock ap = topo_list_next(ap)) { 571*0b32bb8bSEric Schrock np = ap->san_node; 572*0b32bb8bSEric Schrock props = ses_node_props(np); 573*0b32bb8bSEric Schrock 574*0b32bb8bSEric Schrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 575*0b32bb8bSEric Schrock &phys, &n_phys) != 0) 576*0b32bb8bSEric Schrock continue; 577*0b32bb8bSEric Schrock 578*0b32bb8bSEric Schrock all_phys += n_phys; 579*0b32bb8bSEric Schrock } 580*0b32bb8bSEric Schrock 581*0b32bb8bSEric Schrock if (all_phys == 0) 582*0b32bb8bSEric Schrock return (0); 583*0b32bb8bSEric Schrock 584*0b32bb8bSEric Schrock if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL) 585*0b32bb8bSEric Schrock return (-1); 586*0b32bb8bSEric Schrock 587*0b32bb8bSEric Schrock for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 588*0b32bb8bSEric Schrock ap = topo_list_next(ap)) { 589*0b32bb8bSEric Schrock np = ap->san_node; 590*0b32bb8bSEric Schrock props = ses_node_props(np); 591*0b32bb8bSEric Schrock 592*0b32bb8bSEric Schrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 593*0b32bb8bSEric Schrock &phys, &n_phys) != 0) 594940d71d2Seschrock continue; 595940d71d2Seschrock 596*0b32bb8bSEric Schrock for (j = 0; j < n_phys; j++) { 597*0b32bb8bSEric Schrock if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR, 598*0b32bb8bSEric Schrock &addr) != 0) 599*0b32bb8bSEric Schrock continue; 600940d71d2Seschrock 601*0b32bb8bSEric Schrock len = snprintf(NULL, 0, "%016llx", addr) + 1; 602*0b32bb8bSEric Schrock if ((paths[i] = topo_mod_alloc(mod, len)) == NULL) 603*0b32bb8bSEric Schrock goto error; 604*0b32bb8bSEric Schrock 605*0b32bb8bSEric Schrock (void) snprintf(paths[i], len, "%016llx", addr); 606*0b32bb8bSEric Schrock 607*0b32bb8bSEric Schrock ++i; 608*0b32bb8bSEric Schrock } 609940d71d2Seschrock } 610940d71d2Seschrock 611*0b32bb8bSEric Schrock err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 612*0b32bb8bSEric Schrock TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, 613*0b32bb8bSEric Schrock (const char **)paths, i, &terr); 614*0b32bb8bSEric Schrock if (err != 0) 615*0b32bb8bSEric Schrock err = topo_mod_seterrno(mod, terr); 616*0b32bb8bSEric Schrock 617*0b32bb8bSEric Schrock error: 618*0b32bb8bSEric Schrock for (i = 0; i < all_phys && paths[i] != NULL; i++) 619*0b32bb8bSEric Schrock topo_mod_free(mod, paths[i], strlen(paths[i]) + 1); 620*0b32bb8bSEric Schrock topo_mod_free(mod, paths, all_phys * sizeof (char *)); 621*0b32bb8bSEric Schrock 622*0b32bb8bSEric Schrock return (err); 623940d71d2Seschrock } 624940d71d2Seschrock 625940d71d2Seschrock /* 626940d71d2Seschrock * Callback to create a basic node (bay, psu, fan, or controller). 627940d71d2Seschrock */ 628940d71d2Seschrock static int 629940d71d2Seschrock ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp, 630940d71d2Seschrock tnode_t *pnode, const char *nodename, const char *labelname) 631940d71d2Seschrock { 632940d71d2Seschrock ses_node_t *np = snp->sen_node; 633d91236feSeschrock ses_node_t *parent; 634940d71d2Seschrock uint64_t instance = snp->sen_instance; 635940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 636940d71d2Seschrock nvlist_t *props, *aprops; 637940d71d2Seschrock nvlist_t *auth = NULL, *fmri = NULL; 638940d71d2Seschrock tnode_t *tn; 639940d71d2Seschrock char label[128]; 640940d71d2Seschrock int err; 641d91236feSeschrock char *part = NULL, *serial = NULL, *revision = NULL; 642d91236feSeschrock char *desc; 643d91236feSeschrock boolean_t report; 644940d71d2Seschrock 645940d71d2Seschrock props = ses_node_props(np); 646940d71d2Seschrock 647940d71d2Seschrock (void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part); 648940d71d2Seschrock (void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial); 649940d71d2Seschrock 650940d71d2Seschrock topo_mod_dprintf(mod, "adding %s %llu", nodename, instance); 651940d71d2Seschrock 652940d71d2Seschrock /* 653940d71d2Seschrock * Create the node. The interesting information is all copied from the 654940d71d2Seschrock * parent enclosure node, so there is not much to do. 655940d71d2Seschrock */ 656940d71d2Seschrock if ((auth = topo_mod_auth(mod, pnode)) == NULL) 657940d71d2Seschrock goto error; 658940d71d2Seschrock 659d91236feSeschrock /* 660d91236feSeschrock * We want to report revision information for the controller nodes, but 661d91236feSeschrock * we do not get per-element revision information. However, we do have 662d91236feSeschrock * revision information for the entire enclosure, and we can use the 663d91236feSeschrock * 'reported-via' property to know that this controller corresponds to 664d91236feSeschrock * the given revision information. This means we cannot get revision 665d91236feSeschrock * information for targets we are not explicitly connected to, but 666d91236feSeschrock * there is little we can do about the situation. 667d91236feSeschrock */ 668d91236feSeschrock if (strcmp(nodename, CONTROLLER) == 0 && 669d91236feSeschrock nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 && 670d91236feSeschrock report) { 671d91236feSeschrock for (parent = ses_node_parent(np); parent != NULL; 672d91236feSeschrock parent = ses_node_parent(parent)) { 673d91236feSeschrock if (ses_node_type(parent) == SES_NODE_ENCLOSURE) { 674d91236feSeschrock (void) nvlist_lookup_string( 675d91236feSeschrock ses_node_props(parent), 676d91236feSeschrock SES_EN_PROP_REV, &revision); 677d91236feSeschrock break; 678d91236feSeschrock } 679d91236feSeschrock } 680d91236feSeschrock } 681d91236feSeschrock 682940d71d2Seschrock if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 683d91236feSeschrock nodename, (topo_instance_t)instance, NULL, auth, part, revision, 684940d71d2Seschrock serial)) == NULL) { 685940d71d2Seschrock topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 686940d71d2Seschrock topo_mod_errmsg(mod)); 687940d71d2Seschrock goto error; 688940d71d2Seschrock } 689940d71d2Seschrock 690940d71d2Seschrock if ((tn = topo_node_bind(mod, pnode, nodename, 691940d71d2Seschrock instance, fmri)) == NULL) { 692940d71d2Seschrock topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 693940d71d2Seschrock topo_mod_errmsg(mod)); 694940d71d2Seschrock goto error; 695940d71d2Seschrock } 696940d71d2Seschrock 697940d71d2Seschrock /* 698d91236feSeschrock * For the node label, we look for the following in order: 699d91236feSeschrock * 700d91236feSeschrock * <ses-description> 701d91236feSeschrock * <ses-class-description> <instance> 702d91236feSeschrock * <default-type-label> <instance> 703940d71d2Seschrock */ 704d91236feSeschrock if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 || 705d91236feSeschrock desc[0] == '\0') { 706d91236feSeschrock parent = ses_node_parent(np); 707d91236feSeschrock aprops = ses_node_props(parent); 708d91236feSeschrock if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION, 709d91236feSeschrock &desc) == 0 && desc[0] != '\0') 710d91236feSeschrock labelname = desc; 711d91236feSeschrock (void) snprintf(label, sizeof (label), "%s %llu", desc, 712d91236feSeschrock instance); 713d91236feSeschrock desc = label; 714d91236feSeschrock } 715d91236feSeschrock 716d91236feSeschrock if (topo_node_label_set(tn, desc, &err) != 0) 717940d71d2Seschrock goto error; 718940d71d2Seschrock 719940d71d2Seschrock if (ses_set_standard_props(mod, tn, NULL, ses_node_id(np), 720940d71d2Seschrock snp->sen_target->set_devpath) != 0) 721940d71d2Seschrock goto error; 722940d71d2Seschrock 723940d71d2Seschrock if (strcmp(nodename, "bay") == 0) { 724*0b32bb8bSEric Schrock if (ses_add_bay_props(mod, tn, snp) != 0) 725*0b32bb8bSEric Schrock goto error; 726*0b32bb8bSEric Schrock 727940d71d2Seschrock if (ses_create_disk(sdp, tn, props) != 0) 728940d71d2Seschrock goto error; 729d91236feSeschrock 730d91236feSeschrock if (topo_method_register(mod, tn, ses_bay_methods) != 0) { 731d91236feSeschrock topo_mod_dprintf(mod, 732d91236feSeschrock "topo_method_register() failed: %s", 733d91236feSeschrock topo_mod_errmsg(mod)); 734d91236feSeschrock goto error; 735d91236feSeschrock } 736940d71d2Seschrock } else { 737940d71d2Seschrock /* 738d91236feSeschrock * Only fan, psu, and controller nodes have a 'present' method. 739d91236feSeschrock * Bay nodes are always present, and disk nodes are present by 740d91236feSeschrock * virtue of being enumerated. 741940d71d2Seschrock */ 742940d71d2Seschrock if (topo_method_register(mod, tn, ses_component_methods) != 0) { 743940d71d2Seschrock topo_mod_dprintf(mod, 744940d71d2Seschrock "topo_method_register() failed: %s", 745940d71d2Seschrock topo_mod_errmsg(mod)); 746940d71d2Seschrock goto error; 747940d71d2Seschrock } 748940d71d2Seschrock 749940d71d2Seschrock } 750940d71d2Seschrock 751d91236feSeschrock snp->sen_target->set_refcount++; 752d91236feSeschrock topo_node_setspecific(tn, snp->sen_target); 753d91236feSeschrock 754940d71d2Seschrock nvlist_free(auth); 755940d71d2Seschrock nvlist_free(fmri); 756940d71d2Seschrock return (0); 757940d71d2Seschrock 758940d71d2Seschrock error: 759940d71d2Seschrock nvlist_free(auth); 760940d71d2Seschrock nvlist_free(fmri); 761940d71d2Seschrock return (-1); 762940d71d2Seschrock } 763940d71d2Seschrock 764940d71d2Seschrock /* 765940d71d2Seschrock * Instantiate any children of a given type. 766940d71d2Seschrock */ 767940d71d2Seschrock static int 768940d71d2Seschrock ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type, 769d91236feSeschrock const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp, 770d91236feSeschrock boolean_t dorange) 771940d71d2Seschrock { 772940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 773940d71d2Seschrock boolean_t found; 774940d71d2Seschrock uint64_t max; 775940d71d2Seschrock ses_enum_node_t *snp; 776940d71d2Seschrock 777940d71d2Seschrock /* 778940d71d2Seschrock * First go through and count how many matching nodes we have. 779940d71d2Seschrock */ 780940d71d2Seschrock max = 0; 781940d71d2Seschrock found = B_FALSE; 782940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 783940d71d2Seschrock snp = topo_list_next(snp)) { 784940d71d2Seschrock if (snp->sen_type == type) { 785940d71d2Seschrock found = B_TRUE; 786940d71d2Seschrock if (snp->sen_instance > max) 787940d71d2Seschrock max = snp->sen_instance; 788940d71d2Seschrock } 789940d71d2Seschrock } 790940d71d2Seschrock 791940d71d2Seschrock /* 792940d71d2Seschrock * No enclosure should export both DEVICE and ARRAY_DEVICE elements. 793940d71d2Seschrock * Since we map both of these to 'disk', if an enclosure does this, we 794940d71d2Seschrock * just ignore the array elements. 795940d71d2Seschrock */ 796940d71d2Seschrock if (!found || 797940d71d2Seschrock (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev)) 798940d71d2Seschrock return (0); 799940d71d2Seschrock 800940d71d2Seschrock topo_mod_dprintf(mod, "%s: creating %llu %s nodes", 801940d71d2Seschrock cp->sec_csn, max, nodename); 802940d71d2Seschrock 803d91236feSeschrock if (dorange && topo_node_range_create(mod, pnode, 804940d71d2Seschrock nodename, 0, max) != 0) { 805940d71d2Seschrock topo_mod_dprintf(mod, 806940d71d2Seschrock "topo_node_create_range() failed: %s", 807940d71d2Seschrock topo_mod_errmsg(mod)); 808940d71d2Seschrock return (-1); 809940d71d2Seschrock } 810940d71d2Seschrock 811940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 812940d71d2Seschrock snp = topo_list_next(snp)) { 813940d71d2Seschrock if (snp->sen_type == type) { 814940d71d2Seschrock if (ses_create_generic(sdp, snp, pnode, 815940d71d2Seschrock nodename, defaultlabel) != 0) 816940d71d2Seschrock return (-1); 817940d71d2Seschrock } 818940d71d2Seschrock } 819940d71d2Seschrock 820940d71d2Seschrock return (0); 821940d71d2Seschrock } 822940d71d2Seschrock 823940d71d2Seschrock /* 824940d71d2Seschrock * Instantiate a new chassis instance in the topology. 825940d71d2Seschrock */ 826940d71d2Seschrock static int 827940d71d2Seschrock ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp) 828940d71d2Seschrock { 829940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 830940d71d2Seschrock nvlist_t *props; 831940d71d2Seschrock char *raw_manufacturer, *raw_model, *raw_revision; 832940d71d2Seschrock char *manufacturer = NULL, *model = NULL, *product = NULL; 833940d71d2Seschrock char *revision = NULL; 834940d71d2Seschrock char *serial; 835940d71d2Seschrock size_t prodlen; 836940d71d2Seschrock tnode_t *tn; 837940d71d2Seschrock nvlist_t *fmri = NULL, *auth = NULL; 838940d71d2Seschrock int ret = -1; 839940d71d2Seschrock ses_enum_node_t *snp; 840940d71d2Seschrock 841d91236feSeschrock /* 842d91236feSeschrock * Ignore any internal enclosures. 843d91236feSeschrock */ 844d91236feSeschrock if (cp->sec_internal) 845d91236feSeschrock return (0); 846d91236feSeschrock 847940d71d2Seschrock /* 848940d71d2Seschrock * Check to see if there are any devices presennt in the chassis. If 849940d71d2Seschrock * not, ignore the chassis alltogether. This is most useful for 850940d71d2Seschrock * ignoring internal HBAs that present a SES target but don't actually 851940d71d2Seschrock * manage any of the devices. 852940d71d2Seschrock */ 853940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 854940d71d2Seschrock snp = topo_list_next(snp)) { 855940d71d2Seschrock if (snp->sen_type == SES_ET_DEVICE || 856940d71d2Seschrock snp->sen_type == SES_ET_ARRAY_DEVICE) 857940d71d2Seschrock break; 858940d71d2Seschrock } 859940d71d2Seschrock 860940d71d2Seschrock if (snp == NULL) 861940d71d2Seschrock return (0); 862940d71d2Seschrock 863940d71d2Seschrock props = ses_node_props(cp->sec_enclosure); 864940d71d2Seschrock 865940d71d2Seschrock /* 866940d71d2Seschrock * We use the following property mappings: 867940d71d2Seschrock * 868940d71d2Seschrock * manufacturer vendor-id 869940d71d2Seschrock * model product-id 870940d71d2Seschrock * serial-number libses-chassis-serial 871940d71d2Seschrock */ 872940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_VID, 873940d71d2Seschrock &raw_manufacturer) == 0); 874940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0); 875940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_REV, 876940d71d2Seschrock &raw_revision) == 0); 877940d71d2Seschrock verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0); 878940d71d2Seschrock 879940d71d2Seschrock /* 880940d71d2Seschrock * To construct the authority information, we 'clean' each string by 881940d71d2Seschrock * removing any offensive characters and trimmming whitespace. For the 882940d71d2Seschrock * 'product-id', we use a concatenation of 'manufacturer-model'. We 883940d71d2Seschrock * also take the numerical serial number and convert it to a string. 884940d71d2Seschrock */ 885940d71d2Seschrock if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL || 886940d71d2Seschrock (model = disk_auth_clean(mod, raw_model)) == NULL || 887940d71d2Seschrock (revision = disk_auth_clean(mod, raw_revision)) == NULL) { 888940d71d2Seschrock goto error; 889940d71d2Seschrock } 890940d71d2Seschrock 891940d71d2Seschrock prodlen = strlen(manufacturer) + strlen(model) + 2; 892940d71d2Seschrock if ((product = topo_mod_alloc(mod, prodlen)) == NULL) 893940d71d2Seschrock goto error; 894940d71d2Seschrock 895940d71d2Seschrock (void) snprintf(product, prodlen, "%s-%s", manufacturer, model); 896940d71d2Seschrock 897940d71d2Seschrock /* 898940d71d2Seschrock * Construct the topo node and bind it to our parent. 899940d71d2Seschrock */ 900940d71d2Seschrock if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0) 901940d71d2Seschrock goto error; 902940d71d2Seschrock 903940d71d2Seschrock if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 || 904940d71d2Seschrock nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) { 905940d71d2Seschrock (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 906940d71d2Seschrock goto error; 907940d71d2Seschrock } 908940d71d2Seschrock 909940d71d2Seschrock /* 910940d71d2Seschrock * We pass NULL for the parent FMRI because there is no resource 911940d71d2Seschrock * associated with it. For the toplevel enclosure, we leave the 912940d71d2Seschrock * serial/part/revision portions empty, which are reserved for 913940d71d2Seschrock * individual components within the chassis. 914940d71d2Seschrock */ 915940d71d2Seschrock if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION, 916940d71d2Seschrock SES_ENCLOSURE, cp->sec_instance, NULL, auth, 917940d71d2Seschrock model, revision, serial)) == NULL) { 918940d71d2Seschrock topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 919940d71d2Seschrock topo_mod_errmsg(mod)); 920940d71d2Seschrock goto error; 921940d71d2Seschrock } 922940d71d2Seschrock 923940d71d2Seschrock if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE, 924940d71d2Seschrock cp->sec_instance, fmri)) == NULL) { 925940d71d2Seschrock topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 926940d71d2Seschrock topo_mod_errmsg(mod)); 927940d71d2Seschrock goto error; 928940d71d2Seschrock } 929940d71d2Seschrock 930940d71d2Seschrock if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 931940d71d2Seschrock topo_mod_dprintf(mod, 932940d71d2Seschrock "topo_method_register() failed: %s", 933940d71d2Seschrock topo_mod_errmsg(mod)); 934940d71d2Seschrock goto error; 935940d71d2Seschrock } 936940d71d2Seschrock 937940d71d2Seschrock if (ses_set_standard_props(mod, tn, auth, 938940d71d2Seschrock ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0) 939940d71d2Seschrock goto error; 940940d71d2Seschrock 941940d71d2Seschrock /* 942940d71d2Seschrock * Create the nodes for power supplies, fans, and devices. 943940d71d2Seschrock */ 944940d71d2Seschrock if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY, 945d91236feSeschrock PSU, "PSU", cp, B_TRUE) != 0 || 946940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_COOLING, 947d91236feSeschrock FAN, "FAN", cp, B_TRUE) != 0 || 948940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 949d91236feSeschrock CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 || 950940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_DEVICE, 951d91236feSeschrock BAY, "BAY", cp, B_TRUE) != 0 || 952940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 953d91236feSeschrock BAY, "BAY", cp, B_TRUE) != 0) 954940d71d2Seschrock goto error; 955940d71d2Seschrock 956d91236feSeschrock snp->sen_target->set_refcount++; 957d91236feSeschrock topo_node_setspecific(tn, snp->sen_target); 958d91236feSeschrock 959940d71d2Seschrock ret = 0; 960940d71d2Seschrock error: 961940d71d2Seschrock topo_mod_strfree(mod, manufacturer); 962940d71d2Seschrock topo_mod_strfree(mod, model); 963940d71d2Seschrock topo_mod_strfree(mod, revision); 964940d71d2Seschrock topo_mod_strfree(mod, product); 965940d71d2Seschrock 966940d71d2Seschrock nvlist_free(fmri); 967940d71d2Seschrock nvlist_free(auth); 968940d71d2Seschrock return (ret); 969940d71d2Seschrock } 970940d71d2Seschrock 971d91236feSeschrock /* 972d91236feSeschrock * Create a bay node explicitly enumerated via XML. 973d91236feSeschrock */ 974d91236feSeschrock static int 975d91236feSeschrock ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode) 976d91236feSeschrock { 977d91236feSeschrock topo_mod_t *mod = sdp->sed_mod; 978d91236feSeschrock ses_enum_chassis_t *cp; 979d91236feSeschrock 980d91236feSeschrock /* 981d91236feSeschrock * Iterate over chassis looking for an internal enclosure. This 982d91236feSeschrock * property is set via a vendor-specific plugin, and there should only 983d91236feSeschrock * ever be a single internal chassis in a system. 984d91236feSeschrock */ 985d91236feSeschrock for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 986d91236feSeschrock cp = topo_list_next(cp)) { 987d91236feSeschrock if (cp->sec_internal) 988d91236feSeschrock break; 989d91236feSeschrock } 990d91236feSeschrock 991d91236feSeschrock if (cp == NULL) { 992d91236feSeschrock topo_mod_dprintf(mod, "failed to find internal chassis\n"); 993d91236feSeschrock return (-1); 994d91236feSeschrock } 995d91236feSeschrock 996d91236feSeschrock if (ses_create_children(sdp, pnode, SES_ET_DEVICE, 997d91236feSeschrock BAY, "BAY", cp, B_FALSE) != 0 || 998d91236feSeschrock ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE, 999d91236feSeschrock BAY, "BAY", cp, B_FALSE) != 0) 1000d91236feSeschrock return (-1); 1001d91236feSeschrock 1002d91236feSeschrock return (0); 1003d91236feSeschrock } 1004940d71d2Seschrock /* 1005940d71d2Seschrock * Gather nodes from the current SES target into our chassis list, merging the 1006940d71d2Seschrock * results if necessary. 1007940d71d2Seschrock */ 1008940d71d2Seschrock static ses_walk_action_t 1009940d71d2Seschrock ses_enum_gather(ses_node_t *np, void *data) 1010940d71d2Seschrock { 1011940d71d2Seschrock nvlist_t *props = ses_node_props(np); 1012940d71d2Seschrock ses_enum_data_t *sdp = data; 1013940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 1014940d71d2Seschrock ses_enum_chassis_t *cp; 1015940d71d2Seschrock ses_enum_node_t *snp; 1016*0b32bb8bSEric Schrock ses_alt_node_t *sap; 1017940d71d2Seschrock char *csn; 1018940d71d2Seschrock uint64_t instance, type; 1019d91236feSeschrock uint64_t prevstatus, status; 1020d91236feSeschrock boolean_t report, internal; 1021940d71d2Seschrock 1022940d71d2Seschrock if (ses_node_type(np) == SES_NODE_ENCLOSURE) { 1023940d71d2Seschrock /* 1024940d71d2Seschrock * If we have already identified the chassis for this target, 1025940d71d2Seschrock * then this is a secondary enclosure and we should ignore it, 1026940d71d2Seschrock * along with the rest of the tree (since this is depth-first). 1027940d71d2Seschrock */ 1028940d71d2Seschrock if (sdp->sed_current != NULL) 1029940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1030940d71d2Seschrock 1031940d71d2Seschrock /* 1032940d71d2Seschrock * Go through the list of chassis we have seen so far and see 1033940d71d2Seschrock * if this serial number matches one of the known values. 1034940d71d2Seschrock */ 1035940d71d2Seschrock if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, 1036940d71d2Seschrock &csn) != 0) 1037940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1038940d71d2Seschrock 1039940d71d2Seschrock for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 1040940d71d2Seschrock cp = topo_list_next(cp)) { 1041940d71d2Seschrock if (strcmp(cp->sec_csn, csn) == 0) { 1042940d71d2Seschrock topo_mod_dprintf(mod, "%s: part of already " 1043940d71d2Seschrock "known chassis %s", sdp->sed_name, csn); 1044940d71d2Seschrock break; 1045940d71d2Seschrock } 1046940d71d2Seschrock } 1047940d71d2Seschrock 1048940d71d2Seschrock if (cp == NULL) { 1049940d71d2Seschrock topo_mod_dprintf(mod, "%s: creating chassis %s", 1050940d71d2Seschrock sdp->sed_name, csn); 1051940d71d2Seschrock 1052940d71d2Seschrock if ((cp = topo_mod_zalloc(mod, 1053940d71d2Seschrock sizeof (ses_enum_chassis_t))) == NULL) 1054940d71d2Seschrock goto error; 1055940d71d2Seschrock 1056d91236feSeschrock if (nvlist_lookup_boolean_value(props, 1057d91236feSeschrock LIBSES_EN_PROP_INTERNAL, &internal) == 0) 1058d91236feSeschrock cp->sec_internal = internal; 1059d91236feSeschrock 1060940d71d2Seschrock cp->sec_csn = csn; 1061940d71d2Seschrock cp->sec_enclosure = np; 1062940d71d2Seschrock cp->sec_target = sdp->sed_target; 1063940d71d2Seschrock cp->sec_instance = sdp->sed_instance++; 1064940d71d2Seschrock topo_list_append(&sdp->sed_chassis, cp); 1065940d71d2Seschrock } 1066940d71d2Seschrock 1067940d71d2Seschrock topo_list_append(&cp->sec_targets, sdp->sed_target); 1068940d71d2Seschrock sdp->sed_current = cp; 1069940d71d2Seschrock 1070940d71d2Seschrock } else if (ses_node_type(np) == SES_NODE_ELEMENT) { 1071940d71d2Seschrock /* 1072940d71d2Seschrock * If we haven't yet seen an enclosure node and identified the 1073940d71d2Seschrock * current chassis, something is very wrong; bail out. 1074940d71d2Seschrock */ 1075940d71d2Seschrock if (sdp->sed_current == NULL) 1076940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1077940d71d2Seschrock 1078940d71d2Seschrock /* 1079940d71d2Seschrock * If this isn't one of the element types we care about, then 1080940d71d2Seschrock * ignore it. 1081940d71d2Seschrock */ 1082940d71d2Seschrock verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, 1083940d71d2Seschrock &type) == 0); 1084940d71d2Seschrock if (type != SES_ET_DEVICE && 1085940d71d2Seschrock type != SES_ET_ARRAY_DEVICE && 1086940d71d2Seschrock type != SES_ET_COOLING && 1087940d71d2Seschrock type != SES_ET_POWER_SUPPLY && 1088940d71d2Seschrock type != SES_ET_ESC_ELECTRONICS) 1089940d71d2Seschrock return (SES_WALK_ACTION_CONTINUE); 1090940d71d2Seschrock 1091940d71d2Seschrock /* 1092940d71d2Seschrock * Get the current instance number and see if we already know 1093940d71d2Seschrock * about this element. If so, it means we have multiple paths 1094940d71d2Seschrock * to the same elements, and we should ignore the current path. 1095940d71d2Seschrock */ 1096940d71d2Seschrock verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX, 1097940d71d2Seschrock &instance) == 0); 1098940d71d2Seschrock if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE) 1099940d71d2Seschrock (void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER, 1100940d71d2Seschrock &instance); 1101940d71d2Seschrock 1102940d71d2Seschrock cp = sdp->sed_current; 1103940d71d2Seschrock 1104940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1105940d71d2Seschrock snp = topo_list_next(snp)) { 1106940d71d2Seschrock if (snp->sen_type == type && 1107940d71d2Seschrock snp->sen_instance == instance) 1108d91236feSeschrock break; 1109d91236feSeschrock } 1110d91236feSeschrock 1111d91236feSeschrock /* 1112d91236feSeschrock * We prefer the new element under the following circumstances: 1113d91236feSeschrock * 1114d91236feSeschrock * - The currently known element's status is unknown or not 1115d91236feSeschrock * available, but the new element has a known status. This 1116d91236feSeschrock * occurs if a given element is only available through a 1117d91236feSeschrock * particular target. 1118d91236feSeschrock * 1119d91236feSeschrock * - This is an ESC_ELECTRONICS element, and the 'reported-via' 1120d91236feSeschrock * property is set. This allows us to get reliable firmware 1121d91236feSeschrock * revision information from the enclosure node. 1122d91236feSeschrock */ 1123d91236feSeschrock if (snp != NULL) { 1124d91236feSeschrock if (nvlist_lookup_uint64( 1125d91236feSeschrock ses_node_props(snp->sen_node), 1126d91236feSeschrock SES_PROP_STATUS_CODE, &prevstatus) != 0) 1127d91236feSeschrock prevstatus = SES_ESC_UNSUPPORTED; 1128d91236feSeschrock if (nvlist_lookup_uint64( 1129d91236feSeschrock props, SES_PROP_STATUS_CODE, &status) != 0) 1130d91236feSeschrock status = SES_ESC_UNSUPPORTED; 1131d91236feSeschrock if (nvlist_lookup_boolean_value( 1132d91236feSeschrock props, SES_PROP_REPORT, &report) != 0) 1133d91236feSeschrock report = B_FALSE; 1134d91236feSeschrock 1135d91236feSeschrock if ((SES_STATUS_UNAVAIL(prevstatus) && 1136d91236feSeschrock !SES_STATUS_UNAVAIL(status)) || 1137d91236feSeschrock (type == SES_ET_ESC_ELECTRONICS && 1138d91236feSeschrock report)) { 1139d91236feSeschrock snp->sen_node = np; 1140d91236feSeschrock snp->sen_target = sdp->sed_target; 1141d91236feSeschrock } 1142d91236feSeschrock 1143*0b32bb8bSEric Schrock if ((sap = topo_mod_zalloc(mod, 1144*0b32bb8bSEric Schrock sizeof (ses_alt_node_t))) == NULL) 1145*0b32bb8bSEric Schrock goto error; 1146*0b32bb8bSEric Schrock 1147*0b32bb8bSEric Schrock sap->san_node = np; 1148*0b32bb8bSEric Schrock topo_list_append(&snp->sen_alt_nodes, sap); 1149*0b32bb8bSEric Schrock 1150d91236feSeschrock return (SES_WALK_ACTION_CONTINUE); 1151940d71d2Seschrock } 1152940d71d2Seschrock 1153940d71d2Seschrock if ((snp = topo_mod_zalloc(mod, 1154940d71d2Seschrock sizeof (ses_enum_node_t))) == NULL) 1155940d71d2Seschrock goto error; 1156940d71d2Seschrock 1157*0b32bb8bSEric Schrock if ((sap = topo_mod_zalloc(mod, 1158*0b32bb8bSEric Schrock sizeof (ses_alt_node_t))) == NULL) { 1159*0b32bb8bSEric Schrock topo_mod_free(mod, snp, sizeof (ses_enum_node_t)); 1160*0b32bb8bSEric Schrock goto error; 1161*0b32bb8bSEric Schrock } 1162*0b32bb8bSEric Schrock 1163940d71d2Seschrock topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)", 1164940d71d2Seschrock sdp->sed_name, type, instance); 1165940d71d2Seschrock snp->sen_node = np; 1166940d71d2Seschrock snp->sen_type = type; 1167940d71d2Seschrock snp->sen_instance = instance; 1168940d71d2Seschrock snp->sen_target = sdp->sed_target; 1169*0b32bb8bSEric Schrock sap->san_node = np; 1170*0b32bb8bSEric Schrock topo_list_append(&snp->sen_alt_nodes, sap); 1171940d71d2Seschrock topo_list_append(&cp->sec_nodes, snp); 1172940d71d2Seschrock 1173940d71d2Seschrock if (type == SES_ET_DEVICE) 1174940d71d2Seschrock cp->sec_hasdev = B_TRUE; 1175940d71d2Seschrock } 1176940d71d2Seschrock 1177940d71d2Seschrock return (SES_WALK_ACTION_CONTINUE); 1178940d71d2Seschrock 1179940d71d2Seschrock error: 1180940d71d2Seschrock sdp->sed_errno = -1; 1181940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1182940d71d2Seschrock } 1183940d71d2Seschrock 1184940d71d2Seschrock static int 1185940d71d2Seschrock ses_process_dir(const char *dirpath, ses_enum_data_t *sdp) 1186940d71d2Seschrock { 1187940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 1188940d71d2Seschrock DIR *dir; 1189940d71d2Seschrock struct dirent *dp; 1190940d71d2Seschrock char path[PATH_MAX]; 1191940d71d2Seschrock ses_enum_target_t *stp; 1192940d71d2Seschrock int err = -1; 1193940d71d2Seschrock 1194940d71d2Seschrock /* 1195940d71d2Seschrock * Open the SES target directory and iterate over any available 1196940d71d2Seschrock * targets. 1197940d71d2Seschrock */ 1198940d71d2Seschrock if ((dir = opendir(dirpath)) == NULL) { 1199940d71d2Seschrock /* 1200940d71d2Seschrock * If the SES target directory does not exist, then return as if 1201940d71d2Seschrock * there are no active targets. 1202940d71d2Seschrock */ 1203940d71d2Seschrock topo_mod_dprintf(mod, "failed to open ses " 1204940d71d2Seschrock "directory '%s'", dirpath); 1205940d71d2Seschrock return (0); 1206940d71d2Seschrock } 1207940d71d2Seschrock 1208940d71d2Seschrock while ((dp = readdir(dir)) != NULL) { 1209940d71d2Seschrock if (strcmp(dp->d_name, ".") == 0 || 1210940d71d2Seschrock strcmp(dp->d_name, "..") == 0) 1211940d71d2Seschrock continue; 1212940d71d2Seschrock 1213940d71d2Seschrock /* 1214940d71d2Seschrock * Create a new target instance and take a snapshot. 1215940d71d2Seschrock */ 1216940d71d2Seschrock if ((stp = topo_mod_zalloc(mod, 1217940d71d2Seschrock sizeof (ses_enum_target_t))) == NULL) 1218940d71d2Seschrock goto error; 1219940d71d2Seschrock 1220*0b32bb8bSEric Schrock (void) pthread_mutex_init(&stp->set_lock, NULL); 1221*0b32bb8bSEric Schrock 1222940d71d2Seschrock (void) snprintf(path, sizeof (path), "%s/%s", dirpath, 1223940d71d2Seschrock dp->d_name); 1224940d71d2Seschrock 1225940d71d2Seschrock /* 1226940d71d2Seschrock * We keep track of the SES device path and export it on a 1227940d71d2Seschrock * per-node basis to allow higher level software to get to the 1228940d71d2Seschrock * corresponding SES state. 1229940d71d2Seschrock */ 1230940d71d2Seschrock if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) { 1231940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 1232940d71d2Seschrock goto error; 1233940d71d2Seschrock } 1234940d71d2Seschrock 1235940d71d2Seschrock if ((stp->set_target = 1236940d71d2Seschrock ses_open(LIBSES_VERSION, path)) == NULL) { 1237940d71d2Seschrock topo_mod_dprintf(mod, "failed to open ses target " 1238940d71d2Seschrock "'%s': %s", dp->d_name, ses_errmsg()); 1239940d71d2Seschrock topo_mod_strfree(mod, stp->set_devpath); 1240940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 1241940d71d2Seschrock continue; 1242940d71d2Seschrock } 1243940d71d2Seschrock 1244940d71d2Seschrock stp->set_refcount = 1; 1245940d71d2Seschrock sdp->sed_target = stp; 1246940d71d2Seschrock stp->set_snap = ses_snap_hold(stp->set_target); 1247940d71d2Seschrock if (gettimeofday(&stp->set_snaptime, NULL) != 0) 1248940d71d2Seschrock stp->set_snaptime.tv_sec = time(NULL); 1249940d71d2Seschrock 1250940d71d2Seschrock /* 1251940d71d2Seschrock * Enumerate over all SES elements and merge them into the 1252940d71d2Seschrock * correct ses_enum_chassis_t. 1253940d71d2Seschrock */ 1254940d71d2Seschrock sdp->sed_current = NULL; 1255940d71d2Seschrock sdp->sed_errno = 0; 1256940d71d2Seschrock sdp->sed_name = dp->d_name; 1257940d71d2Seschrock (void) ses_walk(stp->set_snap, ses_enum_gather, sdp); 1258940d71d2Seschrock 1259940d71d2Seschrock if (sdp->sed_errno != 0) 1260940d71d2Seschrock goto error; 1261940d71d2Seschrock } 1262940d71d2Seschrock 1263940d71d2Seschrock err = 0; 1264940d71d2Seschrock error: 1265940d71d2Seschrock closedir(dir); 1266940d71d2Seschrock return (err); 1267940d71d2Seschrock } 1268940d71d2Seschrock 1269940d71d2Seschrock static void 1270940d71d2Seschrock ses_release(topo_mod_t *mod, tnode_t *tn) 1271940d71d2Seschrock { 1272940d71d2Seschrock ses_enum_target_t *stp; 1273940d71d2Seschrock 1274940d71d2Seschrock if ((stp = topo_node_getspecific(tn)) != NULL) 1275940d71d2Seschrock ses_target_free(mod, stp); 1276940d71d2Seschrock } 1277940d71d2Seschrock 1278940d71d2Seschrock /*ARGSUSED*/ 1279940d71d2Seschrock static int 1280940d71d2Seschrock ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 1281940d71d2Seschrock topo_instance_t min, topo_instance_t max, void *arg, void *notused) 1282940d71d2Seschrock { 1283940d71d2Seschrock ses_enum_chassis_t *cp; 1284d91236feSeschrock ses_enum_data_t *data; 1285940d71d2Seschrock 1286940d71d2Seschrock /* 1287940d71d2Seschrock * Check to make sure we're being invoked sensibly, and that we're not 1288940d71d2Seschrock * being invoked as part of a post-processing step. 1289940d71d2Seschrock */ 1290d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0) 1291940d71d2Seschrock return (0); 1292940d71d2Seschrock 1293940d71d2Seschrock /* 1294d91236feSeschrock * If this is the first time we've called our enumeration method, then 1295d91236feSeschrock * gather information about any available enclosures. 1296940d71d2Seschrock */ 1297d91236feSeschrock if ((data = topo_mod_getspecific(mod)) == NULL) { 1298d91236feSeschrock if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) == 1299d91236feSeschrock NULL) 1300d91236feSeschrock return (-1); 1301940d71d2Seschrock 1302d91236feSeschrock data->sed_mod = mod; 1303d91236feSeschrock topo_mod_setspecific(mod, data); 1304d91236feSeschrock 1305d91236feSeschrock if (disk_list_gather(mod, &data->sed_disks) != 0) 1306d91236feSeschrock goto error; 1307d91236feSeschrock 1308d91236feSeschrock /* 1309d91236feSeschrock * We search both the ses(7D) and sgen(7D) locations, so we are 1310d91236feSeschrock * independent of any particular driver class bindings. 1311d91236feSeschrock */ 1312d91236feSeschrock if (ses_process_dir("/dev/es", data) != 0 || 1313d91236feSeschrock ses_process_dir("/dev/scsi/ses", data) != 0) 1314d91236feSeschrock goto error; 1315d91236feSeschrock } 1316d91236feSeschrock 1317d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) == 0) { 1318d91236feSeschrock /* 1319d91236feSeschrock * This is a request to enumerate external enclosures. Go 1320d91236feSeschrock * through all the targets and create chassis nodes where 1321d91236feSeschrock * necessary. 1322d91236feSeschrock */ 1323d91236feSeschrock for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 1324d91236feSeschrock cp = topo_list_next(cp)) { 1325d91236feSeschrock if (ses_create_chassis(data, rnode, cp) != 0) 1326d91236feSeschrock goto error; 1327d91236feSeschrock } 1328d91236feSeschrock } else { 1329d91236feSeschrock /* 1330d91236feSeschrock * This is a request to enumerate a specific bay underneath the 1331d91236feSeschrock * root chassis (for internal disks). 1332d91236feSeschrock */ 1333d91236feSeschrock if (ses_create_bays(data, rnode) != 0) 1334940d71d2Seschrock goto error; 1335940d71d2Seschrock } 1336940d71d2Seschrock 1337d91236feSeschrock /* 1338d91236feSeschrock * This is a bit of a kludge. In order to allow internal disks to be 1339d91236feSeschrock * enumerated and share snapshot-specific information with the external 1340d91236feSeschrock * enclosure enumeration, we rely on the fact that we will be invoked 1341d91236feSeschrock * for the 'ses-enclosure' node last. 1342d91236feSeschrock */ 1343d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) == 0) { 1344d91236feSeschrock ses_data_free(data); 1345d91236feSeschrock topo_mod_setspecific(mod, NULL); 1346d91236feSeschrock } 1347940d71d2Seschrock return (0); 1348940d71d2Seschrock 1349940d71d2Seschrock error: 1350d91236feSeschrock ses_data_free(data); 1351d91236feSeschrock topo_mod_setspecific(mod, NULL); 1352940d71d2Seschrock return (-1); 1353940d71d2Seschrock } 1354940d71d2Seschrock 1355940d71d2Seschrock static const topo_modops_t ses_ops = 1356940d71d2Seschrock { ses_enum, ses_release }; 1357940d71d2Seschrock 1358940d71d2Seschrock static topo_modinfo_t ses_info = 1359940d71d2Seschrock { SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops }; 1360940d71d2Seschrock 1361940d71d2Seschrock /*ARGSUSED*/ 1362940d71d2Seschrock int 1363940d71d2Seschrock _topo_init(topo_mod_t *mod, topo_version_t version) 1364940d71d2Seschrock { 1365940d71d2Seschrock if (getenv("TOPOSESDEBUG") != NULL) 1366940d71d2Seschrock topo_mod_setdebug(mod); 1367940d71d2Seschrock 1368940d71d2Seschrock topo_mod_dprintf(mod, "initializing %s enumerator\n", 1369940d71d2Seschrock SES_ENCLOSURE); 1370940d71d2Seschrock 1371940d71d2Seschrock return (topo_mod_register(mod, &ses_info, TOPO_VERSION)); 1372940d71d2Seschrock } 1373940d71d2Seschrock 1374940d71d2Seschrock void 1375940d71d2Seschrock _topo_fini(topo_mod_t *mod) 1376940d71d2Seschrock { 1377940d71d2Seschrock topo_mod_unregister(mod); 1378940d71d2Seschrock } 1379