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