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.
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.
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]
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <scsi/libses.h>
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))))
34 ses_find_enclosure(ses_snap_t
*sp
, uint64_t number
)
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
);
49 * ses_snap_primary_enclosure() finds the primary enclosure for
50 * the supplied ses_snap_t.
53 ses_snap_primary_enclosure(ses_snap_t
*sp
)
55 return (ses_find_enclosure(sp
, 0));
59 ses_node_teardown(ses_node_t
*np
)
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
);
75 ses_node_alloc(ses_snap_t
*sp
, ses_node_t
*pnp
)
79 np
= ses_zalloc(sizeof (ses_node_t
));
82 if (nvlist_alloc(&np
->sn_props
, NV_UNIQUE_NAME
, 0) != 0)
86 np
->sn_id
= sp
->ss_n_nodes
++;
89 ASSERT(sp
->ss_root
== NULL
);
93 np
->sn_prev_sibling
= pnp
->sn_last_child
;
95 if (pnp
->sn_first_child
== NULL
)
96 pnp
->sn_first_child
= np
;
98 pnp
->sn_last_child
->sn_next_sibling
= np
;
100 pnp
->sn_last_child
= np
;
107 ses_node_teardown(sp
->ss_root
);
113 * Parse element type descriptor.
116 elem_parse_td(ses2_td_hdr_impl_t
*tip
, const char *tp
, nvlist_t
*nvl
)
121 SES_NV_ADD(fixed_string
, nverr
, nvl
, SES_PROP_CLASS_DESCRIPTION
,
122 tp
, tip
->sthi_text_len
);
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.
134 ses_build_snap_skel(ses_snap_t
*sp
)
137 ses2_td_hdr_impl_t
*tip
, *ftip
;
138 ses_node_t
*np
, *pnp
, *cnp
, *root
;
140 ses2_config_page_impl_t
*pip
;
141 int i
, j
, n_etds
= 0;
147 pp
= ses_snap_find_page(sp
, SES2_DIAGPAGE_CONFIG
, B_FALSE
);
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
;
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"));
158 * Start with the root of the tree, which is a target node, containing
159 * just the SCSI inquiry properties.
161 if ((root
= ses_node_alloc(sp
, sp
->ss_root
)) == NULL
)
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
));
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
))
178 n_etds
+= eip
->st_hdr
.sehi_n_etd_hdrs
;
180 ftip
= (ses2_td_hdr_impl_t
*)eip
;
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.
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
))
200 if ((np
= ses_node_alloc(sp
, root
)) == NULL
)
203 np
->sn_type
= SES_NODE_ENCLOSURE
;
204 np
->sn_enc_num
= eip
->st_hdr
.sehi_subenclosure_id
;
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
))
211 if (enc_parse_ed(eip
, np
->sn_props
) != 0)
215 if (root
->sn_first_child
== NULL
)
216 return (ses_error(ESES_BAD_RESPONSE
, "no enclosure "
217 "descriptors found"));
219 tp
= (char *)(ftip
+ n_etds
);
221 for (i
= 0, toff
= 0, idx
= eidx
= 0; i
< n_etds
; i
++) {
224 if (!SES_WITHIN_PAGE_STRUCT(tip
, pp
->ssp_page
, pp
->ssp_len
))
227 pnp
= ses_find_enclosure(sp
,
228 tip
->sthi_subenclosure_id
);
230 idx
+= tip
->sthi_max_elements
+ 1;
231 eidx
+= tip
->sthi_max_elements
;
232 toff
+= tip
->sthi_text_len
;
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
;
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;
249 if (tip
->sthi_text_len
> 0 &&
250 SES_WITHIN_PAGE(tp
+ toff
, tip
->sthi_text_len
,
251 pp
->ssp_page
, pp
->ssp_len
)) {
253 toff
+= tip
->sthi_text_len
;
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)
263 idx
+= tip
->sthi_max_elements
+ 1;
264 eidx
+= tip
->sthi_max_elements
;
268 if ((np
= ses_node_alloc(sp
, pnp
)) == NULL
)
271 np
->sn_type
= SES_NODE_AGGREGATE
;
272 np
->sn_enc_num
= tip
->sthi_subenclosure_id
;
274 np
->sn_rootidx
= idx
;
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
);
281 if (tip
->sthi_text_len
> 0 &&
282 SES_WITHIN_PAGE(tp
+ toff
, tip
->sthi_text_len
,
283 pp
->ssp_page
, pp
->ssp_len
)) {
285 toff
+= tip
->sthi_text_len
;
290 if (elem_parse_td(tip
, text
, np
->sn_props
) != 0)
293 idx
+= tip
->sthi_max_elements
+ 1;
295 if (tip
->sthi_max_elements
== 0)
298 for (j
= 0; j
< tip
->sthi_max_elements
; j
++) {
299 cnp
= ses_node_alloc(sp
, np
);
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
);
314 eidx
+= tip
->sthi_max_elements
;
317 np
->sn_snapshot
->ss_n_elem
= idx
;
323 ses_fill_tree(ses_node_t
*np
)
328 for (; np
!= NULL
; np
= np
->sn_next_sibling
) {
329 if (ses_fill_node(np
) != 0)
331 if (ses_fill_tree(np
->sn_first_child
) != 0)
339 ses_fill_snap(ses_snap_t
*sp
)
341 if (ses_build_snap_skel(sp
) != 0)
344 if (ses_fill_tree(sp
->ss_root
) != 0)
351 ses_root_node(ses_snap_t
*sp
)
353 return (sp
->ss_root
);
357 ses_node_sibling(ses_node_t
*np
)
359 return (np
->sn_next_sibling
);
363 ses_node_prev_sibling(ses_node_t
*np
)
365 return (np
->sn_prev_sibling
);
369 ses_node_parent(ses_node_t
*np
)
371 return (np
->sn_parent
);
375 ses_node_child(ses_node_t
*np
)
377 return (np
->sn_first_child
);
381 ses_node_type(ses_node_t
*np
)
383 return (np
->sn_type
);
387 ses_node_snapshot(ses_node_t
*np
)
389 return ((ses_snap_t
*)np
->sn_snapshot
);
393 ses_node_target(ses_node_t
*np
)
395 return (np
->sn_snapshot
->ss_target
);
399 ses_node_props(ses_node_t
*np
)
401 return (np
->sn_props
);
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.
410 ses_node_id(ses_node_t
*np
)
412 return (((uint64_t)np
->sn_snapshot
->ss_generation
<< 32) |