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 }