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