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