xref: /illumos-gate/usr/src/lib/scsi/libses/common/ses_node.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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