16316Seschrock /* 26316Seschrock * CDDL HEADER START 36316Seschrock * 46316Seschrock * The contents of this file are subject to the terms of the 56316Seschrock * Common Development and Distribution License (the "License"). 66316Seschrock * You may not use this file except in compliance with the License. 76316Seschrock * 86316Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 96316Seschrock * or http://www.opensolaris.org/os/licensing. 106316Seschrock * See the License for the specific language governing permissions 116316Seschrock * and limitations under the License. 126316Seschrock * 136316Seschrock * When distributing Covered Code, include this CDDL HEADER in each 146316Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 156316Seschrock * If applicable, add the following below this CDDL HEADER, with the 166316Seschrock * fields enclosed by brackets "[]" replaced with your own identifying 176316Seschrock * information: Portions Copyright [yyyy] [name of copyright owner] 186316Seschrock * 196316Seschrock * CDDL HEADER END 206316Seschrock */ 216316Seschrock 226316Seschrock /* 236316Seschrock * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 246316Seschrock * Use is subject to license terms. 256316Seschrock */ 266316Seschrock 276316Seschrock #pragma ident "%Z%%M% %I% %E% SMI" 286316Seschrock 296316Seschrock #include <scsi/libses.h> 306316Seschrock #include "ses_impl.h" 316316Seschrock 326316Seschrock #define NEXT_ED(eip) \ 336316Seschrock ((ses2_ed_impl_t *)((uint8_t *)(eip) + \ 346316Seschrock ((eip)->st_hdr.sehi_ed_len + sizeof (ses2_ed_hdr_impl_t)))) 356316Seschrock 366316Seschrock static ses_node_t * 376316Seschrock ses_find_enclosure(ses_snap_t *sp, uint64_t number) 386316Seschrock { 396316Seschrock ses_node_t *np; 406316Seschrock 416316Seschrock for (np = sp->ss_root->sn_first_child; np != NULL; 426316Seschrock np = np->sn_next_sibling) { 436316Seschrock ASSERT(np->sn_type == SES_NODE_ENCLOSURE); 446316Seschrock if (np->sn_enc_num == number) 456316Seschrock return ((ses_node_t *)np); 466316Seschrock } 476316Seschrock 486316Seschrock return (NULL); 496316Seschrock } 506316Seschrock 51*6846Sjmcp /* 52*6846Sjmcp * ses_snap_primary_enclosure() finds the primary enclosure for 53*6846Sjmcp * the supplied ses_snap_t. 54*6846Sjmcp */ 55*6846Sjmcp ses_node_t * 56*6846Sjmcp ses_snap_primary_enclosure(ses_snap_t *sp) 57*6846Sjmcp { 58*6846Sjmcp return (ses_find_enclosure(sp, 0)); 59*6846Sjmcp } 60*6846Sjmcp 616316Seschrock void 626316Seschrock ses_node_teardown(ses_node_t *np) 636316Seschrock { 646316Seschrock ses_node_t *rp; 656316Seschrock 666316Seschrock if (np == NULL) 676316Seschrock return; 686316Seschrock 696316Seschrock for (; np != NULL; np = rp) { 706316Seschrock ses_node_teardown(np->sn_first_child); 716316Seschrock rp = np->sn_next_sibling; 726316Seschrock nvlist_free(np->sn_props); 736316Seschrock ses_free(np); 746316Seschrock } 756316Seschrock } 766316Seschrock 776316Seschrock static ses_node_t * 786316Seschrock ses_node_alloc(ses_snap_t *sp, ses_node_t *pnp) 796316Seschrock { 806316Seschrock ses_node_t *np; 816316Seschrock 826316Seschrock np = ses_zalloc(sizeof (ses_node_t)); 836316Seschrock if (np == NULL) 846316Seschrock goto fail; 856316Seschrock if (nvlist_alloc(&np->sn_props, NV_UNIQUE_NAME, 0) != 0) 866316Seschrock goto fail; 876316Seschrock 886316Seschrock np->sn_snapshot = sp; 896316Seschrock np->sn_id = sp->ss_n_nodes++; 906316Seschrock 916316Seschrock if (pnp == NULL) { 926316Seschrock ASSERT(sp->ss_root == NULL); 936316Seschrock sp->ss_root = np; 946316Seschrock } else { 956316Seschrock np->sn_parent = pnp; 966316Seschrock np->sn_prev_sibling = pnp->sn_last_child; 976316Seschrock 986316Seschrock if (pnp->sn_first_child == NULL) 996316Seschrock pnp->sn_first_child = np; 1006316Seschrock else 1016316Seschrock pnp->sn_last_child->sn_next_sibling = np; 1026316Seschrock 1036316Seschrock pnp->sn_last_child = np; 1046316Seschrock } 1056316Seschrock 1066316Seschrock return (np); 1076316Seschrock 1086316Seschrock fail: 1096316Seschrock ses_free(np); 1106316Seschrock ses_node_teardown(sp->ss_root); 1116316Seschrock sp->ss_root = NULL; 1126316Seschrock return (NULL); 1136316Seschrock } 1146316Seschrock 1156316Seschrock /* 1166316Seschrock * Parse element type descriptor. 1176316Seschrock */ 1186316Seschrock static int 1196316Seschrock elem_parse_td(ses2_td_hdr_impl_t *tip, const char *tp, nvlist_t *nvl) 1206316Seschrock { 1216316Seschrock int nverr; 1226316Seschrock 1236316Seschrock if (tp != NULL) 1246316Seschrock SES_NV_ADD(fixed_string, nverr, nvl, SES_PROP_CLASS_DESCRIPTION, 1256316Seschrock tp, tip->sthi_text_len); 1266316Seschrock 1276316Seschrock return (0); 1286316Seschrock } 1296316Seschrock 1306316Seschrock 1316316Seschrock /* 1326316Seschrock * Build a skeleton tree of nodes in the given snapshot. This is the heart of 1336316Seschrock * libses, and is responsible for parsing the config page into a tree and 1346316Seschrock * populating nodes with data from the config page. 1356316Seschrock */ 1366316Seschrock static int 1376316Seschrock ses_build_snap_skel(ses_snap_t *sp) 1386316Seschrock { 1396316Seschrock ses2_ed_impl_t *eip; 1406316Seschrock ses2_td_hdr_impl_t *tip, *ftip; 1416316Seschrock ses_node_t *np, *pnp, *cnp, *root; 1426316Seschrock ses_snap_page_t *pp; 1436316Seschrock ses2_config_page_impl_t *pip; 1446316Seschrock int i, j, n_etds = 0; 1456316Seschrock off_t toff; 1466316Seschrock char *tp, *text; 1476316Seschrock int err; 1486316Seschrock uint64_t idx; 1496316Seschrock 1506316Seschrock pp = ses_snap_find_page(sp, SES2_DIAGPAGE_CONFIG, B_FALSE); 1516316Seschrock if (pp == NULL) 1526316Seschrock return (ses_error(ESES_BAD_RESPONSE, "target does not support " 1536316Seschrock "configuration diagnostic page")); 1546316Seschrock pip = (ses2_config_page_impl_t *)pp->ssp_page; 1556316Seschrock 1566316Seschrock if (pp->ssp_len < offsetof(ses2_config_page_impl_t, scpi_data)) 1576316Seschrock return (ses_error(ESES_BAD_RESPONSE, "no enclosure " 1586316Seschrock "descriptors found")); 1596316Seschrock 1606316Seschrock /* 1616316Seschrock * Start with the root of the tree, which is a target node, containing 1626316Seschrock * just the SCSI inquiry properties. 1636316Seschrock */ 1646316Seschrock if ((root = ses_node_alloc(sp, sp->ss_root)) == NULL) 1656316Seschrock return (-1); 1666316Seschrock 1676316Seschrock root->sn_type = SES_NODE_TARGET; 1686316Seschrock SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_VENDOR, 1696316Seschrock libscsi_vendor(sp->ss_target->st_target)); 1706316Seschrock SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_PRODUCT, 1716316Seschrock libscsi_product(sp->ss_target->st_target)); 1726316Seschrock SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_REVISION, 1736316Seschrock libscsi_revision(sp->ss_target->st_target)); 1746316Seschrock 1756316Seschrock for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0; 1766316Seschrock i < pip->scpi_n_subenclosures + 1; 1776316Seschrock i++, eip = NEXT_ED(eip)) { 1786316Seschrock if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len)) 1796316Seschrock break; 1806316Seschrock 1816316Seschrock n_etds += eip->st_hdr.sehi_n_etd_hdrs; 1826316Seschrock } 1836316Seschrock ftip = (ses2_td_hdr_impl_t *)eip; 1846316Seschrock 1856316Seschrock /* 1866316Seschrock * There should really be only one Enclosure element possible for a 1876316Seschrock * give subenclosure ID. The standard never comes out and says this, 1886316Seschrock * but it does describe this element as "managing the enclosure itself" 1896316Seschrock * which implies rather strongly that the subenclosure ID field is that 1906316Seschrock * of, well, the enclosure itself. Since an enclosure can't contain 1916316Seschrock * itself, it follows logically that each subenclosure has at most one 1926316Seschrock * Enclosure type descriptor elements matching its ID. Of course, some 1936316Seschrock * enclosure firmware is buggy, so this may not always work out; in 1946316Seschrock * this case we just ignore all but the first Enclosure-type element 1956316Seschrock * with our subenclosure ID. 1966316Seschrock */ 1976316Seschrock for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0; 1986316Seschrock i < pip->scpi_n_subenclosures + 1; 1996316Seschrock i++, eip = NEXT_ED(eip)) { 2006316Seschrock if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len)) 2016316Seschrock break; 2026316Seschrock 2036316Seschrock if ((np = ses_node_alloc(sp, root)) == NULL) 2046316Seschrock return (-1); 2056316Seschrock 2066316Seschrock np->sn_type = SES_NODE_ENCLOSURE; 2076316Seschrock np->sn_enc_num = eip->st_hdr.sehi_subenclosure_id; 2086316Seschrock 2096316Seschrock if (!SES_WITHIN_PAGE(eip, eip->st_hdr.sehi_ed_len + 2106316Seschrock sizeof (ses2_ed_hdr_impl_t), 2116316Seschrock pp->ssp_page, pp->ssp_len)) 2126316Seschrock break; 2136316Seschrock 2146316Seschrock if (enc_parse_ed(eip, np->sn_props) != 0) 2156316Seschrock return (-1); 2166316Seschrock } 2176316Seschrock 2186316Seschrock if (root->sn_first_child == NULL) 2196316Seschrock return (ses_error(ESES_BAD_RESPONSE, "no enclosure " 2206316Seschrock "descriptors found")); 2216316Seschrock 2226316Seschrock tp = (char *)(ftip + n_etds); 2236316Seschrock 2246316Seschrock for (i = 0, toff = 0, idx = 0; i < n_etds; i++) { 2256316Seschrock tip = ftip + i; 2266316Seschrock 2276316Seschrock if (!SES_WITHIN_PAGE_STRUCT(tip, pp->ssp_page, pp->ssp_len)) 2286316Seschrock break; 2296316Seschrock 2306316Seschrock pnp = ses_find_enclosure(sp, 2316316Seschrock tip->sthi_subenclosure_id); 2326316Seschrock if (pnp == NULL) { 2336316Seschrock idx += tip->sthi_max_elements + 1; 2346316Seschrock toff += tip->sthi_text_len; 2356316Seschrock continue; 2366316Seschrock } 2376316Seschrock 2386316Seschrock if (tip->sthi_element_type == SES_ET_ENCLOSURE) { 2396316Seschrock if (tip->sthi_max_elements == 0) { 2406316Seschrock SES_NV_ADD(uint64, err, pnp->sn_props, 2416316Seschrock SES_PROP_ELEMENT_INDEX, idx); 2426316Seschrock pnp->sn_rootidx = idx; 2436316Seschrock } else { 2446316Seschrock SES_NV_ADD(uint64, err, pnp->sn_props, 2456316Seschrock SES_PROP_ELEMENT_INDEX, idx + 1); 2466316Seschrock pnp->sn_rootidx = idx + 1; 2476316Seschrock } 2486316Seschrock 2496316Seschrock if (tip->sthi_text_len > 0 && 2506316Seschrock SES_WITHIN_PAGE(tp + toff, tip->sthi_text_len, 2516316Seschrock pp->ssp_page, pp->ssp_len)) { 2526316Seschrock text = tp + toff; 2536316Seschrock toff += tip->sthi_text_len; 2546316Seschrock } else { 2556316Seschrock text = NULL; 2566316Seschrock } 2576316Seschrock 2586316Seschrock SES_NV_ADD(uint64, err, pnp->sn_props, 2596316Seschrock SES_PROP_ELEMENT_TYPE, SES_ET_ENCLOSURE); 2606316Seschrock if (enc_parse_td(tip, text, pnp->sn_props) != 0) 2616316Seschrock return (-1); 2626316Seschrock 2636316Seschrock idx += tip->sthi_max_elements + 1; 2646316Seschrock continue; 2656316Seschrock } 2666316Seschrock 2676316Seschrock if ((np = ses_node_alloc(sp, pnp)) == NULL) 2686316Seschrock return (-1); 2696316Seschrock 2706316Seschrock np->sn_type = SES_NODE_AGGREGATE; 2716316Seschrock np->sn_enc_num = tip->sthi_subenclosure_id; 2726316Seschrock np->sn_parent = pnp; 2736316Seschrock np->sn_rootidx = idx; 2746316Seschrock 2756316Seschrock SES_NV_ADD(uint64, err, np->sn_props, 2766316Seschrock SES_PROP_ELEMENT_INDEX, idx); 2776316Seschrock SES_NV_ADD(uint64, err, np->sn_props, 2786316Seschrock SES_PROP_ELEMENT_TYPE, tip->sthi_element_type); 2796316Seschrock 2806316Seschrock if (tip->sthi_text_len > 0 && 2816316Seschrock SES_WITHIN_PAGE(tp + toff, tip->sthi_text_len, 2826316Seschrock pp->ssp_page, pp->ssp_len)) { 2836316Seschrock text = tp + toff; 2846316Seschrock toff += tip->sthi_text_len; 2856316Seschrock } else { 2866316Seschrock text = NULL; 2876316Seschrock } 2886316Seschrock 2896316Seschrock if (elem_parse_td(tip, text, np->sn_props) != 0) 2906316Seschrock return (-1); 2916316Seschrock 2926316Seschrock idx += tip->sthi_max_elements + 1; 2936316Seschrock 2946316Seschrock if (tip->sthi_max_elements == 0) 2956316Seschrock continue; 2966316Seschrock 2976316Seschrock for (j = 0; j < tip->sthi_max_elements; j++) { 2986316Seschrock cnp = ses_node_alloc(sp, np); 2996316Seschrock if (cnp == NULL) 3006316Seschrock return (-1); 3016316Seschrock 3026316Seschrock cnp->sn_type = SES_NODE_ELEMENT; 3036316Seschrock SES_NV_ADD(uint64, err, cnp->sn_props, 3046316Seschrock SES_PROP_ELEMENT_INDEX, np->sn_rootidx + j + 1); 3056316Seschrock SES_NV_ADD(uint64, err, cnp->sn_props, 3066316Seschrock SES_PROP_ELEMENT_CLASS_INDEX, j); 3076316Seschrock SES_NV_ADD(uint64, err, cnp->sn_props, 3086316Seschrock SES_PROP_ELEMENT_TYPE, tip->sthi_element_type); 3096316Seschrock } 3106316Seschrock } 3116316Seschrock 3126316Seschrock np->sn_snapshot->ss_n_elem = idx; 3136316Seschrock 3146316Seschrock return (0); 3156316Seschrock } 3166316Seschrock 3176316Seschrock static int 3186316Seschrock ses_fill_tree(ses_node_t *np) 3196316Seschrock { 3206316Seschrock if (np == NULL) 3216316Seschrock return (0); 3226316Seschrock 3236316Seschrock for (; np != NULL; np = np->sn_next_sibling) { 3246316Seschrock if (ses_fill_node(np) != 0) 3256316Seschrock return (-1); 3266316Seschrock if (ses_fill_tree(np->sn_first_child) != 0) 3276316Seschrock return (-1); 3286316Seschrock } 3296316Seschrock 3306316Seschrock return (0); 3316316Seschrock } 3326316Seschrock 3336316Seschrock int 3346316Seschrock ses_fill_snap(ses_snap_t *sp) 3356316Seschrock { 3366316Seschrock if (ses_build_snap_skel(sp) != 0) 3376316Seschrock return (-1); 3386316Seschrock 3396316Seschrock if (ses_fill_tree(sp->ss_root) != 0) 3406316Seschrock return (-1); 3416316Seschrock 3426316Seschrock return (0); 3436316Seschrock } 3446316Seschrock 3456316Seschrock ses_node_t * 3466316Seschrock ses_root_node(ses_snap_t *sp) 3476316Seschrock { 3486316Seschrock return (sp->ss_root); 3496316Seschrock } 3506316Seschrock 3516316Seschrock ses_node_t * 3526316Seschrock ses_node_sibling(ses_node_t *np) 3536316Seschrock { 3546316Seschrock return (np->sn_next_sibling); 3556316Seschrock } 3566316Seschrock 3576316Seschrock ses_node_t * 3586316Seschrock ses_node_prev_sibling(ses_node_t *np) 3596316Seschrock { 3606316Seschrock return (np->sn_prev_sibling); 3616316Seschrock } 3626316Seschrock 3636316Seschrock ses_node_t * 3646316Seschrock ses_node_parent(ses_node_t *np) 3656316Seschrock { 3666316Seschrock return (np->sn_parent); 3676316Seschrock } 3686316Seschrock 3696316Seschrock ses_node_t * 3706316Seschrock ses_node_child(ses_node_t *np) 3716316Seschrock { 3726316Seschrock return (np->sn_first_child); 3736316Seschrock } 3746316Seschrock 3756316Seschrock ses_node_type_t 3766316Seschrock ses_node_type(ses_node_t *np) 3776316Seschrock { 3786316Seschrock return (np->sn_type); 3796316Seschrock } 3806316Seschrock 3816316Seschrock ses_snap_t * 3826316Seschrock ses_node_snapshot(ses_node_t *np) 3836316Seschrock { 3846316Seschrock return ((ses_snap_t *)np->sn_snapshot); 3856316Seschrock } 3866316Seschrock 3876316Seschrock ses_target_t * 3886316Seschrock ses_node_target(ses_node_t *np) 3896316Seschrock { 3906316Seschrock return (np->sn_snapshot->ss_target); 3916316Seschrock } 3926316Seschrock 3936316Seschrock nvlist_t * 3946316Seschrock ses_node_props(ses_node_t *np) 3956316Seschrock { 3966316Seschrock return (np->sn_props); 3976316Seschrock } 3986316Seschrock 3996316Seschrock /* 4006316Seschrock * A node identifier is a (generation, index) tuple that can be used to lookup a 4016316Seschrock * node within this target at a later point. This will be valid across 4026316Seschrock * snapshots, though it will return failure if the generation count has changed. 4036316Seschrock */ 4046316Seschrock uint64_t 4056316Seschrock ses_node_id(ses_node_t *np) 4066316Seschrock { 4076316Seschrock return (((uint64_t)np->sn_snapshot->ss_generation << 32) | 4086316Seschrock np->sn_id); 4096316Seschrock } 410