1275c9da8Seschrock /* 2275c9da8Seschrock * CDDL HEADER START 3275c9da8Seschrock * 4275c9da8Seschrock * The contents of this file are subject to the terms of the 5275c9da8Seschrock * Common Development and Distribution License (the "License"). 6275c9da8Seschrock * You may not use this file except in compliance with the License. 7275c9da8Seschrock * 8275c9da8Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9275c9da8Seschrock * or http://www.opensolaris.org/os/licensing. 10275c9da8Seschrock * See the License for the specific language governing permissions 11275c9da8Seschrock * and limitations under the License. 12275c9da8Seschrock * 13275c9da8Seschrock * When distributing Covered Code, include this CDDL HEADER in each 14275c9da8Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15275c9da8Seschrock * If applicable, add the following below this CDDL HEADER, with the 16275c9da8Seschrock * fields enclosed by brackets "[]" replaced with your own identifying 17275c9da8Seschrock * information: Portions Copyright [yyyy] [name of copyright owner] 18275c9da8Seschrock * 19275c9da8Seschrock * CDDL HEADER END 20275c9da8Seschrock */ 21275c9da8Seschrock 22275c9da8Seschrock /* 23*ac88567aSHyon Kim * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24275c9da8Seschrock */ 25275c9da8Seschrock 26275c9da8Seschrock #include <scsi/libses.h> 27275c9da8Seschrock #include "ses_impl.h" 28275c9da8Seschrock 29275c9da8Seschrock #define NEXT_ED(eip) \ 30275c9da8Seschrock ((ses2_ed_impl_t *)((uint8_t *)(eip) + \ 31275c9da8Seschrock ((eip)->st_hdr.sehi_ed_len + sizeof (ses2_ed_hdr_impl_t)))) 32275c9da8Seschrock 33275c9da8Seschrock static ses_node_t * 34275c9da8Seschrock ses_find_enclosure(ses_snap_t *sp, uint64_t number) 35275c9da8Seschrock { 36275c9da8Seschrock ses_node_t *np; 37275c9da8Seschrock 38275c9da8Seschrock for (np = sp->ss_root->sn_first_child; np != NULL; 39275c9da8Seschrock np = np->sn_next_sibling) { 40275c9da8Seschrock ASSERT(np->sn_type == SES_NODE_ENCLOSURE); 41275c9da8Seschrock if (np->sn_enc_num == number) 42275c9da8Seschrock return ((ses_node_t *)np); 43275c9da8Seschrock } 44275c9da8Seschrock 45275c9da8Seschrock return (NULL); 46275c9da8Seschrock } 47275c9da8Seschrock 48c4800545Sjmcp /* 49c4800545Sjmcp * ses_snap_primary_enclosure() finds the primary enclosure for 50c4800545Sjmcp * the supplied ses_snap_t. 51c4800545Sjmcp */ 52c4800545Sjmcp ses_node_t * 53c4800545Sjmcp ses_snap_primary_enclosure(ses_snap_t *sp) 54c4800545Sjmcp { 55c4800545Sjmcp return (ses_find_enclosure(sp, 0)); 56c4800545Sjmcp } 57c4800545Sjmcp 58275c9da8Seschrock void 59275c9da8Seschrock ses_node_teardown(ses_node_t *np) 60275c9da8Seschrock { 61275c9da8Seschrock ses_node_t *rp; 62275c9da8Seschrock 63275c9da8Seschrock if (np == NULL) 64275c9da8Seschrock return; 65275c9da8Seschrock 66275c9da8Seschrock for (; np != NULL; np = rp) { 67275c9da8Seschrock ses_node_teardown(np->sn_first_child); 68275c9da8Seschrock rp = np->sn_next_sibling; 69275c9da8Seschrock nvlist_free(np->sn_props); 70275c9da8Seschrock ses_free(np); 71275c9da8Seschrock } 72275c9da8Seschrock } 73275c9da8Seschrock 74275c9da8Seschrock static ses_node_t * 75275c9da8Seschrock ses_node_alloc(ses_snap_t *sp, ses_node_t *pnp) 76275c9da8Seschrock { 77275c9da8Seschrock ses_node_t *np; 78275c9da8Seschrock 79275c9da8Seschrock np = ses_zalloc(sizeof (ses_node_t)); 80275c9da8Seschrock if (np == NULL) 81275c9da8Seschrock goto fail; 82275c9da8Seschrock if (nvlist_alloc(&np->sn_props, NV_UNIQUE_NAME, 0) != 0) 83275c9da8Seschrock goto fail; 84275c9da8Seschrock 85275c9da8Seschrock np->sn_snapshot = sp; 86275c9da8Seschrock np->sn_id = sp->ss_n_nodes++; 87275c9da8Seschrock 88275c9da8Seschrock if (pnp == NULL) { 89275c9da8Seschrock ASSERT(sp->ss_root == NULL); 90275c9da8Seschrock sp->ss_root = np; 91275c9da8Seschrock } else { 92275c9da8Seschrock np->sn_parent = pnp; 93275c9da8Seschrock np->sn_prev_sibling = pnp->sn_last_child; 94275c9da8Seschrock 95275c9da8Seschrock if (pnp->sn_first_child == NULL) 96275c9da8Seschrock pnp->sn_first_child = np; 97275c9da8Seschrock else 98275c9da8Seschrock pnp->sn_last_child->sn_next_sibling = np; 99275c9da8Seschrock 100275c9da8Seschrock pnp->sn_last_child = np; 101275c9da8Seschrock } 102275c9da8Seschrock 103275c9da8Seschrock return (np); 104275c9da8Seschrock 105275c9da8Seschrock fail: 106275c9da8Seschrock ses_free(np); 107275c9da8Seschrock ses_node_teardown(sp->ss_root); 108275c9da8Seschrock sp->ss_root = NULL; 109275c9da8Seschrock return (NULL); 110275c9da8Seschrock } 111275c9da8Seschrock 112275c9da8Seschrock /* 113275c9da8Seschrock * Parse element type descriptor. 114275c9da8Seschrock */ 115275c9da8Seschrock static int 116275c9da8Seschrock elem_parse_td(ses2_td_hdr_impl_t *tip, const char *tp, nvlist_t *nvl) 117275c9da8Seschrock { 118275c9da8Seschrock int nverr; 119275c9da8Seschrock 120275c9da8Seschrock if (tp != NULL) 121275c9da8Seschrock SES_NV_ADD(fixed_string, nverr, nvl, SES_PROP_CLASS_DESCRIPTION, 122275c9da8Seschrock tp, tip->sthi_text_len); 123275c9da8Seschrock 124275c9da8Seschrock return (0); 125275c9da8Seschrock } 126275c9da8Seschrock 127275c9da8Seschrock 128275c9da8Seschrock /* 129275c9da8Seschrock * Build a skeleton tree of nodes in the given snapshot. This is the heart of 130275c9da8Seschrock * libses, and is responsible for parsing the config page into a tree and 131275c9da8Seschrock * populating nodes with data from the config page. 132275c9da8Seschrock */ 133275c9da8Seschrock static int 134275c9da8Seschrock ses_build_snap_skel(ses_snap_t *sp) 135275c9da8Seschrock { 136275c9da8Seschrock ses2_ed_impl_t *eip; 137275c9da8Seschrock ses2_td_hdr_impl_t *tip, *ftip; 138275c9da8Seschrock ses_node_t *np, *pnp, *cnp, *root; 139275c9da8Seschrock ses_snap_page_t *pp; 140275c9da8Seschrock ses2_config_page_impl_t *pip; 141275c9da8Seschrock int i, j, n_etds = 0; 142275c9da8Seschrock off_t toff; 143275c9da8Seschrock char *tp, *text; 144275c9da8Seschrock int err; 145*ac88567aSHyon Kim uint64_t idx, eidx; 146275c9da8Seschrock 147275c9da8Seschrock pp = ses_snap_find_page(sp, SES2_DIAGPAGE_CONFIG, B_FALSE); 148275c9da8Seschrock if (pp == NULL) 149275c9da8Seschrock return (ses_error(ESES_BAD_RESPONSE, "target does not support " 150275c9da8Seschrock "configuration diagnostic page")); 151275c9da8Seschrock pip = (ses2_config_page_impl_t *)pp->ssp_page; 152275c9da8Seschrock 153275c9da8Seschrock if (pp->ssp_len < offsetof(ses2_config_page_impl_t, scpi_data)) 154275c9da8Seschrock return (ses_error(ESES_BAD_RESPONSE, "no enclosure " 155275c9da8Seschrock "descriptors found")); 156275c9da8Seschrock 157275c9da8Seschrock /* 158275c9da8Seschrock * Start with the root of the tree, which is a target node, containing 159275c9da8Seschrock * just the SCSI inquiry properties. 160275c9da8Seschrock */ 161275c9da8Seschrock if ((root = ses_node_alloc(sp, sp->ss_root)) == NULL) 162275c9da8Seschrock return (-1); 163275c9da8Seschrock 164275c9da8Seschrock root->sn_type = SES_NODE_TARGET; 165275c9da8Seschrock SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_VENDOR, 166275c9da8Seschrock libscsi_vendor(sp->ss_target->st_target)); 167275c9da8Seschrock SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_PRODUCT, 168275c9da8Seschrock libscsi_product(sp->ss_target->st_target)); 169275c9da8Seschrock SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_REVISION, 170275c9da8Seschrock libscsi_revision(sp->ss_target->st_target)); 171275c9da8Seschrock 172275c9da8Seschrock for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0; 173275c9da8Seschrock i < pip->scpi_n_subenclosures + 1; 174275c9da8Seschrock i++, eip = NEXT_ED(eip)) { 175275c9da8Seschrock if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len)) 176275c9da8Seschrock break; 177275c9da8Seschrock 178275c9da8Seschrock n_etds += eip->st_hdr.sehi_n_etd_hdrs; 179275c9da8Seschrock } 180275c9da8Seschrock ftip = (ses2_td_hdr_impl_t *)eip; 181275c9da8Seschrock 182275c9da8Seschrock /* 183275c9da8Seschrock * There should really be only one Enclosure element possible for a 184275c9da8Seschrock * give subenclosure ID. The standard never comes out and says this, 185275c9da8Seschrock * but it does describe this element as "managing the enclosure itself" 186275c9da8Seschrock * which implies rather strongly that the subenclosure ID field is that 187275c9da8Seschrock * of, well, the enclosure itself. Since an enclosure can't contain 188275c9da8Seschrock * itself, it follows logically that each subenclosure has at most one 189275c9da8Seschrock * Enclosure type descriptor elements matching its ID. Of course, some 190275c9da8Seschrock * enclosure firmware is buggy, so this may not always work out; in 191275c9da8Seschrock * this case we just ignore all but the first Enclosure-type element 192275c9da8Seschrock * with our subenclosure ID. 193275c9da8Seschrock */ 194275c9da8Seschrock for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0; 195275c9da8Seschrock i < pip->scpi_n_subenclosures + 1; 196275c9da8Seschrock i++, eip = NEXT_ED(eip)) { 197275c9da8Seschrock if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len)) 198275c9da8Seschrock break; 199275c9da8Seschrock 200275c9da8Seschrock if ((np = ses_node_alloc(sp, root)) == NULL) 201275c9da8Seschrock return (-1); 202275c9da8Seschrock 203275c9da8Seschrock np->sn_type = SES_NODE_ENCLOSURE; 204275c9da8Seschrock np->sn_enc_num = eip->st_hdr.sehi_subenclosure_id; 205275c9da8Seschrock 206275c9da8Seschrock if (!SES_WITHIN_PAGE(eip, eip->st_hdr.sehi_ed_len + 207275c9da8Seschrock sizeof (ses2_ed_hdr_impl_t), 208275c9da8Seschrock pp->ssp_page, pp->ssp_len)) 209275c9da8Seschrock break; 210275c9da8Seschrock 211275c9da8Seschrock if (enc_parse_ed(eip, np->sn_props) != 0) 212275c9da8Seschrock return (-1); 213275c9da8Seschrock } 214275c9da8Seschrock 215275c9da8Seschrock if (root->sn_first_child == NULL) 216275c9da8Seschrock return (ses_error(ESES_BAD_RESPONSE, "no enclosure " 217275c9da8Seschrock "descriptors found")); 218275c9da8Seschrock 219275c9da8Seschrock tp = (char *)(ftip + n_etds); 220275c9da8Seschrock 221*ac88567aSHyon Kim for (i = 0, toff = 0, idx = eidx = 0; i < n_etds; i++) { 222275c9da8Seschrock tip = ftip + i; 223275c9da8Seschrock 224275c9da8Seschrock if (!SES_WITHIN_PAGE_STRUCT(tip, pp->ssp_page, pp->ssp_len)) 225275c9da8Seschrock break; 226275c9da8Seschrock 227275c9da8Seschrock pnp = ses_find_enclosure(sp, 228275c9da8Seschrock tip->sthi_subenclosure_id); 229275c9da8Seschrock if (pnp == NULL) { 230275c9da8Seschrock idx += tip->sthi_max_elements + 1; 231*ac88567aSHyon Kim eidx += tip->sthi_max_elements; 232275c9da8Seschrock toff += tip->sthi_text_len; 233275c9da8Seschrock continue; 234275c9da8Seschrock } 235275c9da8Seschrock 236275c9da8Seschrock if (tip->sthi_element_type == SES_ET_ENCLOSURE) { 237275c9da8Seschrock if (tip->sthi_max_elements == 0) { 238275c9da8Seschrock SES_NV_ADD(uint64, err, pnp->sn_props, 239275c9da8Seschrock SES_PROP_ELEMENT_INDEX, idx); 240275c9da8Seschrock pnp->sn_rootidx = idx; 241275c9da8Seschrock } else { 242275c9da8Seschrock SES_NV_ADD(uint64, err, pnp->sn_props, 243275c9da8Seschrock SES_PROP_ELEMENT_INDEX, idx + 1); 244*ac88567aSHyon Kim SES_NV_ADD(uint64, err, pnp->sn_props, 245*ac88567aSHyon Kim SES_PROP_ELEMENT_ONLY_INDEX, eidx); 246275c9da8Seschrock pnp->sn_rootidx = idx + 1; 247275c9da8Seschrock } 248275c9da8Seschrock 249275c9da8Seschrock if (tip->sthi_text_len > 0 && 250275c9da8Seschrock SES_WITHIN_PAGE(tp + toff, tip->sthi_text_len, 251275c9da8Seschrock pp->ssp_page, pp->ssp_len)) { 252275c9da8Seschrock text = tp + toff; 253275c9da8Seschrock toff += tip->sthi_text_len; 254275c9da8Seschrock } else { 255275c9da8Seschrock text = NULL; 256275c9da8Seschrock } 257275c9da8Seschrock 258275c9da8Seschrock SES_NV_ADD(uint64, err, pnp->sn_props, 259275c9da8Seschrock SES_PROP_ELEMENT_TYPE, SES_ET_ENCLOSURE); 260275c9da8Seschrock if (enc_parse_td(tip, text, pnp->sn_props) != 0) 261275c9da8Seschrock return (-1); 262275c9da8Seschrock 263275c9da8Seschrock idx += tip->sthi_max_elements + 1; 264*ac88567aSHyon Kim eidx += tip->sthi_max_elements; 265275c9da8Seschrock continue; 266275c9da8Seschrock } 267275c9da8Seschrock 268275c9da8Seschrock if ((np = ses_node_alloc(sp, pnp)) == NULL) 269275c9da8Seschrock return (-1); 270275c9da8Seschrock 271275c9da8Seschrock np->sn_type = SES_NODE_AGGREGATE; 272275c9da8Seschrock np->sn_enc_num = tip->sthi_subenclosure_id; 273275c9da8Seschrock np->sn_parent = pnp; 274275c9da8Seschrock np->sn_rootidx = idx; 275275c9da8Seschrock 276275c9da8Seschrock SES_NV_ADD(uint64, err, np->sn_props, 277275c9da8Seschrock SES_PROP_ELEMENT_INDEX, idx); 278275c9da8Seschrock SES_NV_ADD(uint64, err, np->sn_props, 279275c9da8Seschrock SES_PROP_ELEMENT_TYPE, tip->sthi_element_type); 280275c9da8Seschrock 281275c9da8Seschrock if (tip->sthi_text_len > 0 && 282275c9da8Seschrock SES_WITHIN_PAGE(tp + toff, tip->sthi_text_len, 283275c9da8Seschrock pp->ssp_page, pp->ssp_len)) { 284275c9da8Seschrock text = tp + toff; 285275c9da8Seschrock toff += tip->sthi_text_len; 286275c9da8Seschrock } else { 287275c9da8Seschrock text = NULL; 288275c9da8Seschrock } 289275c9da8Seschrock 290275c9da8Seschrock if (elem_parse_td(tip, text, np->sn_props) != 0) 291275c9da8Seschrock return (-1); 292275c9da8Seschrock 293275c9da8Seschrock idx += tip->sthi_max_elements + 1; 294275c9da8Seschrock 295275c9da8Seschrock if (tip->sthi_max_elements == 0) 296275c9da8Seschrock continue; 297275c9da8Seschrock 298275c9da8Seschrock for (j = 0; j < tip->sthi_max_elements; j++) { 299275c9da8Seschrock cnp = ses_node_alloc(sp, np); 300275c9da8Seschrock if (cnp == NULL) 301275c9da8Seschrock return (-1); 302275c9da8Seschrock 303275c9da8Seschrock cnp->sn_type = SES_NODE_ELEMENT; 304275c9da8Seschrock SES_NV_ADD(uint64, err, cnp->sn_props, 305275c9da8Seschrock SES_PROP_ELEMENT_INDEX, np->sn_rootidx + j + 1); 306275c9da8Seschrock SES_NV_ADD(uint64, err, cnp->sn_props, 307*ac88567aSHyon Kim SES_PROP_ELEMENT_ONLY_INDEX, eidx + j); 308*ac88567aSHyon Kim SES_NV_ADD(uint64, err, cnp->sn_props, 309275c9da8Seschrock SES_PROP_ELEMENT_CLASS_INDEX, j); 310275c9da8Seschrock SES_NV_ADD(uint64, err, cnp->sn_props, 311275c9da8Seschrock SES_PROP_ELEMENT_TYPE, tip->sthi_element_type); 312275c9da8Seschrock } 313*ac88567aSHyon Kim 314*ac88567aSHyon Kim eidx += tip->sthi_max_elements; 315275c9da8Seschrock } 316275c9da8Seschrock 317275c9da8Seschrock np->sn_snapshot->ss_n_elem = idx; 318275c9da8Seschrock 319275c9da8Seschrock return (0); 320275c9da8Seschrock } 321275c9da8Seschrock 322275c9da8Seschrock static int 323275c9da8Seschrock ses_fill_tree(ses_node_t *np) 324275c9da8Seschrock { 325275c9da8Seschrock if (np == NULL) 326275c9da8Seschrock return (0); 327275c9da8Seschrock 328275c9da8Seschrock for (; np != NULL; np = np->sn_next_sibling) { 329275c9da8Seschrock if (ses_fill_node(np) != 0) 330275c9da8Seschrock return (-1); 331275c9da8Seschrock if (ses_fill_tree(np->sn_first_child) != 0) 332275c9da8Seschrock return (-1); 333275c9da8Seschrock } 334275c9da8Seschrock 335275c9da8Seschrock return (0); 336275c9da8Seschrock } 337275c9da8Seschrock 338275c9da8Seschrock int 339275c9da8Seschrock ses_fill_snap(ses_snap_t *sp) 340275c9da8Seschrock { 341275c9da8Seschrock if (ses_build_snap_skel(sp) != 0) 342275c9da8Seschrock return (-1); 343275c9da8Seschrock 344275c9da8Seschrock if (ses_fill_tree(sp->ss_root) != 0) 345275c9da8Seschrock return (-1); 346275c9da8Seschrock 347275c9da8Seschrock return (0); 348275c9da8Seschrock } 349275c9da8Seschrock 350275c9da8Seschrock ses_node_t * 351275c9da8Seschrock ses_root_node(ses_snap_t *sp) 352275c9da8Seschrock { 353275c9da8Seschrock return (sp->ss_root); 354275c9da8Seschrock } 355275c9da8Seschrock 356275c9da8Seschrock ses_node_t * 357275c9da8Seschrock ses_node_sibling(ses_node_t *np) 358275c9da8Seschrock { 359275c9da8Seschrock return (np->sn_next_sibling); 360275c9da8Seschrock } 361275c9da8Seschrock 362275c9da8Seschrock ses_node_t * 363275c9da8Seschrock ses_node_prev_sibling(ses_node_t *np) 364275c9da8Seschrock { 365275c9da8Seschrock return (np->sn_prev_sibling); 366275c9da8Seschrock } 367275c9da8Seschrock 368275c9da8Seschrock ses_node_t * 369275c9da8Seschrock ses_node_parent(ses_node_t *np) 370275c9da8Seschrock { 371275c9da8Seschrock return (np->sn_parent); 372275c9da8Seschrock } 373275c9da8Seschrock 374275c9da8Seschrock ses_node_t * 375275c9da8Seschrock ses_node_child(ses_node_t *np) 376275c9da8Seschrock { 377275c9da8Seschrock return (np->sn_first_child); 378275c9da8Seschrock } 379275c9da8Seschrock 380275c9da8Seschrock ses_node_type_t 381275c9da8Seschrock ses_node_type(ses_node_t *np) 382275c9da8Seschrock { 383275c9da8Seschrock return (np->sn_type); 384275c9da8Seschrock } 385275c9da8Seschrock 386275c9da8Seschrock ses_snap_t * 387275c9da8Seschrock ses_node_snapshot(ses_node_t *np) 388275c9da8Seschrock { 389275c9da8Seschrock return ((ses_snap_t *)np->sn_snapshot); 390275c9da8Seschrock } 391275c9da8Seschrock 392275c9da8Seschrock ses_target_t * 393275c9da8Seschrock ses_node_target(ses_node_t *np) 394275c9da8Seschrock { 395275c9da8Seschrock return (np->sn_snapshot->ss_target); 396275c9da8Seschrock } 397275c9da8Seschrock 398275c9da8Seschrock nvlist_t * 399275c9da8Seschrock ses_node_props(ses_node_t *np) 400275c9da8Seschrock { 401275c9da8Seschrock return (np->sn_props); 402275c9da8Seschrock } 403275c9da8Seschrock 404275c9da8Seschrock /* 405275c9da8Seschrock * A node identifier is a (generation, index) tuple that can be used to lookup a 406275c9da8Seschrock * node within this target at a later point. This will be valid across 407275c9da8Seschrock * snapshots, though it will return failure if the generation count has changed. 408275c9da8Seschrock */ 409275c9da8Seschrock uint64_t 410275c9da8Seschrock ses_node_id(ses_node_t *np) 411275c9da8Seschrock { 412275c9da8Seschrock return (((uint64_t)np->sn_snapshot->ss_generation << 32) | 413275c9da8Seschrock np->sn_id); 414275c9da8Seschrock } 415