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 /*
23*12126SHyon.Kim@Sun.COM * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
246316Seschrock */
256316Seschrock
266316Seschrock #include <scsi/libses.h>
276316Seschrock #include "ses_impl.h"
286316Seschrock
296316Seschrock #define NEXT_ED(eip) \
306316Seschrock ((ses2_ed_impl_t *)((uint8_t *)(eip) + \
316316Seschrock ((eip)->st_hdr.sehi_ed_len + sizeof (ses2_ed_hdr_impl_t))))
326316Seschrock
336316Seschrock static ses_node_t *
ses_find_enclosure(ses_snap_t * sp,uint64_t number)346316Seschrock ses_find_enclosure(ses_snap_t *sp, uint64_t number)
356316Seschrock {
366316Seschrock ses_node_t *np;
376316Seschrock
386316Seschrock for (np = sp->ss_root->sn_first_child; np != NULL;
396316Seschrock np = np->sn_next_sibling) {
406316Seschrock ASSERT(np->sn_type == SES_NODE_ENCLOSURE);
416316Seschrock if (np->sn_enc_num == number)
426316Seschrock return ((ses_node_t *)np);
436316Seschrock }
446316Seschrock
456316Seschrock return (NULL);
466316Seschrock }
476316Seschrock
486846Sjmcp /*
496846Sjmcp * ses_snap_primary_enclosure() finds the primary enclosure for
506846Sjmcp * the supplied ses_snap_t.
516846Sjmcp */
526846Sjmcp ses_node_t *
ses_snap_primary_enclosure(ses_snap_t * sp)536846Sjmcp ses_snap_primary_enclosure(ses_snap_t *sp)
546846Sjmcp {
556846Sjmcp return (ses_find_enclosure(sp, 0));
566846Sjmcp }
576846Sjmcp
586316Seschrock void
ses_node_teardown(ses_node_t * np)596316Seschrock ses_node_teardown(ses_node_t *np)
606316Seschrock {
616316Seschrock ses_node_t *rp;
626316Seschrock
636316Seschrock if (np == NULL)
646316Seschrock return;
656316Seschrock
666316Seschrock for (; np != NULL; np = rp) {
676316Seschrock ses_node_teardown(np->sn_first_child);
686316Seschrock rp = np->sn_next_sibling;
696316Seschrock nvlist_free(np->sn_props);
706316Seschrock ses_free(np);
716316Seschrock }
726316Seschrock }
736316Seschrock
746316Seschrock static ses_node_t *
ses_node_alloc(ses_snap_t * sp,ses_node_t * pnp)756316Seschrock ses_node_alloc(ses_snap_t *sp, ses_node_t *pnp)
766316Seschrock {
776316Seschrock ses_node_t *np;
786316Seschrock
796316Seschrock np = ses_zalloc(sizeof (ses_node_t));
806316Seschrock if (np == NULL)
816316Seschrock goto fail;
826316Seschrock if (nvlist_alloc(&np->sn_props, NV_UNIQUE_NAME, 0) != 0)
836316Seschrock goto fail;
846316Seschrock
856316Seschrock np->sn_snapshot = sp;
866316Seschrock np->sn_id = sp->ss_n_nodes++;
876316Seschrock
886316Seschrock if (pnp == NULL) {
896316Seschrock ASSERT(sp->ss_root == NULL);
906316Seschrock sp->ss_root = np;
916316Seschrock } else {
926316Seschrock np->sn_parent = pnp;
936316Seschrock np->sn_prev_sibling = pnp->sn_last_child;
946316Seschrock
956316Seschrock if (pnp->sn_first_child == NULL)
966316Seschrock pnp->sn_first_child = np;
976316Seschrock else
986316Seschrock pnp->sn_last_child->sn_next_sibling = np;
996316Seschrock
1006316Seschrock pnp->sn_last_child = np;
1016316Seschrock }
1026316Seschrock
1036316Seschrock return (np);
1046316Seschrock
1056316Seschrock fail:
1066316Seschrock ses_free(np);
1076316Seschrock ses_node_teardown(sp->ss_root);
1086316Seschrock sp->ss_root = NULL;
1096316Seschrock return (NULL);
1106316Seschrock }
1116316Seschrock
1126316Seschrock /*
1136316Seschrock * Parse element type descriptor.
1146316Seschrock */
1156316Seschrock static int
elem_parse_td(ses2_td_hdr_impl_t * tip,const char * tp,nvlist_t * nvl)1166316Seschrock elem_parse_td(ses2_td_hdr_impl_t *tip, const char *tp, nvlist_t *nvl)
1176316Seschrock {
1186316Seschrock int nverr;
1196316Seschrock
1206316Seschrock if (tp != NULL)
1216316Seschrock SES_NV_ADD(fixed_string, nverr, nvl, SES_PROP_CLASS_DESCRIPTION,
1226316Seschrock tp, tip->sthi_text_len);
1236316Seschrock
1246316Seschrock return (0);
1256316Seschrock }
1266316Seschrock
1276316Seschrock
1286316Seschrock /*
1296316Seschrock * Build a skeleton tree of nodes in the given snapshot. This is the heart of
1306316Seschrock * libses, and is responsible for parsing the config page into a tree and
1316316Seschrock * populating nodes with data from the config page.
1326316Seschrock */
1336316Seschrock static int
ses_build_snap_skel(ses_snap_t * sp)1346316Seschrock ses_build_snap_skel(ses_snap_t *sp)
1356316Seschrock {
1366316Seschrock ses2_ed_impl_t *eip;
1376316Seschrock ses2_td_hdr_impl_t *tip, *ftip;
1386316Seschrock ses_node_t *np, *pnp, *cnp, *root;
1396316Seschrock ses_snap_page_t *pp;
1406316Seschrock ses2_config_page_impl_t *pip;
1416316Seschrock int i, j, n_etds = 0;
1426316Seschrock off_t toff;
1436316Seschrock char *tp, *text;
1446316Seschrock int err;
145*12126SHyon.Kim@Sun.COM uint64_t idx, eidx;
1466316Seschrock
1476316Seschrock pp = ses_snap_find_page(sp, SES2_DIAGPAGE_CONFIG, B_FALSE);
1486316Seschrock if (pp == NULL)
1496316Seschrock return (ses_error(ESES_BAD_RESPONSE, "target does not support "
1506316Seschrock "configuration diagnostic page"));
1516316Seschrock pip = (ses2_config_page_impl_t *)pp->ssp_page;
1526316Seschrock
1536316Seschrock if (pp->ssp_len < offsetof(ses2_config_page_impl_t, scpi_data))
1546316Seschrock return (ses_error(ESES_BAD_RESPONSE, "no enclosure "
1556316Seschrock "descriptors found"));
1566316Seschrock
1576316Seschrock /*
1586316Seschrock * Start with the root of the tree, which is a target node, containing
1596316Seschrock * just the SCSI inquiry properties.
1606316Seschrock */
1616316Seschrock if ((root = ses_node_alloc(sp, sp->ss_root)) == NULL)
1626316Seschrock return (-1);
1636316Seschrock
1646316Seschrock root->sn_type = SES_NODE_TARGET;
1656316Seschrock SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_VENDOR,
1666316Seschrock libscsi_vendor(sp->ss_target->st_target));
1676316Seschrock SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_PRODUCT,
1686316Seschrock libscsi_product(sp->ss_target->st_target));
1696316Seschrock SES_NV_ADD(string, err, root->sn_props, SCSI_PROP_REVISION,
1706316Seschrock libscsi_revision(sp->ss_target->st_target));
1716316Seschrock
1726316Seschrock for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0;
1736316Seschrock i < pip->scpi_n_subenclosures + 1;
1746316Seschrock i++, eip = NEXT_ED(eip)) {
1756316Seschrock if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len))
1766316Seschrock break;
1776316Seschrock
1786316Seschrock n_etds += eip->st_hdr.sehi_n_etd_hdrs;
1796316Seschrock }
1806316Seschrock ftip = (ses2_td_hdr_impl_t *)eip;
1816316Seschrock
1826316Seschrock /*
1836316Seschrock * There should really be only one Enclosure element possible for a
1846316Seschrock * give subenclosure ID. The standard never comes out and says this,
1856316Seschrock * but it does describe this element as "managing the enclosure itself"
1866316Seschrock * which implies rather strongly that the subenclosure ID field is that
1876316Seschrock * of, well, the enclosure itself. Since an enclosure can't contain
1886316Seschrock * itself, it follows logically that each subenclosure has at most one
1896316Seschrock * Enclosure type descriptor elements matching its ID. Of course, some
1906316Seschrock * enclosure firmware is buggy, so this may not always work out; in
1916316Seschrock * this case we just ignore all but the first Enclosure-type element
1926316Seschrock * with our subenclosure ID.
1936316Seschrock */
1946316Seschrock for (eip = (ses2_ed_impl_t *)pip->scpi_data, i = 0;
1956316Seschrock i < pip->scpi_n_subenclosures + 1;
1966316Seschrock i++, eip = NEXT_ED(eip)) {
1976316Seschrock if (!SES_WITHIN_PAGE_STRUCT(eip, pp->ssp_page, pp->ssp_len))
1986316Seschrock break;
1996316Seschrock
2006316Seschrock if ((np = ses_node_alloc(sp, root)) == NULL)
2016316Seschrock return (-1);
2026316Seschrock
2036316Seschrock np->sn_type = SES_NODE_ENCLOSURE;
2046316Seschrock np->sn_enc_num = eip->st_hdr.sehi_subenclosure_id;
2056316Seschrock
2066316Seschrock if (!SES_WITHIN_PAGE(eip, eip->st_hdr.sehi_ed_len +
2076316Seschrock sizeof (ses2_ed_hdr_impl_t),
2086316Seschrock pp->ssp_page, pp->ssp_len))
2096316Seschrock break;
2106316Seschrock
2116316Seschrock if (enc_parse_ed(eip, np->sn_props) != 0)
2126316Seschrock return (-1);
2136316Seschrock }
2146316Seschrock
2156316Seschrock if (root->sn_first_child == NULL)
2166316Seschrock return (ses_error(ESES_BAD_RESPONSE, "no enclosure "
2176316Seschrock "descriptors found"));
2186316Seschrock
2196316Seschrock tp = (char *)(ftip + n_etds);
2206316Seschrock
221*12126SHyon.Kim@Sun.COM for (i = 0, toff = 0, idx = eidx = 0; i < n_etds; i++) {
2226316Seschrock tip = ftip + i;
2236316Seschrock
2246316Seschrock if (!SES_WITHIN_PAGE_STRUCT(tip, pp->ssp_page, pp->ssp_len))
2256316Seschrock break;
2266316Seschrock
2276316Seschrock pnp = ses_find_enclosure(sp,
2286316Seschrock tip->sthi_subenclosure_id);
2296316Seschrock if (pnp == NULL) {
2306316Seschrock idx += tip->sthi_max_elements + 1;
231*12126SHyon.Kim@Sun.COM eidx += tip->sthi_max_elements;
2326316Seschrock toff += tip->sthi_text_len;
2336316Seschrock continue;
2346316Seschrock }
2356316Seschrock
2366316Seschrock if (tip->sthi_element_type == SES_ET_ENCLOSURE) {
2376316Seschrock if (tip->sthi_max_elements == 0) {
2386316Seschrock SES_NV_ADD(uint64, err, pnp->sn_props,
2396316Seschrock SES_PROP_ELEMENT_INDEX, idx);
2406316Seschrock pnp->sn_rootidx = idx;
2416316Seschrock } else {
2426316Seschrock SES_NV_ADD(uint64, err, pnp->sn_props,
2436316Seschrock SES_PROP_ELEMENT_INDEX, idx + 1);
244*12126SHyon.Kim@Sun.COM SES_NV_ADD(uint64, err, pnp->sn_props,
245*12126SHyon.Kim@Sun.COM SES_PROP_ELEMENT_ONLY_INDEX, eidx);
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;
264*12126SHyon.Kim@Sun.COM eidx += tip->sthi_max_elements;
2656316Seschrock continue;
2666316Seschrock }
2676316Seschrock
2686316Seschrock if ((np = ses_node_alloc(sp, pnp)) == NULL)
2696316Seschrock return (-1);
2706316Seschrock
2716316Seschrock np->sn_type = SES_NODE_AGGREGATE;
2726316Seschrock np->sn_enc_num = tip->sthi_subenclosure_id;
2736316Seschrock np->sn_parent = pnp;
2746316Seschrock np->sn_rootidx = idx;
2756316Seschrock
2766316Seschrock SES_NV_ADD(uint64, err, np->sn_props,
2776316Seschrock SES_PROP_ELEMENT_INDEX, idx);
2786316Seschrock SES_NV_ADD(uint64, err, np->sn_props,
2796316Seschrock SES_PROP_ELEMENT_TYPE, tip->sthi_element_type);
2806316Seschrock
2816316Seschrock if (tip->sthi_text_len > 0 &&
2826316Seschrock SES_WITHIN_PAGE(tp + toff, tip->sthi_text_len,
2836316Seschrock pp->ssp_page, pp->ssp_len)) {
2846316Seschrock text = tp + toff;
2856316Seschrock toff += tip->sthi_text_len;
2866316Seschrock } else {
2876316Seschrock text = NULL;
2886316Seschrock }
2896316Seschrock
2906316Seschrock if (elem_parse_td(tip, text, np->sn_props) != 0)
2916316Seschrock return (-1);
2926316Seschrock
2936316Seschrock idx += tip->sthi_max_elements + 1;
2946316Seschrock
2956316Seschrock if (tip->sthi_max_elements == 0)
2966316Seschrock continue;
2976316Seschrock
2986316Seschrock for (j = 0; j < tip->sthi_max_elements; j++) {
2996316Seschrock cnp = ses_node_alloc(sp, np);
3006316Seschrock if (cnp == NULL)
3016316Seschrock return (-1);
3026316Seschrock
3036316Seschrock cnp->sn_type = SES_NODE_ELEMENT;
3046316Seschrock SES_NV_ADD(uint64, err, cnp->sn_props,
3056316Seschrock SES_PROP_ELEMENT_INDEX, np->sn_rootidx + j + 1);
3066316Seschrock SES_NV_ADD(uint64, err, cnp->sn_props,
307*12126SHyon.Kim@Sun.COM SES_PROP_ELEMENT_ONLY_INDEX, eidx + j);
308*12126SHyon.Kim@Sun.COM SES_NV_ADD(uint64, err, cnp->sn_props,
3096316Seschrock SES_PROP_ELEMENT_CLASS_INDEX, j);
3106316Seschrock SES_NV_ADD(uint64, err, cnp->sn_props,
3116316Seschrock SES_PROP_ELEMENT_TYPE, tip->sthi_element_type);
3126316Seschrock }
313*12126SHyon.Kim@Sun.COM
314*12126SHyon.Kim@Sun.COM eidx += tip->sthi_max_elements;
3156316Seschrock }
3166316Seschrock
3176316Seschrock np->sn_snapshot->ss_n_elem = idx;
3186316Seschrock
3196316Seschrock return (0);
3206316Seschrock }
3216316Seschrock
3226316Seschrock static int
ses_fill_tree(ses_node_t * np)3236316Seschrock ses_fill_tree(ses_node_t *np)
3246316Seschrock {
3256316Seschrock if (np == NULL)
3266316Seschrock return (0);
3276316Seschrock
3286316Seschrock for (; np != NULL; np = np->sn_next_sibling) {
3296316Seschrock if (ses_fill_node(np) != 0)
3306316Seschrock return (-1);
3316316Seschrock if (ses_fill_tree(np->sn_first_child) != 0)
3326316Seschrock return (-1);
3336316Seschrock }
3346316Seschrock
3356316Seschrock return (0);
3366316Seschrock }
3376316Seschrock
3386316Seschrock int
ses_fill_snap(ses_snap_t * sp)3396316Seschrock ses_fill_snap(ses_snap_t *sp)
3406316Seschrock {
3416316Seschrock if (ses_build_snap_skel(sp) != 0)
3426316Seschrock return (-1);
3436316Seschrock
3446316Seschrock if (ses_fill_tree(sp->ss_root) != 0)
3456316Seschrock return (-1);
3466316Seschrock
3476316Seschrock return (0);
3486316Seschrock }
3496316Seschrock
3506316Seschrock ses_node_t *
ses_root_node(ses_snap_t * sp)3516316Seschrock ses_root_node(ses_snap_t *sp)
3526316Seschrock {
3536316Seschrock return (sp->ss_root);
3546316Seschrock }
3556316Seschrock
3566316Seschrock ses_node_t *
ses_node_sibling(ses_node_t * np)3576316Seschrock ses_node_sibling(ses_node_t *np)
3586316Seschrock {
3596316Seschrock return (np->sn_next_sibling);
3606316Seschrock }
3616316Seschrock
3626316Seschrock ses_node_t *
ses_node_prev_sibling(ses_node_t * np)3636316Seschrock ses_node_prev_sibling(ses_node_t *np)
3646316Seschrock {
3656316Seschrock return (np->sn_prev_sibling);
3666316Seschrock }
3676316Seschrock
3686316Seschrock ses_node_t *
ses_node_parent(ses_node_t * np)3696316Seschrock ses_node_parent(ses_node_t *np)
3706316Seschrock {
3716316Seschrock return (np->sn_parent);
3726316Seschrock }
3736316Seschrock
3746316Seschrock ses_node_t *
ses_node_child(ses_node_t * np)3756316Seschrock ses_node_child(ses_node_t *np)
3766316Seschrock {
3776316Seschrock return (np->sn_first_child);
3786316Seschrock }
3796316Seschrock
3806316Seschrock ses_node_type_t
ses_node_type(ses_node_t * np)3816316Seschrock ses_node_type(ses_node_t *np)
3826316Seschrock {
3836316Seschrock return (np->sn_type);
3846316Seschrock }
3856316Seschrock
3866316Seschrock ses_snap_t *
ses_node_snapshot(ses_node_t * np)3876316Seschrock ses_node_snapshot(ses_node_t *np)
3886316Seschrock {
3896316Seschrock return ((ses_snap_t *)np->sn_snapshot);
3906316Seschrock }
3916316Seschrock
3926316Seschrock ses_target_t *
ses_node_target(ses_node_t * np)3936316Seschrock ses_node_target(ses_node_t *np)
3946316Seschrock {
3956316Seschrock return (np->sn_snapshot->ss_target);
3966316Seschrock }
3976316Seschrock
3986316Seschrock nvlist_t *
ses_node_props(ses_node_t * np)3996316Seschrock ses_node_props(ses_node_t *np)
4006316Seschrock {
4016316Seschrock return (np->sn_props);
4026316Seschrock }
4036316Seschrock
4046316Seschrock /*
4056316Seschrock * A node identifier is a (generation, index) tuple that can be used to lookup a
4066316Seschrock * node within this target at a later point. This will be valid across
4076316Seschrock * snapshots, though it will return failure if the generation count has changed.
4086316Seschrock */
4096316Seschrock uint64_t
ses_node_id(ses_node_t * np)4106316Seschrock ses_node_id(ses_node_t *np)
4116316Seschrock {
4126316Seschrock return (((uint64_t)np->sn_snapshot->ss_generation << 32) |
4136316Seschrock np->sn_id);
4146316Seschrock }
415