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