1940d71d2Seschrock /* 2940d71d2Seschrock * CDDL HEADER START 3940d71d2Seschrock * 4940d71d2Seschrock * The contents of this file are subject to the terms of the 5940d71d2Seschrock * Common Development and Distribution License (the "License"). 6940d71d2Seschrock * You may not use this file except in compliance with the License. 7940d71d2Seschrock * 8940d71d2Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9940d71d2Seschrock * or http://www.opensolaris.org/os/licensing. 10940d71d2Seschrock * See the License for the specific language governing permissions 11940d71d2Seschrock * and limitations under the License. 12940d71d2Seschrock * 13940d71d2Seschrock * When distributing Covered Code, include this CDDL HEADER in each 14940d71d2Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15940d71d2Seschrock * If applicable, add the following below this CDDL HEADER, with the 16940d71d2Seschrock * fields enclosed by brackets "[]" replaced with your own identifying 17940d71d2Seschrock * information: Portions Copyright [yyyy] [name of copyright owner] 18940d71d2Seschrock * 19940d71d2Seschrock * CDDL HEADER END 20940d71d2Seschrock */ 21940d71d2Seschrock 22940d71d2Seschrock /* 23*6efb64caSEric Schrock * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24940d71d2Seschrock * Use is subject to license terms. 25940d71d2Seschrock */ 26940d71d2Seschrock 27*6efb64caSEric Schrock #include <alloca.h> 28940d71d2Seschrock #include <dirent.h> 29940d71d2Seschrock #include <devid.h> 30940d71d2Seschrock #include <fm/libdiskstatus.h> 31940d71d2Seschrock #include <inttypes.h> 32940d71d2Seschrock #include <pthread.h> 33940d71d2Seschrock #include <strings.h> 34940d71d2Seschrock #include <unistd.h> 35940d71d2Seschrock #include <sys/dkio.h> 36940d71d2Seschrock #include <sys/fm/protocol.h> 37940d71d2Seschrock #include <sys/scsi/scsi_types.h> 38d91236feSeschrock 39940d71d2Seschrock #include "disk.h" 40d91236feSeschrock #include "ses.h" 41940d71d2Seschrock 42940d71d2Seschrock #define SES_VERSION 1 43940d71d2Seschrock 44940d71d2Seschrock #define SES_SNAP_FREQ 1000 /* in milliseconds */ 45940d71d2Seschrock 46d91236feSeschrock #define SES_STATUS_UNAVAIL(s) \ 470b32bb8bSEric Schrock ((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED) 48940d71d2Seschrock 49940d71d2Seschrock /* 50940d71d2Seschrock * Because multiple SES targets can be part of a single chassis, we construct 51940d71d2Seschrock * our own hierarchy that takes this into account. These SES targets may refer 52940d71d2Seschrock * to the same devices (multiple paths) or to different devices (managing 53940d71d2Seschrock * different portions of the space). We arrange things into a 54940d71d2Seschrock * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all 55940d71d2Seschrock * nodes found so far. 56940d71d2Seschrock */ 570b32bb8bSEric Schrock typedef struct ses_alt_node { 580b32bb8bSEric Schrock topo_list_t san_link; 590b32bb8bSEric Schrock ses_node_t *san_node; 600b32bb8bSEric Schrock } ses_alt_node_t; 61940d71d2Seschrock 62940d71d2Seschrock typedef struct ses_enum_node { 63940d71d2Seschrock topo_list_t sen_link; 64940d71d2Seschrock ses_node_t *sen_node; 650b32bb8bSEric Schrock topo_list_t sen_alt_nodes; 66940d71d2Seschrock uint64_t sen_type; 67940d71d2Seschrock uint64_t sen_instance; 68940d71d2Seschrock ses_enum_target_t *sen_target; 69940d71d2Seschrock } ses_enum_node_t; 70940d71d2Seschrock 71940d71d2Seschrock typedef struct ses_enum_chassis { 72940d71d2Seschrock topo_list_t sec_link; 73940d71d2Seschrock topo_list_t sec_nodes; 74940d71d2Seschrock topo_list_t sec_targets; 75940d71d2Seschrock const char *sec_csn; 76940d71d2Seschrock ses_node_t *sec_enclosure; 77940d71d2Seschrock ses_enum_target_t *sec_target; 78940d71d2Seschrock topo_instance_t sec_instance; 79940d71d2Seschrock boolean_t sec_hasdev; 80d91236feSeschrock boolean_t sec_internal; 81940d71d2Seschrock } ses_enum_chassis_t; 82940d71d2Seschrock 83940d71d2Seschrock typedef struct ses_enum_data { 84940d71d2Seschrock topo_list_t sed_disks; 85940d71d2Seschrock topo_list_t sed_chassis; 86940d71d2Seschrock ses_enum_chassis_t *sed_current; 87940d71d2Seschrock ses_enum_target_t *sed_target; 88940d71d2Seschrock int sed_errno; 89940d71d2Seschrock char *sed_name; 90940d71d2Seschrock topo_mod_t *sed_mod; 91940d71d2Seschrock topo_instance_t sed_instance; 92940d71d2Seschrock } ses_enum_data_t; 93940d71d2Seschrock 94940d71d2Seschrock static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 95940d71d2Seschrock nvlist_t **); 96940d71d2Seschrock static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 97940d71d2Seschrock nvlist_t **); 98940d71d2Seschrock 99940d71d2Seschrock static const topo_method_t ses_component_methods[] = { 100940d71d2Seschrock { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, 101940d71d2Seschrock TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present }, 102d91236feSeschrock { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 103d91236feSeschrock TOPO_STABILITY_INTERNAL, ses_node_enum_facility }, 104d91236feSeschrock { NULL } 105d91236feSeschrock }; 106d91236feSeschrock 107d91236feSeschrock static const topo_method_t ses_bay_methods[] = { 108d91236feSeschrock { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 109d91236feSeschrock TOPO_STABILITY_INTERNAL, ses_node_enum_facility }, 110940d71d2Seschrock { NULL } 111940d71d2Seschrock }; 112940d71d2Seschrock 113940d71d2Seschrock static const topo_method_t ses_enclosure_methods[] = { 114940d71d2Seschrock { TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC, 115940d71d2Seschrock TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains }, 116d91236feSeschrock { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 117d91236feSeschrock TOPO_STABILITY_INTERNAL, ses_enc_enum_facility }, 118940d71d2Seschrock { NULL } 119940d71d2Seschrock }; 120940d71d2Seschrock 121940d71d2Seschrock static void 122940d71d2Seschrock ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp) 123940d71d2Seschrock { 124940d71d2Seschrock if (--stp->set_refcount == 0) { 125940d71d2Seschrock ses_snap_rele(stp->set_snap); 126940d71d2Seschrock ses_close(stp->set_target); 127940d71d2Seschrock topo_mod_strfree(mod, stp->set_devpath); 128940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 129940d71d2Seschrock } 130940d71d2Seschrock } 131940d71d2Seschrock 132940d71d2Seschrock static void 133940d71d2Seschrock ses_data_free(ses_enum_data_t *sdp) 134940d71d2Seschrock { 135940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 136940d71d2Seschrock ses_enum_chassis_t *cp; 137940d71d2Seschrock ses_enum_node_t *np; 138940d71d2Seschrock ses_enum_target_t *tp; 1390b32bb8bSEric Schrock ses_alt_node_t *ap; 140940d71d2Seschrock 141940d71d2Seschrock while ((cp = topo_list_next(&sdp->sed_chassis)) != NULL) { 142940d71d2Seschrock topo_list_delete(&sdp->sed_chassis, cp); 143940d71d2Seschrock 144940d71d2Seschrock while ((np = topo_list_next(&cp->sec_nodes)) != NULL) { 1450b32bb8bSEric Schrock while ((ap = topo_list_next(&np->sen_alt_nodes)) != 1460b32bb8bSEric Schrock NULL) { 1470b32bb8bSEric Schrock topo_list_delete(&np->sen_alt_nodes, ap); 1480b32bb8bSEric Schrock topo_mod_free(mod, ap, sizeof (ses_alt_node_t)); 1490b32bb8bSEric Schrock } 150940d71d2Seschrock topo_list_delete(&cp->sec_nodes, np); 151940d71d2Seschrock topo_mod_free(mod, np, sizeof (ses_enum_node_t)); 152940d71d2Seschrock } 153940d71d2Seschrock 154940d71d2Seschrock while ((tp = topo_list_next(&cp->sec_targets)) != NULL) { 155940d71d2Seschrock topo_list_delete(&cp->sec_targets, tp); 156940d71d2Seschrock ses_target_free(mod, tp); 157940d71d2Seschrock } 158940d71d2Seschrock 159940d71d2Seschrock topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t)); 160940d71d2Seschrock } 161940d71d2Seschrock 162940d71d2Seschrock disk_list_free(mod, &sdp->sed_disks); 163d91236feSeschrock topo_mod_free(mod, sdp, sizeof (ses_enum_data_t)); 164940d71d2Seschrock } 165940d71d2Seschrock 166940d71d2Seschrock /* 167940d71d2Seschrock * For enclosure nodes, we have a special contains method. By default, the hc 168940d71d2Seschrock * walker will compare the node name and instance number to determine if an 169940d71d2Seschrock * FMRI matches. For enclosures where the enumeration order is impossible to 170940d71d2Seschrock * predict, we instead use the chassis-id as a unique identifier, and ignore 171940d71d2Seschrock * the instance number. 172940d71d2Seschrock */ 173940d71d2Seschrock static int 174940d71d2Seschrock fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2) 175940d71d2Seschrock { 176940d71d2Seschrock uint8_t v1, v2; 177940d71d2Seschrock nvlist_t **hcp1, **hcp2; 178940d71d2Seschrock int err, i; 179940d71d2Seschrock uint_t nhcp1, nhcp2; 180940d71d2Seschrock nvlist_t *a1, *a2; 181940d71d2Seschrock char *c1, *c2; 182940d71d2Seschrock int mindepth; 183940d71d2Seschrock 184940d71d2Seschrock if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 || 185940d71d2Seschrock nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 || 186940d71d2Seschrock v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION) 187940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION)); 188940d71d2Seschrock 189940d71d2Seschrock err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1); 190940d71d2Seschrock err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2); 191940d71d2Seschrock if (err != 0) 192940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 193940d71d2Seschrock 194940d71d2Seschrock /* 195940d71d2Seschrock * If the chassis-id doesn't match, then these FMRIs are not 196940d71d2Seschrock * equivalent. If one of the FMRIs doesn't have a chassis ID, then we 197940d71d2Seschrock * have no choice but to fall back to the instance ID. 198940d71d2Seschrock */ 199940d71d2Seschrock if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 && 200940d71d2Seschrock nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 && 201940d71d2Seschrock nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 && 202940d71d2Seschrock nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) { 203940d71d2Seschrock if (strcmp(c1, c2) != 0) 204940d71d2Seschrock return (0); 205940d71d2Seschrock 206940d71d2Seschrock mindepth = 1; 207940d71d2Seschrock } else { 208940d71d2Seschrock mindepth = 0; 209940d71d2Seschrock } 210940d71d2Seschrock 211940d71d2Seschrock if (nhcp2 < nhcp1) 212940d71d2Seschrock return (0); 213940d71d2Seschrock 214940d71d2Seschrock for (i = 0; i < nhcp1; i++) { 215940d71d2Seschrock char *nm1 = NULL; 216940d71d2Seschrock char *nm2 = NULL; 217940d71d2Seschrock char *id1 = NULL; 218940d71d2Seschrock char *id2 = NULL; 219940d71d2Seschrock 220940d71d2Seschrock (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1); 221940d71d2Seschrock (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2); 222940d71d2Seschrock (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1); 223940d71d2Seschrock (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2); 224940d71d2Seschrock if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL) 225940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 226940d71d2Seschrock 227940d71d2Seschrock if (strcmp(nm1, nm2) == 0 && 228940d71d2Seschrock (i < mindepth || strcmp(id1, id2) == 0)) 229940d71d2Seschrock continue; 230940d71d2Seschrock 231940d71d2Seschrock return (0); 232940d71d2Seschrock } 233940d71d2Seschrock 234940d71d2Seschrock return (1); 235940d71d2Seschrock } 236940d71d2Seschrock 237940d71d2Seschrock /*ARGSUSED*/ 238940d71d2Seschrock static int 239940d71d2Seschrock ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 240940d71d2Seschrock nvlist_t *in, nvlist_t **out) 241940d71d2Seschrock { 242940d71d2Seschrock int ret; 243940d71d2Seschrock nvlist_t *nv1, *nv2; 244940d71d2Seschrock 245940d71d2Seschrock if (version > TOPO_METH_CONTAINS_VERSION) 246940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 247940d71d2Seschrock 248940d71d2Seschrock if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 || 249940d71d2Seschrock nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0) 250940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 251940d71d2Seschrock 252940d71d2Seschrock ret = fmri_contains(mod, nv1, nv2); 253940d71d2Seschrock if (ret < 0) 254940d71d2Seschrock return (-1); 255940d71d2Seschrock 256940d71d2Seschrock if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) { 25773e32a37SRobert Johnston if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET, 258940d71d2Seschrock ret) == 0) 259940d71d2Seschrock return (0); 260940d71d2Seschrock else 261940d71d2Seschrock nvlist_free(*out); 262940d71d2Seschrock } 263940d71d2Seschrock 264940d71d2Seschrock return (-1); 265940d71d2Seschrock 266940d71d2Seschrock } 267940d71d2Seschrock 268940d71d2Seschrock /* 269d91236feSeschrock * Return a current instance of the node. This is somewhat complicated because 270d91236feSeschrock * we need to take a new snapshot in order to get the new data, but we don't 271940d71d2Seschrock * want to be constantly taking SES snapshots if the consumer is going to do a 272940d71d2Seschrock * series of queries. So we adopt the strategy of assuming that the SES state 273d91236feSeschrock * is not going to be rapidly changing, and limit our snapshot frequency to 274d91236feSeschrock * some defined bounds. 275940d71d2Seschrock */ 276d91236feSeschrock ses_node_t * 2770b32bb8bSEric Schrock ses_node_lock(topo_mod_t *mod, tnode_t *tn) 278940d71d2Seschrock { 279940d71d2Seschrock struct timeval tv; 280940d71d2Seschrock ses_enum_target_t *tp = topo_node_getspecific(tn); 281940d71d2Seschrock uint64_t prev, now; 282940d71d2Seschrock ses_snap_t *snap; 283940d71d2Seschrock int err; 284d91236feSeschrock uint64_t nodeid; 285940d71d2Seschrock ses_node_t *np; 286940d71d2Seschrock 287d91236feSeschrock if (tp == NULL) { 288d91236feSeschrock (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 289d91236feSeschrock return (NULL); 290d91236feSeschrock } 291940d71d2Seschrock 2920b32bb8bSEric Schrock (void) pthread_mutex_lock(&tp->set_lock); 2930b32bb8bSEric Schrock 294940d71d2Seschrock /* 295940d71d2Seschrock * Determine if we need to take a new snapshot. 296940d71d2Seschrock */ 297940d71d2Seschrock if (gettimeofday(&tv, NULL) != 0) { 298940d71d2Seschrock tv.tv_sec = time(NULL); 299940d71d2Seschrock tv.tv_usec = 0; 300940d71d2Seschrock } 301940d71d2Seschrock 302940d71d2Seschrock now = tv.tv_sec * 1000 + tv.tv_usec / 1000; 303940d71d2Seschrock prev = tp->set_snaptime.tv_sec * 1000 + 304940d71d2Seschrock tp->set_snaptime.tv_usec / 1000; 305940d71d2Seschrock 306940d71d2Seschrock if (now - prev > SES_SNAP_FREQ && 307940d71d2Seschrock (snap = ses_snap_new(tp->set_target)) != NULL) { 308940d71d2Seschrock if (ses_snap_generation(snap) != 309940d71d2Seschrock ses_snap_generation(tp->set_snap)) { 310940d71d2Seschrock /* 311940d71d2Seschrock * If we find ourselves in this situation, we're in 312940d71d2Seschrock * trouble. The generation count has changed, which 313940d71d2Seschrock * indicates that our current topology is out of date. 314940d71d2Seschrock * But we need to consult the new topology in order to 315940d71d2Seschrock * determine presence at this moment in time. We can't 316940d71d2Seschrock * go back and change the topo snapshot in situ, so 317d91236feSeschrock * we'll just have to fail the call in this unlikely 318d91236feSeschrock * scenario. 319940d71d2Seschrock */ 320940d71d2Seschrock ses_snap_rele(snap); 321d91236feSeschrock (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 3220b32bb8bSEric Schrock (void) pthread_mutex_unlock(&tp->set_lock); 323d91236feSeschrock return (NULL); 324940d71d2Seschrock } else { 325940d71d2Seschrock ses_snap_rele(tp->set_snap); 326940d71d2Seschrock tp->set_snap = snap; 327940d71d2Seschrock } 328940d71d2Seschrock tp->set_snaptime = tv; 329940d71d2Seschrock } 330940d71d2Seschrock 331940d71d2Seschrock snap = tp->set_snap; 332940d71d2Seschrock 333940d71d2Seschrock verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES, 334940d71d2Seschrock TOPO_PROP_NODE_ID, &nodeid, &err) == 0); 335940d71d2Seschrock verify((np = ses_node_lookup(snap, nodeid)) != NULL); 336d91236feSeschrock 337d91236feSeschrock return (np); 338d91236feSeschrock } 339d91236feSeschrock 3400b32bb8bSEric Schrock /*ARGSUSED*/ 3410b32bb8bSEric Schrock void 3420b32bb8bSEric Schrock ses_node_unlock(topo_mod_t *mod, tnode_t *tn) 3430b32bb8bSEric Schrock { 3440b32bb8bSEric Schrock ses_enum_target_t *tp = topo_node_getspecific(tn); 3450b32bb8bSEric Schrock 3460b32bb8bSEric Schrock verify(tp != NULL); 3470b32bb8bSEric Schrock 3480b32bb8bSEric Schrock (void) pthread_mutex_unlock(&tp->set_lock); 3490b32bb8bSEric Schrock } 3500b32bb8bSEric Schrock 351d91236feSeschrock /* 352d91236feSeschrock * Determine if the element is present. 353d91236feSeschrock */ 354d91236feSeschrock /*ARGSUSED*/ 355d91236feSeschrock static int 356d91236feSeschrock ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 357d91236feSeschrock nvlist_t *in, nvlist_t **out) 358d91236feSeschrock { 359d91236feSeschrock boolean_t present; 360d91236feSeschrock ses_node_t *np; 361d91236feSeschrock nvlist_t *props, *nvl; 362d91236feSeschrock uint64_t status; 363d91236feSeschrock 3640b32bb8bSEric Schrock if ((np = ses_node_lock(mod, tn)) == NULL) 365d91236feSeschrock return (-1); 366d91236feSeschrock 367940d71d2Seschrock verify((props = ses_node_props(np)) != NULL); 368940d71d2Seschrock verify(nvlist_lookup_uint64(props, 369940d71d2Seschrock SES_PROP_STATUS_CODE, &status) == 0); 370940d71d2Seschrock 3710b32bb8bSEric Schrock ses_node_unlock(mod, tn); 3720b32bb8bSEric Schrock 373940d71d2Seschrock present = (status != SES_ESC_NOT_INSTALLED); 374940d71d2Seschrock 375940d71d2Seschrock if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) 376940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 377940d71d2Seschrock 378940d71d2Seschrock if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET, 379940d71d2Seschrock present) != 0) { 380940d71d2Seschrock nvlist_free(nvl); 381940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 382940d71d2Seschrock } 383940d71d2Seschrock 384940d71d2Seschrock *out = nvl; 385940d71d2Seschrock 386940d71d2Seschrock return (0); 387940d71d2Seschrock } 388940d71d2Seschrock 389940d71d2Seschrock /* 390940d71d2Seschrock * Sets standard properties for a ses node (enclosure or bay). This includes 391940d71d2Seschrock * setting the FRU to be the same as the resource, as well as setting the 392940d71d2Seschrock * authority information. 393940d71d2Seschrock */ 394940d71d2Seschrock static int 395940d71d2Seschrock ses_set_standard_props(topo_mod_t *mod, tnode_t *tn, nvlist_t *auth, 396940d71d2Seschrock uint64_t nodeid, const char *path) 397940d71d2Seschrock { 398940d71d2Seschrock int err; 399940d71d2Seschrock char *product, *chassis; 400940d71d2Seschrock nvlist_t *fmri; 401940d71d2Seschrock topo_pgroup_info_t pgi; 402940d71d2Seschrock 403940d71d2Seschrock /* 404940d71d2Seschrock * Set the authority explicitly if specified. 405940d71d2Seschrock */ 406940d71d2Seschrock if (auth) { 407940d71d2Seschrock verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, 408940d71d2Seschrock &product) == 0); 409940d71d2Seschrock verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, 410940d71d2Seschrock &chassis) == 0); 411940d71d2Seschrock if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 412940d71d2Seschrock FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product, 413940d71d2Seschrock &err) != 0 || 414940d71d2Seschrock topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 415940d71d2Seschrock FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis, 416940d71d2Seschrock &err) != 0 || 417940d71d2Seschrock topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 418940d71d2Seschrock FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "", 419940d71d2Seschrock &err) != 0) { 420940d71d2Seschrock topo_mod_dprintf(mod, "failed to add authority " 421d91236feSeschrock "properties: %s\n", topo_strerror(err)); 422940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 423940d71d2Seschrock } 424940d71d2Seschrock } 425940d71d2Seschrock 426940d71d2Seschrock /* 427940d71d2Seschrock * Copy the resource and set that as the FRU. 428940d71d2Seschrock */ 429940d71d2Seschrock if (topo_node_resource(tn, &fmri, &err) != 0) { 430940d71d2Seschrock topo_mod_dprintf(mod, 431940d71d2Seschrock "topo_node_resource() failed : %s\n", 432940d71d2Seschrock topo_strerror(err)); 433940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 434940d71d2Seschrock } 435940d71d2Seschrock 436940d71d2Seschrock if (topo_node_fru_set(tn, fmri, 0, &err) != 0) { 437940d71d2Seschrock topo_mod_dprintf(mod, 438940d71d2Seschrock "topo_node_fru_set() failed : %s\n", 439940d71d2Seschrock topo_strerror(err)); 440940d71d2Seschrock nvlist_free(fmri); 441940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 442940d71d2Seschrock } 443940d71d2Seschrock 444940d71d2Seschrock nvlist_free(fmri); 445940d71d2Seschrock 446940d71d2Seschrock /* 447940d71d2Seschrock * Set the SES-specific properties so that consumers can query 448940d71d2Seschrock * additional information about the particular SES element. 449940d71d2Seschrock */ 450940d71d2Seschrock pgi.tpi_name = TOPO_PGROUP_SES; 451940d71d2Seschrock pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 452940d71d2Seschrock pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 453940d71d2Seschrock pgi.tpi_version = TOPO_VERSION; 454940d71d2Seschrock if (topo_pgroup_create(tn, &pgi, &err) != 0) { 455940d71d2Seschrock topo_mod_dprintf(mod, "failed to create propgroup " 456940d71d2Seschrock "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err)); 457940d71d2Seschrock return (-1); 458940d71d2Seschrock } 459940d71d2Seschrock 460940d71d2Seschrock if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES, 461940d71d2Seschrock TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE, 462940d71d2Seschrock nodeid, &err) != 0) { 463940d71d2Seschrock topo_mod_dprintf(mod, 464940d71d2Seschrock "failed to create property %s: %s\n", 465940d71d2Seschrock TOPO_PROP_NODE_ID, topo_strerror(err)); 466940d71d2Seschrock return (-1); 467940d71d2Seschrock } 468940d71d2Seschrock 469940d71d2Seschrock if (topo_prop_set_string(tn, TOPO_PGROUP_SES, 470940d71d2Seschrock TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE, 471940d71d2Seschrock path, &err) != 0) { 472940d71d2Seschrock topo_mod_dprintf(mod, 473940d71d2Seschrock "failed to create property %s: %s\n", 474940d71d2Seschrock TOPO_PROP_TARGET_PATH, topo_strerror(err)); 475940d71d2Seschrock return (-1); 476940d71d2Seschrock } 477940d71d2Seschrock 478940d71d2Seschrock return (0); 479940d71d2Seschrock } 480940d71d2Seschrock 481940d71d2Seschrock /* 482940d71d2Seschrock * Callback to add a disk to a given bay. We first check the status-code to 483940d71d2Seschrock * determine if a disk is present, ignoring those that aren't in an appropriate 4840b32bb8bSEric Schrock * state. We then scan the parent bay node's SAS address array to determine 4850b32bb8bSEric Schrock * possible attached SAS addresses. We create a disk node if the disk is not 4860b32bb8bSEric Schrock * SAS or the SES target does not support the necessary pages for this; if we 4870b32bb8bSEric Schrock * find the SAS address, we create a disk node and also correlate it with 4880b32bb8bSEric Schrock * the corresponding Solaris device node to fill in the rest of the data. 489940d71d2Seschrock */ 490940d71d2Seschrock static int 491940d71d2Seschrock ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props) 492940d71d2Seschrock { 493940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 494940d71d2Seschrock uint64_t status; 495940d71d2Seschrock nvlist_t **sas; 496940d71d2Seschrock uint_t s, nsas; 4970b32bb8bSEric Schrock char **paths; 4980b32bb8bSEric Schrock int err; 499940d71d2Seschrock 500940d71d2Seschrock /* 501940d71d2Seschrock * Skip devices that are not in a present (and possibly damaged) state. 502940d71d2Seschrock */ 503940d71d2Seschrock if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0) 504940d71d2Seschrock return (0); 505940d71d2Seschrock 506940d71d2Seschrock if (status != SES_ESC_OK && 507940d71d2Seschrock status != SES_ESC_CRITICAL && 508940d71d2Seschrock status != SES_ESC_NONCRITICAL && 509940d71d2Seschrock status != SES_ESC_UNRECOVERABLE && 510940d71d2Seschrock status != SES_ESC_NO_ACCESS) 511940d71d2Seschrock return (0); 512940d71d2Seschrock 513940d71d2Seschrock topo_mod_dprintf(mod, "found attached disk"); 514940d71d2Seschrock 515940d71d2Seschrock /* 516940d71d2Seschrock * Create the disk range. 517940d71d2Seschrock */ 5180b32bb8bSEric Schrock if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) { 519940d71d2Seschrock topo_mod_dprintf(mod, 520940d71d2Seschrock "topo_node_create_range() failed: %s", 521940d71d2Seschrock topo_mod_errmsg(mod)); 522940d71d2Seschrock return (-1); 523940d71d2Seschrock } 524940d71d2Seschrock 525940d71d2Seschrock /* 526940d71d2Seschrock * Look through all SAS addresses and attempt to correlate them to a 527940d71d2Seschrock * known Solaris device. If we don't find a matching node, then we 528940d71d2Seschrock * don't enumerate the disk node. 529940d71d2Seschrock */ 530940d71d2Seschrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 531940d71d2Seschrock &sas, &nsas) != 0) 532940d71d2Seschrock return (0); 533940d71d2Seschrock 5340b32bb8bSEric Schrock if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES, 5350b32bb8bSEric Schrock TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0) 5360b32bb8bSEric Schrock return (0); 5370b32bb8bSEric Schrock 5380b32bb8bSEric Schrock err = 0; 5390b32bb8bSEric Schrock 540940d71d2Seschrock for (s = 0; s < nsas; s++) { 5410b32bb8bSEric Schrock if (disk_declare_addr(mod, pnode, 5420b32bb8bSEric Schrock &sdp->sed_disks, paths[s]) != 0 && 5430b32bb8bSEric Schrock topo_mod_errno(mod) != EMOD_NODE_BOUND) { 5440b32bb8bSEric Schrock err = -1; 5450b32bb8bSEric Schrock break; 5460b32bb8bSEric Schrock } 5470b32bb8bSEric Schrock } 5480b32bb8bSEric Schrock 5490b32bb8bSEric Schrock for (s = 0; s < nsas; s++) 5500b32bb8bSEric Schrock topo_mod_free(mod, paths[s], strlen(paths[s]) + 1); 5510b32bb8bSEric Schrock topo_mod_free(mod, paths, nsas * sizeof (char *)); 5520b32bb8bSEric Schrock 5530b32bb8bSEric Schrock return (err); 5540b32bb8bSEric Schrock } 5550b32bb8bSEric Schrock 5560b32bb8bSEric Schrock static int 5570b32bb8bSEric Schrock ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp) 5580b32bb8bSEric Schrock { 5590b32bb8bSEric Schrock ses_alt_node_t *ap; 5600b32bb8bSEric Schrock ses_node_t *np; 5610b32bb8bSEric Schrock nvlist_t *props; 5620b32bb8bSEric Schrock 5630b32bb8bSEric Schrock nvlist_t **phys; 5640b32bb8bSEric Schrock uint_t i, j, n_phys, all_phys = 0; 5650b32bb8bSEric Schrock char **paths; 5660b32bb8bSEric Schrock uint64_t addr; 5670b32bb8bSEric Schrock size_t len; 5680b32bb8bSEric Schrock int terr, err = -1; 5690b32bb8bSEric Schrock 5700b32bb8bSEric Schrock for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 5710b32bb8bSEric Schrock ap = topo_list_next(ap)) { 5720b32bb8bSEric Schrock np = ap->san_node; 5730b32bb8bSEric Schrock props = ses_node_props(np); 5740b32bb8bSEric Schrock 5750b32bb8bSEric Schrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 5760b32bb8bSEric Schrock &phys, &n_phys) != 0) 5770b32bb8bSEric Schrock continue; 5780b32bb8bSEric Schrock 5790b32bb8bSEric Schrock all_phys += n_phys; 5800b32bb8bSEric Schrock } 5810b32bb8bSEric Schrock 5820b32bb8bSEric Schrock if (all_phys == 0) 5830b32bb8bSEric Schrock return (0); 5840b32bb8bSEric Schrock 5850b32bb8bSEric Schrock if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL) 5860b32bb8bSEric Schrock return (-1); 5870b32bb8bSEric Schrock 5880b32bb8bSEric Schrock for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 5890b32bb8bSEric Schrock ap = topo_list_next(ap)) { 5900b32bb8bSEric Schrock np = ap->san_node; 5910b32bb8bSEric Schrock props = ses_node_props(np); 5920b32bb8bSEric Schrock 5930b32bb8bSEric Schrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 5940b32bb8bSEric Schrock &phys, &n_phys) != 0) 595940d71d2Seschrock continue; 596940d71d2Seschrock 5970b32bb8bSEric Schrock for (j = 0; j < n_phys; j++) { 5980b32bb8bSEric Schrock if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR, 5990b32bb8bSEric Schrock &addr) != 0) 6000b32bb8bSEric Schrock continue; 601940d71d2Seschrock 6020b32bb8bSEric Schrock len = snprintf(NULL, 0, "%016llx", addr) + 1; 6030b32bb8bSEric Schrock if ((paths[i] = topo_mod_alloc(mod, len)) == NULL) 6040b32bb8bSEric Schrock goto error; 6050b32bb8bSEric Schrock 6060b32bb8bSEric Schrock (void) snprintf(paths[i], len, "%016llx", addr); 6070b32bb8bSEric Schrock 6080b32bb8bSEric Schrock ++i; 6090b32bb8bSEric Schrock } 610940d71d2Seschrock } 611940d71d2Seschrock 6120b32bb8bSEric Schrock err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 6130b32bb8bSEric Schrock TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, 6140b32bb8bSEric Schrock (const char **)paths, i, &terr); 6150b32bb8bSEric Schrock if (err != 0) 6160b32bb8bSEric Schrock err = topo_mod_seterrno(mod, terr); 6170b32bb8bSEric Schrock 6180b32bb8bSEric Schrock error: 6190b32bb8bSEric Schrock for (i = 0; i < all_phys && paths[i] != NULL; i++) 6200b32bb8bSEric Schrock topo_mod_free(mod, paths[i], strlen(paths[i]) + 1); 6210b32bb8bSEric Schrock topo_mod_free(mod, paths, all_phys * sizeof (char *)); 6220b32bb8bSEric Schrock 6230b32bb8bSEric Schrock return (err); 624940d71d2Seschrock } 625940d71d2Seschrock 626940d71d2Seschrock /* 627940d71d2Seschrock * Callback to create a basic node (bay, psu, fan, or controller). 628940d71d2Seschrock */ 629940d71d2Seschrock static int 630940d71d2Seschrock ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp, 631940d71d2Seschrock tnode_t *pnode, const char *nodename, const char *labelname) 632940d71d2Seschrock { 633940d71d2Seschrock ses_node_t *np = snp->sen_node; 634d91236feSeschrock ses_node_t *parent; 635940d71d2Seschrock uint64_t instance = snp->sen_instance; 636940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 637940d71d2Seschrock nvlist_t *props, *aprops; 638940d71d2Seschrock nvlist_t *auth = NULL, *fmri = NULL; 639940d71d2Seschrock tnode_t *tn; 640940d71d2Seschrock char label[128]; 641940d71d2Seschrock int err; 642d91236feSeschrock char *part = NULL, *serial = NULL, *revision = NULL; 643d91236feSeschrock char *desc; 644d91236feSeschrock boolean_t report; 645940d71d2Seschrock 646940d71d2Seschrock props = ses_node_props(np); 647940d71d2Seschrock 648940d71d2Seschrock (void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part); 649940d71d2Seschrock (void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial); 650940d71d2Seschrock 651940d71d2Seschrock topo_mod_dprintf(mod, "adding %s %llu", nodename, instance); 652940d71d2Seschrock 653940d71d2Seschrock /* 654940d71d2Seschrock * Create the node. The interesting information is all copied from the 655940d71d2Seschrock * parent enclosure node, so there is not much to do. 656940d71d2Seschrock */ 657940d71d2Seschrock if ((auth = topo_mod_auth(mod, pnode)) == NULL) 658940d71d2Seschrock goto error; 659940d71d2Seschrock 660d91236feSeschrock /* 661d91236feSeschrock * We want to report revision information for the controller nodes, but 662d91236feSeschrock * we do not get per-element revision information. However, we do have 663d91236feSeschrock * revision information for the entire enclosure, and we can use the 664d91236feSeschrock * 'reported-via' property to know that this controller corresponds to 665d91236feSeschrock * the given revision information. This means we cannot get revision 666d91236feSeschrock * information for targets we are not explicitly connected to, but 667d91236feSeschrock * there is little we can do about the situation. 668d91236feSeschrock */ 669d91236feSeschrock if (strcmp(nodename, CONTROLLER) == 0 && 670d91236feSeschrock nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 && 671d91236feSeschrock report) { 672d91236feSeschrock for (parent = ses_node_parent(np); parent != NULL; 673d91236feSeschrock parent = ses_node_parent(parent)) { 674d91236feSeschrock if (ses_node_type(parent) == SES_NODE_ENCLOSURE) { 675d91236feSeschrock (void) nvlist_lookup_string( 676d91236feSeschrock ses_node_props(parent), 677d91236feSeschrock SES_EN_PROP_REV, &revision); 678d91236feSeschrock break; 679d91236feSeschrock } 680d91236feSeschrock } 681d91236feSeschrock } 682d91236feSeschrock 683940d71d2Seschrock if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 684d91236feSeschrock nodename, (topo_instance_t)instance, NULL, auth, part, revision, 685940d71d2Seschrock serial)) == NULL) { 686940d71d2Seschrock topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 687940d71d2Seschrock topo_mod_errmsg(mod)); 688940d71d2Seschrock goto error; 689940d71d2Seschrock } 690940d71d2Seschrock 691940d71d2Seschrock if ((tn = topo_node_bind(mod, pnode, nodename, 692940d71d2Seschrock instance, fmri)) == NULL) { 693940d71d2Seschrock topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 694940d71d2Seschrock topo_mod_errmsg(mod)); 695940d71d2Seschrock goto error; 696940d71d2Seschrock } 697940d71d2Seschrock 698940d71d2Seschrock /* 699d91236feSeschrock * For the node label, we look for the following in order: 700d91236feSeschrock * 701d91236feSeschrock * <ses-description> 702d91236feSeschrock * <ses-class-description> <instance> 703d91236feSeschrock * <default-type-label> <instance> 704940d71d2Seschrock */ 705d91236feSeschrock if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 || 706d91236feSeschrock desc[0] == '\0') { 707d91236feSeschrock parent = ses_node_parent(np); 708d91236feSeschrock aprops = ses_node_props(parent); 709d91236feSeschrock if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION, 710d91236feSeschrock &desc) == 0 && desc[0] != '\0') 711d91236feSeschrock labelname = desc; 712d91236feSeschrock (void) snprintf(label, sizeof (label), "%s %llu", desc, 713d91236feSeschrock instance); 714d91236feSeschrock desc = label; 715d91236feSeschrock } 716d91236feSeschrock 717d91236feSeschrock if (topo_node_label_set(tn, desc, &err) != 0) 718940d71d2Seschrock goto error; 719940d71d2Seschrock 720940d71d2Seschrock if (ses_set_standard_props(mod, tn, NULL, ses_node_id(np), 721940d71d2Seschrock snp->sen_target->set_devpath) != 0) 722940d71d2Seschrock goto error; 723940d71d2Seschrock 724940d71d2Seschrock if (strcmp(nodename, "bay") == 0) { 7250b32bb8bSEric Schrock if (ses_add_bay_props(mod, tn, snp) != 0) 7260b32bb8bSEric Schrock goto error; 7270b32bb8bSEric Schrock 728940d71d2Seschrock if (ses_create_disk(sdp, tn, props) != 0) 729940d71d2Seschrock goto error; 730d91236feSeschrock 731d91236feSeschrock if (topo_method_register(mod, tn, ses_bay_methods) != 0) { 732d91236feSeschrock topo_mod_dprintf(mod, 733d91236feSeschrock "topo_method_register() failed: %s", 734d91236feSeschrock topo_mod_errmsg(mod)); 735d91236feSeschrock goto error; 736d91236feSeschrock } 737940d71d2Seschrock } else { 738940d71d2Seschrock /* 739d91236feSeschrock * Only fan, psu, and controller nodes have a 'present' method. 740d91236feSeschrock * Bay nodes are always present, and disk nodes are present by 741d91236feSeschrock * virtue of being enumerated. 742940d71d2Seschrock */ 743940d71d2Seschrock if (topo_method_register(mod, tn, ses_component_methods) != 0) { 744940d71d2Seschrock topo_mod_dprintf(mod, 745940d71d2Seschrock "topo_method_register() failed: %s", 746940d71d2Seschrock topo_mod_errmsg(mod)); 747940d71d2Seschrock goto error; 748940d71d2Seschrock } 749940d71d2Seschrock 750940d71d2Seschrock } 751940d71d2Seschrock 752d91236feSeschrock snp->sen_target->set_refcount++; 753d91236feSeschrock topo_node_setspecific(tn, snp->sen_target); 754d91236feSeschrock 755940d71d2Seschrock nvlist_free(auth); 756940d71d2Seschrock nvlist_free(fmri); 757940d71d2Seschrock return (0); 758940d71d2Seschrock 759940d71d2Seschrock error: 760940d71d2Seschrock nvlist_free(auth); 761940d71d2Seschrock nvlist_free(fmri); 762940d71d2Seschrock return (-1); 763940d71d2Seschrock } 764940d71d2Seschrock 765940d71d2Seschrock /* 766940d71d2Seschrock * Instantiate any children of a given type. 767940d71d2Seschrock */ 768940d71d2Seschrock static int 769940d71d2Seschrock ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type, 770d91236feSeschrock const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp, 771d91236feSeschrock boolean_t dorange) 772940d71d2Seschrock { 773940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 774940d71d2Seschrock boolean_t found; 775940d71d2Seschrock uint64_t max; 776940d71d2Seschrock ses_enum_node_t *snp; 777940d71d2Seschrock 778940d71d2Seschrock /* 779940d71d2Seschrock * First go through and count how many matching nodes we have. 780940d71d2Seschrock */ 781940d71d2Seschrock max = 0; 782940d71d2Seschrock found = B_FALSE; 783940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 784940d71d2Seschrock snp = topo_list_next(snp)) { 785940d71d2Seschrock if (snp->sen_type == type) { 786940d71d2Seschrock found = B_TRUE; 787940d71d2Seschrock if (snp->sen_instance > max) 788940d71d2Seschrock max = snp->sen_instance; 789940d71d2Seschrock } 790940d71d2Seschrock } 791940d71d2Seschrock 792940d71d2Seschrock /* 793940d71d2Seschrock * No enclosure should export both DEVICE and ARRAY_DEVICE elements. 794940d71d2Seschrock * Since we map both of these to 'disk', if an enclosure does this, we 795940d71d2Seschrock * just ignore the array elements. 796940d71d2Seschrock */ 797940d71d2Seschrock if (!found || 798940d71d2Seschrock (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev)) 799940d71d2Seschrock return (0); 800940d71d2Seschrock 801940d71d2Seschrock topo_mod_dprintf(mod, "%s: creating %llu %s nodes", 802940d71d2Seschrock cp->sec_csn, max, nodename); 803940d71d2Seschrock 804d91236feSeschrock if (dorange && topo_node_range_create(mod, pnode, 805940d71d2Seschrock nodename, 0, max) != 0) { 806940d71d2Seschrock topo_mod_dprintf(mod, 807940d71d2Seschrock "topo_node_create_range() failed: %s", 808940d71d2Seschrock topo_mod_errmsg(mod)); 809940d71d2Seschrock return (-1); 810940d71d2Seschrock } 811940d71d2Seschrock 812940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 813940d71d2Seschrock snp = topo_list_next(snp)) { 814940d71d2Seschrock if (snp->sen_type == type) { 815940d71d2Seschrock if (ses_create_generic(sdp, snp, pnode, 816940d71d2Seschrock nodename, defaultlabel) != 0) 817940d71d2Seschrock return (-1); 818940d71d2Seschrock } 819940d71d2Seschrock } 820940d71d2Seschrock 821940d71d2Seschrock return (0); 822940d71d2Seschrock } 823940d71d2Seschrock 824940d71d2Seschrock /* 825940d71d2Seschrock * Instantiate a new chassis instance in the topology. 826940d71d2Seschrock */ 827940d71d2Seschrock static int 828940d71d2Seschrock ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp) 829940d71d2Seschrock { 830940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 831940d71d2Seschrock nvlist_t *props; 832940d71d2Seschrock char *raw_manufacturer, *raw_model, *raw_revision; 833940d71d2Seschrock char *manufacturer = NULL, *model = NULL, *product = NULL; 834940d71d2Seschrock char *revision = NULL; 835940d71d2Seschrock char *serial; 836*6efb64caSEric Schrock char **paths; 837940d71d2Seschrock size_t prodlen; 838940d71d2Seschrock tnode_t *tn; 839940d71d2Seschrock nvlist_t *fmri = NULL, *auth = NULL; 840940d71d2Seschrock int ret = -1; 841940d71d2Seschrock ses_enum_node_t *snp; 842*6efb64caSEric Schrock ses_enum_target_t *stp; 843*6efb64caSEric Schrock int i, err; 844940d71d2Seschrock 845d91236feSeschrock /* 846d91236feSeschrock * Ignore any internal enclosures. 847d91236feSeschrock */ 848d91236feSeschrock if (cp->sec_internal) 849d91236feSeschrock return (0); 850d91236feSeschrock 851940d71d2Seschrock /* 852940d71d2Seschrock * Check to see if there are any devices presennt in the chassis. If 853940d71d2Seschrock * not, ignore the chassis alltogether. This is most useful for 854940d71d2Seschrock * ignoring internal HBAs that present a SES target but don't actually 855940d71d2Seschrock * manage any of the devices. 856940d71d2Seschrock */ 857940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 858940d71d2Seschrock snp = topo_list_next(snp)) { 859940d71d2Seschrock if (snp->sen_type == SES_ET_DEVICE || 860940d71d2Seschrock snp->sen_type == SES_ET_ARRAY_DEVICE) 861940d71d2Seschrock break; 862940d71d2Seschrock } 863940d71d2Seschrock 864940d71d2Seschrock if (snp == NULL) 865940d71d2Seschrock return (0); 866940d71d2Seschrock 867940d71d2Seschrock props = ses_node_props(cp->sec_enclosure); 868940d71d2Seschrock 869940d71d2Seschrock /* 870940d71d2Seschrock * We use the following property mappings: 871940d71d2Seschrock * 872940d71d2Seschrock * manufacturer vendor-id 873940d71d2Seschrock * model product-id 874940d71d2Seschrock * serial-number libses-chassis-serial 875940d71d2Seschrock */ 876940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_VID, 877940d71d2Seschrock &raw_manufacturer) == 0); 878940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0); 879940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_REV, 880940d71d2Seschrock &raw_revision) == 0); 881940d71d2Seschrock verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0); 882940d71d2Seschrock 883940d71d2Seschrock /* 884940d71d2Seschrock * To construct the authority information, we 'clean' each string by 885940d71d2Seschrock * removing any offensive characters and trimmming whitespace. For the 886940d71d2Seschrock * 'product-id', we use a concatenation of 'manufacturer-model'. We 887940d71d2Seschrock * also take the numerical serial number and convert it to a string. 888940d71d2Seschrock */ 889940d71d2Seschrock if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL || 890940d71d2Seschrock (model = disk_auth_clean(mod, raw_model)) == NULL || 891940d71d2Seschrock (revision = disk_auth_clean(mod, raw_revision)) == NULL) { 892940d71d2Seschrock goto error; 893940d71d2Seschrock } 894940d71d2Seschrock 895940d71d2Seschrock prodlen = strlen(manufacturer) + strlen(model) + 2; 896940d71d2Seschrock if ((product = topo_mod_alloc(mod, prodlen)) == NULL) 897940d71d2Seschrock goto error; 898940d71d2Seschrock 899940d71d2Seschrock (void) snprintf(product, prodlen, "%s-%s", manufacturer, model); 900940d71d2Seschrock 901940d71d2Seschrock /* 902940d71d2Seschrock * Construct the topo node and bind it to our parent. 903940d71d2Seschrock */ 904940d71d2Seschrock if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0) 905940d71d2Seschrock goto error; 906940d71d2Seschrock 907940d71d2Seschrock if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 || 908940d71d2Seschrock nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) { 909940d71d2Seschrock (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 910940d71d2Seschrock goto error; 911940d71d2Seschrock } 912940d71d2Seschrock 913940d71d2Seschrock /* 914940d71d2Seschrock * We pass NULL for the parent FMRI because there is no resource 915940d71d2Seschrock * associated with it. For the toplevel enclosure, we leave the 916940d71d2Seschrock * serial/part/revision portions empty, which are reserved for 917940d71d2Seschrock * individual components within the chassis. 918940d71d2Seschrock */ 919940d71d2Seschrock if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION, 920940d71d2Seschrock SES_ENCLOSURE, cp->sec_instance, NULL, auth, 921940d71d2Seschrock model, revision, serial)) == NULL) { 922940d71d2Seschrock topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 923940d71d2Seschrock topo_mod_errmsg(mod)); 924940d71d2Seschrock goto error; 925940d71d2Seschrock } 926940d71d2Seschrock 927940d71d2Seschrock if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE, 928940d71d2Seschrock cp->sec_instance, fmri)) == NULL) { 929940d71d2Seschrock topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 930940d71d2Seschrock topo_mod_errmsg(mod)); 931940d71d2Seschrock goto error; 932940d71d2Seschrock } 933940d71d2Seschrock 934940d71d2Seschrock if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 935940d71d2Seschrock topo_mod_dprintf(mod, 936940d71d2Seschrock "topo_method_register() failed: %s", 937940d71d2Seschrock topo_mod_errmsg(mod)); 938940d71d2Seschrock goto error; 939940d71d2Seschrock } 940940d71d2Seschrock 941940d71d2Seschrock if (ses_set_standard_props(mod, tn, auth, 942940d71d2Seschrock ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0) 943940d71d2Seschrock goto error; 944940d71d2Seschrock 945*6efb64caSEric Schrock /* 946*6efb64caSEric Schrock * For enclosures, we want to include all possible targets (for upgrade 947*6efb64caSEric Schrock * purposes). 948*6efb64caSEric Schrock */ 949*6efb64caSEric Schrock for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL; 950*6efb64caSEric Schrock stp = topo_list_next(stp), i++) 951*6efb64caSEric Schrock ; 952*6efb64caSEric Schrock 953*6efb64caSEric Schrock verify(i != 0); 954*6efb64caSEric Schrock paths = alloca(i * sizeof (char *)); 955*6efb64caSEric Schrock 956*6efb64caSEric Schrock for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL; 957*6efb64caSEric Schrock stp = topo_list_next(stp), i++) 958*6efb64caSEric Schrock paths[i] = stp->set_devpath; 959*6efb64caSEric Schrock 960*6efb64caSEric Schrock 961*6efb64caSEric Schrock if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 962*6efb64caSEric Schrock TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths, 963*6efb64caSEric Schrock i, &err) != 0) { 964*6efb64caSEric Schrock topo_mod_dprintf(mod, 965*6efb64caSEric Schrock "failed to create property %s: %s\n", 966*6efb64caSEric Schrock TOPO_PROP_PATHS, topo_strerror(err)); 967*6efb64caSEric Schrock goto error; 968*6efb64caSEric Schrock } 969*6efb64caSEric Schrock 970940d71d2Seschrock /* 971940d71d2Seschrock * Create the nodes for power supplies, fans, and devices. 972940d71d2Seschrock */ 973940d71d2Seschrock if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY, 974d91236feSeschrock PSU, "PSU", cp, B_TRUE) != 0 || 975940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_COOLING, 976d91236feSeschrock FAN, "FAN", cp, B_TRUE) != 0 || 977940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 978d91236feSeschrock CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 || 979940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_DEVICE, 980d91236feSeschrock BAY, "BAY", cp, B_TRUE) != 0 || 981940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 982d91236feSeschrock BAY, "BAY", cp, B_TRUE) != 0) 983940d71d2Seschrock goto error; 984940d71d2Seschrock 985*6efb64caSEric Schrock cp->sec_target->set_refcount++; 986*6efb64caSEric Schrock topo_node_setspecific(tn, cp->sec_target); 987d91236feSeschrock 988940d71d2Seschrock ret = 0; 989940d71d2Seschrock error: 990940d71d2Seschrock topo_mod_strfree(mod, manufacturer); 991940d71d2Seschrock topo_mod_strfree(mod, model); 992940d71d2Seschrock topo_mod_strfree(mod, revision); 993940d71d2Seschrock topo_mod_strfree(mod, product); 994940d71d2Seschrock 995940d71d2Seschrock nvlist_free(fmri); 996940d71d2Seschrock nvlist_free(auth); 997940d71d2Seschrock return (ret); 998940d71d2Seschrock } 999940d71d2Seschrock 1000d91236feSeschrock /* 1001d91236feSeschrock * Create a bay node explicitly enumerated via XML. 1002d91236feSeschrock */ 1003d91236feSeschrock static int 1004d91236feSeschrock ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode) 1005d91236feSeschrock { 1006d91236feSeschrock topo_mod_t *mod = sdp->sed_mod; 1007d91236feSeschrock ses_enum_chassis_t *cp; 1008d91236feSeschrock 1009d91236feSeschrock /* 1010d91236feSeschrock * Iterate over chassis looking for an internal enclosure. This 1011d91236feSeschrock * property is set via a vendor-specific plugin, and there should only 1012d91236feSeschrock * ever be a single internal chassis in a system. 1013d91236feSeschrock */ 1014d91236feSeschrock for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 1015d91236feSeschrock cp = topo_list_next(cp)) { 1016d91236feSeschrock if (cp->sec_internal) 1017d91236feSeschrock break; 1018d91236feSeschrock } 1019d91236feSeschrock 1020d91236feSeschrock if (cp == NULL) { 1021d91236feSeschrock topo_mod_dprintf(mod, "failed to find internal chassis\n"); 1022d91236feSeschrock return (-1); 1023d91236feSeschrock } 1024d91236feSeschrock 1025d91236feSeschrock if (ses_create_children(sdp, pnode, SES_ET_DEVICE, 1026d91236feSeschrock BAY, "BAY", cp, B_FALSE) != 0 || 1027d91236feSeschrock ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE, 1028d91236feSeschrock BAY, "BAY", cp, B_FALSE) != 0) 1029d91236feSeschrock return (-1); 1030d91236feSeschrock 1031d91236feSeschrock return (0); 1032d91236feSeschrock } 1033940d71d2Seschrock /* 1034940d71d2Seschrock * Gather nodes from the current SES target into our chassis list, merging the 1035940d71d2Seschrock * results if necessary. 1036940d71d2Seschrock */ 1037940d71d2Seschrock static ses_walk_action_t 1038940d71d2Seschrock ses_enum_gather(ses_node_t *np, void *data) 1039940d71d2Seschrock { 1040940d71d2Seschrock nvlist_t *props = ses_node_props(np); 1041940d71d2Seschrock ses_enum_data_t *sdp = data; 1042940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 1043940d71d2Seschrock ses_enum_chassis_t *cp; 1044940d71d2Seschrock ses_enum_node_t *snp; 10450b32bb8bSEric Schrock ses_alt_node_t *sap; 1046940d71d2Seschrock char *csn; 1047940d71d2Seschrock uint64_t instance, type; 1048d91236feSeschrock uint64_t prevstatus, status; 1049*6efb64caSEric Schrock boolean_t report, internal, ident; 1050940d71d2Seschrock 1051940d71d2Seschrock if (ses_node_type(np) == SES_NODE_ENCLOSURE) { 1052940d71d2Seschrock /* 1053940d71d2Seschrock * If we have already identified the chassis for this target, 1054940d71d2Seschrock * then this is a secondary enclosure and we should ignore it, 1055940d71d2Seschrock * along with the rest of the tree (since this is depth-first). 1056940d71d2Seschrock */ 1057940d71d2Seschrock if (sdp->sed_current != NULL) 1058940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1059940d71d2Seschrock 1060940d71d2Seschrock /* 1061940d71d2Seschrock * Go through the list of chassis we have seen so far and see 1062940d71d2Seschrock * if this serial number matches one of the known values. 1063940d71d2Seschrock */ 1064940d71d2Seschrock if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, 1065940d71d2Seschrock &csn) != 0) 1066940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1067940d71d2Seschrock 1068940d71d2Seschrock for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 1069940d71d2Seschrock cp = topo_list_next(cp)) { 1070940d71d2Seschrock if (strcmp(cp->sec_csn, csn) == 0) { 1071940d71d2Seschrock topo_mod_dprintf(mod, "%s: part of already " 1072940d71d2Seschrock "known chassis %s", sdp->sed_name, csn); 1073940d71d2Seschrock break; 1074940d71d2Seschrock } 1075940d71d2Seschrock } 1076940d71d2Seschrock 1077940d71d2Seschrock if (cp == NULL) { 1078940d71d2Seschrock topo_mod_dprintf(mod, "%s: creating chassis %s", 1079940d71d2Seschrock sdp->sed_name, csn); 1080940d71d2Seschrock 1081940d71d2Seschrock if ((cp = topo_mod_zalloc(mod, 1082940d71d2Seschrock sizeof (ses_enum_chassis_t))) == NULL) 1083940d71d2Seschrock goto error; 1084940d71d2Seschrock 1085d91236feSeschrock if (nvlist_lookup_boolean_value(props, 1086d91236feSeschrock LIBSES_EN_PROP_INTERNAL, &internal) == 0) 1087d91236feSeschrock cp->sec_internal = internal; 1088d91236feSeschrock 1089940d71d2Seschrock cp->sec_csn = csn; 1090940d71d2Seschrock cp->sec_enclosure = np; 1091940d71d2Seschrock cp->sec_target = sdp->sed_target; 1092940d71d2Seschrock cp->sec_instance = sdp->sed_instance++; 1093940d71d2Seschrock topo_list_append(&sdp->sed_chassis, cp); 1094*6efb64caSEric Schrock } else { 1095*6efb64caSEric Schrock /* 1096*6efb64caSEric Schrock * For the enclosure node, it is possible to have 1097*6efb64caSEric Schrock * multiple targets, only one of which support an 1098*6efb64caSEric Schrock * enclosure element descriptor. We assume that this 1099*6efb64caSEric Schrock * is the one that is responsible for managing the 1100*6efb64caSEric Schrock * enclosure itself, so we prefer one with the 1101*6efb64caSEric Schrock * SES_PROP_IDENT property (which is only present for a 1102*6efb64caSEric Schrock * target that has an enclosure element descriptor). 1103*6efb64caSEric Schrock */ 1104*6efb64caSEric Schrock if (nvlist_lookup_boolean_value(props, SES_PROP_IDENT, 1105*6efb64caSEric Schrock &ident) == 0) { 1106*6efb64caSEric Schrock topo_mod_dprintf(mod, 1107*6efb64caSEric Schrock "overriding enclosure node"); 1108*6efb64caSEric Schrock cp->sec_enclosure = np; 1109*6efb64caSEric Schrock cp->sec_target = sdp->sed_target; 1110*6efb64caSEric Schrock } 1111940d71d2Seschrock } 1112940d71d2Seschrock 1113940d71d2Seschrock topo_list_append(&cp->sec_targets, sdp->sed_target); 1114940d71d2Seschrock sdp->sed_current = cp; 1115940d71d2Seschrock 1116940d71d2Seschrock } else if (ses_node_type(np) == SES_NODE_ELEMENT) { 1117940d71d2Seschrock /* 1118940d71d2Seschrock * If we haven't yet seen an enclosure node and identified the 1119940d71d2Seschrock * current chassis, something is very wrong; bail out. 1120940d71d2Seschrock */ 1121940d71d2Seschrock if (sdp->sed_current == NULL) 1122940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1123940d71d2Seschrock 1124940d71d2Seschrock /* 1125940d71d2Seschrock * If this isn't one of the element types we care about, then 1126940d71d2Seschrock * ignore it. 1127940d71d2Seschrock */ 1128940d71d2Seschrock verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, 1129940d71d2Seschrock &type) == 0); 1130940d71d2Seschrock if (type != SES_ET_DEVICE && 1131940d71d2Seschrock type != SES_ET_ARRAY_DEVICE && 1132940d71d2Seschrock type != SES_ET_COOLING && 1133940d71d2Seschrock type != SES_ET_POWER_SUPPLY && 1134940d71d2Seschrock type != SES_ET_ESC_ELECTRONICS) 1135940d71d2Seschrock return (SES_WALK_ACTION_CONTINUE); 1136940d71d2Seschrock 1137940d71d2Seschrock /* 1138940d71d2Seschrock * Get the current instance number and see if we already know 1139940d71d2Seschrock * about this element. If so, it means we have multiple paths 1140940d71d2Seschrock * to the same elements, and we should ignore the current path. 1141940d71d2Seschrock */ 1142940d71d2Seschrock verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX, 1143940d71d2Seschrock &instance) == 0); 1144940d71d2Seschrock if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE) 1145940d71d2Seschrock (void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER, 1146940d71d2Seschrock &instance); 1147940d71d2Seschrock 1148940d71d2Seschrock cp = sdp->sed_current; 1149940d71d2Seschrock 1150940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1151940d71d2Seschrock snp = topo_list_next(snp)) { 1152940d71d2Seschrock if (snp->sen_type == type && 1153940d71d2Seschrock snp->sen_instance == instance) 1154d91236feSeschrock break; 1155d91236feSeschrock } 1156d91236feSeschrock 1157d91236feSeschrock /* 1158d91236feSeschrock * We prefer the new element under the following circumstances: 1159d91236feSeschrock * 1160d91236feSeschrock * - The currently known element's status is unknown or not 1161d91236feSeschrock * available, but the new element has a known status. This 1162d91236feSeschrock * occurs if a given element is only available through a 1163d91236feSeschrock * particular target. 1164d91236feSeschrock * 1165d91236feSeschrock * - This is an ESC_ELECTRONICS element, and the 'reported-via' 1166d91236feSeschrock * property is set. This allows us to get reliable firmware 1167d91236feSeschrock * revision information from the enclosure node. 1168d91236feSeschrock */ 1169d91236feSeschrock if (snp != NULL) { 1170d91236feSeschrock if (nvlist_lookup_uint64( 1171d91236feSeschrock ses_node_props(snp->sen_node), 1172d91236feSeschrock SES_PROP_STATUS_CODE, &prevstatus) != 0) 1173d91236feSeschrock prevstatus = SES_ESC_UNSUPPORTED; 1174d91236feSeschrock if (nvlist_lookup_uint64( 1175d91236feSeschrock props, SES_PROP_STATUS_CODE, &status) != 0) 1176d91236feSeschrock status = SES_ESC_UNSUPPORTED; 1177d91236feSeschrock if (nvlist_lookup_boolean_value( 1178d91236feSeschrock props, SES_PROP_REPORT, &report) != 0) 1179d91236feSeschrock report = B_FALSE; 1180d91236feSeschrock 1181d91236feSeschrock if ((SES_STATUS_UNAVAIL(prevstatus) && 1182d91236feSeschrock !SES_STATUS_UNAVAIL(status)) || 1183d91236feSeschrock (type == SES_ET_ESC_ELECTRONICS && 1184d91236feSeschrock report)) { 1185d91236feSeschrock snp->sen_node = np; 1186d91236feSeschrock snp->sen_target = sdp->sed_target; 1187d91236feSeschrock } 1188d91236feSeschrock 11890b32bb8bSEric Schrock if ((sap = topo_mod_zalloc(mod, 11900b32bb8bSEric Schrock sizeof (ses_alt_node_t))) == NULL) 11910b32bb8bSEric Schrock goto error; 11920b32bb8bSEric Schrock 11930b32bb8bSEric Schrock sap->san_node = np; 11940b32bb8bSEric Schrock topo_list_append(&snp->sen_alt_nodes, sap); 11950b32bb8bSEric Schrock 1196d91236feSeschrock return (SES_WALK_ACTION_CONTINUE); 1197940d71d2Seschrock } 1198940d71d2Seschrock 1199940d71d2Seschrock if ((snp = topo_mod_zalloc(mod, 1200940d71d2Seschrock sizeof (ses_enum_node_t))) == NULL) 1201940d71d2Seschrock goto error; 1202940d71d2Seschrock 12030b32bb8bSEric Schrock if ((sap = topo_mod_zalloc(mod, 12040b32bb8bSEric Schrock sizeof (ses_alt_node_t))) == NULL) { 12050b32bb8bSEric Schrock topo_mod_free(mod, snp, sizeof (ses_enum_node_t)); 12060b32bb8bSEric Schrock goto error; 12070b32bb8bSEric Schrock } 12080b32bb8bSEric Schrock 1209940d71d2Seschrock topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)", 1210940d71d2Seschrock sdp->sed_name, type, instance); 1211940d71d2Seschrock snp->sen_node = np; 1212940d71d2Seschrock snp->sen_type = type; 1213940d71d2Seschrock snp->sen_instance = instance; 1214940d71d2Seschrock snp->sen_target = sdp->sed_target; 12150b32bb8bSEric Schrock sap->san_node = np; 12160b32bb8bSEric Schrock topo_list_append(&snp->sen_alt_nodes, sap); 1217940d71d2Seschrock topo_list_append(&cp->sec_nodes, snp); 1218940d71d2Seschrock 1219940d71d2Seschrock if (type == SES_ET_DEVICE) 1220940d71d2Seschrock cp->sec_hasdev = B_TRUE; 1221940d71d2Seschrock } 1222940d71d2Seschrock 1223940d71d2Seschrock return (SES_WALK_ACTION_CONTINUE); 1224940d71d2Seschrock 1225940d71d2Seschrock error: 1226940d71d2Seschrock sdp->sed_errno = -1; 1227940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 1228940d71d2Seschrock } 1229940d71d2Seschrock 1230940d71d2Seschrock static int 1231940d71d2Seschrock ses_process_dir(const char *dirpath, ses_enum_data_t *sdp) 1232940d71d2Seschrock { 1233940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 1234940d71d2Seschrock DIR *dir; 1235940d71d2Seschrock struct dirent *dp; 1236940d71d2Seschrock char path[PATH_MAX]; 1237940d71d2Seschrock ses_enum_target_t *stp; 1238940d71d2Seschrock int err = -1; 1239940d71d2Seschrock 1240940d71d2Seschrock /* 1241940d71d2Seschrock * Open the SES target directory and iterate over any available 1242940d71d2Seschrock * targets. 1243940d71d2Seschrock */ 1244940d71d2Seschrock if ((dir = opendir(dirpath)) == NULL) { 1245940d71d2Seschrock /* 1246940d71d2Seschrock * If the SES target directory does not exist, then return as if 1247940d71d2Seschrock * there are no active targets. 1248940d71d2Seschrock */ 1249940d71d2Seschrock topo_mod_dprintf(mod, "failed to open ses " 1250940d71d2Seschrock "directory '%s'", dirpath); 1251940d71d2Seschrock return (0); 1252940d71d2Seschrock } 1253940d71d2Seschrock 1254940d71d2Seschrock while ((dp = readdir(dir)) != NULL) { 1255940d71d2Seschrock if (strcmp(dp->d_name, ".") == 0 || 1256940d71d2Seschrock strcmp(dp->d_name, "..") == 0) 1257940d71d2Seschrock continue; 1258940d71d2Seschrock 1259940d71d2Seschrock /* 1260940d71d2Seschrock * Create a new target instance and take a snapshot. 1261940d71d2Seschrock */ 1262940d71d2Seschrock if ((stp = topo_mod_zalloc(mod, 1263940d71d2Seschrock sizeof (ses_enum_target_t))) == NULL) 1264940d71d2Seschrock goto error; 1265940d71d2Seschrock 12660b32bb8bSEric Schrock (void) pthread_mutex_init(&stp->set_lock, NULL); 12670b32bb8bSEric Schrock 1268940d71d2Seschrock (void) snprintf(path, sizeof (path), "%s/%s", dirpath, 1269940d71d2Seschrock dp->d_name); 1270940d71d2Seschrock 1271940d71d2Seschrock /* 1272940d71d2Seschrock * We keep track of the SES device path and export it on a 1273940d71d2Seschrock * per-node basis to allow higher level software to get to the 1274940d71d2Seschrock * corresponding SES state. 1275940d71d2Seschrock */ 1276940d71d2Seschrock if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) { 1277940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 1278940d71d2Seschrock goto error; 1279940d71d2Seschrock } 1280940d71d2Seschrock 1281940d71d2Seschrock if ((stp->set_target = 1282940d71d2Seschrock ses_open(LIBSES_VERSION, path)) == NULL) { 1283940d71d2Seschrock topo_mod_dprintf(mod, "failed to open ses target " 1284940d71d2Seschrock "'%s': %s", dp->d_name, ses_errmsg()); 1285940d71d2Seschrock topo_mod_strfree(mod, stp->set_devpath); 1286940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 1287940d71d2Seschrock continue; 1288940d71d2Seschrock } 1289940d71d2Seschrock 1290940d71d2Seschrock stp->set_refcount = 1; 1291940d71d2Seschrock sdp->sed_target = stp; 1292940d71d2Seschrock stp->set_snap = ses_snap_hold(stp->set_target); 1293940d71d2Seschrock if (gettimeofday(&stp->set_snaptime, NULL) != 0) 1294940d71d2Seschrock stp->set_snaptime.tv_sec = time(NULL); 1295940d71d2Seschrock 1296940d71d2Seschrock /* 1297940d71d2Seschrock * Enumerate over all SES elements and merge them into the 1298940d71d2Seschrock * correct ses_enum_chassis_t. 1299940d71d2Seschrock */ 1300940d71d2Seschrock sdp->sed_current = NULL; 1301940d71d2Seschrock sdp->sed_errno = 0; 1302940d71d2Seschrock sdp->sed_name = dp->d_name; 1303940d71d2Seschrock (void) ses_walk(stp->set_snap, ses_enum_gather, sdp); 1304940d71d2Seschrock 1305940d71d2Seschrock if (sdp->sed_errno != 0) 1306940d71d2Seschrock goto error; 1307940d71d2Seschrock } 1308940d71d2Seschrock 1309940d71d2Seschrock err = 0; 1310940d71d2Seschrock error: 1311940d71d2Seschrock closedir(dir); 1312940d71d2Seschrock return (err); 1313940d71d2Seschrock } 1314940d71d2Seschrock 1315940d71d2Seschrock static void 1316940d71d2Seschrock ses_release(topo_mod_t *mod, tnode_t *tn) 1317940d71d2Seschrock { 1318940d71d2Seschrock ses_enum_target_t *stp; 1319940d71d2Seschrock 1320940d71d2Seschrock if ((stp = topo_node_getspecific(tn)) != NULL) 1321940d71d2Seschrock ses_target_free(mod, stp); 1322940d71d2Seschrock } 1323940d71d2Seschrock 1324940d71d2Seschrock /*ARGSUSED*/ 1325940d71d2Seschrock static int 1326940d71d2Seschrock ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 1327940d71d2Seschrock topo_instance_t min, topo_instance_t max, void *arg, void *notused) 1328940d71d2Seschrock { 1329940d71d2Seschrock ses_enum_chassis_t *cp; 1330d91236feSeschrock ses_enum_data_t *data; 1331940d71d2Seschrock 1332940d71d2Seschrock /* 1333940d71d2Seschrock * Check to make sure we're being invoked sensibly, and that we're not 1334940d71d2Seschrock * being invoked as part of a post-processing step. 1335940d71d2Seschrock */ 1336d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0) 1337940d71d2Seschrock return (0); 1338940d71d2Seschrock 1339940d71d2Seschrock /* 1340d91236feSeschrock * If this is the first time we've called our enumeration method, then 1341d91236feSeschrock * gather information about any available enclosures. 1342940d71d2Seschrock */ 1343d91236feSeschrock if ((data = topo_mod_getspecific(mod)) == NULL) { 1344d91236feSeschrock if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) == 1345d91236feSeschrock NULL) 1346d91236feSeschrock return (-1); 1347940d71d2Seschrock 1348d91236feSeschrock data->sed_mod = mod; 1349d91236feSeschrock topo_mod_setspecific(mod, data); 1350d91236feSeschrock 1351d91236feSeschrock if (disk_list_gather(mod, &data->sed_disks) != 0) 1352d91236feSeschrock goto error; 1353d91236feSeschrock 1354d91236feSeschrock /* 1355d91236feSeschrock * We search both the ses(7D) and sgen(7D) locations, so we are 1356d91236feSeschrock * independent of any particular driver class bindings. 1357d91236feSeschrock */ 1358d91236feSeschrock if (ses_process_dir("/dev/es", data) != 0 || 1359d91236feSeschrock ses_process_dir("/dev/scsi/ses", data) != 0) 1360d91236feSeschrock goto error; 1361d91236feSeschrock } 1362d91236feSeschrock 1363d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) == 0) { 1364d91236feSeschrock /* 1365d91236feSeschrock * This is a request to enumerate external enclosures. Go 1366d91236feSeschrock * through all the targets and create chassis nodes where 1367d91236feSeschrock * necessary. 1368d91236feSeschrock */ 1369d91236feSeschrock for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 1370d91236feSeschrock cp = topo_list_next(cp)) { 1371d91236feSeschrock if (ses_create_chassis(data, rnode, cp) != 0) 1372d91236feSeschrock goto error; 1373d91236feSeschrock } 1374d91236feSeschrock } else { 1375d91236feSeschrock /* 1376d91236feSeschrock * This is a request to enumerate a specific bay underneath the 1377d91236feSeschrock * root chassis (for internal disks). 1378d91236feSeschrock */ 1379d91236feSeschrock if (ses_create_bays(data, rnode) != 0) 1380940d71d2Seschrock goto error; 1381940d71d2Seschrock } 1382940d71d2Seschrock 1383d91236feSeschrock /* 1384d91236feSeschrock * This is a bit of a kludge. In order to allow internal disks to be 1385d91236feSeschrock * enumerated and share snapshot-specific information with the external 1386d91236feSeschrock * enclosure enumeration, we rely on the fact that we will be invoked 1387d91236feSeschrock * for the 'ses-enclosure' node last. 1388d91236feSeschrock */ 1389d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) == 0) { 1390d91236feSeschrock ses_data_free(data); 1391d91236feSeschrock topo_mod_setspecific(mod, NULL); 1392d91236feSeschrock } 1393940d71d2Seschrock return (0); 1394940d71d2Seschrock 1395940d71d2Seschrock error: 1396d91236feSeschrock ses_data_free(data); 1397d91236feSeschrock topo_mod_setspecific(mod, NULL); 1398940d71d2Seschrock return (-1); 1399940d71d2Seschrock } 1400940d71d2Seschrock 1401940d71d2Seschrock static const topo_modops_t ses_ops = 1402940d71d2Seschrock { ses_enum, ses_release }; 1403940d71d2Seschrock 1404940d71d2Seschrock static topo_modinfo_t ses_info = 1405940d71d2Seschrock { SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops }; 1406940d71d2Seschrock 1407940d71d2Seschrock /*ARGSUSED*/ 1408940d71d2Seschrock int 1409940d71d2Seschrock _topo_init(topo_mod_t *mod, topo_version_t version) 1410940d71d2Seschrock { 1411940d71d2Seschrock if (getenv("TOPOSESDEBUG") != NULL) 1412940d71d2Seschrock topo_mod_setdebug(mod); 1413940d71d2Seschrock 1414940d71d2Seschrock topo_mod_dprintf(mod, "initializing %s enumerator\n", 1415940d71d2Seschrock SES_ENCLOSURE); 1416940d71d2Seschrock 1417940d71d2Seschrock return (topo_mod_register(mod, &ses_info, TOPO_VERSION)); 1418940d71d2Seschrock } 1419940d71d2Seschrock 1420940d71d2Seschrock void 1421940d71d2Seschrock _topo_fini(topo_mod_t *mod) 1422940d71d2Seschrock { 1423940d71d2Seschrock topo_mod_unregister(mod); 1424940d71d2Seschrock } 1425