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