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