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 * 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 * 53 ses_snap_primary_enclosure(ses_snap_t *sp) 54 { 55 return (ses_find_enclosure(sp, 0)); 56 } 57 58 void 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 * 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 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 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 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 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 * 351 ses_root_node(ses_snap_t *sp) 352 { 353 return (sp->ss_root); 354 } 355 356 ses_node_t * 357 ses_node_sibling(ses_node_t *np) 358 { 359 return (np->sn_next_sibling); 360 } 361 362 ses_node_t * 363 ses_node_prev_sibling(ses_node_t *np) 364 { 365 return (np->sn_prev_sibling); 366 } 367 368 ses_node_t * 369 ses_node_parent(ses_node_t *np) 370 { 371 return (np->sn_parent); 372 } 373 374 ses_node_t * 375 ses_node_child(ses_node_t *np) 376 { 377 return (np->sn_first_child); 378 } 379 380 ses_node_type_t 381 ses_node_type(ses_node_t *np) 382 { 383 return (np->sn_type); 384 } 385 386 ses_snap_t * 387 ses_node_snapshot(ses_node_t *np) 388 { 389 return ((ses_snap_t *)np->sn_snapshot); 390 } 391 392 ses_target_t * 393 ses_node_target(ses_node_t *np) 394 { 395 return (np->sn_snapshot->ss_target); 396 } 397 398 nvlist_t * 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 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