1*6316Seschrock /* 2*6316Seschrock * CDDL HEADER START 3*6316Seschrock * 4*6316Seschrock * The contents of this file are subject to the terms of the 5*6316Seschrock * Common Development and Distribution License (the "License"). 6*6316Seschrock * You may not use this file except in compliance with the License. 7*6316Seschrock * 8*6316Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*6316Seschrock * or http://www.opensolaris.org/os/licensing. 10*6316Seschrock * See the License for the specific language governing permissions 11*6316Seschrock * and limitations under the License. 12*6316Seschrock * 13*6316Seschrock * When distributing Covered Code, include this CDDL HEADER in each 14*6316Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*6316Seschrock * If applicable, add the following below this CDDL HEADER, with the 16*6316Seschrock * fields enclosed by brackets "[]" replaced with your own identifying 17*6316Seschrock * information: Portions Copyright [yyyy] [name of copyright owner] 18*6316Seschrock * 19*6316Seschrock * CDDL HEADER END 20*6316Seschrock */ 21*6316Seschrock 22*6316Seschrock /* 23*6316Seschrock * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24*6316Seschrock * Use is subject to license terms. 25*6316Seschrock */ 26*6316Seschrock 27*6316Seschrock #pragma ident "%Z%%M% %I% %E% SMI" 28*6316Seschrock 29*6316Seschrock #include <scsi/libses.h> 30*6316Seschrock #include "ses_impl.h" 31*6316Seschrock 32*6316Seschrock #define NEXT_ED(eip) \ 33*6316Seschrock ((ses2_ed_impl_t *)((uint8_t *)(eip) + \ 34*6316Seschrock ((eip)->st_hdr.sehi_ed_len + sizeof (ses2_ed_hdr_impl_t)))) 35*6316Seschrock 36*6316Seschrock static ses_node_t * 37*6316Seschrock ses_find_enclosure(ses_snap_t *sp, uint64_t number) 38*6316Seschrock { 39*6316Seschrock ses_node_t *np; 40*6316Seschrock 41*6316Seschrock for (np = sp->ss_root->sn_first_child; np != NULL; 42*6316Seschrock np = np->sn_next_sibling) { 43*6316Seschrock ASSERT(np->sn_type == SES_NODE_ENCLOSURE); 44*6316Seschrock if (np->sn_enc_num == number) 45*6316Seschrock return ((ses_node_t *)np); 46*6316Seschrock } 47*6316Seschrock 48*6316Seschrock return (NULL); 49*6316Seschrock } 50*6316Seschrock 51*6316Seschrock void 52*6316Seschrock ses_node_teardown(ses_node_t *np) 53*6316Seschrock { 54*6316Seschrock ses_node_t *rp; 55*6316Seschrock 56*6316Seschrock if (np == NULL) 57*6316Seschrock return; 58*6316Seschrock 59*6316Seschrock for (; np != NULL; np = rp) { 60*6316Seschrock ses_node_teardown(np->sn_first_child); 61*6316Seschrock rp = np->sn_next_sibling; 62*6316Seschrock nvlist_free(np->sn_props); 63*6316Seschrock ses_free(np); 64*6316Seschrock } 65*6316Seschrock } 66*6316Seschrock 67*6316Seschrock static ses_node_t * 68*6316Seschrock ses_node_alloc(ses_snap_t *sp, ses_node_t *pnp) 69*6316Seschrock { 70*6316Seschrock ses_node_t *np; 71*6316Seschrock 72*6316Seschrock np = ses_zalloc(sizeof (ses_node_t)); 73*6316Seschrock if (np == NULL) 74*6316Seschrock goto fail; 75*6316Seschrock if (nvlist_alloc(&np->sn_props, NV_UNIQUE_NAME, 0) != 0) 76*6316Seschrock goto fail; 77*6316Seschrock 78*6316Seschrock np->sn_snapshot = sp; 79*6316Seschrock np->sn_id = sp->ss_n_nodes++; 80*6316Seschrock 81*6316Seschrock if (pnp == NULL) { 82*6316Seschrock ASSERT(sp->ss_root == NULL); 83*6316Seschrock sp->ss_root = np; 84*6316Seschrock } else { 85*6316Seschrock np->sn_parent = pnp; 86*6316Seschrock np->sn_prev_sibling = pnp->sn_last_child; 87*6316Seschrock 88*6316Seschrock if (pnp->sn_first_child == NULL) 89*6316Seschrock pnp->sn_first_child = np; 90*6316Seschrock else 91*6316Seschrock pnp->sn_last_child->sn_next_sibling = np; 92*6316Seschrock 93*6316Seschrock pnp->sn_last_child = np; 94*6316Seschrock } 95*6316Seschrock 96*6316Seschrock return (np); 97*6316Seschrock 98*6316Seschrock fail: 99*6316Seschrock ses_free(np); 100*6316Seschrock ses_node_teardown(sp->ss_root); 101*6316Seschrock sp->ss_root = NULL; 102*6316Seschrock return (NULL); 103*6316Seschrock } 104*6316Seschrock 105*6316Seschrock /* 106*6316Seschrock * Parse element type descriptor. 107*6316Seschrock */ 108*6316Seschrock static int 109*6316Seschrock elem_parse_td(ses2_td_hdr_impl_t *tip, const char *tp, nvlist_t *nvl) 110*6316Seschrock { 111*6316Seschrock int nverr; 112*6316Seschrock 113*6316Seschrock if (tp != NULL) 114*6316Seschrock SES_NV_ADD(fixed_string, nverr, nvl, SES_PROP_CLASS_DESCRIPTION, 115*6316Seschrock tp, tip->sthi_text_len); 116*6316Seschrock 117*6316Seschrock return (0); 118*6316Seschrock } 119*6316Seschrock 120*6316Seschrock 121*6316Seschrock /* 122*6316Seschrock * Build a skeleton tree of nodes in the given snapshot. This is the heart of 123*6316Seschrock * libses, and is responsible for parsing the config page into a tree and 124*6316Seschrock * populating nodes with data from the config page. 125*6316Seschrock */ 126*6316Seschrock static int 127*6316Seschrock ses_build_snap_skel(ses_snap_t *sp) 128*6316Seschrock { 129*6316Seschrock ses2_ed_impl_t *eip; 130*6316Seschrock ses2_td_hdr_impl_t *tip, *ftip; 131*6316Seschrock ses_node_t *np, *pnp, *cnp, *root; 132*6316Seschrock ses_snap_page_t *pp; 133*6316Seschrock ses2_config_page_impl_t *pip; 134*6316Seschrock int i, j, n_etds = 0; 135*6316Seschrock off_t toff; 136*6316Seschrock char *tp, *text; 137*6316Seschrock int err; 138*6316Seschrock uint64_t idx; 139*6316Seschrock 140*6316Seschrock pp = ses_snap_find_page(sp, SES2_DIAGPAGE_CONFIG, B_FALSE); 141*6316Seschrock if (pp == NULL) 142*6316Seschrock return (ses_error(ESES_BAD_RESPONSE, "target does not support " 143*6316Seschrock "configuration diagnostic page")); 144*6316Seschrock pip = (ses2_config_page_impl_t *)pp->ssp_page; 145*6316Seschrock 146*6316Seschrock if (pp->ssp_len < offsetof(ses2_config_page_impl_t, scpi_data)) 147*6316Seschrock return (ses_error(ESES_BAD_RESPONSE, "no enclosure " 148*6316Seschrock "descriptors found")); 149*6316Seschrock 150*6316Seschrock /* 151*6316Seschrock * Start with the root of the tree, which is a target node, containing 152*6316Seschrock * just the SCSI inquiry properties. 153*6316Seschrock */ 154*6316Seschrock if ((root = ses_node_alloc(sp, sp->ss_root)) == NULL) 155*6316Seschrock return (-1); 156*6316Seschrock 157*6316Seschrock root->sn_type = SES_NODE_TARGET; 158*6316Seschrock SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_VENDOR, 159*6316Seschrock libscsi_vendor(sp->ss_target->st_target)); 160*6316Seschrock SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_PRODUCT, 161*6316Seschrock libscsi_product(sp->ss_target->st_target)); 162*6316Seschrock SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_REVISION, 163*6316Seschrock libscsi_revision(sp->ss_target->st_target)); 164*6316Seschrock 165*6316Seschrock for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0; 166*6316Seschrock i < pip->scpi_n_subenclosures + 1; 167*6316Seschrock i++, eip = NEXT_ED(eip)) { 168*6316Seschrock if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len)) 169*6316Seschrock break; 170*6316Seschrock 171*6316Seschrock n_etds += eip->st_hdr.sehi_n_etd_hdrs; 172*6316Seschrock } 173*6316Seschrock ftip = (ses2_td_hdr_impl_t *)eip; 174*6316Seschrock 175*6316Seschrock /* 176*6316Seschrock * There should really be only one Enclosure element possible for a 177*6316Seschrock * give subenclosure ID. The standard never comes out and says this, 178*6316Seschrock * but it does describe this element as "managing the enclosure itself" 179*6316Seschrock * which implies rather strongly that the subenclosure ID field is that 180*6316Seschrock * of, well, the enclosure itself. Since an enclosure can't contain 181*6316Seschrock * itself, it follows logically that each subenclosure has at most one 182*6316Seschrock * Enclosure type descriptor elements matching its ID. Of course, some 183*6316Seschrock * enclosure firmware is buggy, so this may not always work out; in 184*6316Seschrock * this case we just ignore all but the first Enclosure-type element 185*6316Seschrock * with our subenclosure ID. 186*6316Seschrock */ 187*6316Seschrock for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0; 188*6316Seschrock i < pip->scpi_n_subenclosures + 1; 189*6316Seschrock i++, eip = NEXT_ED(eip)) { 190*6316Seschrock if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len)) 191*6316Seschrock break; 192*6316Seschrock 193*6316Seschrock if ((np = ses_node_alloc(sp, root)) == NULL) 194*6316Seschrock return (-1); 195*6316Seschrock 196*6316Seschrock np->sn_type = SES_NODE_ENCLOSURE; 197*6316Seschrock np->sn_enc_num = eip->st_hdr.sehi_subenclosure_id; 198*6316Seschrock 199*6316Seschrock if (!SES_WITHIN_PAGE(eip, eip->st_hdr.sehi_ed_len + 200*6316Seschrock sizeof (ses2_ed_hdr_impl_t), 201*6316Seschrock pp->ssp_page, pp->ssp_len)) 202*6316Seschrock break; 203*6316Seschrock 204*6316Seschrock if (enc_parse_ed(eip, np->sn_props) != 0) 205*6316Seschrock return (-1); 206*6316Seschrock } 207*6316Seschrock 208*6316Seschrock if (root->sn_first_child == NULL) 209*6316Seschrock return (ses_error(ESES_BAD_RESPONSE, "no enclosure " 210*6316Seschrock "descriptors found")); 211*6316Seschrock 212*6316Seschrock tp = (char *)(ftip + n_etds); 213*6316Seschrock 214*6316Seschrock for (i = 0, toff = 0, idx = 0; i < n_etds; i++) { 215*6316Seschrock tip = ftip + i; 216*6316Seschrock 217*6316Seschrock if (!SES_WITHIN_PAGE_STRUCT(tip, pp->ssp_page, pp->ssp_len)) 218*6316Seschrock break; 219*6316Seschrock 220*6316Seschrock pnp = ses_find_enclosure(sp, 221*6316Seschrock tip->sthi_subenclosure_id); 222*6316Seschrock if (pnp == NULL) { 223*6316Seschrock idx += tip->sthi_max_elements + 1; 224*6316Seschrock toff += tip->sthi_text_len; 225*6316Seschrock continue; 226*6316Seschrock } 227*6316Seschrock 228*6316Seschrock if (tip->sthi_element_type == SES_ET_ENCLOSURE) { 229*6316Seschrock if (tip->sthi_max_elements == 0) { 230*6316Seschrock SES_NV_ADD(uint64, err, pnp->sn_props, 231*6316Seschrock SES_PROP_ELEMENT_INDEX, idx); 232*6316Seschrock pnp->sn_rootidx = idx; 233*6316Seschrock } else { 234*6316Seschrock SES_NV_ADD(uint64, err, pnp->sn_props, 235*6316Seschrock SES_PROP_ELEMENT_INDEX, idx + 1); 236*6316Seschrock pnp->sn_rootidx = idx + 1; 237*6316Seschrock } 238*6316Seschrock 239*6316Seschrock if (tip->sthi_text_len > 0 && 240*6316Seschrock SES_WITHIN_PAGE(tp + toff, tip->sthi_text_len, 241*6316Seschrock pp->ssp_page, pp->ssp_len)) { 242*6316Seschrock text = tp + toff; 243*6316Seschrock toff += tip->sthi_text_len; 244*6316Seschrock } else { 245*6316Seschrock text = NULL; 246*6316Seschrock } 247*6316Seschrock 248*6316Seschrock SES_NV_ADD(uint64, err, pnp->sn_props, 249*6316Seschrock SES_PROP_ELEMENT_TYPE, SES_ET_ENCLOSURE); 250*6316Seschrock if (enc_parse_td(tip, text, pnp->sn_props) != 0) 251*6316Seschrock return (-1); 252*6316Seschrock 253*6316Seschrock idx += tip->sthi_max_elements + 1; 254*6316Seschrock continue; 255*6316Seschrock } 256*6316Seschrock 257*6316Seschrock if ((np = ses_node_alloc(sp, pnp)) == NULL) 258*6316Seschrock return (-1); 259*6316Seschrock 260*6316Seschrock np->sn_type = SES_NODE_AGGREGATE; 261*6316Seschrock np->sn_enc_num = tip->sthi_subenclosure_id; 262*6316Seschrock np->sn_parent = pnp; 263*6316Seschrock np->sn_rootidx = idx; 264*6316Seschrock 265*6316Seschrock SES_NV_ADD(uint64, err, np->sn_props, 266*6316Seschrock SES_PROP_ELEMENT_INDEX, idx); 267*6316Seschrock SES_NV_ADD(uint64, err, np->sn_props, 268*6316Seschrock SES_PROP_ELEMENT_TYPE, tip->sthi_element_type); 269*6316Seschrock 270*6316Seschrock if (tip->sthi_text_len > 0 && 271*6316Seschrock SES_WITHIN_PAGE(tp + toff, tip->sthi_text_len, 272*6316Seschrock pp->ssp_page, pp->ssp_len)) { 273*6316Seschrock text = tp + toff; 274*6316Seschrock toff += tip->sthi_text_len; 275*6316Seschrock } else { 276*6316Seschrock text = NULL; 277*6316Seschrock } 278*6316Seschrock 279*6316Seschrock if (elem_parse_td(tip, text, np->sn_props) != 0) 280*6316Seschrock return (-1); 281*6316Seschrock 282*6316Seschrock idx += tip->sthi_max_elements + 1; 283*6316Seschrock 284*6316Seschrock if (tip->sthi_max_elements == 0) 285*6316Seschrock continue; 286*6316Seschrock 287*6316Seschrock for (j = 0; j < tip->sthi_max_elements; j++) { 288*6316Seschrock cnp = ses_node_alloc(sp, np); 289*6316Seschrock if (cnp == NULL) 290*6316Seschrock return (-1); 291*6316Seschrock 292*6316Seschrock cnp->sn_type = SES_NODE_ELEMENT; 293*6316Seschrock SES_NV_ADD(uint64, err, cnp->sn_props, 294*6316Seschrock SES_PROP_ELEMENT_INDEX, np->sn_rootidx + j + 1); 295*6316Seschrock SES_NV_ADD(uint64, err, cnp->sn_props, 296*6316Seschrock SES_PROP_ELEMENT_CLASS_INDEX, j); 297*6316Seschrock SES_NV_ADD(uint64, err, cnp->sn_props, 298*6316Seschrock SES_PROP_ELEMENT_TYPE, tip->sthi_element_type); 299*6316Seschrock } 300*6316Seschrock } 301*6316Seschrock 302*6316Seschrock np->sn_snapshot->ss_n_elem = idx; 303*6316Seschrock 304*6316Seschrock return (0); 305*6316Seschrock } 306*6316Seschrock 307*6316Seschrock static int 308*6316Seschrock ses_fill_tree(ses_node_t *np) 309*6316Seschrock { 310*6316Seschrock if (np == NULL) 311*6316Seschrock return (0); 312*6316Seschrock 313*6316Seschrock for (; np != NULL; np = np->sn_next_sibling) { 314*6316Seschrock if (ses_fill_node(np) != 0) 315*6316Seschrock return (-1); 316*6316Seschrock if (ses_fill_tree(np->sn_first_child) != 0) 317*6316Seschrock return (-1); 318*6316Seschrock } 319*6316Seschrock 320*6316Seschrock return (0); 321*6316Seschrock } 322*6316Seschrock 323*6316Seschrock int 324*6316Seschrock ses_fill_snap(ses_snap_t *sp) 325*6316Seschrock { 326*6316Seschrock if (ses_build_snap_skel(sp) != 0) 327*6316Seschrock return (-1); 328*6316Seschrock 329*6316Seschrock if (ses_fill_tree(sp->ss_root) != 0) 330*6316Seschrock return (-1); 331*6316Seschrock 332*6316Seschrock return (0); 333*6316Seschrock } 334*6316Seschrock 335*6316Seschrock ses_node_t * 336*6316Seschrock ses_root_node(ses_snap_t *sp) 337*6316Seschrock { 338*6316Seschrock return (sp->ss_root); 339*6316Seschrock } 340*6316Seschrock 341*6316Seschrock ses_node_t * 342*6316Seschrock ses_node_sibling(ses_node_t *np) 343*6316Seschrock { 344*6316Seschrock return (np->sn_next_sibling); 345*6316Seschrock } 346*6316Seschrock 347*6316Seschrock ses_node_t * 348*6316Seschrock ses_node_prev_sibling(ses_node_t *np) 349*6316Seschrock { 350*6316Seschrock return (np->sn_prev_sibling); 351*6316Seschrock } 352*6316Seschrock 353*6316Seschrock ses_node_t * 354*6316Seschrock ses_node_parent(ses_node_t *np) 355*6316Seschrock { 356*6316Seschrock return (np->sn_parent); 357*6316Seschrock } 358*6316Seschrock 359*6316Seschrock ses_node_t * 360*6316Seschrock ses_node_child(ses_node_t *np) 361*6316Seschrock { 362*6316Seschrock return (np->sn_first_child); 363*6316Seschrock } 364*6316Seschrock 365*6316Seschrock ses_node_type_t 366*6316Seschrock ses_node_type(ses_node_t *np) 367*6316Seschrock { 368*6316Seschrock return (np->sn_type); 369*6316Seschrock } 370*6316Seschrock 371*6316Seschrock ses_snap_t * 372*6316Seschrock ses_node_snapshot(ses_node_t *np) 373*6316Seschrock { 374*6316Seschrock return ((ses_snap_t *)np->sn_snapshot); 375*6316Seschrock } 376*6316Seschrock 377*6316Seschrock ses_target_t * 378*6316Seschrock ses_node_target(ses_node_t *np) 379*6316Seschrock { 380*6316Seschrock return (np->sn_snapshot->ss_target); 381*6316Seschrock } 382*6316Seschrock 383*6316Seschrock nvlist_t * 384*6316Seschrock ses_node_props(ses_node_t *np) 385*6316Seschrock { 386*6316Seschrock return (np->sn_props); 387*6316Seschrock } 388*6316Seschrock 389*6316Seschrock /* 390*6316Seschrock * A node identifier is a (generation, index) tuple that can be used to lookup a 391*6316Seschrock * node within this target at a later point. This will be valid across 392*6316Seschrock * snapshots, though it will return failure if the generation count has changed. 393*6316Seschrock */ 394*6316Seschrock uint64_t 395*6316Seschrock ses_node_id(ses_node_t *np) 396*6316Seschrock { 397*6316Seschrock return (((uint64_t)np->sn_snapshot->ss_generation << 32) | 398*6316Seschrock np->sn_id); 399*6316Seschrock } 400