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 /* 2344ed9dbbSStephen Hanson * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24940d71d2Seschrock */ 25940d71d2Seschrock 266efb64caSEric Schrock #include <alloca.h> 27940d71d2Seschrock #include <dirent.h> 28940d71d2Seschrock #include <devid.h> 29940d71d2Seschrock #include <fm/libdiskstatus.h> 30940d71d2Seschrock #include <inttypes.h> 31940d71d2Seschrock #include <pthread.h> 32940d71d2Seschrock #include <strings.h> 33*ac88567aSHyon Kim #include <string.h> 34940d71d2Seschrock #include <unistd.h> 35940d71d2Seschrock #include <sys/dkio.h> 36940d71d2Seschrock #include <sys/fm/protocol.h> 3744ed9dbbSStephen Hanson #include <sys/libdevid.h> 38940d71d2Seschrock #include <sys/scsi/scsi_types.h> 39*ac88567aSHyon Kim #include <sys/byteorder.h> 40d91236feSeschrock 41940d71d2Seschrock #include "disk.h" 42d91236feSeschrock #include "ses.h" 43940d71d2Seschrock 44940d71d2Seschrock #define SES_VERSION 1 45940d71d2Seschrock 46*ac88567aSHyon Kim #define SES_STARTING_SUBCHASSIS 256 /* valid subchassis IDs are uint8_t */ 47*ac88567aSHyon Kim #define NO_SUBCHASSIS ((uint64_t)-1) 48*ac88567aSHyon Kim 49525b85dbSEric Schrock static int ses_snap_freq = 250; /* in milliseconds */ 50940d71d2Seschrock 51d91236feSeschrock #define SES_STATUS_UNAVAIL(s) \ 520b32bb8bSEric Schrock ((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED) 53940d71d2Seschrock 54940d71d2Seschrock /* 55940d71d2Seschrock * Because multiple SES targets can be part of a single chassis, we construct 56940d71d2Seschrock * our own hierarchy that takes this into account. These SES targets may refer 57940d71d2Seschrock * to the same devices (multiple paths) or to different devices (managing 58940d71d2Seschrock * different portions of the space). We arrange things into a 59940d71d2Seschrock * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all 60940d71d2Seschrock * nodes found so far. 61940d71d2Seschrock */ 620b32bb8bSEric Schrock typedef struct ses_alt_node { 630b32bb8bSEric Schrock topo_list_t san_link; 640b32bb8bSEric Schrock ses_node_t *san_node; 650b32bb8bSEric Schrock } ses_alt_node_t; 66940d71d2Seschrock 67940d71d2Seschrock typedef struct ses_enum_node { 68940d71d2Seschrock topo_list_t sen_link; 69940d71d2Seschrock ses_node_t *sen_node; 700b32bb8bSEric Schrock topo_list_t sen_alt_nodes; 71940d71d2Seschrock uint64_t sen_type; 72940d71d2Seschrock uint64_t sen_instance; 73940d71d2Seschrock ses_enum_target_t *sen_target; 74940d71d2Seschrock } ses_enum_node_t; 75940d71d2Seschrock 76940d71d2Seschrock typedef struct ses_enum_chassis { 77940d71d2Seschrock topo_list_t sec_link; 7853dbcc59SSundeep Panicker topo_list_t sec_subchassis; 79940d71d2Seschrock topo_list_t sec_nodes; 80940d71d2Seschrock topo_list_t sec_targets; 81940d71d2Seschrock const char *sec_csn; 82940d71d2Seschrock ses_node_t *sec_enclosure; 83940d71d2Seschrock ses_enum_target_t *sec_target; 84940d71d2Seschrock topo_instance_t sec_instance; 8553dbcc59SSundeep Panicker topo_instance_t sec_scinstance; 86*ac88567aSHyon Kim topo_instance_t sec_maxinstance; 87940d71d2Seschrock boolean_t sec_hasdev; 88d91236feSeschrock boolean_t sec_internal; 89940d71d2Seschrock } ses_enum_chassis_t; 90940d71d2Seschrock 91940d71d2Seschrock typedef struct ses_enum_data { 92*ac88567aSHyon Kim topo_list_t sed_devs; 93940d71d2Seschrock topo_list_t sed_chassis; 94940d71d2Seschrock ses_enum_chassis_t *sed_current; 95940d71d2Seschrock ses_enum_target_t *sed_target; 96940d71d2Seschrock int sed_errno; 97940d71d2Seschrock char *sed_name; 98940d71d2Seschrock topo_mod_t *sed_mod; 99940d71d2Seschrock topo_instance_t sed_instance; 100940d71d2Seschrock } ses_enum_data_t; 101940d71d2Seschrock 102*ac88567aSHyon Kim typedef struct sas_connector_phy_data { 103*ac88567aSHyon Kim uint64_t index; 104*ac88567aSHyon Kim uint64_t phy_mask; 105*ac88567aSHyon Kim } sas_connector_phy_data_t; 106*ac88567aSHyon Kim 107*ac88567aSHyon Kim typedef struct sas_connector_type { 108*ac88567aSHyon Kim uint64_t type; 109*ac88567aSHyon Kim char *name; 110*ac88567aSHyon Kim } sas_connector_type_t; 111*ac88567aSHyon Kim 112*ac88567aSHyon Kim static const sas_connector_type_t sas_connector_type_list[] = { 113*ac88567aSHyon Kim { 0x0, "Information unknown" }, 114*ac88567aSHyon Kim { 0x1, "External SAS 4x receptacle (see SAS-2 and SFF-8470)" }, 115*ac88567aSHyon Kim { 0x2, "Exteranl Mini SAS 4x receptacle (see SAS-2 and SFF-8088)" }, 116*ac88567aSHyon Kim { 0xF, "Vendor-specific external connector" }, 117*ac88567aSHyon Kim { 0x10, "Internal wide SAS 4i plug (see SAS-2 and SFF-8484)" }, 118*ac88567aSHyon Kim { 0x11, 119*ac88567aSHyon Kim "Internal wide Mini SAS 4i receptacle (see SAS-2 and SFF-8087)" }, 120*ac88567aSHyon Kim { 0x20, "Internal SAS Drive receptacle (see SAS-2 and SFF-8482)" }, 121*ac88567aSHyon Kim { 0x21, "Internal SATA host plug (see SAS-2 and SATA-2)" }, 122*ac88567aSHyon Kim { 0x22, "Internal SAS Drive plug (see SAS-2 and SFF-8482)" }, 123*ac88567aSHyon Kim { 0x23, "Internal SATA device plug (see SAS-2 and SATA-2)" }, 124*ac88567aSHyon Kim { 0x2F, "Internal SAS virtual connector" }, 125*ac88567aSHyon Kim { 0x3F, "Vendor-specific internal connector" }, 126*ac88567aSHyon Kim { 0x70, "Other Vendor-specific connector" }, 127*ac88567aSHyon Kim { 0x71, "Other Vendor-specific connector" }, 128*ac88567aSHyon Kim { 0x72, "Other Vendor-specific connector" }, 129*ac88567aSHyon Kim { 0x73, "Other Vendor-specific connector" }, 130*ac88567aSHyon Kim { 0x74, "Other Vendor-specific connector" }, 131*ac88567aSHyon Kim { 0x75, "Other Vendor-specific connector" }, 132*ac88567aSHyon Kim { 0x76, "Other Vendor-specific connector" }, 133*ac88567aSHyon Kim { 0x77, "Other Vendor-specific connector" }, 134*ac88567aSHyon Kim { 0x78, "Other Vendor-specific connector" }, 135*ac88567aSHyon Kim { 0x79, "Other Vendor-specific connector" }, 136*ac88567aSHyon Kim { 0x7A, "Other Vendor-specific connector" }, 137*ac88567aSHyon Kim { 0x7B, "Other Vendor-specific connector" }, 138*ac88567aSHyon Kim { 0x7C, "Other Vendor-specific connector" }, 139*ac88567aSHyon Kim { 0x7D, "Other Vendor-specific connector" }, 140*ac88567aSHyon Kim { 0x7E, "Other Vendor-specific connector" }, 141*ac88567aSHyon Kim { 0x7F, "Other Vendor-specific connector" }, 142*ac88567aSHyon Kim { 0x80, "Not Defined" } 143*ac88567aSHyon Kim }; 144*ac88567aSHyon Kim 145*ac88567aSHyon Kim #define SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED 0x80 146*ac88567aSHyon Kim #define SAS_CONNECTOR_TYPE_NOT_DEFINED \ 147*ac88567aSHyon Kim "Connector type not definedi by SES-2 standard" 148*ac88567aSHyon Kim #define SAS_CONNECTOR_TYPE_RESERVED \ 149*ac88567aSHyon Kim "Connector type reserved by SES-2 standard" 150*ac88567aSHyon Kim 15153dbcc59SSundeep Panicker typedef enum { 15253dbcc59SSundeep Panicker SES_NEW_CHASSIS = 0x1, 15353dbcc59SSundeep Panicker SES_NEW_SUBCHASSIS = 0x2, 15453dbcc59SSundeep Panicker SES_DUP_CHASSIS = 0x4, 15553dbcc59SSundeep Panicker SES_DUP_SUBCHASSIS = 0x8 15653dbcc59SSundeep Panicker } ses_chassis_type_e; 15753dbcc59SSundeep Panicker 158*ac88567aSHyon Kim static const topo_pgroup_info_t io_pgroup = { 159*ac88567aSHyon Kim TOPO_PGROUP_IO, 160*ac88567aSHyon Kim TOPO_STABILITY_PRIVATE, 161*ac88567aSHyon Kim TOPO_STABILITY_PRIVATE, 162*ac88567aSHyon Kim 1 163*ac88567aSHyon Kim }; 164*ac88567aSHyon Kim 165*ac88567aSHyon Kim static const topo_pgroup_info_t storage_pgroup = { 166*ac88567aSHyon Kim TOPO_PGROUP_STORAGE, 167*ac88567aSHyon Kim TOPO_STABILITY_PRIVATE, 168*ac88567aSHyon Kim TOPO_STABILITY_PRIVATE, 169*ac88567aSHyon Kim 1 170*ac88567aSHyon Kim }; 171*ac88567aSHyon Kim 172940d71d2Seschrock static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 173940d71d2Seschrock nvlist_t **); 174940d71d2Seschrock static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 175940d71d2Seschrock nvlist_t **); 176940d71d2Seschrock 177940d71d2Seschrock static const topo_method_t ses_component_methods[] = { 178940d71d2Seschrock { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, 179940d71d2Seschrock TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present }, 180d91236feSeschrock { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 181d91236feSeschrock TOPO_STABILITY_INTERNAL, ses_node_enum_facility }, 18288045cffSRobert Johnston { TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC, 18388045cffSRobert Johnston TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL, 18488045cffSRobert Johnston topo_method_sensor_failure }, 185d91236feSeschrock { NULL } 186d91236feSeschrock }; 187d91236feSeschrock 188d91236feSeschrock static const topo_method_t ses_bay_methods[] = { 189d91236feSeschrock { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 190d91236feSeschrock TOPO_STABILITY_INTERNAL, ses_node_enum_facility }, 191940d71d2Seschrock { NULL } 192940d71d2Seschrock }; 193940d71d2Seschrock 194940d71d2Seschrock static const topo_method_t ses_enclosure_methods[] = { 195940d71d2Seschrock { TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC, 196940d71d2Seschrock TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains }, 197d91236feSeschrock { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0, 198d91236feSeschrock TOPO_STABILITY_INTERNAL, ses_enc_enum_facility }, 199940d71d2Seschrock { NULL } 200940d71d2Seschrock }; 201940d71d2Seschrock 202940d71d2Seschrock static void 203940d71d2Seschrock ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp) 204940d71d2Seschrock { 205940d71d2Seschrock if (--stp->set_refcount == 0) { 206940d71d2Seschrock ses_snap_rele(stp->set_snap); 207940d71d2Seschrock ses_close(stp->set_target); 208940d71d2Seschrock topo_mod_strfree(mod, stp->set_devpath); 209940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 210940d71d2Seschrock } 211940d71d2Seschrock } 212940d71d2Seschrock 213940d71d2Seschrock static void 21453dbcc59SSundeep Panicker ses_data_free(ses_enum_data_t *sdp, ses_enum_chassis_t *pcp) 215940d71d2Seschrock { 216940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 217940d71d2Seschrock ses_enum_chassis_t *cp; 218940d71d2Seschrock ses_enum_node_t *np; 219940d71d2Seschrock ses_enum_target_t *tp; 2200b32bb8bSEric Schrock ses_alt_node_t *ap; 22153dbcc59SSundeep Panicker topo_list_t *cpl; 22253dbcc59SSundeep Panicker 22353dbcc59SSundeep Panicker 22453dbcc59SSundeep Panicker if (pcp != NULL) 22553dbcc59SSundeep Panicker cpl = &pcp->sec_subchassis; 22653dbcc59SSundeep Panicker else 22753dbcc59SSundeep Panicker cpl = &sdp->sed_chassis; 228940d71d2Seschrock 22953dbcc59SSundeep Panicker while ((cp = topo_list_next(cpl)) != NULL) { 23053dbcc59SSundeep Panicker topo_list_delete(cpl, cp); 231940d71d2Seschrock 232940d71d2Seschrock while ((np = topo_list_next(&cp->sec_nodes)) != NULL) { 2330b32bb8bSEric Schrock while ((ap = topo_list_next(&np->sen_alt_nodes)) != 2340b32bb8bSEric Schrock NULL) { 2350b32bb8bSEric Schrock topo_list_delete(&np->sen_alt_nodes, ap); 2360b32bb8bSEric Schrock topo_mod_free(mod, ap, sizeof (ses_alt_node_t)); 2370b32bb8bSEric Schrock } 238940d71d2Seschrock topo_list_delete(&cp->sec_nodes, np); 239940d71d2Seschrock topo_mod_free(mod, np, sizeof (ses_enum_node_t)); 240940d71d2Seschrock } 241940d71d2Seschrock 242940d71d2Seschrock while ((tp = topo_list_next(&cp->sec_targets)) != NULL) { 243940d71d2Seschrock topo_list_delete(&cp->sec_targets, tp); 244940d71d2Seschrock ses_target_free(mod, tp); 245940d71d2Seschrock } 246940d71d2Seschrock 247940d71d2Seschrock topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t)); 248940d71d2Seschrock } 249940d71d2Seschrock 25053dbcc59SSundeep Panicker if (pcp == NULL) { 251*ac88567aSHyon Kim dev_list_free(mod, &sdp->sed_devs); 25253dbcc59SSundeep Panicker topo_mod_free(mod, sdp, sizeof (ses_enum_data_t)); 25353dbcc59SSundeep Panicker } 254940d71d2Seschrock } 255940d71d2Seschrock 256940d71d2Seschrock /* 257940d71d2Seschrock * For enclosure nodes, we have a special contains method. By default, the hc 258940d71d2Seschrock * walker will compare the node name and instance number to determine if an 259940d71d2Seschrock * FMRI matches. For enclosures where the enumeration order is impossible to 260940d71d2Seschrock * predict, we instead use the chassis-id as a unique identifier, and ignore 261940d71d2Seschrock * the instance number. 262940d71d2Seschrock */ 263940d71d2Seschrock static int 264940d71d2Seschrock fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2) 265940d71d2Seschrock { 266940d71d2Seschrock uint8_t v1, v2; 267940d71d2Seschrock nvlist_t **hcp1, **hcp2; 268940d71d2Seschrock int err, i; 269940d71d2Seschrock uint_t nhcp1, nhcp2; 270940d71d2Seschrock nvlist_t *a1, *a2; 271940d71d2Seschrock char *c1, *c2; 272940d71d2Seschrock int mindepth; 273940d71d2Seschrock 274940d71d2Seschrock if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 || 275940d71d2Seschrock nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 || 276940d71d2Seschrock v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION) 277940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION)); 278940d71d2Seschrock 279940d71d2Seschrock err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1); 280940d71d2Seschrock err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2); 281940d71d2Seschrock if (err != 0) 282940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 283940d71d2Seschrock 284940d71d2Seschrock /* 285940d71d2Seschrock * If the chassis-id doesn't match, then these FMRIs are not 286940d71d2Seschrock * equivalent. If one of the FMRIs doesn't have a chassis ID, then we 287940d71d2Seschrock * have no choice but to fall back to the instance ID. 288940d71d2Seschrock */ 289940d71d2Seschrock if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 && 290940d71d2Seschrock nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 && 291940d71d2Seschrock nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 && 292940d71d2Seschrock nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) { 293940d71d2Seschrock if (strcmp(c1, c2) != 0) 294940d71d2Seschrock return (0); 295940d71d2Seschrock 296940d71d2Seschrock mindepth = 1; 297940d71d2Seschrock } else { 298940d71d2Seschrock mindepth = 0; 299940d71d2Seschrock } 300940d71d2Seschrock 301940d71d2Seschrock if (nhcp2 < nhcp1) 302940d71d2Seschrock return (0); 303940d71d2Seschrock 304940d71d2Seschrock for (i = 0; i < nhcp1; i++) { 305940d71d2Seschrock char *nm1 = NULL; 306940d71d2Seschrock char *nm2 = NULL; 307940d71d2Seschrock char *id1 = NULL; 308940d71d2Seschrock char *id2 = NULL; 309940d71d2Seschrock 310940d71d2Seschrock (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1); 311940d71d2Seschrock (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2); 312940d71d2Seschrock (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1); 313940d71d2Seschrock (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2); 314940d71d2Seschrock if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL) 315940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 316940d71d2Seschrock 317940d71d2Seschrock if (strcmp(nm1, nm2) == 0 && 318940d71d2Seschrock (i < mindepth || strcmp(id1, id2) == 0)) 319940d71d2Seschrock continue; 320940d71d2Seschrock 321940d71d2Seschrock return (0); 322940d71d2Seschrock } 323940d71d2Seschrock 324940d71d2Seschrock return (1); 325940d71d2Seschrock } 326940d71d2Seschrock 327940d71d2Seschrock /*ARGSUSED*/ 328940d71d2Seschrock static int 329940d71d2Seschrock ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 330940d71d2Seschrock nvlist_t *in, nvlist_t **out) 331940d71d2Seschrock { 332940d71d2Seschrock int ret; 333940d71d2Seschrock nvlist_t *nv1, *nv2; 334940d71d2Seschrock 335940d71d2Seschrock if (version > TOPO_METH_CONTAINS_VERSION) 336940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 337940d71d2Seschrock 338940d71d2Seschrock if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 || 339940d71d2Seschrock nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0) 340940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 341940d71d2Seschrock 342940d71d2Seschrock ret = fmri_contains(mod, nv1, nv2); 343940d71d2Seschrock if (ret < 0) 344940d71d2Seschrock return (-1); 345940d71d2Seschrock 346940d71d2Seschrock if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) { 34773e32a37SRobert Johnston if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET, 348940d71d2Seschrock ret) == 0) 349940d71d2Seschrock return (0); 350940d71d2Seschrock else 351940d71d2Seschrock nvlist_free(*out); 352940d71d2Seschrock } 353940d71d2Seschrock 354940d71d2Seschrock return (-1); 355940d71d2Seschrock 356940d71d2Seschrock } 357940d71d2Seschrock 358940d71d2Seschrock /* 359d91236feSeschrock * Return a current instance of the node. This is somewhat complicated because 360d91236feSeschrock * we need to take a new snapshot in order to get the new data, but we don't 361940d71d2Seschrock * want to be constantly taking SES snapshots if the consumer is going to do a 362940d71d2Seschrock * series of queries. So we adopt the strategy of assuming that the SES state 363d91236feSeschrock * is not going to be rapidly changing, and limit our snapshot frequency to 364d91236feSeschrock * some defined bounds. 365940d71d2Seschrock */ 366d91236feSeschrock ses_node_t * 3670b32bb8bSEric Schrock ses_node_lock(topo_mod_t *mod, tnode_t *tn) 368940d71d2Seschrock { 369940d71d2Seschrock ses_enum_target_t *tp = topo_node_getspecific(tn); 370525b85dbSEric Schrock hrtime_t now; 371940d71d2Seschrock ses_snap_t *snap; 372940d71d2Seschrock int err; 373d91236feSeschrock uint64_t nodeid; 374940d71d2Seschrock ses_node_t *np; 375940d71d2Seschrock 376d91236feSeschrock if (tp == NULL) { 377d91236feSeschrock (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 378d91236feSeschrock return (NULL); 379d91236feSeschrock } 380940d71d2Seschrock 3810b32bb8bSEric Schrock (void) pthread_mutex_lock(&tp->set_lock); 3820b32bb8bSEric Schrock 383940d71d2Seschrock /* 384940d71d2Seschrock * Determine if we need to take a new snapshot. 385940d71d2Seschrock */ 386525b85dbSEric Schrock now = gethrtime(); 387940d71d2Seschrock 388525b85dbSEric Schrock if (now - tp->set_snaptime > (ses_snap_freq * 1000 * 1000) && 389940d71d2Seschrock (snap = ses_snap_new(tp->set_target)) != NULL) { 390940d71d2Seschrock if (ses_snap_generation(snap) != 391940d71d2Seschrock ses_snap_generation(tp->set_snap)) { 392940d71d2Seschrock /* 393940d71d2Seschrock * If we find ourselves in this situation, we're in 394940d71d2Seschrock * trouble. The generation count has changed, which 395940d71d2Seschrock * indicates that our current topology is out of date. 396940d71d2Seschrock * But we need to consult the new topology in order to 397940d71d2Seschrock * determine presence at this moment in time. We can't 398940d71d2Seschrock * go back and change the topo snapshot in situ, so 399d91236feSeschrock * we'll just have to fail the call in this unlikely 400d91236feSeschrock * scenario. 401940d71d2Seschrock */ 402940d71d2Seschrock ses_snap_rele(snap); 403d91236feSeschrock (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP); 4040b32bb8bSEric Schrock (void) pthread_mutex_unlock(&tp->set_lock); 405d91236feSeschrock return (NULL); 406940d71d2Seschrock } else { 407940d71d2Seschrock ses_snap_rele(tp->set_snap); 408940d71d2Seschrock tp->set_snap = snap; 409940d71d2Seschrock } 410525b85dbSEric Schrock tp->set_snaptime = gethrtime(); 411940d71d2Seschrock } 412940d71d2Seschrock 413940d71d2Seschrock snap = tp->set_snap; 414940d71d2Seschrock 415940d71d2Seschrock verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES, 416940d71d2Seschrock TOPO_PROP_NODE_ID, &nodeid, &err) == 0); 417940d71d2Seschrock verify((np = ses_node_lookup(snap, nodeid)) != NULL); 418d91236feSeschrock 419d91236feSeschrock return (np); 420d91236feSeschrock } 421d91236feSeschrock 4220b32bb8bSEric Schrock /*ARGSUSED*/ 4230b32bb8bSEric Schrock void 4240b32bb8bSEric Schrock ses_node_unlock(topo_mod_t *mod, tnode_t *tn) 4250b32bb8bSEric Schrock { 4260b32bb8bSEric Schrock ses_enum_target_t *tp = topo_node_getspecific(tn); 4270b32bb8bSEric Schrock 4280b32bb8bSEric Schrock verify(tp != NULL); 4290b32bb8bSEric Schrock 4300b32bb8bSEric Schrock (void) pthread_mutex_unlock(&tp->set_lock); 4310b32bb8bSEric Schrock } 4320b32bb8bSEric Schrock 433d91236feSeschrock /* 434d91236feSeschrock * Determine if the element is present. 435d91236feSeschrock */ 436d91236feSeschrock /*ARGSUSED*/ 437d91236feSeschrock static int 438d91236feSeschrock ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version, 439d91236feSeschrock nvlist_t *in, nvlist_t **out) 440d91236feSeschrock { 441d91236feSeschrock boolean_t present; 442d91236feSeschrock ses_node_t *np; 443d91236feSeschrock nvlist_t *props, *nvl; 444d91236feSeschrock uint64_t status; 445d91236feSeschrock 4460b32bb8bSEric Schrock if ((np = ses_node_lock(mod, tn)) == NULL) 447d91236feSeschrock return (-1); 448d91236feSeschrock 449940d71d2Seschrock verify((props = ses_node_props(np)) != NULL); 450940d71d2Seschrock verify(nvlist_lookup_uint64(props, 451940d71d2Seschrock SES_PROP_STATUS_CODE, &status) == 0); 452940d71d2Seschrock 4530b32bb8bSEric Schrock ses_node_unlock(mod, tn); 4540b32bb8bSEric Schrock 455940d71d2Seschrock present = (status != SES_ESC_NOT_INSTALLED); 456940d71d2Seschrock 457940d71d2Seschrock if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) 458940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 459940d71d2Seschrock 460940d71d2Seschrock if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET, 461940d71d2Seschrock present) != 0) { 462940d71d2Seschrock nvlist_free(nvl); 463940d71d2Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 464940d71d2Seschrock } 465940d71d2Seschrock 466940d71d2Seschrock *out = nvl; 467940d71d2Seschrock 468940d71d2Seschrock return (0); 469940d71d2Seschrock } 470940d71d2Seschrock 471940d71d2Seschrock /* 472*ac88567aSHyon Kim * Sets standard properties for a ses node (enclosure, bay, controller 473*ac88567aSHyon Kim * or expander). 474*ac88567aSHyon Kim * This includes setting the FRU, as well as setting the 475*ac88567aSHyon Kim * authority information. When the fru topo node(frutn) is not NULL 476*ac88567aSHyon Kim * its resouce should be used as FRU. 477940d71d2Seschrock */ 478940d71d2Seschrock static int 479*ac88567aSHyon Kim ses_set_standard_props(topo_mod_t *mod, tnode_t *frutn, tnode_t *tn, 480*ac88567aSHyon Kim nvlist_t *auth, uint64_t nodeid, const char *path) 481940d71d2Seschrock { 482940d71d2Seschrock int err; 483940d71d2Seschrock char *product, *chassis; 484940d71d2Seschrock nvlist_t *fmri; 485940d71d2Seschrock topo_pgroup_info_t pgi; 486940d71d2Seschrock 487940d71d2Seschrock /* 488940d71d2Seschrock * Set the authority explicitly if specified. 489940d71d2Seschrock */ 490940d71d2Seschrock if (auth) { 491940d71d2Seschrock verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, 492940d71d2Seschrock &product) == 0); 493940d71d2Seschrock verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, 494940d71d2Seschrock &chassis) == 0); 495940d71d2Seschrock if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 496940d71d2Seschrock FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product, 497940d71d2Seschrock &err) != 0 || 498940d71d2Seschrock topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 499940d71d2Seschrock FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis, 500940d71d2Seschrock &err) != 0 || 501940d71d2Seschrock topo_prop_set_string(tn, FM_FMRI_AUTHORITY, 502940d71d2Seschrock FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "", 503940d71d2Seschrock &err) != 0) { 504940d71d2Seschrock topo_mod_dprintf(mod, "failed to add authority " 505d91236feSeschrock "properties: %s\n", topo_strerror(err)); 506940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 507940d71d2Seschrock } 508940d71d2Seschrock } 509940d71d2Seschrock 510940d71d2Seschrock /* 511940d71d2Seschrock * Copy the resource and set that as the FRU. 512940d71d2Seschrock */ 513*ac88567aSHyon Kim if (frutn != NULL) { 514*ac88567aSHyon Kim if (topo_node_resource(frutn, &fmri, &err) != 0) { 515*ac88567aSHyon Kim topo_mod_dprintf(mod, 516*ac88567aSHyon Kim "topo_node_resource() failed : %s\n", 517*ac88567aSHyon Kim topo_strerror(err)); 518*ac88567aSHyon Kim return (topo_mod_seterrno(mod, err)); 519*ac88567aSHyon Kim } 520*ac88567aSHyon Kim } else { 521*ac88567aSHyon Kim if (topo_node_resource(tn, &fmri, &err) != 0) { 522*ac88567aSHyon Kim topo_mod_dprintf(mod, 523*ac88567aSHyon Kim "topo_node_resource() failed : %s\n", 524*ac88567aSHyon Kim topo_strerror(err)); 525*ac88567aSHyon Kim return (topo_mod_seterrno(mod, err)); 526*ac88567aSHyon Kim } 527940d71d2Seschrock } 528940d71d2Seschrock 529940d71d2Seschrock if (topo_node_fru_set(tn, fmri, 0, &err) != 0) { 530940d71d2Seschrock topo_mod_dprintf(mod, 531940d71d2Seschrock "topo_node_fru_set() failed : %s\n", 532940d71d2Seschrock topo_strerror(err)); 533940d71d2Seschrock nvlist_free(fmri); 534940d71d2Seschrock return (topo_mod_seterrno(mod, err)); 535940d71d2Seschrock } 536940d71d2Seschrock 537940d71d2Seschrock nvlist_free(fmri); 538940d71d2Seschrock 539940d71d2Seschrock /* 540940d71d2Seschrock * Set the SES-specific properties so that consumers can query 541940d71d2Seschrock * additional information about the particular SES element. 542940d71d2Seschrock */ 543940d71d2Seschrock pgi.tpi_name = TOPO_PGROUP_SES; 544940d71d2Seschrock pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 545940d71d2Seschrock pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 546940d71d2Seschrock pgi.tpi_version = TOPO_VERSION; 547940d71d2Seschrock if (topo_pgroup_create(tn, &pgi, &err) != 0) { 548940d71d2Seschrock topo_mod_dprintf(mod, "failed to create propgroup " 549940d71d2Seschrock "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err)); 550940d71d2Seschrock return (-1); 551940d71d2Seschrock } 552940d71d2Seschrock 553940d71d2Seschrock if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES, 554940d71d2Seschrock TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE, 555940d71d2Seschrock nodeid, &err) != 0) { 556940d71d2Seschrock topo_mod_dprintf(mod, 557940d71d2Seschrock "failed to create property %s: %s\n", 558940d71d2Seschrock TOPO_PROP_NODE_ID, topo_strerror(err)); 559940d71d2Seschrock return (-1); 560940d71d2Seschrock } 561940d71d2Seschrock 562940d71d2Seschrock if (topo_prop_set_string(tn, TOPO_PGROUP_SES, 563940d71d2Seschrock TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE, 564940d71d2Seschrock path, &err) != 0) { 565940d71d2Seschrock topo_mod_dprintf(mod, 566940d71d2Seschrock "failed to create property %s: %s\n", 567940d71d2Seschrock TOPO_PROP_TARGET_PATH, topo_strerror(err)); 568940d71d2Seschrock return (-1); 569940d71d2Seschrock } 570940d71d2Seschrock 571940d71d2Seschrock return (0); 572940d71d2Seschrock } 573940d71d2Seschrock 574940d71d2Seschrock /* 575940d71d2Seschrock * Callback to add a disk to a given bay. We first check the status-code to 576940d71d2Seschrock * determine if a disk is present, ignoring those that aren't in an appropriate 5770b32bb8bSEric Schrock * state. We then scan the parent bay node's SAS address array to determine 5780b32bb8bSEric Schrock * possible attached SAS addresses. We create a disk node if the disk is not 5790b32bb8bSEric Schrock * SAS or the SES target does not support the necessary pages for this; if we 5800b32bb8bSEric Schrock * find the SAS address, we create a disk node and also correlate it with 5810b32bb8bSEric Schrock * the corresponding Solaris device node to fill in the rest of the data. 582940d71d2Seschrock */ 583940d71d2Seschrock static int 584940d71d2Seschrock ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props) 585940d71d2Seschrock { 586940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 587940d71d2Seschrock uint64_t status; 588940d71d2Seschrock nvlist_t **sas; 589940d71d2Seschrock uint_t s, nsas; 5900b32bb8bSEric Schrock char **paths; 59144ed9dbbSStephen Hanson int err, ret; 59244ed9dbbSStephen Hanson tnode_t *child = NULL; 593940d71d2Seschrock 594940d71d2Seschrock /* 595940d71d2Seschrock * Skip devices that are not in a present (and possibly damaged) state. 596940d71d2Seschrock */ 597940d71d2Seschrock if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0) 598940d71d2Seschrock return (0); 599940d71d2Seschrock 600*ac88567aSHyon Kim if (status != SES_ESC_UNSUPPORTED && 601*ac88567aSHyon Kim status != SES_ESC_OK && 602940d71d2Seschrock status != SES_ESC_CRITICAL && 603940d71d2Seschrock status != SES_ESC_NONCRITICAL && 604940d71d2Seschrock status != SES_ESC_UNRECOVERABLE && 605940d71d2Seschrock status != SES_ESC_NO_ACCESS) 606940d71d2Seschrock return (0); 607940d71d2Seschrock 608940d71d2Seschrock topo_mod_dprintf(mod, "found attached disk"); 609940d71d2Seschrock 610940d71d2Seschrock /* 611940d71d2Seschrock * Create the disk range. 612940d71d2Seschrock */ 6130b32bb8bSEric Schrock if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) { 614940d71d2Seschrock topo_mod_dprintf(mod, 615940d71d2Seschrock "topo_node_create_range() failed: %s", 616940d71d2Seschrock topo_mod_errmsg(mod)); 617940d71d2Seschrock return (-1); 618940d71d2Seschrock } 619940d71d2Seschrock 620940d71d2Seschrock /* 621940d71d2Seschrock * Look through all SAS addresses and attempt to correlate them to a 622940d71d2Seschrock * known Solaris device. If we don't find a matching node, then we 623940d71d2Seschrock * don't enumerate the disk node. 624940d71d2Seschrock */ 625940d71d2Seschrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 626940d71d2Seschrock &sas, &nsas) != 0) 627940d71d2Seschrock return (0); 628940d71d2Seschrock 6290b32bb8bSEric Schrock if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES, 6300b32bb8bSEric Schrock TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0) 6310b32bb8bSEric Schrock return (0); 6320b32bb8bSEric Schrock 6330b32bb8bSEric Schrock err = 0; 6340b32bb8bSEric Schrock 635940d71d2Seschrock for (s = 0; s < nsas; s++) { 636*ac88567aSHyon Kim ret = disk_declare_addr(mod, pnode, &sdp->sed_devs, paths[s], 63744ed9dbbSStephen Hanson &child); 63844ed9dbbSStephen Hanson if (ret == 0) { 63944ed9dbbSStephen Hanson break; 64044ed9dbbSStephen Hanson } else if (ret < 0) { 6410b32bb8bSEric Schrock err = -1; 6420b32bb8bSEric Schrock break; 6430b32bb8bSEric Schrock } 6440b32bb8bSEric Schrock } 6450b32bb8bSEric Schrock 64644ed9dbbSStephen Hanson if (s == nsas) 64744ed9dbbSStephen Hanson disk_declare_non_enumerated(mod, pnode, &child); 64844ed9dbbSStephen Hanson 64944ed9dbbSStephen Hanson /* copy sas_addresses (target-ports) from parent (with 'w'added) */ 65044ed9dbbSStephen Hanson if (child != NULL) { 65144ed9dbbSStephen Hanson int i; 65244ed9dbbSStephen Hanson char **tports; 65344ed9dbbSStephen Hanson uint64_t wwn; 65444ed9dbbSStephen Hanson 65544ed9dbbSStephen Hanson tports = topo_mod_zalloc(mod, sizeof (char *) * nsas); 65644ed9dbbSStephen Hanson if (tports != NULL) { 65744ed9dbbSStephen Hanson for (i = 0; i < nsas; i++) { 65844ed9dbbSStephen Hanson if (scsi_wwnstr_to_wwn(paths[i], &wwn) != 65944ed9dbbSStephen Hanson DDI_SUCCESS) 66044ed9dbbSStephen Hanson break; 66144ed9dbbSStephen Hanson tports[i] = scsi_wwn_to_wwnstr(wwn, 1, NULL); 66244ed9dbbSStephen Hanson if (tports[i] == NULL) 66344ed9dbbSStephen Hanson break; 66444ed9dbbSStephen Hanson } 66544ed9dbbSStephen Hanson /* if they all worked then create the property */ 66644ed9dbbSStephen Hanson if (i == nsas) 66744ed9dbbSStephen Hanson (void) topo_prop_set_string_array(child, 66844ed9dbbSStephen Hanson TOPO_PGROUP_STORAGE, 66944ed9dbbSStephen Hanson TOPO_STORAGE_TARGET_PORT_L0IDS, 67044ed9dbbSStephen Hanson TOPO_PROP_IMMUTABLE, (const char **)tports, 67144ed9dbbSStephen Hanson nsas, &err); 67244ed9dbbSStephen Hanson 67344ed9dbbSStephen Hanson for (i = 0; i < nsas; i++) 67444ed9dbbSStephen Hanson if (tports[i] != NULL) 67544ed9dbbSStephen Hanson scsi_free_wwnstr(tports[i]); 67644ed9dbbSStephen Hanson topo_mod_free(mod, tports, sizeof (char *) * nsas); 67744ed9dbbSStephen Hanson } 67844ed9dbbSStephen Hanson } 67944ed9dbbSStephen Hanson 6800b32bb8bSEric Schrock for (s = 0; s < nsas; s++) 6810b32bb8bSEric Schrock topo_mod_free(mod, paths[s], strlen(paths[s]) + 1); 6820b32bb8bSEric Schrock topo_mod_free(mod, paths, nsas * sizeof (char *)); 6830b32bb8bSEric Schrock 6840b32bb8bSEric Schrock return (err); 6850b32bb8bSEric Schrock } 6860b32bb8bSEric Schrock 6870b32bb8bSEric Schrock static int 6880b32bb8bSEric Schrock ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp) 6890b32bb8bSEric Schrock { 6900b32bb8bSEric Schrock ses_alt_node_t *ap; 6910b32bb8bSEric Schrock ses_node_t *np; 6920b32bb8bSEric Schrock nvlist_t *props; 6930b32bb8bSEric Schrock 6940b32bb8bSEric Schrock nvlist_t **phys; 6950b32bb8bSEric Schrock uint_t i, j, n_phys, all_phys = 0; 6960b32bb8bSEric Schrock char **paths; 6970b32bb8bSEric Schrock uint64_t addr; 6980b32bb8bSEric Schrock size_t len; 6990b32bb8bSEric Schrock int terr, err = -1; 7000b32bb8bSEric Schrock 7010b32bb8bSEric Schrock for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 7020b32bb8bSEric Schrock ap = topo_list_next(ap)) { 7030b32bb8bSEric Schrock np = ap->san_node; 7040b32bb8bSEric Schrock props = ses_node_props(np); 7050b32bb8bSEric Schrock 7060b32bb8bSEric Schrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 7070b32bb8bSEric Schrock &phys, &n_phys) != 0) 7080b32bb8bSEric Schrock continue; 7090b32bb8bSEric Schrock 7100b32bb8bSEric Schrock all_phys += n_phys; 7110b32bb8bSEric Schrock } 7120b32bb8bSEric Schrock 7130b32bb8bSEric Schrock if (all_phys == 0) 7140b32bb8bSEric Schrock return (0); 7150b32bb8bSEric Schrock 7160b32bb8bSEric Schrock if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL) 7170b32bb8bSEric Schrock return (-1); 7180b32bb8bSEric Schrock 7190b32bb8bSEric Schrock for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL; 7200b32bb8bSEric Schrock ap = topo_list_next(ap)) { 7210b32bb8bSEric Schrock np = ap->san_node; 7220b32bb8bSEric Schrock props = ses_node_props(np); 7230b32bb8bSEric Schrock 7240b32bb8bSEric Schrock if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 7250b32bb8bSEric Schrock &phys, &n_phys) != 0) 726940d71d2Seschrock continue; 727940d71d2Seschrock 7280b32bb8bSEric Schrock for (j = 0; j < n_phys; j++) { 7290b32bb8bSEric Schrock if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR, 7300b32bb8bSEric Schrock &addr) != 0) 7310b32bb8bSEric Schrock continue; 732940d71d2Seschrock 7330b32bb8bSEric Schrock len = snprintf(NULL, 0, "%016llx", addr) + 1; 7340b32bb8bSEric Schrock if ((paths[i] = topo_mod_alloc(mod, len)) == NULL) 7350b32bb8bSEric Schrock goto error; 7360b32bb8bSEric Schrock 7370b32bb8bSEric Schrock (void) snprintf(paths[i], len, "%016llx", addr); 7380b32bb8bSEric Schrock 7390b32bb8bSEric Schrock ++i; 7400b32bb8bSEric Schrock } 741940d71d2Seschrock } 742940d71d2Seschrock 7430b32bb8bSEric Schrock err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 7440b32bb8bSEric Schrock TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, 7450b32bb8bSEric Schrock (const char **)paths, i, &terr); 7460b32bb8bSEric Schrock if (err != 0) 7470b32bb8bSEric Schrock err = topo_mod_seterrno(mod, terr); 7480b32bb8bSEric Schrock 7490b32bb8bSEric Schrock error: 7500b32bb8bSEric Schrock for (i = 0; i < all_phys && paths[i] != NULL; i++) 7510b32bb8bSEric Schrock topo_mod_free(mod, paths[i], strlen(paths[i]) + 1); 7520b32bb8bSEric Schrock topo_mod_free(mod, paths, all_phys * sizeof (char *)); 7530b32bb8bSEric Schrock 7540b32bb8bSEric Schrock return (err); 755940d71d2Seschrock } 756940d71d2Seschrock 757940d71d2Seschrock /* 758*ac88567aSHyon Kim * Callback to create a basic node (bay, psu, fan, or controller and expander). 759940d71d2Seschrock */ 760940d71d2Seschrock static int 761940d71d2Seschrock ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp, 762*ac88567aSHyon Kim tnode_t *pnode, const char *nodename, const char *labelname, tnode_t **node) 763940d71d2Seschrock { 764940d71d2Seschrock ses_node_t *np = snp->sen_node; 765d91236feSeschrock ses_node_t *parent; 766940d71d2Seschrock uint64_t instance = snp->sen_instance; 767940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 768940d71d2Seschrock nvlist_t *props, *aprops; 769940d71d2Seschrock nvlist_t *auth = NULL, *fmri = NULL; 770*ac88567aSHyon Kim tnode_t *tn = NULL, *frutn = NULL; 771940d71d2Seschrock char label[128]; 772940d71d2Seschrock int err; 773d91236feSeschrock char *part = NULL, *serial = NULL, *revision = NULL; 774d91236feSeschrock char *desc; 775d91236feSeschrock boolean_t report; 776940d71d2Seschrock 777940d71d2Seschrock props = ses_node_props(np); 778940d71d2Seschrock 779940d71d2Seschrock (void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part); 780940d71d2Seschrock (void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial); 781940d71d2Seschrock 782940d71d2Seschrock topo_mod_dprintf(mod, "adding %s %llu", nodename, instance); 783940d71d2Seschrock 784940d71d2Seschrock /* 785940d71d2Seschrock * Create the node. The interesting information is all copied from the 786940d71d2Seschrock * parent enclosure node, so there is not much to do. 787940d71d2Seschrock */ 788940d71d2Seschrock if ((auth = topo_mod_auth(mod, pnode)) == NULL) 789940d71d2Seschrock goto error; 790940d71d2Seschrock 791d91236feSeschrock /* 792d91236feSeschrock * We want to report revision information for the controller nodes, but 793d91236feSeschrock * we do not get per-element revision information. However, we do have 794d91236feSeschrock * revision information for the entire enclosure, and we can use the 795d91236feSeschrock * 'reported-via' property to know that this controller corresponds to 796d91236feSeschrock * the given revision information. This means we cannot get revision 797d91236feSeschrock * information for targets we are not explicitly connected to, but 798d91236feSeschrock * there is little we can do about the situation. 799d91236feSeschrock */ 800d91236feSeschrock if (strcmp(nodename, CONTROLLER) == 0 && 801d91236feSeschrock nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 && 802d91236feSeschrock report) { 803d91236feSeschrock for (parent = ses_node_parent(np); parent != NULL; 804d91236feSeschrock parent = ses_node_parent(parent)) { 805d91236feSeschrock if (ses_node_type(parent) == SES_NODE_ENCLOSURE) { 806d91236feSeschrock (void) nvlist_lookup_string( 807d91236feSeschrock ses_node_props(parent), 808d91236feSeschrock SES_EN_PROP_REV, &revision); 809d91236feSeschrock break; 810d91236feSeschrock } 811d91236feSeschrock } 812d91236feSeschrock } 813d91236feSeschrock 814940d71d2Seschrock if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 815d91236feSeschrock nodename, (topo_instance_t)instance, NULL, auth, part, revision, 816940d71d2Seschrock serial)) == NULL) { 817940d71d2Seschrock topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 818940d71d2Seschrock topo_mod_errmsg(mod)); 819940d71d2Seschrock goto error; 820940d71d2Seschrock } 821940d71d2Seschrock 822940d71d2Seschrock if ((tn = topo_node_bind(mod, pnode, nodename, 823940d71d2Seschrock instance, fmri)) == NULL) { 824940d71d2Seschrock topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 825940d71d2Seschrock topo_mod_errmsg(mod)); 826940d71d2Seschrock goto error; 827940d71d2Seschrock } 828940d71d2Seschrock 829940d71d2Seschrock /* 830d91236feSeschrock * For the node label, we look for the following in order: 831d91236feSeschrock * 832d91236feSeschrock * <ses-description> 833d91236feSeschrock * <ses-class-description> <instance> 834d91236feSeschrock * <default-type-label> <instance> 835940d71d2Seschrock */ 836d91236feSeschrock if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 || 837d91236feSeschrock desc[0] == '\0') { 838d91236feSeschrock parent = ses_node_parent(np); 839d91236feSeschrock aprops = ses_node_props(parent); 840d91236feSeschrock if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION, 841*ac88567aSHyon Kim &desc) != 0 || desc[0] == '\0') 842*ac88567aSHyon Kim desc = (char *)labelname; 843d91236feSeschrock (void) snprintf(label, sizeof (label), "%s %llu", desc, 844d91236feSeschrock instance); 845d91236feSeschrock desc = label; 846d91236feSeschrock } 847d91236feSeschrock 848d91236feSeschrock if (topo_node_label_set(tn, desc, &err) != 0) 849940d71d2Seschrock goto error; 850940d71d2Seschrock 851*ac88567aSHyon Kim /* 852*ac88567aSHyon Kim * For an expander node, set the FRU to its parent(controller). 853*ac88567aSHyon Kim * For a connector node, set the FRU to its grand parent(controller). 854*ac88567aSHyon Kim */ 855*ac88567aSHyon Kim if (strcmp(nodename, SASEXPANDER) == 0) { 856*ac88567aSHyon Kim frutn = pnode; 857*ac88567aSHyon Kim } else if (strcmp(nodename, RECEPTACLE) == 0) { 858*ac88567aSHyon Kim frutn = topo_node_parent(pnode); 859*ac88567aSHyon Kim } 860*ac88567aSHyon Kim 861*ac88567aSHyon Kim if (ses_set_standard_props(mod, frutn, tn, NULL, ses_node_id(np), 862940d71d2Seschrock snp->sen_target->set_devpath) != 0) 863940d71d2Seschrock goto error; 864940d71d2Seschrock 865*ac88567aSHyon Kim if (strcmp(nodename, BAY) == 0) { 8660b32bb8bSEric Schrock if (ses_add_bay_props(mod, tn, snp) != 0) 8670b32bb8bSEric Schrock goto error; 8680b32bb8bSEric Schrock 869940d71d2Seschrock if (ses_create_disk(sdp, tn, props) != 0) 870940d71d2Seschrock goto error; 871d91236feSeschrock 872d91236feSeschrock if (topo_method_register(mod, tn, ses_bay_methods) != 0) { 873d91236feSeschrock topo_mod_dprintf(mod, 874d91236feSeschrock "topo_method_register() failed: %s", 875d91236feSeschrock topo_mod_errmsg(mod)); 876d91236feSeschrock goto error; 877d91236feSeschrock } 878*ac88567aSHyon Kim } else if ((strcmp(nodename, FAN) == 0) || 879*ac88567aSHyon Kim (strcmp(nodename, PSU) == 0) || 880*ac88567aSHyon Kim (strcmp(nodename, CONTROLLER) == 0)) { 881940d71d2Seschrock /* 882d91236feSeschrock * Only fan, psu, and controller nodes have a 'present' method. 883d91236feSeschrock * Bay nodes are always present, and disk nodes are present by 884*ac88567aSHyon Kim * virtue of being enumerated and SAS expander nodes and 885*ac88567aSHyon Kim * SAS connector nodes are also always present once 886*ac88567aSHyon Kim * the parent controller is found. 887940d71d2Seschrock */ 888940d71d2Seschrock if (topo_method_register(mod, tn, ses_component_methods) != 0) { 889940d71d2Seschrock topo_mod_dprintf(mod, 890940d71d2Seschrock "topo_method_register() failed: %s", 891940d71d2Seschrock topo_mod_errmsg(mod)); 892940d71d2Seschrock goto error; 893940d71d2Seschrock } 894940d71d2Seschrock 895940d71d2Seschrock } 896940d71d2Seschrock 897d91236feSeschrock snp->sen_target->set_refcount++; 898d91236feSeschrock topo_node_setspecific(tn, snp->sen_target); 899d91236feSeschrock 900940d71d2Seschrock nvlist_free(auth); 901940d71d2Seschrock nvlist_free(fmri); 902*ac88567aSHyon Kim if (node != NULL) *node = tn; 903*ac88567aSHyon Kim return (0); 904*ac88567aSHyon Kim 905*ac88567aSHyon Kim error: 906*ac88567aSHyon Kim nvlist_free(auth); 907*ac88567aSHyon Kim nvlist_free(fmri); 908*ac88567aSHyon Kim return (-1); 909*ac88567aSHyon Kim } 910*ac88567aSHyon Kim 911*ac88567aSHyon Kim /* 912*ac88567aSHyon Kim * Create SAS expander specific props. 913*ac88567aSHyon Kim */ 914*ac88567aSHyon Kim /*ARGSUSED*/ 915*ac88567aSHyon Kim static int 916*ac88567aSHyon Kim ses_set_expander_props(ses_enum_data_t *sdp, ses_enum_node_t *snp, 917*ac88567aSHyon Kim tnode_t *ptnode, tnode_t *tnode, int *phycount, int64_t *connlist) 918*ac88567aSHyon Kim { 919*ac88567aSHyon Kim ses_node_t *np = snp->sen_node; 920*ac88567aSHyon Kim topo_mod_t *mod = sdp->sed_mod; 921*ac88567aSHyon Kim nvlist_t *auth = NULL, *fmri = NULL; 922*ac88567aSHyon Kim nvlist_t *props, **phylist; 923*ac88567aSHyon Kim int err, i; 924*ac88567aSHyon Kim uint_t pcount; 925*ac88567aSHyon Kim uint64_t sasaddr, connidx; 926*ac88567aSHyon Kim char sasaddr_str[17]; 927*ac88567aSHyon Kim boolean_t found = B_FALSE; 928*ac88567aSHyon Kim dev_di_node_t *dnode; 929*ac88567aSHyon Kim 930*ac88567aSHyon Kim props = ses_node_props(np); 931*ac88567aSHyon Kim 932*ac88567aSHyon Kim /* 933*ac88567aSHyon Kim * the uninstalled expander is not enumerated by checking 934*ac88567aSHyon Kim * the element status code. No present present' method provided. 935*ac88567aSHyon Kim */ 936*ac88567aSHyon Kim /* 937*ac88567aSHyon Kim * Get the Expander SAS address. It should exist. 938*ac88567aSHyon Kim */ 939*ac88567aSHyon Kim if (nvlist_lookup_uint64(props, SES_EXP_PROP_SAS_ADDR, 940*ac88567aSHyon Kim &sasaddr) != 0) { 941*ac88567aSHyon Kim topo_mod_dprintf(mod, 942*ac88567aSHyon Kim "Failed to get prop %s.", SES_EXP_PROP_SAS_ADDR); 943*ac88567aSHyon Kim goto error; 944*ac88567aSHyon Kim } 945*ac88567aSHyon Kim 946*ac88567aSHyon Kim (void) sprintf(sasaddr_str, "%llx", sasaddr); 947*ac88567aSHyon Kim 948*ac88567aSHyon Kim /* search matching dev_di_node. */ 949*ac88567aSHyon Kim for (dnode = topo_list_next(&sdp->sed_devs); dnode != NULL; 950*ac88567aSHyon Kim dnode = topo_list_next(dnode)) { 951*ac88567aSHyon Kim if (strstr(dnode->ddn_dpath, sasaddr_str) != NULL) { 952*ac88567aSHyon Kim found = B_TRUE; 953*ac88567aSHyon Kim break; 954*ac88567aSHyon Kim } 955*ac88567aSHyon Kim } 956*ac88567aSHyon Kim 957*ac88567aSHyon Kim if (!found) { 958*ac88567aSHyon Kim topo_mod_dprintf(mod, 959*ac88567aSHyon Kim "ses_set_expander_props: Failed to find matching " 960*ac88567aSHyon Kim "devinfo node for Exapnder SAS address %s", 961*ac88567aSHyon Kim SES_EXP_PROP_SAS_ADDR); 962*ac88567aSHyon Kim /* continue on to get storage group props. */ 963*ac88567aSHyon Kim } else { 964*ac88567aSHyon Kim /* create/set the devfs-path and devid in the io group */ 965*ac88567aSHyon Kim if (topo_pgroup_create(tnode, &io_pgroup, &err) != 0) { 966*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 967*ac88567aSHyon Kim "create io error %s\n", topo_strerror(err)); 968*ac88567aSHyon Kim goto error; 969*ac88567aSHyon Kim } else { 970*ac88567aSHyon Kim if (topo_prop_set_string(tnode, TOPO_PGROUP_IO, 971*ac88567aSHyon Kim TOPO_IO_DEV_PATH, TOPO_PROP_IMMUTABLE, 972*ac88567aSHyon Kim dnode->ddn_dpath, &err) != 0) { 973*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 974*ac88567aSHyon Kim "set dev error %s\n", topo_strerror(err)); 975*ac88567aSHyon Kim } 976*ac88567aSHyon Kim if (topo_prop_set_string(tnode, TOPO_PGROUP_IO, 977*ac88567aSHyon Kim TOPO_IO_DEVID, TOPO_PROP_IMMUTABLE, 978*ac88567aSHyon Kim dnode->ddn_devid, &err) != 0) { 979*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 980*ac88567aSHyon Kim "set devid error %s\n", topo_strerror(err)); 981*ac88567aSHyon Kim } 982*ac88567aSHyon Kim if (dnode->ddn_ppath_count != 0 && 983*ac88567aSHyon Kim topo_prop_set_string_array(tnode, TOPO_PGROUP_IO, 984*ac88567aSHyon Kim TOPO_IO_PHYS_PATH, TOPO_PROP_IMMUTABLE, 985*ac88567aSHyon Kim (const char **)dnode->ddn_ppath, 986*ac88567aSHyon Kim dnode->ddn_ppath_count, &err) != 0) { 987*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 988*ac88567aSHyon Kim "set phys-path error %s\n", 989*ac88567aSHyon Kim topo_strerror(err)); 990*ac88567aSHyon Kim } 991*ac88567aSHyon Kim } 992*ac88567aSHyon Kim } 993*ac88567aSHyon Kim 994*ac88567aSHyon Kim /* create the storage group */ 995*ac88567aSHyon Kim if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) { 996*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 997*ac88567aSHyon Kim "create storage error %s\n", topo_strerror(err)); 998*ac88567aSHyon Kim goto error; 999*ac88567aSHyon Kim } else { 1000*ac88567aSHyon Kim /* set the SAS address prop of the expander. */ 1001*ac88567aSHyon Kim if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE, 1002*ac88567aSHyon Kim TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, sasaddr_str, 1003*ac88567aSHyon Kim &err) != 0) { 1004*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 1005*ac88567aSHyon Kim "set %S error %s\n", TOPO_PROP_SAS_ADDR, 1006*ac88567aSHyon Kim topo_strerror(err)); 1007*ac88567aSHyon Kim } 1008*ac88567aSHyon Kim 1009*ac88567aSHyon Kim /* Get the phy information for the expander */ 1010*ac88567aSHyon Kim if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, 1011*ac88567aSHyon Kim &phylist, &pcount) != 0) { 1012*ac88567aSHyon Kim topo_mod_dprintf(mod, 1013*ac88567aSHyon Kim "Failed to get prop %s.", SES_SAS_PROP_PHYS); 1014*ac88567aSHyon Kim } else { 1015*ac88567aSHyon Kim /* 1016*ac88567aSHyon Kim * For each phy, get the connector element index and 1017*ac88567aSHyon Kim * stores into connector element index array. 1018*ac88567aSHyon Kim */ 1019*ac88567aSHyon Kim *phycount = pcount; 1020*ac88567aSHyon Kim for (i = 0; i < pcount; i++) { 1021*ac88567aSHyon Kim if (nvlist_lookup_uint64(phylist[i], 1022*ac88567aSHyon Kim SES_PROP_CE_IDX, &connidx) == 0) { 1023*ac88567aSHyon Kim if (connidx != 0xff) { 1024*ac88567aSHyon Kim connlist[i] = connidx; 1025*ac88567aSHyon Kim } else { 1026*ac88567aSHyon Kim connlist[i] = -1; 1027*ac88567aSHyon Kim } 1028*ac88567aSHyon Kim } else { 1029*ac88567aSHyon Kim /* Fail to get the index. set to -1. */ 1030*ac88567aSHyon Kim connlist[i] = -1; 1031*ac88567aSHyon Kim } 1032*ac88567aSHyon Kim } 1033*ac88567aSHyon Kim 1034*ac88567aSHyon Kim /* set the phy count prop of the expander. */ 1035*ac88567aSHyon Kim if (topo_prop_set_uint64(tnode, TOPO_PGROUP_STORAGE, 1036*ac88567aSHyon Kim TOPO_PROP_PHY_COUNT, TOPO_PROP_IMMUTABLE, pcount, 1037*ac88567aSHyon Kim &err) != 0) { 1038*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 1039*ac88567aSHyon Kim "set %S error %s\n", TOPO_PROP_PHY_COUNT, 1040*ac88567aSHyon Kim topo_strerror(err)); 1041*ac88567aSHyon Kim } 1042*ac88567aSHyon Kim 1043*ac88567aSHyon Kim /* 1044*ac88567aSHyon Kim * set the connector element index of 1045*ac88567aSHyon Kim * the expander phys. 1046*ac88567aSHyon Kim */ 1047*ac88567aSHyon Kim } 1048*ac88567aSHyon Kim 1049*ac88567aSHyon Kim /* populate other misc storage group properties */ 1050*ac88567aSHyon Kim if (found) { 1051*ac88567aSHyon Kim if (dnode->ddn_mfg && (topo_prop_set_string(tnode, 1052*ac88567aSHyon Kim TOPO_PGROUP_STORAGE, TOPO_STORAGE_MANUFACTURER, 1053*ac88567aSHyon Kim TOPO_PROP_IMMUTABLE, dnode->ddn_mfg, &err) != 0)) { 1054*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 1055*ac88567aSHyon Kim "set mfg error %s\n", topo_strerror(err)); 1056*ac88567aSHyon Kim } 1057*ac88567aSHyon Kim 1058*ac88567aSHyon Kim if (dnode->ddn_model && (topo_prop_set_string(tnode, 1059*ac88567aSHyon Kim TOPO_PGROUP_STORAGE, TOPO_STORAGE_MODEL, 1060*ac88567aSHyon Kim TOPO_PROP_IMMUTABLE, 1061*ac88567aSHyon Kim dnode->ddn_model, &err) != 0)) { 1062*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 1063*ac88567aSHyon Kim "set model error %s\n", topo_strerror(err)); 1064*ac88567aSHyon Kim } 1065*ac88567aSHyon Kim 1066*ac88567aSHyon Kim if (dnode->ddn_serial && (topo_prop_set_string(tnode, 1067*ac88567aSHyon Kim TOPO_PGROUP_STORAGE, TOPO_STORAGE_SERIAL_NUM, 1068*ac88567aSHyon Kim TOPO_PROP_IMMUTABLE, 1069*ac88567aSHyon Kim dnode->ddn_serial, &err) != 0)) { 1070*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 1071*ac88567aSHyon Kim "set serial error %s\n", 1072*ac88567aSHyon Kim topo_strerror(err)); 1073*ac88567aSHyon Kim } 1074*ac88567aSHyon Kim 1075*ac88567aSHyon Kim if (dnode->ddn_firm && (topo_prop_set_string(tnode, 1076*ac88567aSHyon Kim TOPO_PGROUP_STORAGE, 1077*ac88567aSHyon Kim TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE, 1078*ac88567aSHyon Kim dnode->ddn_firm, &err) != 0)) { 1079*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 1080*ac88567aSHyon Kim "set firm error %s\n", topo_strerror(err)); 1081*ac88567aSHyon Kim } 1082*ac88567aSHyon Kim } 1083*ac88567aSHyon Kim } 1084*ac88567aSHyon Kim 1085940d71d2Seschrock return (0); 1086940d71d2Seschrock 1087940d71d2Seschrock error: 1088940d71d2Seschrock nvlist_free(auth); 1089940d71d2Seschrock nvlist_free(fmri); 1090940d71d2Seschrock return (-1); 1091940d71d2Seschrock } 1092940d71d2Seschrock 1093*ac88567aSHyon Kim /* 1094*ac88567aSHyon Kim * Create SAS expander specific props. 1095*ac88567aSHyon Kim */ 1096*ac88567aSHyon Kim /*ARGSUSED*/ 1097*ac88567aSHyon Kim static int 1098*ac88567aSHyon Kim ses_set_connector_props(ses_enum_data_t *sdp, ses_enum_node_t *snp, 1099*ac88567aSHyon Kim tnode_t *tnode, int64_t phy_mask) 1100*ac88567aSHyon Kim { 1101*ac88567aSHyon Kim ses_node_t *np = snp->sen_node; 1102*ac88567aSHyon Kim topo_mod_t *mod = sdp->sed_mod; 1103*ac88567aSHyon Kim nvlist_t *props; 1104*ac88567aSHyon Kim int err, i; 1105*ac88567aSHyon Kim uint64_t conntype; 1106*ac88567aSHyon Kim char phymask_str[17], *conntype_str; 1107*ac88567aSHyon Kim boolean_t found; 1108*ac88567aSHyon Kim 1109*ac88567aSHyon Kim props = ses_node_props(np); 1110*ac88567aSHyon Kim 1111*ac88567aSHyon Kim /* 1112*ac88567aSHyon Kim * convert phy mask to string. 1113*ac88567aSHyon Kim */ 1114*ac88567aSHyon Kim (void) snprintf(phymask_str, 17, "%llx", phy_mask); 1115*ac88567aSHyon Kim 1116*ac88567aSHyon Kim /* create the storage group */ 1117*ac88567aSHyon Kim if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) { 1118*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 1119*ac88567aSHyon Kim "create storage error %s\n", topo_strerror(err)); 1120*ac88567aSHyon Kim return (-1); 1121*ac88567aSHyon Kim } else { 1122*ac88567aSHyon Kim /* set the SAS address prop of the expander. */ 1123*ac88567aSHyon Kim if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE, 1124*ac88567aSHyon Kim TOPO_STORAGE_SAS_PHY_MASK, TOPO_PROP_IMMUTABLE, 1125*ac88567aSHyon Kim phymask_str, &err) != 0) { 1126*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 1127*ac88567aSHyon Kim "set %S error %s\n", TOPO_STORAGE_SAS_PHY_MASK, 1128*ac88567aSHyon Kim topo_strerror(err)); 1129*ac88567aSHyon Kim } 1130*ac88567aSHyon Kim 1131*ac88567aSHyon Kim /* Get the connector type information for the expander */ 1132*ac88567aSHyon Kim if (nvlist_lookup_uint64(props, 1133*ac88567aSHyon Kim SES_SC_PROP_CONNECTOR_TYPE, &conntype) != 0) { 1134*ac88567aSHyon Kim topo_mod_dprintf(mod, "Failed to get prop %s.", 1135*ac88567aSHyon Kim TOPO_STORAGE_SAS_PHY_MASK); 1136*ac88567aSHyon Kim } else { 1137*ac88567aSHyon Kim found = B_FALSE; 1138*ac88567aSHyon Kim for (i = 0; ; i++) { 1139*ac88567aSHyon Kim if (sas_connector_type_list[i].type == 1140*ac88567aSHyon Kim SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) { 1141*ac88567aSHyon Kim break; 1142*ac88567aSHyon Kim } 1143*ac88567aSHyon Kim if (sas_connector_type_list[i].type == 1144*ac88567aSHyon Kim conntype) { 1145*ac88567aSHyon Kim conntype_str = 1146*ac88567aSHyon Kim sas_connector_type_list[i].name; 1147*ac88567aSHyon Kim found = B_TRUE; 1148*ac88567aSHyon Kim break; 1149*ac88567aSHyon Kim } 1150*ac88567aSHyon Kim } 1151*ac88567aSHyon Kim 1152*ac88567aSHyon Kim if (!found) { 1153*ac88567aSHyon Kim if (conntype < 1154*ac88567aSHyon Kim SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) { 1155*ac88567aSHyon Kim conntype_str = 1156*ac88567aSHyon Kim SAS_CONNECTOR_TYPE_RESERVED; 1157*ac88567aSHyon Kim } else { 1158*ac88567aSHyon Kim conntype_str = 1159*ac88567aSHyon Kim SAS_CONNECTOR_TYPE_NOT_DEFINED; 1160*ac88567aSHyon Kim } 1161*ac88567aSHyon Kim } 1162*ac88567aSHyon Kim 1163*ac88567aSHyon Kim /* set the phy count prop of the expander. */ 1164*ac88567aSHyon Kim if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE, 1165*ac88567aSHyon Kim TOPO_STORAGE_SAS_CONNECTOR_TYPE, 1166*ac88567aSHyon Kim TOPO_PROP_IMMUTABLE, conntype_str, &err) != 0) { 1167*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_set_expander_props: " 1168*ac88567aSHyon Kim "set %S error %s\n", TOPO_PROP_PHY_COUNT, 1169*ac88567aSHyon Kim topo_strerror(err)); 1170*ac88567aSHyon Kim } 1171*ac88567aSHyon Kim } 1172*ac88567aSHyon Kim } 1173*ac88567aSHyon Kim 1174*ac88567aSHyon Kim return (0); 1175*ac88567aSHyon Kim } 1176*ac88567aSHyon Kim 1177*ac88567aSHyon Kim /* 1178*ac88567aSHyon Kim * Instantiate SAS expander nodes for a given ESC Electronics node(controller) 1179*ac88567aSHyon Kim * nodes. 1180*ac88567aSHyon Kim */ 1181*ac88567aSHyon Kim /*ARGSUSED*/ 1182*ac88567aSHyon Kim static int 1183*ac88567aSHyon Kim ses_create_esc_sasspecific(ses_enum_data_t *sdp, ses_enum_node_t *snp, 1184*ac88567aSHyon Kim tnode_t *pnode, ses_enum_chassis_t *cp, 1185*ac88567aSHyon Kim boolean_t dorange) 1186*ac88567aSHyon Kim { 1187*ac88567aSHyon Kim topo_mod_t *mod = sdp->sed_mod; 1188*ac88567aSHyon Kim tnode_t *exptn, *contn; 1189*ac88567aSHyon Kim boolean_t found; 1190*ac88567aSHyon Kim sas_connector_phy_data_t connectors[64] = {NULL}; 1191*ac88567aSHyon Kim uint64_t max; 1192*ac88567aSHyon Kim ses_enum_node_t *ctlsnp, *xsnp, *consnp; 1193*ac88567aSHyon Kim ses_node_t *np = snp->sen_node; 1194*ac88567aSHyon Kim nvlist_t *props, *psprops; 1195*ac88567aSHyon Kim uint64_t index, psindex, conindex, psstatus, i, j, count; 1196*ac88567aSHyon Kim int64_t cidxlist[256] = {NULL}; 1197*ac88567aSHyon Kim int phycount; 1198*ac88567aSHyon Kim 1199*ac88567aSHyon Kim props = ses_node_props(np); 1200*ac88567aSHyon Kim 1201*ac88567aSHyon Kim if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_ONLY_INDEX, 1202*ac88567aSHyon Kim &index) != 0) 1203*ac88567aSHyon Kim return (-1); 1204*ac88567aSHyon Kim 1205*ac88567aSHyon Kim /* 1206*ac88567aSHyon Kim * For SES constroller node, check to see if there are 1207*ac88567aSHyon Kim * associated SAS expanders. 1208*ac88567aSHyon Kim */ 1209*ac88567aSHyon Kim found = B_FALSE; 1210*ac88567aSHyon Kim max = 0; 1211*ac88567aSHyon Kim for (ctlsnp = topo_list_next(&cp->sec_nodes); ctlsnp != NULL; 1212*ac88567aSHyon Kim ctlsnp = topo_list_next(ctlsnp)) { 1213*ac88567aSHyon Kim if (ctlsnp->sen_type == SES_ET_SAS_EXPANDER) { 1214*ac88567aSHyon Kim found = B_TRUE; 1215*ac88567aSHyon Kim if (ctlsnp->sen_instance > max) 1216*ac88567aSHyon Kim max = ctlsnp->sen_instance; 1217*ac88567aSHyon Kim } 1218*ac88567aSHyon Kim } 1219*ac88567aSHyon Kim 1220*ac88567aSHyon Kim /* 1221*ac88567aSHyon Kim * No SAS expander found notthing to process. 1222*ac88567aSHyon Kim */ 1223*ac88567aSHyon Kim if (!found) 1224*ac88567aSHyon Kim return (0); 1225*ac88567aSHyon Kim 1226*ac88567aSHyon Kim topo_mod_dprintf(mod, "%s Controller %d: creating " 1227*ac88567aSHyon Kim "%llu %s nodes", cp->sec_csn, index, max + 1, SASEXPANDER); 1228*ac88567aSHyon Kim 1229*ac88567aSHyon Kim /* 1230*ac88567aSHyon Kim * The max number represent the number of elements 1231*ac88567aSHyon Kim * deducted from the highest SES_PROP_ELEMENT_CLASS_INDEX 1232*ac88567aSHyon Kim * of SET_ET_SAS_EXPANDER type element. 1233*ac88567aSHyon Kim * 1234*ac88567aSHyon Kim * There may be multiple ESC Electronics element(controllers) 1235*ac88567aSHyon Kim * within JBOD(typicall two for redundancy) and SAS expander 1236*ac88567aSHyon Kim * elements are associated with only one of them. We are 1237*ac88567aSHyon Kim * still creating the range based max number here. 1238*ac88567aSHyon Kim * That will cover the case that all expanders are associated 1239*ac88567aSHyon Kim * with one SES controller. 1240*ac88567aSHyon Kim */ 1241*ac88567aSHyon Kim if (dorange && topo_node_range_create(mod, pnode, 1242*ac88567aSHyon Kim SASEXPANDER, 0, max) != 0) { 1243*ac88567aSHyon Kim topo_mod_dprintf(mod, 1244*ac88567aSHyon Kim "topo_node_create_range() failed: %s", 1245*ac88567aSHyon Kim topo_mod_errmsg(mod)); 1246*ac88567aSHyon Kim return (-1); 1247*ac88567aSHyon Kim } 1248*ac88567aSHyon Kim 1249*ac88567aSHyon Kim /* 1250*ac88567aSHyon Kim * Search exapnders with the parent index matching with 1251*ac88567aSHyon Kim * ESC Electronics element index. 1252*ac88567aSHyon Kim * Note the index used here is a global index across 1253*ac88567aSHyon Kim * SES elements. 1254*ac88567aSHyon Kim */ 1255*ac88567aSHyon Kim for (xsnp = topo_list_next(&cp->sec_nodes); xsnp != NULL; 1256*ac88567aSHyon Kim xsnp = topo_list_next(xsnp)) { 1257*ac88567aSHyon Kim if (xsnp->sen_type == SES_ET_SAS_EXPANDER) { 1258*ac88567aSHyon Kim /* 1259*ac88567aSHyon Kim * get the parent ESC controller. 1260*ac88567aSHyon Kim */ 1261*ac88567aSHyon Kim psprops = ses_node_props(xsnp->sen_node); 1262*ac88567aSHyon Kim if (nvlist_lookup_uint64(psprops, 1263*ac88567aSHyon Kim SES_PROP_STATUS_CODE, &psstatus) == 0) { 1264*ac88567aSHyon Kim if (psstatus == SES_ESC_NOT_INSTALLED) { 1265*ac88567aSHyon Kim /* 1266*ac88567aSHyon Kim * Not installed. 1267*ac88567aSHyon Kim * Don't create a ndoe. 1268*ac88567aSHyon Kim */ 1269*ac88567aSHyon Kim continue; 1270*ac88567aSHyon Kim } 1271*ac88567aSHyon Kim } else { 1272*ac88567aSHyon Kim /* 1273*ac88567aSHyon Kim * The element should have status code. 1274*ac88567aSHyon Kim * If not there is no way to find 1275*ac88567aSHyon Kim * out if the expander element exist or 1276*ac88567aSHyon Kim * not. 1277*ac88567aSHyon Kim */ 1278*ac88567aSHyon Kim continue; 1279*ac88567aSHyon Kim } 1280*ac88567aSHyon Kim 1281*ac88567aSHyon Kim /* Get the physical parent index to compare. */ 1282*ac88567aSHyon Kim if (nvlist_lookup_uint64(psprops, 1283*ac88567aSHyon Kim LIBSES_PROP_PHYS_PARENT, &psindex) == 0) { 1284*ac88567aSHyon Kim if (index == psindex) { 1285*ac88567aSHyon Kim /* indentation moved forward */ 1286*ac88567aSHyon Kim /* 1287*ac88567aSHyon Kim * Handle basic node information of SAS expander 1288*ac88567aSHyon Kim * element - binding to parent node and 1289*ac88567aSHyon Kim * allocating FMRI... 1290*ac88567aSHyon Kim */ 1291*ac88567aSHyon Kim if (ses_create_generic(sdp, xsnp, pnode, SASEXPANDER, 1292*ac88567aSHyon Kim "SAS-EXPANDER", &exptn) != 0) 1293*ac88567aSHyon Kim continue; 1294*ac88567aSHyon Kim /* 1295*ac88567aSHyon Kim * Now handle SAS expander unique portion of node creation. 1296*ac88567aSHyon Kim * The max nubmer of the phy count is 256 since SES-2 1297*ac88567aSHyon Kim * defines as 1 byte field. The cidxlist has the same 1298*ac88567aSHyon Kim * number of elements. 1299*ac88567aSHyon Kim * 1300*ac88567aSHyon Kim * We use size 64 array to store the connectors. 1301*ac88567aSHyon Kim * Typically a connectors associated with 4 phys so that 1302*ac88567aSHyon Kim * matches with the max number of connecters associated 1303*ac88567aSHyon Kim * with an expander. 1304*ac88567aSHyon Kim * The phy count goes up to 38 for Sun supported 1305*ac88567aSHyon Kim * JBOD. 1306*ac88567aSHyon Kim */ 1307*ac88567aSHyon Kim memset(cidxlist, 0, sizeof (int64_t) * 64); 1308*ac88567aSHyon Kim if (ses_set_expander_props(sdp, xsnp, pnode, exptn, &phycount, 1309*ac88567aSHyon Kim cidxlist) != 0) { 1310*ac88567aSHyon Kim /* 1311*ac88567aSHyon Kim * error on getting specific prop failed. 1312*ac88567aSHyon Kim * continue on. Note that the node is 1313*ac88567aSHyon Kim * left bound. 1314*ac88567aSHyon Kim */ 1315*ac88567aSHyon Kim continue; 1316*ac88567aSHyon Kim } 1317*ac88567aSHyon Kim 1318*ac88567aSHyon Kim /* 1319*ac88567aSHyon Kim * count represetns the number of connectors discovered so far. 1320*ac88567aSHyon Kim */ 1321*ac88567aSHyon Kim count = 0; 1322*ac88567aSHyon Kim memset(connectors, 0, sizeof (sas_connector_phy_data_t) * 64); 1323*ac88567aSHyon Kim for (i = 0; i < phycount; i++) { 1324*ac88567aSHyon Kim if (cidxlist[i] != -1) { 1325*ac88567aSHyon Kim /* connector index is valid. */ 1326*ac88567aSHyon Kim for (j = 0; j < count; j++) { 1327*ac88567aSHyon Kim if (connectors[j].index == 1328*ac88567aSHyon Kim cidxlist[i]) { 1329*ac88567aSHyon Kim /* 1330*ac88567aSHyon Kim * Just update phy mask. 1331*ac88567aSHyon Kim * The postion for connector 1332*ac88567aSHyon Kim * index lists(cidxlist index) 1333*ac88567aSHyon Kim * is set. 1334*ac88567aSHyon Kim */ 1335*ac88567aSHyon Kim connectors[j].phy_mask = 1336*ac88567aSHyon Kim connectors[j].phy_mask | 1337*ac88567aSHyon Kim (1ULL << i); 1338*ac88567aSHyon Kim break; 1339*ac88567aSHyon Kim } 1340*ac88567aSHyon Kim } 1341*ac88567aSHyon Kim /* 1342*ac88567aSHyon Kim * If j and count matche a new connector 1343*ac88567aSHyon Kim * index is found. 1344*ac88567aSHyon Kim */ 1345*ac88567aSHyon Kim if (j == count) { 1346*ac88567aSHyon Kim /* add a new index and phy mask. */ 1347*ac88567aSHyon Kim connectors[count].index = cidxlist[i]; 1348*ac88567aSHyon Kim connectors[count].phy_mask = 1349*ac88567aSHyon Kim connectors[count].phy_mask | 1350*ac88567aSHyon Kim (1ULL << i); 1351*ac88567aSHyon Kim count++; 1352*ac88567aSHyon Kim } 1353*ac88567aSHyon Kim } 1354*ac88567aSHyon Kim } 1355*ac88567aSHyon Kim 1356*ac88567aSHyon Kim /* 1357*ac88567aSHyon Kim * create range for the connector nodes. 1358*ac88567aSHyon Kim * The class index of the ses connector element 1359*ac88567aSHyon Kim * is set as the instance nubmer for the node. 1360*ac88567aSHyon Kim * Even though one expander may not have all connectors 1361*ac88567aSHyon Kim * are associated with we are creating the range with 1362*ac88567aSHyon Kim * max possible instance number. 1363*ac88567aSHyon Kim */ 1364*ac88567aSHyon Kim found = B_FALSE; 1365*ac88567aSHyon Kim max = 0; 1366*ac88567aSHyon Kim for (consnp = topo_list_next(&cp->sec_nodes); 1367*ac88567aSHyon Kim consnp != NULL; consnp = topo_list_next(consnp)) { 1368*ac88567aSHyon Kim if (consnp->sen_type == SES_ET_SAS_CONNECTOR) { 1369*ac88567aSHyon Kim psprops = ses_node_props(consnp->sen_node); 1370*ac88567aSHyon Kim found = B_TRUE; 1371*ac88567aSHyon Kim if (consnp->sen_instance > max) 1372*ac88567aSHyon Kim max = consnp->sen_instance; 1373*ac88567aSHyon Kim } 1374*ac88567aSHyon Kim } 1375*ac88567aSHyon Kim 1376*ac88567aSHyon Kim /* 1377*ac88567aSHyon Kim * No SAS connector found nothing to process. 1378*ac88567aSHyon Kim */ 1379*ac88567aSHyon Kim if (!found) 1380*ac88567aSHyon Kim return (0); 1381*ac88567aSHyon Kim 1382*ac88567aSHyon Kim if (dorange && topo_node_range_create(mod, exptn, 1383*ac88567aSHyon Kim RECEPTACLE, 0, max) != 0) { 1384*ac88567aSHyon Kim topo_mod_dprintf(mod, 1385*ac88567aSHyon Kim "topo_node_create_range() failed: %s", 1386*ac88567aSHyon Kim topo_mod_errmsg(mod)); 1387*ac88567aSHyon Kim return (-1); 1388*ac88567aSHyon Kim } 1389*ac88567aSHyon Kim 1390*ac88567aSHyon Kim /* search matching connector element using the index. */ 1391*ac88567aSHyon Kim for (i = 0; i < count; i++) { 1392*ac88567aSHyon Kim found = B_FALSE; 1393*ac88567aSHyon Kim for (consnp = topo_list_next(&cp->sec_nodes); 1394*ac88567aSHyon Kim consnp != NULL; consnp = topo_list_next(consnp)) { 1395*ac88567aSHyon Kim if (consnp->sen_type == SES_ET_SAS_CONNECTOR) { 1396*ac88567aSHyon Kim psprops = ses_node_props( 1397*ac88567aSHyon Kim consnp->sen_node); 1398*ac88567aSHyon Kim /* 1399*ac88567aSHyon Kim * Get the physical parent index to 1400*ac88567aSHyon Kim * compare. 1401*ac88567aSHyon Kim * The connector elements are children 1402*ac88567aSHyon Kim * of ESC Electronics element even 1403*ac88567aSHyon Kim * though we enumerate them under 1404*ac88567aSHyon Kim * an expander in libtopo. 1405*ac88567aSHyon Kim */ 1406*ac88567aSHyon Kim if (nvlist_lookup_uint64(psprops, 1407*ac88567aSHyon Kim SES_PROP_ELEMENT_ONLY_INDEX, 1408*ac88567aSHyon Kim &conindex) == 0) { 1409*ac88567aSHyon Kim if (conindex == 1410*ac88567aSHyon Kim connectors[i].index) { 1411*ac88567aSHyon Kim found = B_TRUE; 1412*ac88567aSHyon Kim break; 1413*ac88567aSHyon Kim } 1414*ac88567aSHyon Kim } 1415*ac88567aSHyon Kim } 1416*ac88567aSHyon Kim } 1417*ac88567aSHyon Kim 1418*ac88567aSHyon Kim /* now create a libtopo node. */ 1419*ac88567aSHyon Kim if (found) { 1420*ac88567aSHyon Kim /* Create generic props. */ 1421*ac88567aSHyon Kim if (ses_create_generic(sdp, consnp, exptn, 1422*ac88567aSHyon Kim RECEPTACLE, "RECEPTACLE", &contn) != 1423*ac88567aSHyon Kim 0) { 1424*ac88567aSHyon Kim continue; 1425*ac88567aSHyon Kim } 1426*ac88567aSHyon Kim /* Create connector specific props. */ 1427*ac88567aSHyon Kim if (ses_set_connector_props(sdp, consnp, 1428*ac88567aSHyon Kim contn, connectors[i].phy_mask) != 0) { 1429*ac88567aSHyon Kim continue; 1430*ac88567aSHyon Kim } 1431*ac88567aSHyon Kim } 1432*ac88567aSHyon Kim } 1433*ac88567aSHyon Kim /* end indentation change */ 1434*ac88567aSHyon Kim } 1435*ac88567aSHyon Kim } 1436*ac88567aSHyon Kim } 1437*ac88567aSHyon Kim } 1438*ac88567aSHyon Kim 1439*ac88567aSHyon Kim return (0); 1440*ac88567aSHyon Kim } 1441*ac88567aSHyon Kim 1442*ac88567aSHyon Kim /* 1443*ac88567aSHyon Kim * Instantiate any protocol specific portion of a node. 1444*ac88567aSHyon Kim */ 1445*ac88567aSHyon Kim /*ARGSUSED*/ 1446*ac88567aSHyon Kim static int 1447*ac88567aSHyon Kim ses_create_protocol_specific(ses_enum_data_t *sdp, ses_enum_node_t *snp, 1448*ac88567aSHyon Kim tnode_t *pnode, uint64_t type, ses_enum_chassis_t *cp, 1449*ac88567aSHyon Kim boolean_t dorange) 1450*ac88567aSHyon Kim { 1451*ac88567aSHyon Kim 1452*ac88567aSHyon Kim if (type == SES_ET_ESC_ELECTRONICS) { 1453*ac88567aSHyon Kim /* create SAS specific children(expanders and connectors. */ 1454*ac88567aSHyon Kim return (ses_create_esc_sasspecific(sdp, snp, pnode, cp, 1455*ac88567aSHyon Kim dorange)); 1456*ac88567aSHyon Kim } 1457*ac88567aSHyon Kim 1458*ac88567aSHyon Kim return (0); 1459*ac88567aSHyon Kim } 1460*ac88567aSHyon Kim 1461940d71d2Seschrock /* 1462940d71d2Seschrock * Instantiate any children of a given type. 1463940d71d2Seschrock */ 1464940d71d2Seschrock static int 1465940d71d2Seschrock ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type, 1466d91236feSeschrock const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp, 1467d91236feSeschrock boolean_t dorange) 1468940d71d2Seschrock { 1469940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 1470940d71d2Seschrock boolean_t found; 1471940d71d2Seschrock uint64_t max; 1472940d71d2Seschrock ses_enum_node_t *snp; 1473*ac88567aSHyon Kim tnode_t *tn; 1474940d71d2Seschrock 1475940d71d2Seschrock /* 1476940d71d2Seschrock * First go through and count how many matching nodes we have. 1477940d71d2Seschrock */ 1478940d71d2Seschrock max = 0; 1479940d71d2Seschrock found = B_FALSE; 1480940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1481940d71d2Seschrock snp = topo_list_next(snp)) { 1482940d71d2Seschrock if (snp->sen_type == type) { 1483940d71d2Seschrock found = B_TRUE; 1484940d71d2Seschrock if (snp->sen_instance > max) 1485940d71d2Seschrock max = snp->sen_instance; 1486940d71d2Seschrock } 1487940d71d2Seschrock } 1488940d71d2Seschrock 1489940d71d2Seschrock /* 1490940d71d2Seschrock * No enclosure should export both DEVICE and ARRAY_DEVICE elements. 1491940d71d2Seschrock * Since we map both of these to 'disk', if an enclosure does this, we 1492940d71d2Seschrock * just ignore the array elements. 1493940d71d2Seschrock */ 1494940d71d2Seschrock if (!found || 1495940d71d2Seschrock (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev)) 1496940d71d2Seschrock return (0); 1497940d71d2Seschrock 1498940d71d2Seschrock topo_mod_dprintf(mod, "%s: creating %llu %s nodes", 149953dbcc59SSundeep Panicker cp->sec_csn, max + 1, nodename); 1500940d71d2Seschrock 1501d91236feSeschrock if (dorange && topo_node_range_create(mod, pnode, 1502940d71d2Seschrock nodename, 0, max) != 0) { 1503940d71d2Seschrock topo_mod_dprintf(mod, 1504940d71d2Seschrock "topo_node_create_range() failed: %s", 1505940d71d2Seschrock topo_mod_errmsg(mod)); 1506940d71d2Seschrock return (-1); 1507940d71d2Seschrock } 1508940d71d2Seschrock 1509940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1510940d71d2Seschrock snp = topo_list_next(snp)) { 1511940d71d2Seschrock if (snp->sen_type == type) { 1512940d71d2Seschrock if (ses_create_generic(sdp, snp, pnode, 1513*ac88567aSHyon Kim nodename, defaultlabel, &tn) != 0) 1514940d71d2Seschrock return (-1); 1515*ac88567aSHyon Kim /* 1516*ac88567aSHyon Kim * For some SES element there may be protocol specific 1517*ac88567aSHyon Kim * information to process. Here we are processing 1518*ac88567aSHyon Kim * the association between enclosure controller and 1519*ac88567aSHyon Kim * SAS expanders. 1520*ac88567aSHyon Kim */ 1521*ac88567aSHyon Kim if (type == SES_ET_ESC_ELECTRONICS) { 1522*ac88567aSHyon Kim /* create SAS expander node */ 1523*ac88567aSHyon Kim if (ses_create_protocol_specific(sdp, snp, 1524*ac88567aSHyon Kim tn, type, cp, dorange) != 0) { 1525*ac88567aSHyon Kim return (-1); 1526*ac88567aSHyon Kim } 1527*ac88567aSHyon Kim } 1528*ac88567aSHyon Kim 1529940d71d2Seschrock } 1530940d71d2Seschrock } 1531940d71d2Seschrock 1532940d71d2Seschrock return (0); 1533940d71d2Seschrock } 1534940d71d2Seschrock 153553dbcc59SSundeep Panicker /* 153653dbcc59SSundeep Panicker * Instantiate a new subchassis instance in the topology. 153753dbcc59SSundeep Panicker */ 153853dbcc59SSundeep Panicker static int 153953dbcc59SSundeep Panicker ses_create_subchassis(ses_enum_data_t *sdp, tnode_t *pnode, 154053dbcc59SSundeep Panicker ses_enum_chassis_t *scp) 154153dbcc59SSundeep Panicker { 154253dbcc59SSundeep Panicker topo_mod_t *mod = sdp->sed_mod; 154353dbcc59SSundeep Panicker tnode_t *tn; 154453dbcc59SSundeep Panicker nvlist_t *props; 154553dbcc59SSundeep Panicker nvlist_t *auth = NULL, *fmri = NULL; 154653dbcc59SSundeep Panicker uint64_t instance = scp->sec_instance; 154753dbcc59SSundeep Panicker char *desc; 154853dbcc59SSundeep Panicker char label[128]; 154953dbcc59SSundeep Panicker char **paths; 155053dbcc59SSundeep Panicker int i, err; 155153dbcc59SSundeep Panicker ses_enum_target_t *stp; 155253dbcc59SSundeep Panicker int ret = -1; 155353dbcc59SSundeep Panicker 155453dbcc59SSundeep Panicker /* 155553dbcc59SSundeep Panicker * Copy authority information from parent enclosure node 155653dbcc59SSundeep Panicker */ 155753dbcc59SSundeep Panicker if ((auth = topo_mod_auth(mod, pnode)) == NULL) 155853dbcc59SSundeep Panicker goto error; 155953dbcc59SSundeep Panicker 156053dbcc59SSundeep Panicker /* 156153dbcc59SSundeep Panicker * Record the subchassis serial number in the FMRI. 156253dbcc59SSundeep Panicker * For now, we assume that logical id is the subchassis serial number. 156353dbcc59SSundeep Panicker * If this assumption changes in future, then the following 156453dbcc59SSundeep Panicker * piece of code will need to be updated via an RFE. 156553dbcc59SSundeep Panicker */ 156653dbcc59SSundeep Panicker if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 1567*ac88567aSHyon Kim SUBCHASSIS, (topo_instance_t)instance, NULL, auth, NULL, NULL, 1568*ac88567aSHyon Kim NULL)) == NULL) { 156953dbcc59SSundeep Panicker topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 157053dbcc59SSundeep Panicker topo_mod_errmsg(mod)); 157153dbcc59SSundeep Panicker goto error; 157253dbcc59SSundeep Panicker } 157353dbcc59SSundeep Panicker 157453dbcc59SSundeep Panicker if ((tn = topo_node_bind(mod, pnode, SUBCHASSIS, 157553dbcc59SSundeep Panicker instance, fmri)) == NULL) { 157653dbcc59SSundeep Panicker topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 157753dbcc59SSundeep Panicker topo_mod_errmsg(mod)); 157853dbcc59SSundeep Panicker goto error; 157953dbcc59SSundeep Panicker } 158053dbcc59SSundeep Panicker 158153dbcc59SSundeep Panicker props = ses_node_props(scp->sec_enclosure); 158253dbcc59SSundeep Panicker 158353dbcc59SSundeep Panicker /* 158453dbcc59SSundeep Panicker * Look for the subchassis label in the following order: 158553dbcc59SSundeep Panicker * <ses-description> 158653dbcc59SSundeep Panicker * <ses-class-description> <instance> 158753dbcc59SSundeep Panicker * <default-type-label> <instance> 158853dbcc59SSundeep Panicker * 158953dbcc59SSundeep Panicker * For subchassis, the default label is "SUBCHASSIS" 159053dbcc59SSundeep Panicker */ 159153dbcc59SSundeep Panicker if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 || 159253dbcc59SSundeep Panicker desc[0] == '\0') { 159353dbcc59SSundeep Panicker if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION, 159453dbcc59SSundeep Panicker &desc) == 0 && desc[0] != '\0') 159553dbcc59SSundeep Panicker (void) snprintf(label, sizeof (label), "%s %llu", desc, 159653dbcc59SSundeep Panicker instance); 159753dbcc59SSundeep Panicker else 159853dbcc59SSundeep Panicker (void) snprintf(label, sizeof (label), 159953dbcc59SSundeep Panicker "SUBCHASSIS %llu", instance); 160053dbcc59SSundeep Panicker desc = label; 160153dbcc59SSundeep Panicker } 160253dbcc59SSundeep Panicker 160353dbcc59SSundeep Panicker if (topo_node_label_set(tn, desc, &err) != 0) 160453dbcc59SSundeep Panicker goto error; 160553dbcc59SSundeep Panicker 1606*ac88567aSHyon Kim if (ses_set_standard_props(mod, NULL, tn, NULL, 160753dbcc59SSundeep Panicker ses_node_id(scp->sec_enclosure), scp->sec_target->set_devpath) != 0) 160853dbcc59SSundeep Panicker goto error; 160953dbcc59SSundeep Panicker 1610*ac88567aSHyon Kim /* 1611*ac88567aSHyon Kim * Set the 'chassis-type' property for this subchassis. This is either 1612*ac88567aSHyon Kim * 'ses-class-description' or 'subchassis'. 1613*ac88567aSHyon Kim */ 1614*ac88567aSHyon Kim if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION, &desc) != 0) 1615*ac88567aSHyon Kim desc = "subchassis"; 1616*ac88567aSHyon Kim 1617*ac88567aSHyon Kim if (topo_prop_set_string(tn, TOPO_PGROUP_SES, 1618*ac88567aSHyon Kim TOPO_PROP_CHASSIS_TYPE, TOPO_PROP_IMMUTABLE, desc, &err) != 0) { 1619*ac88567aSHyon Kim topo_mod_dprintf(mod, "failed to create property %s: %s\n", 1620*ac88567aSHyon Kim TOPO_PROP_CHASSIS_TYPE, topo_strerror(err)); 1621*ac88567aSHyon Kim goto error; 1622*ac88567aSHyon Kim } 1623*ac88567aSHyon Kim 162453dbcc59SSundeep Panicker /* 162553dbcc59SSundeep Panicker * For enclosures, we want to include all possible targets (for upgrade 162653dbcc59SSundeep Panicker * purposes). 162753dbcc59SSundeep Panicker */ 162853dbcc59SSundeep Panicker for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL; 162953dbcc59SSundeep Panicker stp = topo_list_next(stp), i++) 163053dbcc59SSundeep Panicker ; 163153dbcc59SSundeep Panicker 163253dbcc59SSundeep Panicker verify(i != 0); 163353dbcc59SSundeep Panicker paths = alloca(i * sizeof (char *)); 163453dbcc59SSundeep Panicker 163553dbcc59SSundeep Panicker for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL; 163653dbcc59SSundeep Panicker stp = topo_list_next(stp), i++) 163753dbcc59SSundeep Panicker paths[i] = stp->set_devpath; 163853dbcc59SSundeep Panicker 163953dbcc59SSundeep Panicker if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 164053dbcc59SSundeep Panicker TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths, 164153dbcc59SSundeep Panicker i, &err) != 0) { 164253dbcc59SSundeep Panicker topo_mod_dprintf(mod, "failed to create property %s: %s\n", 164353dbcc59SSundeep Panicker TOPO_PROP_PATHS, topo_strerror(err)); 164453dbcc59SSundeep Panicker goto error; 164553dbcc59SSundeep Panicker } 164653dbcc59SSundeep Panicker 164753dbcc59SSundeep Panicker if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 164853dbcc59SSundeep Panicker topo_mod_dprintf(mod, "topo_method_register() failed: %s", 164953dbcc59SSundeep Panicker topo_mod_errmsg(mod)); 165053dbcc59SSundeep Panicker goto error; 165153dbcc59SSundeep Panicker } 165253dbcc59SSundeep Panicker 165353dbcc59SSundeep Panicker /* 165453dbcc59SSundeep Panicker * Create the nodes for controllers and bays. 165553dbcc59SSundeep Panicker */ 165653dbcc59SSundeep Panicker if (ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 165753dbcc59SSundeep Panicker CONTROLLER, "CONTROLLER", scp, B_TRUE) != 0 || 165853dbcc59SSundeep Panicker ses_create_children(sdp, tn, SES_ET_DEVICE, 165953dbcc59SSundeep Panicker BAY, "BAY", scp, B_TRUE) != 0 || 166053dbcc59SSundeep Panicker ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 166153dbcc59SSundeep Panicker BAY, "BAY", scp, B_TRUE) != 0) 166253dbcc59SSundeep Panicker goto error; 166353dbcc59SSundeep Panicker 166453dbcc59SSundeep Panicker ret = 0; 166553dbcc59SSundeep Panicker 166653dbcc59SSundeep Panicker error: 166753dbcc59SSundeep Panicker nvlist_free(auth); 166853dbcc59SSundeep Panicker nvlist_free(fmri); 166953dbcc59SSundeep Panicker return (ret); 167053dbcc59SSundeep Panicker } 167153dbcc59SSundeep Panicker 1672940d71d2Seschrock /* 1673940d71d2Seschrock * Instantiate a new chassis instance in the topology. 1674940d71d2Seschrock */ 1675940d71d2Seschrock static int 1676940d71d2Seschrock ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp) 1677940d71d2Seschrock { 1678940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 1679940d71d2Seschrock nvlist_t *props; 1680940d71d2Seschrock char *raw_manufacturer, *raw_model, *raw_revision; 1681940d71d2Seschrock char *manufacturer = NULL, *model = NULL, *product = NULL; 1682940d71d2Seschrock char *revision = NULL; 1683940d71d2Seschrock char *serial; 16846efb64caSEric Schrock char **paths; 1685940d71d2Seschrock size_t prodlen; 1686940d71d2Seschrock tnode_t *tn; 1687940d71d2Seschrock nvlist_t *fmri = NULL, *auth = NULL; 1688940d71d2Seschrock int ret = -1; 1689940d71d2Seschrock ses_enum_node_t *snp; 16906efb64caSEric Schrock ses_enum_target_t *stp; 169153dbcc59SSundeep Panicker ses_enum_chassis_t *scp; 16926efb64caSEric Schrock int i, err; 169353dbcc59SSundeep Panicker uint64_t sc_count = 0; 1694940d71d2Seschrock 1695d91236feSeschrock /* 1696d91236feSeschrock * Ignore any internal enclosures. 1697d91236feSeschrock */ 1698d91236feSeschrock if (cp->sec_internal) 1699d91236feSeschrock return (0); 1700d91236feSeschrock 1701940d71d2Seschrock /* 1702940d71d2Seschrock * Check to see if there are any devices presennt in the chassis. If 1703940d71d2Seschrock * not, ignore the chassis alltogether. This is most useful for 1704940d71d2Seschrock * ignoring internal HBAs that present a SES target but don't actually 1705940d71d2Seschrock * manage any of the devices. 1706940d71d2Seschrock */ 1707940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 1708940d71d2Seschrock snp = topo_list_next(snp)) { 1709940d71d2Seschrock if (snp->sen_type == SES_ET_DEVICE || 1710940d71d2Seschrock snp->sen_type == SES_ET_ARRAY_DEVICE) 1711940d71d2Seschrock break; 1712940d71d2Seschrock } 1713940d71d2Seschrock 1714940d71d2Seschrock if (snp == NULL) 1715940d71d2Seschrock return (0); 1716940d71d2Seschrock 1717940d71d2Seschrock props = ses_node_props(cp->sec_enclosure); 1718940d71d2Seschrock 1719940d71d2Seschrock /* 1720940d71d2Seschrock * We use the following property mappings: 1721940d71d2Seschrock * 1722940d71d2Seschrock * manufacturer vendor-id 1723940d71d2Seschrock * model product-id 1724940d71d2Seschrock * serial-number libses-chassis-serial 1725940d71d2Seschrock */ 1726940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_VID, 1727940d71d2Seschrock &raw_manufacturer) == 0); 1728940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0); 1729940d71d2Seschrock verify(nvlist_lookup_string(props, SES_EN_PROP_REV, 1730940d71d2Seschrock &raw_revision) == 0); 1731940d71d2Seschrock verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0); 1732940d71d2Seschrock 1733940d71d2Seschrock /* 1734940d71d2Seschrock * To construct the authority information, we 'clean' each string by 1735940d71d2Seschrock * removing any offensive characters and trimmming whitespace. For the 1736940d71d2Seschrock * 'product-id', we use a concatenation of 'manufacturer-model'. We 1737940d71d2Seschrock * also take the numerical serial number and convert it to a string. 1738940d71d2Seschrock */ 1739940d71d2Seschrock if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL || 1740940d71d2Seschrock (model = disk_auth_clean(mod, raw_model)) == NULL || 1741940d71d2Seschrock (revision = disk_auth_clean(mod, raw_revision)) == NULL) { 1742940d71d2Seschrock goto error; 1743940d71d2Seschrock } 1744940d71d2Seschrock 1745940d71d2Seschrock prodlen = strlen(manufacturer) + strlen(model) + 2; 1746940d71d2Seschrock if ((product = topo_mod_alloc(mod, prodlen)) == NULL) 1747940d71d2Seschrock goto error; 1748940d71d2Seschrock 1749940d71d2Seschrock (void) snprintf(product, prodlen, "%s-%s", manufacturer, model); 1750940d71d2Seschrock 1751940d71d2Seschrock /* 1752940d71d2Seschrock * Construct the topo node and bind it to our parent. 1753940d71d2Seschrock */ 1754940d71d2Seschrock if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0) 1755940d71d2Seschrock goto error; 1756940d71d2Seschrock 1757940d71d2Seschrock if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 || 1758940d71d2Seschrock nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) { 1759940d71d2Seschrock (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 1760940d71d2Seschrock goto error; 1761940d71d2Seschrock } 1762940d71d2Seschrock 1763940d71d2Seschrock /* 1764940d71d2Seschrock * We pass NULL for the parent FMRI because there is no resource 1765940d71d2Seschrock * associated with it. For the toplevel enclosure, we leave the 1766940d71d2Seschrock * serial/part/revision portions empty, which are reserved for 1767940d71d2Seschrock * individual components within the chassis. 1768940d71d2Seschrock */ 1769940d71d2Seschrock if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION, 1770940d71d2Seschrock SES_ENCLOSURE, cp->sec_instance, NULL, auth, 1771940d71d2Seschrock model, revision, serial)) == NULL) { 1772940d71d2Seschrock topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 1773940d71d2Seschrock topo_mod_errmsg(mod)); 1774940d71d2Seschrock goto error; 1775940d71d2Seschrock } 1776940d71d2Seschrock 1777940d71d2Seschrock if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE, 1778940d71d2Seschrock cp->sec_instance, fmri)) == NULL) { 1779940d71d2Seschrock topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 1780940d71d2Seschrock topo_mod_errmsg(mod)); 1781940d71d2Seschrock goto error; 1782940d71d2Seschrock } 1783940d71d2Seschrock 1784940d71d2Seschrock if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) { 1785940d71d2Seschrock topo_mod_dprintf(mod, 1786940d71d2Seschrock "topo_method_register() failed: %s", 1787940d71d2Seschrock topo_mod_errmsg(mod)); 1788940d71d2Seschrock goto error; 1789940d71d2Seschrock } 1790940d71d2Seschrock 1791*ac88567aSHyon Kim if (ses_set_standard_props(mod, NULL, tn, auth, 1792940d71d2Seschrock ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0) 1793940d71d2Seschrock goto error; 1794940d71d2Seschrock 17956efb64caSEric Schrock /* 17966efb64caSEric Schrock * For enclosures, we want to include all possible targets (for upgrade 17976efb64caSEric Schrock * purposes). 17986efb64caSEric Schrock */ 17996efb64caSEric Schrock for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL; 18006efb64caSEric Schrock stp = topo_list_next(stp), i++) 18016efb64caSEric Schrock ; 18026efb64caSEric Schrock 18036efb64caSEric Schrock verify(i != 0); 18046efb64caSEric Schrock paths = alloca(i * sizeof (char *)); 18056efb64caSEric Schrock 18066efb64caSEric Schrock for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL; 18076efb64caSEric Schrock stp = topo_list_next(stp), i++) 18086efb64caSEric Schrock paths[i] = stp->set_devpath; 18096efb64caSEric Schrock 18106efb64caSEric Schrock 18116efb64caSEric Schrock if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES, 18126efb64caSEric Schrock TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths, 18136efb64caSEric Schrock i, &err) != 0) { 18146efb64caSEric Schrock topo_mod_dprintf(mod, 18156efb64caSEric Schrock "failed to create property %s: %s\n", 18166efb64caSEric Schrock TOPO_PROP_PATHS, topo_strerror(err)); 18176efb64caSEric Schrock goto error; 18186efb64caSEric Schrock } 18196efb64caSEric Schrock 1820940d71d2Seschrock /* 1821*ac88567aSHyon Kim * Create the nodes for power supplies, fans, controllers and devices. 1822*ac88567aSHyon Kim * Note that SAS exopander nodes and connector nodes are handled 1823*ac88567aSHyon Kim * through protocol specific processing of controllers. 1824940d71d2Seschrock */ 1825940d71d2Seschrock if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY, 1826d91236feSeschrock PSU, "PSU", cp, B_TRUE) != 0 || 1827940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_COOLING, 1828d91236feSeschrock FAN, "FAN", cp, B_TRUE) != 0 || 1829940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS, 1830d91236feSeschrock CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 || 1831940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_DEVICE, 1832d91236feSeschrock BAY, "BAY", cp, B_TRUE) != 0 || 1833940d71d2Seschrock ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE, 1834d91236feSeschrock BAY, "BAY", cp, B_TRUE) != 0) 1835940d71d2Seschrock goto error; 1836940d71d2Seschrock 1837*ac88567aSHyon Kim if (cp->sec_maxinstance >= 0 && 1838*ac88567aSHyon Kim (topo_node_range_create(mod, tn, SUBCHASSIS, 0, 1839*ac88567aSHyon Kim cp->sec_maxinstance) != 0)) { 184053dbcc59SSundeep Panicker topo_mod_dprintf(mod, "topo_node_create_range() failed: %s", 184153dbcc59SSundeep Panicker topo_mod_errmsg(mod)); 184253dbcc59SSundeep Panicker goto error; 184353dbcc59SSundeep Panicker } 184453dbcc59SSundeep Panicker 184553dbcc59SSundeep Panicker for (scp = topo_list_next(&cp->sec_subchassis); scp != NULL; 184653dbcc59SSundeep Panicker scp = topo_list_next(scp)) { 184753dbcc59SSundeep Panicker 184853dbcc59SSundeep Panicker if (ses_create_subchassis(sdp, tn, scp) != 0) 184953dbcc59SSundeep Panicker goto error; 185053dbcc59SSundeep Panicker 185153dbcc59SSundeep Panicker topo_mod_dprintf(mod, "created Subchassis node with " 1852*ac88567aSHyon Kim "instance %u\nand target (%s) under Chassis with CSN %s", 1853*ac88567aSHyon Kim scp->sec_instance, scp->sec_target->set_devpath, 1854*ac88567aSHyon Kim cp->sec_csn); 185553dbcc59SSundeep Panicker 185653dbcc59SSundeep Panicker sc_count++; 185753dbcc59SSundeep Panicker } 185853dbcc59SSundeep Panicker 185953dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: created %llu %s nodes", 186053dbcc59SSundeep Panicker cp->sec_csn, sc_count, SUBCHASSIS); 186153dbcc59SSundeep Panicker 18626efb64caSEric Schrock cp->sec_target->set_refcount++; 18636efb64caSEric Schrock topo_node_setspecific(tn, cp->sec_target); 1864d91236feSeschrock 1865940d71d2Seschrock ret = 0; 1866940d71d2Seschrock error: 1867940d71d2Seschrock topo_mod_strfree(mod, manufacturer); 1868940d71d2Seschrock topo_mod_strfree(mod, model); 1869940d71d2Seschrock topo_mod_strfree(mod, revision); 1870940d71d2Seschrock topo_mod_strfree(mod, product); 1871940d71d2Seschrock 1872940d71d2Seschrock nvlist_free(fmri); 1873940d71d2Seschrock nvlist_free(auth); 1874940d71d2Seschrock return (ret); 1875940d71d2Seschrock } 1876940d71d2Seschrock 1877d91236feSeschrock /* 1878d91236feSeschrock * Create a bay node explicitly enumerated via XML. 1879d91236feSeschrock */ 1880d91236feSeschrock static int 1881d91236feSeschrock ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode) 1882d91236feSeschrock { 1883d91236feSeschrock topo_mod_t *mod = sdp->sed_mod; 1884d91236feSeschrock ses_enum_chassis_t *cp; 1885d91236feSeschrock 1886d91236feSeschrock /* 1887d91236feSeschrock * Iterate over chassis looking for an internal enclosure. This 1888d91236feSeschrock * property is set via a vendor-specific plugin, and there should only 1889d91236feSeschrock * ever be a single internal chassis in a system. 1890d91236feSeschrock */ 1891d91236feSeschrock for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 1892d91236feSeschrock cp = topo_list_next(cp)) { 1893d91236feSeschrock if (cp->sec_internal) 1894d91236feSeschrock break; 1895d91236feSeschrock } 1896d91236feSeschrock 1897d91236feSeschrock if (cp == NULL) { 1898d91236feSeschrock topo_mod_dprintf(mod, "failed to find internal chassis\n"); 1899d91236feSeschrock return (-1); 1900d91236feSeschrock } 1901d91236feSeschrock 1902d91236feSeschrock if (ses_create_children(sdp, pnode, SES_ET_DEVICE, 1903d91236feSeschrock BAY, "BAY", cp, B_FALSE) != 0 || 1904d91236feSeschrock ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE, 1905d91236feSeschrock BAY, "BAY", cp, B_FALSE) != 0) 1906d91236feSeschrock return (-1); 1907d91236feSeschrock 1908d91236feSeschrock return (0); 1909d91236feSeschrock } 191053dbcc59SSundeep Panicker 191153dbcc59SSundeep Panicker /* 191253dbcc59SSundeep Panicker * Initialize chassis or subchassis. 191353dbcc59SSundeep Panicker */ 191453dbcc59SSundeep Panicker static int 191553dbcc59SSundeep Panicker ses_init_chassis(topo_mod_t *mod, ses_enum_data_t *sdp, ses_enum_chassis_t *pcp, 191653dbcc59SSundeep Panicker ses_enum_chassis_t *cp, ses_node_t *np, nvlist_t *props, 1917*ac88567aSHyon Kim uint64_t subchassis, ses_chassis_type_e flags) 191853dbcc59SSundeep Panicker { 191953dbcc59SSundeep Panicker boolean_t internal, ident; 192053dbcc59SSundeep Panicker 192153dbcc59SSundeep Panicker assert((flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS | 192253dbcc59SSundeep Panicker SES_DUP_CHASSIS | SES_DUP_SUBCHASSIS)) != 0); 192353dbcc59SSundeep Panicker 1924*ac88567aSHyon Kim assert(cp != NULL); 1925*ac88567aSHyon Kim assert(np != NULL); 1926*ac88567aSHyon Kim assert(props != NULL); 192753dbcc59SSundeep Panicker 192853dbcc59SSundeep Panicker if (flags & (SES_NEW_SUBCHASSIS | SES_DUP_SUBCHASSIS)) 192953dbcc59SSundeep Panicker assert(pcp != NULL); 193053dbcc59SSundeep Panicker 1931*ac88567aSHyon Kim topo_mod_dprintf(mod, "ses_init_chassis: %s: index %llu, flags (%d)", 1932*ac88567aSHyon Kim sdp->sed_name, subchassis, flags); 193353dbcc59SSundeep Panicker 193453dbcc59SSundeep Panicker if (flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS)) { 193553dbcc59SSundeep Panicker 193653dbcc59SSundeep Panicker topo_mod_dprintf(mod, "new chassis/subchassis"); 193753dbcc59SSundeep Panicker if (nvlist_lookup_boolean_value(props, 193853dbcc59SSundeep Panicker LIBSES_EN_PROP_INTERNAL, &internal) == 0) 193953dbcc59SSundeep Panicker cp->sec_internal = internal; 194053dbcc59SSundeep Panicker 194153dbcc59SSundeep Panicker cp->sec_enclosure = np; 194253dbcc59SSundeep Panicker cp->sec_target = sdp->sed_target; 194353dbcc59SSundeep Panicker 194453dbcc59SSundeep Panicker if (flags & SES_NEW_CHASSIS) { 1945*ac88567aSHyon Kim if (!cp->sec_internal) 1946*ac88567aSHyon Kim cp->sec_instance = sdp->sed_instance++; 194753dbcc59SSundeep Panicker topo_list_append(&sdp->sed_chassis, cp); 194853dbcc59SSundeep Panicker } else { 1949*ac88567aSHyon Kim if (subchassis != NO_SUBCHASSIS) 1950*ac88567aSHyon Kim cp->sec_instance = subchassis; 1951*ac88567aSHyon Kim else 1952*ac88567aSHyon Kim cp->sec_instance = pcp->sec_scinstance++; 1953*ac88567aSHyon Kim 1954*ac88567aSHyon Kim if (cp->sec_instance > pcp->sec_maxinstance) 1955*ac88567aSHyon Kim pcp->sec_maxinstance = cp->sec_instance; 1956*ac88567aSHyon Kim 195753dbcc59SSundeep Panicker topo_list_append(&pcp->sec_subchassis, cp); 195853dbcc59SSundeep Panicker } 195953dbcc59SSundeep Panicker 196053dbcc59SSundeep Panicker } else { 196153dbcc59SSundeep Panicker topo_mod_dprintf(mod, "dup chassis/subchassis"); 196253dbcc59SSundeep Panicker if (nvlist_lookup_boolean_value(props, 196353dbcc59SSundeep Panicker SES_PROP_IDENT, &ident) == 0) { 196453dbcc59SSundeep Panicker topo_mod_dprintf(mod, "overriding enclosure node"); 196553dbcc59SSundeep Panicker 196653dbcc59SSundeep Panicker cp->sec_enclosure = np; 196753dbcc59SSundeep Panicker cp->sec_target = sdp->sed_target; 196853dbcc59SSundeep Panicker } 196953dbcc59SSundeep Panicker } 197053dbcc59SSundeep Panicker 197153dbcc59SSundeep Panicker topo_list_append(&cp->sec_targets, sdp->sed_target); 197253dbcc59SSundeep Panicker sdp->sed_current = cp; 197353dbcc59SSundeep Panicker 197453dbcc59SSundeep Panicker return (0); 197553dbcc59SSundeep Panicker } 197653dbcc59SSundeep Panicker 1977940d71d2Seschrock /* 1978940d71d2Seschrock * Gather nodes from the current SES target into our chassis list, merging the 1979940d71d2Seschrock * results if necessary. 1980940d71d2Seschrock */ 1981940d71d2Seschrock static ses_walk_action_t 1982940d71d2Seschrock ses_enum_gather(ses_node_t *np, void *data) 1983940d71d2Seschrock { 1984940d71d2Seschrock nvlist_t *props = ses_node_props(np); 1985940d71d2Seschrock ses_enum_data_t *sdp = data; 1986940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 198753dbcc59SSundeep Panicker ses_enum_chassis_t *cp, *scp; 1988940d71d2Seschrock ses_enum_node_t *snp; 19890b32bb8bSEric Schrock ses_alt_node_t *sap; 1990940d71d2Seschrock char *csn; 1991940d71d2Seschrock uint64_t instance, type; 1992d91236feSeschrock uint64_t prevstatus, status; 199353dbcc59SSundeep Panicker boolean_t report; 1994*ac88567aSHyon Kim uint64_t subchassis = NO_SUBCHASSIS; 1995940d71d2Seschrock 1996940d71d2Seschrock if (ses_node_type(np) == SES_NODE_ENCLOSURE) { 1997940d71d2Seschrock /* 1998940d71d2Seschrock * If we have already identified the chassis for this target, 1999940d71d2Seschrock * then this is a secondary enclosure and we should ignore it, 2000940d71d2Seschrock * along with the rest of the tree (since this is depth-first). 2001940d71d2Seschrock */ 2002940d71d2Seschrock if (sdp->sed_current != NULL) 2003940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 2004940d71d2Seschrock 2005940d71d2Seschrock /* 2006940d71d2Seschrock * Go through the list of chassis we have seen so far and see 2007940d71d2Seschrock * if this serial number matches one of the known values. 200853dbcc59SSundeep Panicker * If so, check whether this enclosure is a subchassis. 2009940d71d2Seschrock */ 2010940d71d2Seschrock if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, 2011940d71d2Seschrock &csn) != 0) 2012940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 2013940d71d2Seschrock 2014*ac88567aSHyon Kim (void) nvlist_lookup_uint64(props, LIBSES_EN_PROP_SUBCHASSIS_ID, 2015*ac88567aSHyon Kim &subchassis); 201653dbcc59SSundeep Panicker 201753dbcc59SSundeep Panicker topo_mod_dprintf(mod, "ses_enum_gather: Enclosure Node (%s) " 2018*ac88567aSHyon Kim "CSN (%s), subchassis (%llu)", sdp->sed_name, csn, 2019*ac88567aSHyon Kim subchassis); 202053dbcc59SSundeep Panicker 202153dbcc59SSundeep Panicker /* 202253dbcc59SSundeep Panicker * We need to determine whether this enclosure node 202353dbcc59SSundeep Panicker * represents a chassis or a subchassis. Since we may 202453dbcc59SSundeep Panicker * receive the enclosure nodes in a non-deterministic 202553dbcc59SSundeep Panicker * manner, we need to account for all possible combinations: 202653dbcc59SSundeep Panicker * 1. Chassis for the current CSN has not yet been 202753dbcc59SSundeep Panicker * allocated 202853dbcc59SSundeep Panicker * 1.1 This is a new chassis: 202953dbcc59SSundeep Panicker * allocate and instantiate the chassis 203053dbcc59SSundeep Panicker * 1.2 This is a new subchassis: 203153dbcc59SSundeep Panicker * allocate a placeholder chassis 203253dbcc59SSundeep Panicker * allocate and instantiate the subchassis 203353dbcc59SSundeep Panicker * link the subchassis to the chassis 203453dbcc59SSundeep Panicker * 2. Chassis for the current CSN has been allocated 203553dbcc59SSundeep Panicker * 2.1 This is a duplicate chassis enclosure 203653dbcc59SSundeep Panicker * check whether to override old chassis 203753dbcc59SSundeep Panicker * append to chassis' target list 203853dbcc59SSundeep Panicker * 2.2 Only placeholder chassis exists 203953dbcc59SSundeep Panicker * fill in the chassis fields 204053dbcc59SSundeep Panicker * 2.3 This is a new subchassis 204153dbcc59SSundeep Panicker * allocate and instantiate the subchassis 204253dbcc59SSundeep Panicker * link the subchassis to the chassis 204353dbcc59SSundeep Panicker * 2.4 This is a duplicate subchassis enclosure 204453dbcc59SSundeep Panicker * check whether to override old chassis 204553dbcc59SSundeep Panicker * append to chassis' target list 204653dbcc59SSundeep Panicker */ 204753dbcc59SSundeep Panicker 2048940d71d2Seschrock for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL; 204953dbcc59SSundeep Panicker cp = topo_list_next(cp)) 205053dbcc59SSundeep Panicker if (strcmp(cp->sec_csn, csn) == 0) 2051940d71d2Seschrock break; 2052940d71d2Seschrock 2053940d71d2Seschrock if (cp == NULL) { 205453dbcc59SSundeep Panicker /* 1. Haven't seen a chassis with this CSN before */ 2055940d71d2Seschrock 2056940d71d2Seschrock if ((cp = topo_mod_zalloc(mod, 2057940d71d2Seschrock sizeof (ses_enum_chassis_t))) == NULL) 2058940d71d2Seschrock goto error; 2059940d71d2Seschrock 2060*ac88567aSHyon Kim cp->sec_scinstance = SES_STARTING_SUBCHASSIS; 2061*ac88567aSHyon Kim cp->sec_maxinstance = -1; 2062940d71d2Seschrock cp->sec_csn = csn; 206353dbcc59SSundeep Panicker 2064*ac88567aSHyon Kim if (subchassis == NO_SUBCHASSIS) { 206553dbcc59SSundeep Panicker /* 1.1 This is a new chassis */ 206653dbcc59SSundeep Panicker 206753dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Initialize new " 2068*ac88567aSHyon Kim "chassis with CSN %s", sdp->sed_name, csn); 206953dbcc59SSundeep Panicker 207053dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, NULL, cp, 2071*ac88567aSHyon Kim np, props, NO_SUBCHASSIS, 2072*ac88567aSHyon Kim SES_NEW_CHASSIS) < 0) 207353dbcc59SSundeep Panicker goto error; 207453dbcc59SSundeep Panicker } else { 207553dbcc59SSundeep Panicker /* 1.2 This is a new subchassis */ 207653dbcc59SSundeep Panicker 207753dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Initialize new " 2078*ac88567aSHyon Kim "subchassis with CSN %s and index %llu", 2079*ac88567aSHyon Kim sdp->sed_name, csn, subchassis); 208053dbcc59SSundeep Panicker 208153dbcc59SSundeep Panicker if ((scp = topo_mod_zalloc(mod, 208253dbcc59SSundeep Panicker sizeof (ses_enum_chassis_t))) == NULL) 208353dbcc59SSundeep Panicker goto error; 208453dbcc59SSundeep Panicker 208553dbcc59SSundeep Panicker scp->sec_csn = csn; 208653dbcc59SSundeep Panicker 2087*ac88567aSHyon Kim if (ses_init_chassis(mod, sdp, cp, scp, np, 2088*ac88567aSHyon Kim props, subchassis, SES_NEW_SUBCHASSIS) < 0) 208953dbcc59SSundeep Panicker goto error; 209053dbcc59SSundeep Panicker } 20916efb64caSEric Schrock } else { 2092*ac88567aSHyon Kim /* 2093*ac88567aSHyon Kim * We have a chassis or subchassis with this CSN. If 2094*ac88567aSHyon Kim * it's a chassis, we must check to see whether it is 2095*ac88567aSHyon Kim * a placeholder previously created because we found a 2096*ac88567aSHyon Kim * subchassis with this CSN. We will know that because 2097*ac88567aSHyon Kim * the sec_target value will not be set; it is set only 2098*ac88567aSHyon Kim * in ses_init_chassis(). In that case, initialise it 2099*ac88567aSHyon Kim * as a new chassis; otherwise, it's a duplicate and we 2100*ac88567aSHyon Kim * need to append only. 2101*ac88567aSHyon Kim */ 2102*ac88567aSHyon Kim if (subchassis == NO_SUBCHASSIS) { 2103*ac88567aSHyon Kim if (cp->sec_target != NULL) { 210453dbcc59SSundeep Panicker /* 2.1 This is a duplicate chassis */ 210553dbcc59SSundeep Panicker 210653dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Append " 2107*ac88567aSHyon Kim "duplicate chassis with CSN (%s)", 2108*ac88567aSHyon Kim sdp->sed_name, csn); 210953dbcc59SSundeep Panicker 211053dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, NULL, cp, 2111*ac88567aSHyon Kim np, props, NO_SUBCHASSIS, 211253dbcc59SSundeep Panicker SES_DUP_CHASSIS) < 0) 211353dbcc59SSundeep Panicker goto error; 211453dbcc59SSundeep Panicker } else { 2115*ac88567aSHyon Kim /* Placeholder chassis - init it up */ 211653dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Initialize" 2117*ac88567aSHyon Kim "placeholder chassis with CSN %s", 2118*ac88567aSHyon Kim sdp->sed_name, csn); 211953dbcc59SSundeep Panicker 2120*ac88567aSHyon Kim if (ses_init_chassis(mod, sdp, NULL, 2121*ac88567aSHyon Kim cp, np, props, NO_SUBCHASSIS, 212253dbcc59SSundeep Panicker SES_NEW_CHASSIS) < 0) 212353dbcc59SSundeep Panicker goto error; 212453dbcc59SSundeep Panicker 212553dbcc59SSundeep Panicker } 212653dbcc59SSundeep Panicker } else { 212753dbcc59SSundeep Panicker /* This is a subchassis */ 212853dbcc59SSundeep Panicker 212953dbcc59SSundeep Panicker for (scp = topo_list_next(&cp->sec_subchassis); 213053dbcc59SSundeep Panicker scp != NULL; scp = topo_list_next(scp)) 2131*ac88567aSHyon Kim if (scp->sec_instance == subchassis) 213253dbcc59SSundeep Panicker break; 213353dbcc59SSundeep Panicker 213453dbcc59SSundeep Panicker if (scp == NULL) { 213553dbcc59SSundeep Panicker /* 2.3 This is a new subchassis */ 213653dbcc59SSundeep Panicker 213753dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Initialize " 213853dbcc59SSundeep Panicker "new subchassis with CSN (%s) " 213953dbcc59SSundeep Panicker "and LID (%s)", 2140*ac88567aSHyon Kim sdp->sed_name, csn); 214153dbcc59SSundeep Panicker 214253dbcc59SSundeep Panicker if ((scp = topo_mod_zalloc(mod, 214353dbcc59SSundeep Panicker sizeof (ses_enum_chassis_t))) 214453dbcc59SSundeep Panicker == NULL) 214553dbcc59SSundeep Panicker goto error; 214653dbcc59SSundeep Panicker 214753dbcc59SSundeep Panicker scp->sec_csn = csn; 214853dbcc59SSundeep Panicker 214953dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, cp, scp, 2150*ac88567aSHyon Kim np, props, subchassis, 215153dbcc59SSundeep Panicker SES_NEW_SUBCHASSIS) < 0) 215253dbcc59SSundeep Panicker goto error; 215353dbcc59SSundeep Panicker } else { 215453dbcc59SSundeep Panicker /* 2.4 This is a duplicate subchassis */ 215553dbcc59SSundeep Panicker 215653dbcc59SSundeep Panicker topo_mod_dprintf(mod, "%s: Append " 215753dbcc59SSundeep Panicker "duplicate subchassis with " 2158*ac88567aSHyon Kim "CSN (%s)", sdp->sed_name, csn); 215953dbcc59SSundeep Panicker 216053dbcc59SSundeep Panicker if (ses_init_chassis(mod, sdp, cp, scp, 2161*ac88567aSHyon Kim np, props, subchassis, 216253dbcc59SSundeep Panicker SES_DUP_SUBCHASSIS) < 0) 216353dbcc59SSundeep Panicker goto error; 216453dbcc59SSundeep Panicker } 21656efb64caSEric Schrock } 2166940d71d2Seschrock } 2167940d71d2Seschrock } else if (ses_node_type(np) == SES_NODE_ELEMENT) { 2168940d71d2Seschrock /* 2169940d71d2Seschrock * If we haven't yet seen an enclosure node and identified the 2170940d71d2Seschrock * current chassis, something is very wrong; bail out. 2171940d71d2Seschrock */ 2172940d71d2Seschrock if (sdp->sed_current == NULL) 2173940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 2174940d71d2Seschrock 2175940d71d2Seschrock /* 2176940d71d2Seschrock * If this isn't one of the element types we care about, then 2177940d71d2Seschrock * ignore it. 2178940d71d2Seschrock */ 2179940d71d2Seschrock verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, 2180940d71d2Seschrock &type) == 0); 2181940d71d2Seschrock if (type != SES_ET_DEVICE && 2182940d71d2Seschrock type != SES_ET_ARRAY_DEVICE && 2183940d71d2Seschrock type != SES_ET_COOLING && 2184940d71d2Seschrock type != SES_ET_POWER_SUPPLY && 2185*ac88567aSHyon Kim type != SES_ET_ESC_ELECTRONICS && 2186*ac88567aSHyon Kim type != SES_ET_SAS_EXPANDER && 2187*ac88567aSHyon Kim type != SES_ET_SAS_CONNECTOR) 2188940d71d2Seschrock return (SES_WALK_ACTION_CONTINUE); 2189940d71d2Seschrock 2190940d71d2Seschrock /* 2191940d71d2Seschrock * Get the current instance number and see if we already know 2192940d71d2Seschrock * about this element. If so, it means we have multiple paths 2193940d71d2Seschrock * to the same elements, and we should ignore the current path. 2194940d71d2Seschrock */ 2195940d71d2Seschrock verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX, 2196940d71d2Seschrock &instance) == 0); 2197940d71d2Seschrock if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE) 2198940d71d2Seschrock (void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER, 2199940d71d2Seschrock &instance); 2200940d71d2Seschrock 2201940d71d2Seschrock cp = sdp->sed_current; 2202940d71d2Seschrock 2203940d71d2Seschrock for (snp = topo_list_next(&cp->sec_nodes); snp != NULL; 2204940d71d2Seschrock snp = topo_list_next(snp)) { 2205940d71d2Seschrock if (snp->sen_type == type && 2206940d71d2Seschrock snp->sen_instance == instance) 2207d91236feSeschrock break; 2208d91236feSeschrock } 2209d91236feSeschrock 2210d91236feSeschrock /* 2211d91236feSeschrock * We prefer the new element under the following circumstances: 2212d91236feSeschrock * 2213d91236feSeschrock * - The currently known element's status is unknown or not 2214d91236feSeschrock * available, but the new element has a known status. This 2215d91236feSeschrock * occurs if a given element is only available through a 2216d91236feSeschrock * particular target. 2217d91236feSeschrock * 2218d91236feSeschrock * - This is an ESC_ELECTRONICS element, and the 'reported-via' 2219d91236feSeschrock * property is set. This allows us to get reliable firmware 2220d91236feSeschrock * revision information from the enclosure node. 2221d91236feSeschrock */ 2222d91236feSeschrock if (snp != NULL) { 2223d91236feSeschrock if (nvlist_lookup_uint64( 2224d91236feSeschrock ses_node_props(snp->sen_node), 2225d91236feSeschrock SES_PROP_STATUS_CODE, &prevstatus) != 0) 2226d91236feSeschrock prevstatus = SES_ESC_UNSUPPORTED; 2227d91236feSeschrock if (nvlist_lookup_uint64( 2228d91236feSeschrock props, SES_PROP_STATUS_CODE, &status) != 0) 2229d91236feSeschrock status = SES_ESC_UNSUPPORTED; 2230d91236feSeschrock if (nvlist_lookup_boolean_value( 2231d91236feSeschrock props, SES_PROP_REPORT, &report) != 0) 2232d91236feSeschrock report = B_FALSE; 2233d91236feSeschrock 2234d91236feSeschrock if ((SES_STATUS_UNAVAIL(prevstatus) && 2235d91236feSeschrock !SES_STATUS_UNAVAIL(status)) || 2236d91236feSeschrock (type == SES_ET_ESC_ELECTRONICS && 2237d91236feSeschrock report)) { 2238d91236feSeschrock snp->sen_node = np; 2239d91236feSeschrock snp->sen_target = sdp->sed_target; 2240d91236feSeschrock } 2241d91236feSeschrock 22420b32bb8bSEric Schrock if ((sap = topo_mod_zalloc(mod, 22430b32bb8bSEric Schrock sizeof (ses_alt_node_t))) == NULL) 22440b32bb8bSEric Schrock goto error; 22450b32bb8bSEric Schrock 22460b32bb8bSEric Schrock sap->san_node = np; 22470b32bb8bSEric Schrock topo_list_append(&snp->sen_alt_nodes, sap); 22480b32bb8bSEric Schrock 2249d91236feSeschrock return (SES_WALK_ACTION_CONTINUE); 2250940d71d2Seschrock } 2251940d71d2Seschrock 2252940d71d2Seschrock if ((snp = topo_mod_zalloc(mod, 2253940d71d2Seschrock sizeof (ses_enum_node_t))) == NULL) 2254940d71d2Seschrock goto error; 2255940d71d2Seschrock 22560b32bb8bSEric Schrock if ((sap = topo_mod_zalloc(mod, 22570b32bb8bSEric Schrock sizeof (ses_alt_node_t))) == NULL) { 22580b32bb8bSEric Schrock topo_mod_free(mod, snp, sizeof (ses_enum_node_t)); 22590b32bb8bSEric Schrock goto error; 22600b32bb8bSEric Schrock } 22610b32bb8bSEric Schrock 2262940d71d2Seschrock topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)", 2263940d71d2Seschrock sdp->sed_name, type, instance); 2264940d71d2Seschrock snp->sen_node = np; 2265940d71d2Seschrock snp->sen_type = type; 2266940d71d2Seschrock snp->sen_instance = instance; 2267940d71d2Seschrock snp->sen_target = sdp->sed_target; 22680b32bb8bSEric Schrock sap->san_node = np; 22690b32bb8bSEric Schrock topo_list_append(&snp->sen_alt_nodes, sap); 2270940d71d2Seschrock topo_list_append(&cp->sec_nodes, snp); 2271940d71d2Seschrock 2272940d71d2Seschrock if (type == SES_ET_DEVICE) 2273940d71d2Seschrock cp->sec_hasdev = B_TRUE; 2274940d71d2Seschrock } 2275940d71d2Seschrock 2276940d71d2Seschrock return (SES_WALK_ACTION_CONTINUE); 2277940d71d2Seschrock 2278940d71d2Seschrock error: 2279940d71d2Seschrock sdp->sed_errno = -1; 2280940d71d2Seschrock return (SES_WALK_ACTION_TERMINATE); 2281940d71d2Seschrock } 2282940d71d2Seschrock 2283940d71d2Seschrock static int 2284940d71d2Seschrock ses_process_dir(const char *dirpath, ses_enum_data_t *sdp) 2285940d71d2Seschrock { 2286940d71d2Seschrock topo_mod_t *mod = sdp->sed_mod; 2287940d71d2Seschrock DIR *dir; 2288940d71d2Seschrock struct dirent *dp; 2289940d71d2Seschrock char path[PATH_MAX]; 2290940d71d2Seschrock ses_enum_target_t *stp; 2291940d71d2Seschrock int err = -1; 2292940d71d2Seschrock 2293940d71d2Seschrock /* 2294940d71d2Seschrock * Open the SES target directory and iterate over any available 2295940d71d2Seschrock * targets. 2296940d71d2Seschrock */ 2297940d71d2Seschrock if ((dir = opendir(dirpath)) == NULL) { 2298940d71d2Seschrock /* 2299940d71d2Seschrock * If the SES target directory does not exist, then return as if 2300940d71d2Seschrock * there are no active targets. 2301940d71d2Seschrock */ 2302940d71d2Seschrock topo_mod_dprintf(mod, "failed to open ses " 2303940d71d2Seschrock "directory '%s'", dirpath); 2304940d71d2Seschrock return (0); 2305940d71d2Seschrock } 2306940d71d2Seschrock 2307940d71d2Seschrock while ((dp = readdir(dir)) != NULL) { 2308940d71d2Seschrock if (strcmp(dp->d_name, ".") == 0 || 2309940d71d2Seschrock strcmp(dp->d_name, "..") == 0) 2310940d71d2Seschrock continue; 2311940d71d2Seschrock 2312940d71d2Seschrock /* 2313940d71d2Seschrock * Create a new target instance and take a snapshot. 2314940d71d2Seschrock */ 2315940d71d2Seschrock if ((stp = topo_mod_zalloc(mod, 2316940d71d2Seschrock sizeof (ses_enum_target_t))) == NULL) 2317940d71d2Seschrock goto error; 2318940d71d2Seschrock 23190b32bb8bSEric Schrock (void) pthread_mutex_init(&stp->set_lock, NULL); 23200b32bb8bSEric Schrock 2321940d71d2Seschrock (void) snprintf(path, sizeof (path), "%s/%s", dirpath, 2322940d71d2Seschrock dp->d_name); 2323940d71d2Seschrock 2324940d71d2Seschrock /* 2325940d71d2Seschrock * We keep track of the SES device path and export it on a 2326940d71d2Seschrock * per-node basis to allow higher level software to get to the 2327940d71d2Seschrock * corresponding SES state. 2328940d71d2Seschrock */ 2329940d71d2Seschrock if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) { 2330940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 2331940d71d2Seschrock goto error; 2332940d71d2Seschrock } 2333940d71d2Seschrock 2334940d71d2Seschrock if ((stp->set_target = 2335940d71d2Seschrock ses_open(LIBSES_VERSION, path)) == NULL) { 2336940d71d2Seschrock topo_mod_dprintf(mod, "failed to open ses target " 2337940d71d2Seschrock "'%s': %s", dp->d_name, ses_errmsg()); 2338940d71d2Seschrock topo_mod_strfree(mod, stp->set_devpath); 2339940d71d2Seschrock topo_mod_free(mod, stp, sizeof (ses_enum_target_t)); 2340940d71d2Seschrock continue; 2341940d71d2Seschrock } 2342940d71d2Seschrock 2343940d71d2Seschrock stp->set_refcount = 1; 2344940d71d2Seschrock sdp->sed_target = stp; 2345940d71d2Seschrock stp->set_snap = ses_snap_hold(stp->set_target); 2346525b85dbSEric Schrock stp->set_snaptime = gethrtime(); 2347940d71d2Seschrock 2348940d71d2Seschrock /* 2349940d71d2Seschrock * Enumerate over all SES elements and merge them into the 2350940d71d2Seschrock * correct ses_enum_chassis_t. 2351940d71d2Seschrock */ 2352940d71d2Seschrock sdp->sed_current = NULL; 2353940d71d2Seschrock sdp->sed_errno = 0; 2354940d71d2Seschrock sdp->sed_name = dp->d_name; 2355940d71d2Seschrock (void) ses_walk(stp->set_snap, ses_enum_gather, sdp); 2356940d71d2Seschrock 2357940d71d2Seschrock if (sdp->sed_errno != 0) 2358940d71d2Seschrock goto error; 2359940d71d2Seschrock } 2360940d71d2Seschrock 2361940d71d2Seschrock err = 0; 2362940d71d2Seschrock error: 2363940d71d2Seschrock closedir(dir); 2364940d71d2Seschrock return (err); 2365940d71d2Seschrock } 2366940d71d2Seschrock 2367940d71d2Seschrock static void 2368940d71d2Seschrock ses_release(topo_mod_t *mod, tnode_t *tn) 2369940d71d2Seschrock { 2370940d71d2Seschrock ses_enum_target_t *stp; 2371940d71d2Seschrock 2372940d71d2Seschrock if ((stp = topo_node_getspecific(tn)) != NULL) 2373940d71d2Seschrock ses_target_free(mod, stp); 2374940d71d2Seschrock } 2375940d71d2Seschrock 2376940d71d2Seschrock /*ARGSUSED*/ 2377940d71d2Seschrock static int 2378940d71d2Seschrock ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 2379940d71d2Seschrock topo_instance_t min, topo_instance_t max, void *arg, void *notused) 2380940d71d2Seschrock { 2381940d71d2Seschrock ses_enum_chassis_t *cp; 2382d91236feSeschrock ses_enum_data_t *data; 2383940d71d2Seschrock 2384940d71d2Seschrock /* 2385940d71d2Seschrock * Check to make sure we're being invoked sensibly, and that we're not 2386940d71d2Seschrock * being invoked as part of a post-processing step. 2387940d71d2Seschrock */ 2388d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0) 2389940d71d2Seschrock return (0); 2390940d71d2Seschrock 2391940d71d2Seschrock /* 2392d91236feSeschrock * If this is the first time we've called our enumeration method, then 2393d91236feSeschrock * gather information about any available enclosures. 2394940d71d2Seschrock */ 2395d91236feSeschrock if ((data = topo_mod_getspecific(mod)) == NULL) { 2396d91236feSeschrock if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) == 2397d91236feSeschrock NULL) 2398d91236feSeschrock return (-1); 2399940d71d2Seschrock 2400d91236feSeschrock data->sed_mod = mod; 2401d91236feSeschrock topo_mod_setspecific(mod, data); 2402d91236feSeschrock 2403*ac88567aSHyon Kim if (dev_list_gather(mod, &data->sed_devs) != 0) 2404d91236feSeschrock goto error; 2405d91236feSeschrock 2406d91236feSeschrock /* 2407d91236feSeschrock * We search both the ses(7D) and sgen(7D) locations, so we are 2408d91236feSeschrock * independent of any particular driver class bindings. 2409d91236feSeschrock */ 2410d91236feSeschrock if (ses_process_dir("/dev/es", data) != 0 || 2411d91236feSeschrock ses_process_dir("/dev/scsi/ses", data) != 0) 2412d91236feSeschrock goto error; 2413d91236feSeschrock } 2414d91236feSeschrock 2415d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) == 0) { 2416d91236feSeschrock /* 2417d91236feSeschrock * This is a request to enumerate external enclosures. Go 2418d91236feSeschrock * through all the targets and create chassis nodes where 2419d91236feSeschrock * necessary. 2420d91236feSeschrock */ 2421d91236feSeschrock for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 2422d91236feSeschrock cp = topo_list_next(cp)) { 2423d91236feSeschrock if (ses_create_chassis(data, rnode, cp) != 0) 2424d91236feSeschrock goto error; 2425d91236feSeschrock } 2426d91236feSeschrock } else { 2427d91236feSeschrock /* 2428d91236feSeschrock * This is a request to enumerate a specific bay underneath the 2429d91236feSeschrock * root chassis (for internal disks). 2430d91236feSeschrock */ 2431d91236feSeschrock if (ses_create_bays(data, rnode) != 0) 2432940d71d2Seschrock goto error; 2433940d71d2Seschrock } 2434940d71d2Seschrock 2435d91236feSeschrock /* 2436d91236feSeschrock * This is a bit of a kludge. In order to allow internal disks to be 2437d91236feSeschrock * enumerated and share snapshot-specific information with the external 2438d91236feSeschrock * enclosure enumeration, we rely on the fact that we will be invoked 2439d91236feSeschrock * for the 'ses-enclosure' node last. 2440d91236feSeschrock */ 2441d91236feSeschrock if (strcmp(name, SES_ENCLOSURE) == 0) { 244253dbcc59SSundeep Panicker for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 244353dbcc59SSundeep Panicker cp = topo_list_next(cp)) 244453dbcc59SSundeep Panicker ses_data_free(data, cp); 244553dbcc59SSundeep Panicker ses_data_free(data, NULL); 2446d91236feSeschrock topo_mod_setspecific(mod, NULL); 2447d91236feSeschrock } 2448940d71d2Seschrock return (0); 2449940d71d2Seschrock 2450940d71d2Seschrock error: 245153dbcc59SSundeep Panicker for (cp = topo_list_next(&data->sed_chassis); cp != NULL; 245253dbcc59SSundeep Panicker cp = topo_list_next(cp)) 245353dbcc59SSundeep Panicker ses_data_free(data, cp); 245453dbcc59SSundeep Panicker ses_data_free(data, NULL); 2455d91236feSeschrock topo_mod_setspecific(mod, NULL); 2456940d71d2Seschrock return (-1); 2457940d71d2Seschrock } 2458940d71d2Seschrock 2459940d71d2Seschrock static const topo_modops_t ses_ops = 2460940d71d2Seschrock { ses_enum, ses_release }; 2461940d71d2Seschrock 2462940d71d2Seschrock static topo_modinfo_t ses_info = 2463940d71d2Seschrock { SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops }; 2464940d71d2Seschrock 2465940d71d2Seschrock /*ARGSUSED*/ 2466940d71d2Seschrock int 2467940d71d2Seschrock _topo_init(topo_mod_t *mod, topo_version_t version) 2468940d71d2Seschrock { 2469940d71d2Seschrock if (getenv("TOPOSESDEBUG") != NULL) 2470940d71d2Seschrock topo_mod_setdebug(mod); 2471940d71d2Seschrock 2472940d71d2Seschrock topo_mod_dprintf(mod, "initializing %s enumerator\n", 2473940d71d2Seschrock SES_ENCLOSURE); 2474940d71d2Seschrock 2475940d71d2Seschrock return (topo_mod_register(mod, &ses_info, TOPO_VERSION)); 2476940d71d2Seschrock } 2477940d71d2Seschrock 2478940d71d2Seschrock void 2479940d71d2Seschrock _topo_fini(topo_mod_t *mod) 2480940d71d2Seschrock { 2481940d71d2Seschrock topo_mod_unregister(mod); 2482940d71d2Seschrock } 2483