xref: /illumos-gate/usr/src/lib/scsi/libses/common/ses_node.c (revision ac88567a7a5bb7f01cf22cf366bc9d6203e24d7a)
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 
33 static ses_node_t *
ses_find_enclosure(ses_snap_t * sp,uint64_t number)34 ses_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  */
52 ses_node_t *
ses_snap_primary_enclosure(ses_snap_t * sp)53 ses_snap_primary_enclosure(ses_snap_t *sp)
54 {
55 	return (ses_find_enclosure(sp, 0));
56 }
57 
58 void
ses_node_teardown(ses_node_t * np)59 ses_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 
74 static ses_node_t *
ses_node_alloc(ses_snap_t * sp,ses_node_t * pnp)75 ses_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 
105 fail:
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  */
115 static int
elem_parse_td(ses2_td_hdr_impl_t * tip,const char * tp,nvlist_t * nvl)116 elem_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  */
133 static int
ses_build_snap_skel(ses_snap_t * sp)134 ses_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 
322 static int
ses_fill_tree(ses_node_t * np)323 ses_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 
338 int
ses_fill_snap(ses_snap_t * sp)339 ses_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 
350 ses_node_t *
ses_root_node(ses_snap_t * sp)351 ses_root_node(ses_snap_t *sp)
352 {
353 	return (sp->ss_root);
354 }
355 
356 ses_node_t *
ses_node_sibling(ses_node_t * np)357 ses_node_sibling(ses_node_t *np)
358 {
359 	return (np->sn_next_sibling);
360 }
361 
362 ses_node_t *
ses_node_prev_sibling(ses_node_t * np)363 ses_node_prev_sibling(ses_node_t *np)
364 {
365 	return (np->sn_prev_sibling);
366 }
367 
368 ses_node_t *
ses_node_parent(ses_node_t * np)369 ses_node_parent(ses_node_t *np)
370 {
371 	return (np->sn_parent);
372 }
373 
374 ses_node_t *
ses_node_child(ses_node_t * np)375 ses_node_child(ses_node_t *np)
376 {
377 	return (np->sn_first_child);
378 }
379 
380 ses_node_type_t
ses_node_type(ses_node_t * np)381 ses_node_type(ses_node_t *np)
382 {
383 	return (np->sn_type);
384 }
385 
386 ses_snap_t *
ses_node_snapshot(ses_node_t * np)387 ses_node_snapshot(ses_node_t *np)
388 {
389 	return ((ses_snap_t *)np->sn_snapshot);
390 }
391 
392 ses_target_t *
ses_node_target(ses_node_t * np)393 ses_node_target(ses_node_t *np)
394 {
395 	return (np->sn_snapshot->ss_target);
396 }
397 
398 nvlist_t *
ses_node_props(ses_node_t * np)399 ses_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  */
409 uint64_t
ses_node_id(ses_node_t * np)410 ses_node_id(ses_node_t *np)
411 {
412 	return (((uint64_t)np->sn_snapshot->ss_generation << 32) |
413 	    np->sn_id);
414 }
415