Print this page
    
NEX-13190 fmd core dump with assertion failure
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-3829 libtopo ses module unload takes too long
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
re #10360 rb4192 Unable to use /usr/lib/fm/fmd/fmtopo to discover SES / drive topology with the Dell MD1200.
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/lib/fm/topo/modules/common/ses/ses.c
          +++ new/usr/src/lib/fm/topo/modules/common/ses/ses.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  
    | 
      ↓ open down ↓ | 
    14 lines elided | 
    
      ↑ open up ↑ | 
  
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  24   24   * Copyright 2012 Milan Jurik. All rights reserved.
  25      - * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
       25 + * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  26   26   * Copyright (c) 2017, Joyent, Inc.
  27   27   */
  28   28  
  29   29  #include <alloca.h>
  30   30  #include <dirent.h>
  31   31  #include <devid.h>
  32   32  #include <fm/libdiskstatus.h>
  33   33  #include <inttypes.h>
  34   34  #include <pthread.h>
  35   35  #include <strings.h>
  36   36  #include <string.h>
  37   37  #include <unistd.h>
  38   38  #include <sys/dkio.h>
  39   39  #include <sys/fm/protocol.h>
  40   40  #include <sys/libdevid.h>
  41   41  #include <sys/scsi/scsi_types.h>
  42   42  #include <sys/byteorder.h>
  43   43  #include <pthread.h>
  44   44  #include <signal.h>
  45   45  #include <fcntl.h>
  46   46  #include <sys/ctfs.h>
  47   47  #include <libcontract.h>
  48   48  #include <poll.h>
  49   49  #include <sys/contract/device.h>
  50   50  #include <libsysevent.h>
  51   51  #include <sys/sysevent/eventdefs.h>
  52   52  #include <scsi/plugins/ses/vendor/sun.h>
  53   53  
  54   54  #include "disk.h"
  55   55  #include "ses.h"
  56   56  
  57   57  #define SES_VERSION     1
  58   58  
  59   59  #define SES_STARTING_SUBCHASSIS 256     /* valid subchassis IDs are uint8_t */
  60   60  #define NO_SUBCHASSIS   ((uint64_t)-1)
  61   61  
  62   62  static int ses_snap_freq = 250;         /* in milliseconds */
  63   63  
  64   64  #define SES_STATUS_UNAVAIL(s)   \
  65   65          ((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED)
  66   66  
  67   67  #define HR_SECOND   1000000000
  68   68  
  69   69  /*
  70   70   * Because multiple SES targets can be part of a single chassis, we construct
  71   71   * our own hierarchy that takes this into account.  These SES targets may refer
  72   72   * to the same devices (multiple paths) or to different devices (managing
  73   73   * different portions of the space).  We arrange things into a
  74   74   * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all
  75   75   * nodes found so far.
  76   76   */
  77   77  typedef struct ses_alt_node {
  78   78          topo_list_t             san_link;
  79   79          ses_node_t              *san_node;
  80   80  } ses_alt_node_t;
  81   81  
  82   82  typedef struct ses_enum_node {
  83   83          topo_list_t             sen_link;
  84   84          ses_node_t              *sen_node;
  85   85          topo_list_t             sen_alt_nodes;
  86   86          uint64_t                sen_type;
  87   87          uint64_t                sen_instance;
  88   88          ses_enum_target_t       *sen_target;
  89   89  } ses_enum_node_t;
  90   90  
  91   91  typedef struct ses_enum_chassis {
  92   92          topo_list_t             sec_link;
  93   93          topo_list_t             sec_subchassis;
  94   94          topo_list_t             sec_nodes;
  95   95          topo_list_t             sec_targets;
  96   96          const char              *sec_csn;
  97   97          ses_node_t              *sec_enclosure;
  98   98          ses_enum_target_t       *sec_target;
  99   99          topo_instance_t         sec_instance;
 100  100          topo_instance_t         sec_scinstance;
 101  101          topo_instance_t         sec_maxinstance;
 102  102          boolean_t               sec_hasdev;
 103  103          boolean_t               sec_internal;
 104  104  } ses_enum_chassis_t;
 105  105  
 106  106  typedef struct ses_enum_data {
 107  107          topo_list_t             sed_devs;
 108  108          topo_list_t             sed_chassis;
 109  109          ses_enum_chassis_t      *sed_current;
 110  110          ses_enum_target_t       *sed_target;
 111  111          int                     sed_errno;
 112  112          char                    *sed_name;
 113  113          topo_mod_t              *sed_mod;
 114  114          topo_instance_t         sed_instance;
 115  115  } ses_enum_data_t;
 116  116  
 117  117  typedef struct sas_connector_phy_data {
 118  118          uint64_t    scpd_index;
 119  119          uint64_t    scpd_pm;
 120  120  } sas_connector_phy_data_t;
 121  121  
 122  122  typedef struct sas_connector_type {
 123  123          uint64_t    sct_type;
 124  124          char        *sct_name;
 125  125  } sas_connector_type_t;
 126  126  
 127  127  static const sas_connector_type_t sas_connector_type_list[] = {
 128  128          {   0x0, "Information unknown"  },
 129  129          {   0x1, "External SAS 4x receptacle (see SAS-2 and SFF-8470)"  },
 130  130          {   0x2, "Exteranl Mini SAS 4x receptacle (see SAS-2 and SFF-8088)" },
 131  131          {   0x3, "QSFP+ receptacle (see SAS-2.1 and SFF-8436)" },
 132  132          {   0x4, "Mini SAS 4x active receptacle (see SAS-2.1 and SFF-8088)" },
 133  133          {   0x5, "Mini SAS HD 4x receptacle (see SAS-2.1 and SFF-8644)" },
 134  134          {   0x6, "Mini SAS HD 8x receptacle (see SAS-2.1 and SFF-8644)" },
 135  135          {   0x7, "Mini SAS HD 16x receptacle (see SAS-2.1 and SFF-8644)" },
 136  136          {   0xF, "Vendor-specific external connector"   },
 137  137          {   0x10, "Internal wide SAS 4i plug (see SAS-2 and SFF-8484)"  },
 138  138          {   0x11,
 139  139          "Internal wide Mini SAS 4i receptacle (see SAS-2 and SFF-8087)" },
 140  140          {   0x12, "Mini SAS HD 4i receptacle (see SAS-2.1 and SFF-8643)" },
 141  141          {   0x20, "Internal SAS Drive receptacle (see SAS-2 and SFF-8482)" },
 142  142          {   0x21, "Internal SATA host plug (see SAS-2 and SATA-2)"      },
 143  143          {   0x22, "Internal SAS Drive plug (see SAS-2 and SFF-8482)"    },
 144  144          {   0x23, "Internal SATA device plug (see SAS-2 and SATA-2)"    },
 145  145          {   0x24, "Micro SAS receptacle (see SAS-2.14)" },
 146  146          {   0x25, "Micro SATA device plug (see SAS-2.1 and SATA)"       },
 147  147          {   0x26, "Micro SAS plug (see SAS-2.1 and SFF-8486)"   },
 148  148          {   0x27, "Micro SAS/SATA plug (see SAS-2.1 and SFF-8486)"      },
 149  149          {   0x28,
 150  150          "12 Gb/s SAS Drive backplane receptacle (see SAS-34 and SFF-8680)" },
 151  151          {   0x29, "12Gb/s SAS Drive Plug (see SAS-3 and SFF-8680)"      },
 152  152          {   0x2A, "Multifunction 12 Gb/s 6x Unshielded receptacle connector "
 153  153                  "receptacle (see SAS-3 and SFF-8639)"   },
 154  154          {   0x2B, "Multifunction 12 Gb/s 6x Unshielded receptable connector "
 155  155                  "plug (see SAS-3 and SFF-8639)" },
 156  156          {   0x2F, "Internal SAS virtual connector"  },
 157  157          {   0x3F, "Vendor-specific internal connector"  },
 158  158          {   0x70, "Other Vendor-specific connector"     },
 159  159          {   0x71, "Other Vendor-specific connector"     },
 160  160          {   0x72, "Other Vendor-specific connector"     },
 161  161          {   0x73, "Other Vendor-specific connector"     },
 162  162          {   0x74, "Other Vendor-specific connector"     },
 163  163          {   0x75, "Other Vendor-specific connector"     },
 164  164          {   0x76, "Other Vendor-specific connector"     },
 165  165          {   0x77, "Other Vendor-specific connector"     },
 166  166          {   0x78, "Other Vendor-specific connector"     },
 167  167          {   0x79, "Other Vendor-specific connector"     },
 168  168          {   0x7A, "Other Vendor-specific connector"     },
 169  169          {   0x7B, "Other Vendor-specific connector"     },
 170  170          {   0x7C, "Other Vendor-specific connector"     },
 171  171          {   0x7D, "Other Vendor-specific connector"     },
 172  172          {   0x7E, "Other Vendor-specific connector"     },
 173  173          {   0x7F, "Other Vendor-specific connector"     },
 174  174          {   0x80, "Not Defined" }
 175  175  };
 176  176  
 177  177  #define SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED  0x80
 178  178  #define SAS_CONNECTOR_TYPE_NOT_DEFINED \
 179  179          "Connector type not defined by SES-2 standard"
 180  180  #define SAS_CONNECTOR_TYPE_RESERVED \
 181  181          "Connector type reserved by SES-2 standard"
 182  182  
 183  183  typedef struct phys_enum_type {
 184  184          uint64_t    pet_type;
 185  185          char        *pet_nodename;
 186  186          char        *pet_defaultlabel;
 187  187          boolean_t   pet_dorange;
 188  188  } phys_enum_type_t;
 189  189  
 190  190  static const phys_enum_type_t phys_enum_type_list[] = {
 191  191          {   SES_ET_ARRAY_DEVICE, BAY, "BAY", B_TRUE  },
 192  192          {   SES_ET_COOLING, FAN, "FAN", B_TRUE  },
 193  193          {   SES_ET_DEVICE, BAY, "BAY", B_TRUE  },
 194  194          {   SES_ET_ESC_ELECTRONICS, CONTROLLER, "CONTROLLER", B_TRUE  },
 195  195          {   SES_ET_POWER_SUPPLY, PSU, "PSU", B_TRUE  },
 196  196          {   SES_ET_SUNW_FANBOARD, FANBOARD, "FANBOARD", B_TRUE  },
 197  197          {   SES_ET_SUNW_FANMODULE, FANMODULE, "FANMODULE", B_TRUE  },
 198  198          {   SES_ET_SUNW_POWERBOARD, POWERBOARD, "POWERBOARD", B_TRUE  },
 199  199          {   SES_ET_SUNW_POWERMODULE, POWERMODULE, "POWERMODULE", B_TRUE  }
 200  200  };
 201  201  
 202  202  #define N_PHYS_ENUM_TYPES (sizeof (phys_enum_type_list) / \
 203  203          sizeof (phys_enum_type_list[0]))
 204  204  
 205  205  /*
 206  206   * Structure for the hierarchical tree for element nodes.
 207  207   */
 208  208  typedef struct ses_phys_tree {
 209  209      ses_node_t  *spt_snode;
 210  210      ses_enum_node_t     *spt_senumnode;
 211  211      boolean_t   spt_isfru;
 212  212      uint64_t    spt_eonlyindex;
 213  213      uint64_t    spt_cindex;
 214  214      uint64_t    spt_pindex;
 215  215      uint64_t    spt_maxinst;
 216  216      struct ses_phys_tree    *spt_parent;
 217  217      struct ses_phys_tree    *spt_child;
 218  218      struct ses_phys_tree    *spt_sibling;
 219  219      tnode_t     *spt_tnode;
 220  220  } ses_phys_tree_t;
 221  221  
 222  222  typedef enum {
 223  223          SES_NEW_CHASSIS         = 0x1,
 224  224          SES_NEW_SUBCHASSIS      = 0x2,
 225  225          SES_DUP_CHASSIS         = 0x4,
 226  226          SES_DUP_SUBCHASSIS      = 0x8
 227  227  } ses_chassis_type_e;
 228  228  
 229  229  
 230  230  static const topo_pgroup_info_t storage_pgroup = {
 231  231          TOPO_PGROUP_STORAGE,
 232  232          TOPO_STABILITY_PRIVATE,
 233  233          TOPO_STABILITY_PRIVATE,
 234  234          1
 235  235  };
 236  236  
 237  237  static const topo_pgroup_info_t smp_pgroup = {
 238  238          TOPO_PGROUP_SMP,
 239  239          TOPO_STABILITY_PRIVATE,
 240  240          TOPO_STABILITY_PRIVATE,
 241  241          1
 242  242  };
 243  243  
 244  244  static const topo_pgroup_info_t ses_pgroup = {
 245  245          TOPO_PGROUP_SES,
 246  246          TOPO_STABILITY_PRIVATE,
 247  247          TOPO_STABILITY_PRIVATE,
 248  248          1
 249  249  };
 250  250  
 251  251  static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
 252  252      nvlist_t **);
 253  253  static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
 254  254      nvlist_t **);
 255  255  
 256  256  static const topo_method_t ses_component_methods[] = {
 257  257          { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
 258  258              TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present },
 259  259          { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
 260  260              TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
 261  261          { TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC,
 262  262              TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL,
 263  263              topo_method_sensor_failure },
 264  264          { NULL }
 265  265  };
 266  266  
 267  267  static const topo_method_t ses_bay_methods[] = {
 268  268          { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
 269  269              TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
 270  270          { NULL }
 271  271  };
 272  272  
 273  273  static const topo_method_t ses_enclosure_methods[] = {
 274  274          { TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC,
 275  275              TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains },
 276  276          { TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
 277  277              TOPO_STABILITY_INTERNAL, ses_enc_enum_facility },
 278  278          { NULL }
 279  279  };
 280  280  
 281  281  /*
 282  282   * Functions for tracking ses devices which we were unable to open. We retry
 283  283   * these at regular intervals using ses_recheck_dir() and if we find that we
 284  284   * can now open any of them then we send a sysevent to indicate that a new topo
 285  285   * snapshot should be taken.
 286  286   */
 287  287  typedef struct ses_open_fail_list {
 288  288          struct ses_open_fail_list       *sof_next;
 289  289          char                            *sof_path;
 290  290  } ses_open_fail_list_t;
 291  291  
 292  292  static ses_open_fail_list_t *ses_sofh;
 293  293  static pthread_mutex_t ses_sofmt;
 294  294  static void ses_ct_print(char *ptr);
 295  295  
 296  296  static void
 297  297  ses_recheck_dir()
 298  298  {
 299  299          ses_target_t *target;
 300  300          sysevent_id_t eid;
 301  301          char buf[80];
 302  302          ses_open_fail_list_t *sof;
 303  303  
 304  304          /*
 305  305           * check list of "unable to open" devices
 306  306           */
 307  307          (void) pthread_mutex_lock(&ses_sofmt);
 308  308          for (sof = ses_sofh; sof != NULL; sof = sof->sof_next) {
 309  309                  /*
 310  310                   * see if we can open it now
 311  311                   */
 312  312                  if ((target = ses_open(LIBSES_VERSION,
 313  313                      sof->sof_path)) == NULL) {
 314  314                          (void) snprintf(buf, sizeof (buf),
 315  315                              "recheck_dir - still can't open %s", sof->sof_path);
 316  316                          ses_ct_print(buf);
 317  317                          continue;
 318  318                  }
 319  319  
 320  320                  /*
 321  321                   * ok - better force a new snapshot
 322  322                   */
 323  323                  (void) snprintf(buf, sizeof (buf),
 324  324                      "recheck_dir - can now open %s", sof->sof_path);
 325  325                  ses_ct_print(buf);
 326  326                  (void) sysevent_post_event(EC_PLATFORM, ESC_PLATFORM_SP_RESET,
 327  327                      SUNW_VENDOR, "fmd", NULL, &eid);
 328  328                  ses_close(target);
 329  329                  break;
 330  330          }
 331  331          (void) pthread_mutex_unlock(&ses_sofmt);
 332  332  }
 333  333  
 334  334  static void
 335  335  ses_sof_alloc(topo_mod_t *mod, char *path)
 336  336  {
 337  337          ses_open_fail_list_t *sof;
 338  338  
 339  339          (void) pthread_mutex_lock(&ses_sofmt);
 340  340          sof = topo_mod_zalloc(mod, sizeof (*sof));
 341  341          topo_mod_dprintf(mod, "sof_alloc %s", path);
 342  342          sof->sof_path = path;
 343  343          sof->sof_next = ses_sofh;
 344  344          ses_sofh = sof;
 345  345          (void) pthread_mutex_unlock(&ses_sofmt);
 346  346  }
 347  347  
 348  348  static void
 349  349  ses_sof_freeall(topo_mod_t *mod)
 350  350  {
 351  351          ses_open_fail_list_t *sof, *next_sof;
 352  352  
 353  353          (void) pthread_mutex_lock(&ses_sofmt);
 354  354          for (sof = ses_sofh; sof != NULL; sof = next_sof) {
 355  355                  next_sof = sof->sof_next;
 356  356                  topo_mod_dprintf(mod, "sof_freeall %s", sof->sof_path);
 357  357                  topo_mod_strfree(mod, sof->sof_path);
 358  358                  topo_mod_free(mod, sof, sizeof (*sof));
 359  359          }
 360  360          ses_sofh = NULL;
 361  361          (void) pthread_mutex_unlock(&ses_sofmt);
 362  362  }
 363  363  
 364  364  /*
 365  365   * functions for verifying that the ses_enum_target_t held in a device
 366  366   * contract's cookie field is still valid (it may have been freed by
 367  367   * ses_release()).
 368  368   */
 369  369  typedef struct ses_stp_list {
 370  370          struct ses_stp_list     *ssl_next;
 371  371          ses_enum_target_t       *ssl_tgt;
 372  372  } ses_stp_list_t;
 373  373  
 374  374  static ses_stp_list_t *ses_sslh;
 375  375  static pthread_mutex_t ses_sslmt;
 376  376  
 377  377  static void
 378  378  ses_ssl_alloc(topo_mod_t *mod, ses_enum_target_t *stp)
 379  379  {
 380  380          ses_stp_list_t *ssl;
 381  381  
 382  382          (void) pthread_mutex_lock(&ses_sslmt);
 383  383          ssl = topo_mod_zalloc(mod, sizeof (*ssl));
 384  384          topo_mod_dprintf(mod, "ssl_alloc %p", stp);
 385  385          ssl->ssl_tgt = stp;
 386  386          ssl->ssl_next = ses_sslh;
 387  387          ses_sslh = ssl;
 388  388          (void) pthread_mutex_unlock(&ses_sslmt);
 389  389  }
 390  390  
 391  391  static void
 392  392  ses_ssl_free(topo_mod_t *mod, ses_enum_target_t *stp)
 393  393  {
 394  394          ses_stp_list_t *ssl, *prev_ssl;
 395  395  
 396  396          (void) pthread_mutex_lock(&ses_sslmt);
 397  397          prev_ssl = NULL;
 398  398          for (ssl = ses_sslh; ssl != NULL; ssl = ssl->ssl_next) {
 399  399                  if (ssl->ssl_tgt == stp) {
 400  400                          topo_mod_dprintf(mod, "ssl_free %p", ssl->ssl_tgt);
 401  401                          if (prev_ssl == NULL)
 402  402                                  ses_sslh = ssl->ssl_next;
 403  403                          else
 404  404                                  prev_ssl->ssl_next = ssl->ssl_next;
 405  405                          topo_mod_free(mod, ssl, sizeof (*ssl));
 406  406                          break;
 407  407                  }
 408  408                  prev_ssl = ssl;
 409  409          }
 410  410          (void) pthread_mutex_unlock(&ses_sslmt);
 411  411  }
 412  412  
 413  413  static int
 414  414  ses_ssl_valid(ses_enum_target_t *stp)
 415  415  {
 416  416          ses_stp_list_t *ssl;
 417  417  
 418  418          for (ssl = ses_sslh; ssl != NULL; ssl = ssl->ssl_next)
 419  419                  if (ssl->ssl_tgt == stp)
 420  420                          return (1);
 421  421          return (0);
 422  422  }
 423  423  
 424  424  /*
 425  425   * Functions for creating and destroying a background thread
 426  426   * (ses_contract_thread) used for detecting when ses devices have been
 427  427   * retired/unretired.
 428  428   */
 429  429  static struct ses_thread_s {
 430  430          pthread_mutex_t mt;
 431  431          pthread_t tid;
 432  432          int thr_sig;
 433  433          int doexit;
 434  434          int count;
 435  435  } sesthread = {
 436  436          PTHREAD_MUTEX_INITIALIZER,
 437  437          0,
 438  438          SIGTERM,
 439  439          0,
 440  440          0
 441  441  };
 442  442  
 443  443  typedef struct ses_mod_list {
 444  444          struct ses_mod_list     *smod_next;
 445  445          topo_mod_t              *smod_mod;
 446  446  } ses_mod_list_t;
 447  447  
 448  448  static ses_mod_list_t *ses_smod;
 449  449  
 450  450  static void
 451  451  ses_ct_print(char *ptr)
 452  452  {
 453  453          (void) pthread_mutex_lock(&sesthread.mt);
 454  454          if (ses_smod != NULL && ses_smod->smod_mod != NULL)
 455  455                  topo_mod_dprintf(ses_smod->smod_mod, ptr);
 456  456          (void) pthread_mutex_unlock(&sesthread.mt);
 457  457  }
 458  458  
 459  459  /*ARGSUSED*/
 460  460  static void *
 461  461  ses_contract_thread(void *arg)
 462  462  {
 463  463          int efd, ctlfd, statfd;
 464  464          ct_evthdl_t ev;
 465  465          ctevid_t evid;
 466  466          uint_t event;
 467  467          char path[PATH_MAX];
 468  468          char buf[80];
 469  469          ses_enum_target_t *stp;
 470  470          ct_stathdl_t stathdl;
 471  471          ctid_t ctid;
 472  472          struct pollfd fds;
 473  473          int pollret;
 474  474          sigset_t sigset;
 475  475  
 476  476          ses_ct_print("start contract event thread");
 477  477          efd = open64(CTFS_ROOT "/device/pbundle", O_RDONLY);
 478  478          fds.fd = efd;
 479  479          fds.events = POLLIN;
 480  480          fds.revents = 0;
 481  481          sigaddset(&sigset, sesthread.thr_sig);
 482  482          pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
 483  483          for (;;) {
 484  484                  /* check if we've been asked to exit */
 485  485                  (void) pthread_mutex_lock(&sesthread.mt);
 486  486                  if (sesthread.doexit) {
 487  487                          (void) pthread_mutex_unlock(&sesthread.mt);
 488  488                          break;
 489  489                  }
 490  490                  (void) pthread_mutex_unlock(&sesthread.mt);
 491  491  
 492  492                  /* poll until an event arrives */
 493  493                  if ((pollret = poll(&fds, 1, 10000)) <= 0) {
 494  494                          if (pollret == 0)
 495  495                                  ses_recheck_dir();
 496  496                          continue;
 497  497                  }
 498  498  
 499  499                  /* read the event */
 500  500                  (void) pthread_mutex_lock(&ses_sslmt);
 501  501                  ses_ct_print("read contract event");
 502  502                  if (ct_event_read(efd, &ev) != 0) {
 503  503                          (void) pthread_mutex_unlock(&ses_sslmt);
 504  504                          continue;
 505  505                  }
 506  506  
 507  507                  /* see if it is an event we are expecting */
 508  508                  ctid = ct_event_get_ctid(ev);
 509  509                  (void) snprintf(buf, sizeof (buf),
 510  510                      "got contract event ctid=%d", ctid);
 511  511                  ses_ct_print(buf);
 512  512                  event = ct_event_get_type(ev);
 513  513                  if (event != CT_DEV_EV_OFFLINE && event != CT_EV_NEGEND) {
 514  514                          (void) snprintf(buf, sizeof (buf),
 515  515                              "bad contract event %x", event);
 516  516                          ses_ct_print(buf);
 517  517                          ct_event_free(ev);
 518  518                          (void) pthread_mutex_unlock(&ses_sslmt);
 519  519                          continue;
 520  520                  }
 521  521  
 522  522                  /* find target pointer saved in cookie */
 523  523                  evid = ct_event_get_evid(ev);
 524  524                  (void) snprintf(path, PATH_MAX, CTFS_ROOT "/device/%ld/status",
 525  525                      ctid);
 526  526                  statfd = open64(path, O_RDONLY);
 527  527                  (void) ct_status_read(statfd, CTD_COMMON, &stathdl);
 528  528                  stp = (ses_enum_target_t *)(uintptr_t)
 529  529                      ct_status_get_cookie(stathdl);
 530  530                  ct_status_free(stathdl);
 531  531                  (void) close(statfd);
 532  532  
 533  533                  /* check if target pointer is still valid */
 534  534                  if (ses_ssl_valid(stp) == 0) {
 535  535                          (void) snprintf(buf, sizeof (buf),
 536  536                              "contract already abandoned %x", event);
 537  537                          ses_ct_print(buf);
 538  538                          (void) snprintf(path, PATH_MAX,
 539  539                              CTFS_ROOT "/device/%ld/ctl", ctid);
 540  540                          ctlfd = open64(path, O_WRONLY);
 541  541                          if (event != CT_EV_NEGEND)
 542  542                                  (void) ct_ctl_ack(ctlfd, evid);
 543  543                          else
 544  544                                  (void) ct_ctl_abandon(ctlfd);
 545  545                          (void) close(ctlfd);
 546  546                          ct_event_free(ev);
 547  547                          (void) pthread_mutex_unlock(&ses_sslmt);
 548  548                          continue;
 549  549                  }
 550  550  
 551  551                  /* find control device for ack/abandon */
 552  552                  (void) pthread_mutex_lock(&stp->set_lock);
 553  553                  (void) snprintf(path, PATH_MAX, CTFS_ROOT "/device/%ld/ctl",
 554  554                      ctid);
 555  555                  ctlfd = open64(path, O_WRONLY);
 556  556                  if (event != CT_EV_NEGEND) {
 557  557                          /* if this is an offline event, do the offline */
 558  558                          ses_ct_print("got contract offline event");
 559  559                          if (stp->set_target) {
 560  560                                  ses_ct_print("contract thread rele");
 561  561                                  ses_snap_rele(stp->set_snap);
 562  562                                  ses_close(stp->set_target);
 563  563                                  stp->set_target = NULL;
 564  564                          }
 565  565                          (void) ct_ctl_ack(ctlfd, evid);
 566  566                  } else {
 567  567                          /* if this is the negend, then abandon the contract */
 568  568                          ses_ct_print("got contract negend");
 569  569                          if (stp->set_ctid) {
 570  570                                  (void) snprintf(buf, sizeof (buf),
 571  571                                      "abandon old contract %d", stp->set_ctid);
 572  572                                  ses_ct_print(buf);
 573  573                                  stp->set_ctid = NULL;
 574  574                          }
 575  575                          (void) ct_ctl_abandon(ctlfd);
 576  576                  }
 577  577                  (void) close(ctlfd);
 578  578                  (void) pthread_mutex_unlock(&stp->set_lock);
 579  579                  ct_event_free(ev);
 580  580                  (void) pthread_mutex_unlock(&ses_sslmt);
 581  581          }
 582  582          (void) close(efd);
 583  583          return (NULL);
 584  584  }
 585  585  
 586  586  int
 587  587  find_thr_sig(void)
 588  588  {
 589  589          int i;
 590  590          sigset_t oset, rset;
 591  591          int sig[] = {SIGTERM, SIGUSR1, SIGUSR2};
 592  592          int sig_sz = sizeof (sig) / sizeof (int);
 593  593          int rc = SIGTERM;
 594  594  
 595  595          /* prefered set of signals that are likely used to terminate threads */
 596  596          (void) sigemptyset(&oset);
 597  597          (void) pthread_sigmask(SIG_SETMASK, NULL, &oset);
 598  598          for (i = 0; i < sig_sz; i++) {
 599  599                  if (sigismember(&oset, sig[i]) == 0) {
 600  600                          return (sig[i]);
 601  601                  }
 602  602          }
 603  603  
 604  604          /* reserved set of signals that are not allowed to terminate thread */
 605  605          (void) sigemptyset(&rset);
 606  606          (void) sigaddset(&rset, SIGABRT);
 607  607          (void) sigaddset(&rset, SIGKILL);
 608  608          (void) sigaddset(&rset, SIGSTOP);
 609  609          (void) sigaddset(&rset, SIGCANCEL);
 610  610  
 611  611          /* Find signal that is not masked and not in the reserved list. */
 612  612          for (i = 1; i < MAXSIG; i++) {
 613  613                  if (sigismember(&rset, i) == 1) {
 614  614                          continue;
 615  615                  }
 616  616                  if (sigismember(&oset, i) == 0) {
 617  617                          return (i);
 618  618                  }
 619  619          }
 620  620  
 621  621          return (rc);
 622  622  }
 623  623  
 624  624  /*ARGSUSED*/
 625  625  static void
 626  626  ses_handler(int sig)
 627  627  {
 628  628  }
 629  629  
 630  630  static void
 631  631  ses_thread_init(topo_mod_t *mod)
 632  632  {
 633  633          pthread_attr_t *attr = NULL;
 634  634          struct sigaction act;
 635  635          ses_mod_list_t *smod;
 636  636  
 637  637          (void) pthread_mutex_lock(&sesthread.mt);
 638  638          sesthread.count++;
 639  639          smod = topo_mod_zalloc(mod, sizeof (*smod));
 640  640          smod->smod_mod = mod;
 641  641          smod->smod_next = ses_smod;
 642  642          ses_smod = smod;
 643  643          if (sesthread.tid == 0) {
 644  644                  /* find a suitable signal to use for killing the thread below */
 645  645                  sesthread.thr_sig = find_thr_sig();
 646  646  
 647  647                  /* if don't have a handler for this signal, create one */
 648  648                  (void) sigaction(sesthread.thr_sig, NULL, &act);
 649  649                  if (act.sa_handler == SIG_DFL || act.sa_handler == SIG_IGN)
 650  650                          act.sa_handler = ses_handler;
 651  651                  (void) sigaction(sesthread.thr_sig, &act, NULL);
 652  652  
 653  653                  /* create a thread to listen for offline events */
 654  654                  (void) pthread_create(&sesthread.tid,
 655  655                      attr, ses_contract_thread, NULL);
 656  656          }
 657  657          (void) pthread_mutex_unlock(&sesthread.mt);
 658  658  }
 659  659  
 660  660  static void
 661  661  ses_thread_fini(topo_mod_t *mod)
 662  662  {
 663  663          ses_mod_list_t *smod, *prev_smod;
 664  664  
 665  665          (void) pthread_mutex_lock(&sesthread.mt);
 666  666          prev_smod = NULL;
 667  667          for (smod = ses_smod; smod != NULL; smod = smod->smod_next) {
 668  668                  if (smod->smod_mod == mod) {
 669  669                          if (prev_smod == NULL)
 670  670                                  ses_smod = smod->smod_next;
 671  671                          else
 672  672                                  prev_smod->smod_next = smod->smod_next;
 673  673                          topo_mod_free(mod, smod, sizeof (*smod));
 674  674                          break;
 675  675                  }
 676  676                  prev_smod = smod;
 677  677          }
 678  678          if (--sesthread.count > 0) {
 679  679                  (void) pthread_mutex_unlock(&sesthread.mt);
 680  680                  return;
 681  681          }
 682  682          sesthread.doexit = 1;
 683  683          (void) pthread_mutex_unlock(&sesthread.mt);
 684  684          (void) pthread_kill(sesthread.tid, sesthread.thr_sig);
 685  685          (void) pthread_join(sesthread.tid, NULL);
 686  686          sesthread.tid = 0;
 687  687  }
 688  688  
 689  689  static void
 690  690  ses_create_contract(topo_mod_t *mod, ses_enum_target_t *stp)
 691  691  {
 692  692          int tfd, len, rval;
 693  693          char link_path[PATH_MAX];
 694  694  
 695  695          stp->set_ctid = NULL;
 696  696  
 697  697          /* convert "/dev" path into "/devices" path */
 698  698          if ((len = readlink(stp->set_devpath, link_path, PATH_MAX)) < 0) {
 699  699                  topo_mod_dprintf(mod, "readlink failed");
 700  700                  return;
 701  701          }
 702  702          link_path[len] = '\0';
 703  703  
 704  704          /* set up template to create new contract */
 705  705          tfd = open64(CTFS_ROOT "/device/template", O_RDWR);
 706  706          (void) ct_tmpl_set_critical(tfd, CT_DEV_EV_OFFLINE);
 707  707          (void) ct_tmpl_set_cookie(tfd, (uint64_t)(uintptr_t)stp);
 708  708  
 709  709          /* strip "../../devices" off the front and create the contract */
 710  710          if ((rval = ct_dev_tmpl_set_minor(tfd, &link_path[13])) != 0)
 711  711                  topo_mod_dprintf(mod, "failed to set minor %s rval = %d",
 712  712                      &link_path[13], rval);
 713  713          else if ((rval = ct_tmpl_create(tfd, &stp->set_ctid)) != 0)
 714  714                  topo_mod_dprintf(mod, "failed to create ctid rval = %d", rval);
 715  715          else
 716  716                  topo_mod_dprintf(mod, "created ctid=%d", stp->set_ctid);
 717  717          (void) close(tfd);
 718  718  }
 719  719  
 720  720  static void
 721  721  ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp)
 722  722  {
 723  723          if (--stp->set_refcount == 0) {
 724  724                  /* check if already closed due to contract offline request */
 725  725                  (void) pthread_mutex_lock(&stp->set_lock);
 726  726                  if (stp->set_target) {
 727  727                          ses_snap_rele(stp->set_snap);
 728  728                          ses_close(stp->set_target);
 729  729                          stp->set_target = NULL;
 730  730                  }
 731  731                  if (stp->set_ctid) {
 732  732                          int ctlfd;
 733  733                          char path[PATH_MAX];
 734  734  
 735  735                          topo_mod_dprintf(mod, "abandon old contract %d",
 736  736                              stp->set_ctid);
 737  737                          (void) snprintf(path, PATH_MAX,
 738  738                              CTFS_ROOT "/device/%ld/ctl", stp->set_ctid);
 739  739                          ctlfd = open64(path, O_WRONLY);
 740  740                          (void) ct_ctl_abandon(ctlfd);
 741  741                          (void) close(ctlfd);
 742  742                          stp->set_ctid = NULL;
 743  743                  }
 744  744                  (void) pthread_mutex_unlock(&stp->set_lock);
 745  745                  ses_ssl_free(mod, stp);
 746  746                  topo_mod_strfree(mod, stp->set_devpath);
 747  747                  topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
 748  748          }
 749  749  }
 750  750  
 751  751  static void
 752  752  ses_data_free(ses_enum_data_t *sdp, ses_enum_chassis_t *pcp)
 753  753  {
 754  754          topo_mod_t *mod = sdp->sed_mod;
 755  755          ses_enum_chassis_t *cp;
 756  756          ses_enum_node_t *np;
 757  757          ses_enum_target_t *tp;
 758  758          ses_alt_node_t *ap;
 759  759          topo_list_t *cpl;
 760  760  
 761  761  
 762  762          if (pcp != NULL)
 763  763                  cpl = &pcp->sec_subchassis;
 764  764          else
 765  765                  cpl = &sdp->sed_chassis;
 766  766  
 767  767          while ((cp = topo_list_next(cpl)) != NULL) {
 768  768                  topo_list_delete(cpl, cp);
 769  769  
 770  770                  while ((np = topo_list_next(&cp->sec_nodes)) != NULL) {
 771  771                          while ((ap = topo_list_next(&np->sen_alt_nodes)) !=
 772  772                              NULL) {
 773  773                                  topo_list_delete(&np->sen_alt_nodes, ap);
 774  774                                  topo_mod_free(mod, ap, sizeof (ses_alt_node_t));
 775  775                          }
 776  776                          topo_list_delete(&cp->sec_nodes, np);
 777  777                          topo_mod_free(mod, np, sizeof (ses_enum_node_t));
 778  778                  }
 779  779  
 780  780                  while ((tp = topo_list_next(&cp->sec_targets)) != NULL) {
 781  781                          topo_list_delete(&cp->sec_targets, tp);
 782  782                          ses_target_free(mod, tp);
 783  783                  }
 784  784  
 785  785                  topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t));
 786  786          }
 787  787  
 788  788          if (pcp == NULL) {
 789  789                  dev_list_free(mod, &sdp->sed_devs);
 790  790                  topo_mod_free(mod, sdp, sizeof (ses_enum_data_t));
 791  791          }
 792  792  }
 793  793  
 794  794  /*
 795  795   * For enclosure nodes, we have a special contains method.  By default, the hc
 796  796   * walker will compare the node name and instance number to determine if an
 797  797   * FMRI matches.  For enclosures where the enumeration order is impossible to
 798  798   * predict, we instead use the chassis-id as a unique identifier, and ignore
 799  799   * the instance number.
 800  800   */
 801  801  static int
 802  802  fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2)
 803  803  {
 804  804          uint8_t v1, v2;
 805  805          nvlist_t **hcp1, **hcp2;
 806  806          int err, i;
 807  807          uint_t nhcp1, nhcp2;
 808  808          nvlist_t *a1, *a2;
 809  809          char *c1, *c2;
 810  810          int mindepth;
 811  811  
 812  812          if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 ||
 813  813              nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 ||
 814  814              v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION)
 815  815                  return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
 816  816  
 817  817          err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1);
 818  818          err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2);
 819  819          if (err != 0)
 820  820                  return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
 821  821  
 822  822          /*
 823  823           * If the chassis-id doesn't match, then these FMRIs are not
 824  824           * equivalent.  If one of the FMRIs doesn't have a chassis ID, then we
 825  825           * have no choice but to fall back to the instance ID.
 826  826           */
 827  827          if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 &&
 828  828              nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 &&
 829  829              nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 &&
 830  830              nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) {
 831  831                  if (strcmp(c1, c2) != 0)
 832  832                          return (0);
 833  833  
 834  834                  mindepth = 1;
 835  835          } else {
 836  836                  mindepth = 0;
 837  837          }
 838  838  
 839  839          if (nhcp2 < nhcp1)
 840  840                  return (0);
 841  841  
 842  842          for (i = 0; i < nhcp1; i++) {
 843  843                  char *nm1 = NULL;
 844  844                  char *nm2 = NULL;
 845  845                  char *id1 = NULL;
 846  846                  char *id2 = NULL;
 847  847  
 848  848                  (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1);
 849  849                  (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2);
 850  850                  (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1);
 851  851                  (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2);
 852  852                  if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL)
 853  853                          return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
 854  854  
 855  855                  if (strcmp(nm1, nm2) == 0 &&
 856  856                      (i < mindepth || strcmp(id1, id2) == 0))
 857  857                          continue;
 858  858  
 859  859                  return (0);
 860  860          }
 861  861  
 862  862          return (1);
 863  863  }
 864  864  
 865  865  /*ARGSUSED*/
 866  866  static int
 867  867  ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
 868  868      nvlist_t *in, nvlist_t **out)
 869  869  {
 870  870          int ret;
 871  871          nvlist_t *nv1, *nv2;
 872  872  
 873  873          if (version > TOPO_METH_CONTAINS_VERSION)
 874  874                  return (topo_mod_seterrno(mod, EMOD_VER_NEW));
 875  875  
 876  876          if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 ||
 877  877              nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0)
 878  878                  return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
 879  879  
 880  880          ret = fmri_contains(mod, nv1, nv2);
 881  881          if (ret < 0)
 882  882                  return (-1);
 883  883  
 884  884          if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) {
 885  885                  if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET,
 886  886                      ret) == 0)
 887  887                          return (0);
 888  888                  else
 889  889                          nvlist_free(*out);
 890  890          }
 891  891  
 892  892          return (-1);
 893  893  
 894  894  }
 895  895  
 896  896  /*
 897  897   * Return a current instance of the node.  This is somewhat complicated because
 898  898   * we need to take a new snapshot in order to get the new data, but we don't
 899  899   * want to be constantly taking SES snapshots if the consumer is going to do a
 900  900   * series of queries.  So we adopt the strategy of assuming that the SES state
 901  901   * is not going to be rapidly changing, and limit our snapshot frequency to
 902  902   * some defined bounds.
 903  903   */
 904  904  ses_node_t *
 905  905  ses_node_lock(topo_mod_t *mod, tnode_t *tn)
 906  906  {
 907  907          ses_enum_target_t *tp = topo_node_getspecific(tn);
 908  908          hrtime_t now;
 909  909          ses_snap_t *snap;
 910  910          int err;
 911  911          uint64_t nodeid;
 912  912          ses_node_t *np;
 913  913  
 914  914          if (tp == NULL) {
 915  915                  (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
 916  916                  return (NULL);
 917  917          }
 918  918  
 919  919          (void) pthread_mutex_lock(&tp->set_lock);
 920  920  
 921  921          /*
 922  922           * Determine if we need to take a new snapshot.
 923  923           */
 924  924          now = gethrtime();
 925  925  
 926  926          if (tp->set_target == NULL) {
 927  927                  /*
 928  928                   * We may have closed the device but not yet abandoned the
 929  929                   * contract (ie we've had the offline event but not yet the
 930  930                   * negend). If so, just return failure.
 931  931                   */
 932  932                  if (tp->set_ctid != NULL) {
 933  933                          (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
 934  934                          (void) pthread_mutex_unlock(&tp->set_lock);
 935  935                          return (NULL);
 936  936                  }
 937  937  
 938  938                  /*
 939  939                   * The device has been closed due to a contract offline
 940  940                   * request, then we need to reopen it and create a new contract.
 941  941                   */
 942  942                  if ((tp->set_target =
 943  943                      ses_open(LIBSES_VERSION, tp->set_devpath)) == NULL) {
 944  944                          sysevent_id_t eid;
 945  945  
 946  946                          (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
 947  947                          (void) pthread_mutex_unlock(&tp->set_lock);
 948  948                          topo_mod_dprintf(mod, "recheck_dir - "
 949  949                              "can no longer open %s", tp->set_devpath);
 950  950                          (void) sysevent_post_event(EC_PLATFORM,
 951  951                              ESC_PLATFORM_SP_RESET, SUNW_VENDOR, "fmd", NULL,
 952  952                              &eid);
 953  953                          return (NULL);
 954  954                  }
 955  955                  topo_mod_dprintf(mod, "reopen contract");
 956  956                  ses_create_contract(mod, tp);
 957  957                  tp->set_snap = ses_snap_hold(tp->set_target);
 958  958                  tp->set_snaptime = gethrtime();
 959  959          } else if (now - tp->set_snaptime > (ses_snap_freq * 1000 * 1000) &&
 960  960              (snap = ses_snap_new(tp->set_target)) != NULL) {
 961  961                  if (ses_snap_generation(snap) !=
 962  962                      ses_snap_generation(tp->set_snap)) {
 963  963                          /*
 964  964                           * If we find ourselves in this situation, we're in
 965  965                           * trouble.  The generation count has changed, which
 966  966                           * indicates that our current topology is out of date.
 967  967                           * But we need to consult the new topology in order to
 968  968                           * determine presence at this moment in time.  We can't
 969  969                           * go back and change the topo snapshot in situ, so
 970  970                           * we'll just have to fail the call in this unlikely
 971  971                           * scenario.
 972  972                           */
 973  973                          ses_snap_rele(snap);
 974  974                          (void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
 975  975                          (void) pthread_mutex_unlock(&tp->set_lock);
 976  976                          return (NULL);
 977  977                  } else {
 978  978                          ses_snap_rele(tp->set_snap);
 979  979                          tp->set_snap = snap;
 980  980                  }
 981  981                  tp->set_snaptime = gethrtime();
 982  982          }
 983  983  
 984  984          snap = tp->set_snap;
 985  985  
 986  986          verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES,
 987  987              TOPO_PROP_NODE_ID, &nodeid, &err) == 0);
 988  988          verify((np = ses_node_lookup(snap, nodeid)) != NULL);
 989  989  
 990  990          return (np);
 991  991  }
 992  992  
 993  993  /*ARGSUSED*/
 994  994  void
 995  995  ses_node_unlock(topo_mod_t *mod, tnode_t *tn)
 996  996  {
 997  997          ses_enum_target_t *tp = topo_node_getspecific(tn);
 998  998  
 999  999          verify(tp != NULL);
1000 1000  
1001 1001          (void) pthread_mutex_unlock(&tp->set_lock);
1002 1002  }
1003 1003  
1004 1004  /*
1005 1005   * Determine if the element is present.
1006 1006   */
1007 1007  /*ARGSUSED*/
1008 1008  static int
1009 1009  ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
  
    | 
      ↓ open down ↓ | 
    974 lines elided | 
    
      ↑ open up ↑ | 
  
1010 1010      nvlist_t *in, nvlist_t **out)
1011 1011  {
1012 1012          boolean_t present;
1013 1013          ses_node_t *np;
1014 1014          nvlist_t *props, *nvl;
1015 1015          uint64_t status;
1016 1016  
1017 1017          if ((np = ses_node_lock(mod, tn)) == NULL)
1018 1018                  return (-1);
1019 1019  
1020      -        verify((props = ses_node_props(np)) != NULL);
1021      -        verify(nvlist_lookup_uint64(props,
1022      -            SES_PROP_STATUS_CODE, &status) == 0);
     1020 +        /*
     1021 +         * If the SES properties are not there or
     1022 +         * status cannot be determined, continue
     1023 +         * and indicate status is unknown.
     1024 +         */
     1025 +        if (((props = ses_node_props(np)) == NULL) ||
     1026 +            (nvlist_lookup_uint64(props,
     1027 +            SES_PROP_STATUS_CODE, &status) != 0)) {
     1028 +                status = SES_ESC_UNKNOWN;
     1029 +        }
1023 1030  
1024 1031          ses_node_unlock(mod, tn);
1025 1032  
1026 1033          present = (status != SES_ESC_NOT_INSTALLED);
1027 1034  
1028 1035          if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
1029 1036                  return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
1030 1037  
1031 1038          if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET,
1032 1039              present) != 0) {
1033 1040                  nvlist_free(nvl);
1034 1041                  return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
1035 1042          }
1036 1043  
1037 1044          *out = nvl;
1038 1045  
1039 1046          return (0);
1040 1047  }
1041 1048  
1042 1049  /*
1043 1050   * Sets standard properties for a ses node (enclosure, bay, controller
1044 1051   * or expander).
1045 1052   * This includes setting the FRU, as well as setting the
1046 1053   * authority information.  When  the fru topo node(frutn) is not NULL
1047 1054   * its resouce should be used as FRU.
1048 1055   */
1049 1056  static int
1050 1057  ses_set_standard_props(topo_mod_t *mod, tnode_t *frutn, tnode_t *tn,
1051 1058      nvlist_t *auth, uint64_t nodeid, const char *path)
1052 1059  {
1053 1060          int err;
1054 1061          char *product, *chassis;
1055 1062          nvlist_t *fmri;
1056 1063  
1057 1064          /*
1058 1065           * Set the authority explicitly if specified.
1059 1066           */
1060 1067          if (auth) {
1061 1068                  verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT,
1062 1069                      &product) == 0);
1063 1070                  verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS,
1064 1071                      &chassis) == 0);
1065 1072                  if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
1066 1073                      FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product,
1067 1074                      &err) != 0 ||
1068 1075                      topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
1069 1076                      FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis,
1070 1077                      &err) != 0 ||
1071 1078                      topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
1072 1079                      FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "",
1073 1080                      &err) != 0) {
1074 1081                          topo_mod_dprintf(mod, "failed to add authority "
1075 1082                              "properties: %s\n", topo_strerror(err));
1076 1083                          return (topo_mod_seterrno(mod, err));
1077 1084                  }
1078 1085          }
1079 1086  
1080 1087          /*
1081 1088           * Copy the resource and set that as the FRU.
1082 1089           */
1083 1090          if (frutn != NULL) {
1084 1091                  if (topo_node_resource(frutn, &fmri, &err) != 0) {
1085 1092                          topo_mod_dprintf(mod,
1086 1093                              "topo_node_resource() failed : %s\n",
1087 1094                              topo_strerror(err));
1088 1095                          return (topo_mod_seterrno(mod, err));
1089 1096                  }
1090 1097          } else {
1091 1098                  if (topo_node_resource(tn, &fmri, &err) != 0) {
1092 1099                          topo_mod_dprintf(mod,
1093 1100                              "topo_node_resource() failed : %s\n",
1094 1101                              topo_strerror(err));
1095 1102                          return (topo_mod_seterrno(mod, err));
1096 1103                  }
1097 1104          }
1098 1105  
1099 1106          if (topo_node_fru_set(tn, fmri, 0, &err) != 0) {
1100 1107                  topo_mod_dprintf(mod,
1101 1108                      "topo_node_fru_set() failed : %s\n",
1102 1109                      topo_strerror(err));
1103 1110                  nvlist_free(fmri);
1104 1111                  return (topo_mod_seterrno(mod, err));
1105 1112          }
1106 1113  
1107 1114          nvlist_free(fmri);
1108 1115  
1109 1116          /*
1110 1117           * Set the SES-specific properties so that consumers can query
1111 1118           * additional information about the particular SES element.
1112 1119           */
1113 1120          if (topo_pgroup_create(tn, &ses_pgroup, &err) != 0) {
1114 1121                  topo_mod_dprintf(mod, "failed to create propgroup "
1115 1122                      "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err));
1116 1123                  return (-1);
1117 1124          }
1118 1125  
1119 1126          if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES,
1120 1127              TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE,
1121 1128              nodeid, &err) != 0) {
1122 1129                  topo_mod_dprintf(mod,
1123 1130                      "failed to create property %s: %s\n",
1124 1131                      TOPO_PROP_NODE_ID, topo_strerror(err));
1125 1132                  return (-1);
1126 1133          }
1127 1134  
1128 1135          if (topo_prop_set_string(tn, TOPO_PGROUP_SES,
1129 1136              TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE,
1130 1137              path, &err) != 0) {
1131 1138                  topo_mod_dprintf(mod,
1132 1139                      "failed to create property %s: %s\n",
1133 1140                      TOPO_PROP_TARGET_PATH, topo_strerror(err));
1134 1141                  return (-1);
1135 1142          }
1136 1143  
1137 1144          return (0);
1138 1145  }
1139 1146  
1140 1147  /*
1141 1148   * Iterate over the SES phy information. If any of the ports indicates that it's
1142 1149   * a SATA device and we haven't matched any disk devices yet, that means
1143 1150   * that the HBA was able to create a WWN for the SATA device based on its GUID,
1144 1151   * which is good. However, SES includes the WWN for the device's STP bridge. In
1145 1152   * theory, if the driver includes the WWN based on the SATA guid then it should
1146 1153   * also set the bridge-port property indicating the WWN that should match the
1147 1154   * SATA device.
1148 1155   */
1149 1156  static int
1150 1157  ses_create_disk_bridge(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props,
1151 1158      tnode_t **child)
1152 1159  {
1153 1160          nvlist_t **phys;
1154 1161          uint_t i, n_phys;
1155 1162          topo_mod_t *mod = sdp->sed_mod;
1156 1163  
1157 1164          if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS, &phys,
1158 1165              &n_phys) != 0)
1159 1166                  return (1);
1160 1167  
1161 1168          for (i = 0; i < n_phys; i++) {
1162 1169                  uint64_t wwn;
1163 1170                  boolean_t sata;
1164 1171                  char wwnstr[64];
1165 1172  
1166 1173                  if (nvlist_lookup_uint64(phys[i], SES_SAS_PROP_ADDR,
1167 1174                      &wwn) != 0 || wwn == 0) {
1168 1175                          continue;
1169 1176                  }
1170 1177  
1171 1178                  if (nvlist_lookup_boolean_value(phys[i],
1172 1179                      SES_SAS_PROP_SATA_DEVICE, &sata) != 0 || !sata) {
1173 1180                          continue;
1174 1181                  }
1175 1182  
1176 1183                  if (scsi_wwn_to_wwnstr(wwn, 0, wwnstr) == NULL)
1177 1184                          continue;
1178 1185  
1179 1186                  if (disk_declare_bridge(mod, pnode, &sdp->sed_devs,
1180 1187                      wwnstr, child) == 0) {
1181 1188                          return (0);
1182 1189                  }
1183 1190  
1184 1191          }
1185 1192  
1186 1193          return (1);
1187 1194  }
1188 1195  
1189 1196  /*
1190 1197   * Callback to add a disk to a given bay.  We first check the status-code to
1191 1198   * determine if a disk is present, ignoring those that aren't in an appropriate
1192 1199   * state.  We then scan the parent bay node's SAS address array to determine
1193 1200   * possible attached SAS addresses.  We create a disk node if the disk is not
1194 1201   * SAS or the SES target does not support the necessary pages for this; if we
1195 1202   * find the SAS address, we create a disk node and also correlate it with
1196 1203   * the corresponding Solaris device node to fill in the rest of the data.
1197 1204   */
1198 1205  static int
1199 1206  ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props)
1200 1207  {
1201 1208          topo_mod_t *mod = sdp->sed_mod;
1202 1209          uint64_t status;
1203 1210          uint_t s, nsas;
1204 1211          char **paths;
1205 1212          int err, ret;
1206 1213          tnode_t *child = NULL;
1207 1214  
1208 1215          /*
1209 1216           * Skip devices that are not in a present (and possibly damaged) state.
1210 1217           */
1211 1218          if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0)
1212 1219                  return (0);
1213 1220  
1214 1221          if (status != SES_ESC_UNSUPPORTED &&
1215 1222              status != SES_ESC_OK &&
1216 1223              status != SES_ESC_CRITICAL &&
1217 1224              status != SES_ESC_NONCRITICAL &&
1218 1225              status != SES_ESC_UNRECOVERABLE &&
1219 1226              status != SES_ESC_NO_ACCESS &&
1220 1227              status != SES_ESC_UNKNOWN)
1221 1228                  return (0);
1222 1229  
1223 1230          topo_mod_dprintf(mod, "found attached disk");
1224 1231  
1225 1232          /*
1226 1233           * Create the disk range.
1227 1234           */
1228 1235          if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) {
1229 1236                  topo_mod_dprintf(mod,
1230 1237                      "topo_node_create_range() failed: %s",
1231 1238                      topo_mod_errmsg(mod));
1232 1239                  return (-1);
1233 1240          }
1234 1241  
1235 1242          /*
1236 1243           * Look through all SAS addresses and attempt to correlate them to a
1237 1244           * known Solaris device.  If we don't find a matching node, then we
1238 1245           * don't enumerate the disk node.
1239 1246           * Note that TOPO_PROP_SAS_ADDR prop includes SAS address from
1240 1247           * alternate elements that represent the same device.
1241 1248           */
1242 1249          if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES,
1243 1250              TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0)
1244 1251                  return (0);
1245 1252  
1246 1253          err = 0;
1247 1254  
1248 1255          for (s = 0; s < nsas; s++) {
1249 1256                  ret = disk_declare_addr(mod, pnode, &sdp->sed_devs, paths[s],
1250 1257                      &child);
1251 1258                  if (ret == 0) {
1252 1259                          break;
1253 1260                  } else if (ret < 0) {
1254 1261                          err = -1;
1255 1262                          break;
1256 1263                  }
1257 1264          }
1258 1265  
1259 1266          /*
1260 1267           * We need to take another pass through the properties for this bay by
1261 1268           * iterating over the phys and noting if any of these are SATA. Note,
1262 1269           * this information isn't commonly part of the topo tree at this time,
1263 1270           * hence why we end up going back and iterating over the properties
1264 1271           * ourselves.
1265 1272           */
1266 1273          if (s == nsas) {
1267 1274                  if (ses_create_disk_bridge(sdp, pnode, props, &child) != 0)
1268 1275                          (void) disk_declare_non_enumerated(mod, pnode, &child);
1269 1276          }
1270 1277  
1271 1278          /* copy sas_addresses (target-ports) from parent (with 'w'added) */
1272 1279          if (child != NULL) {
1273 1280                  int i;
1274 1281                  char **tports;
1275 1282                  uint64_t wwn;
1276 1283  
1277 1284                  tports = topo_mod_zalloc(mod, sizeof (char *) * nsas);
1278 1285                  if (tports != NULL) {
1279 1286                          for (i = 0; i < nsas; i++) {
1280 1287                                  if (scsi_wwnstr_to_wwn(paths[i], &wwn) !=
1281 1288                                      DDI_SUCCESS)
1282 1289                                          break;
1283 1290                                  tports[i] = scsi_wwn_to_wwnstr(wwn, 1, NULL);
1284 1291                                  if (tports[i] == NULL)
1285 1292                                          break;
1286 1293                          }
1287 1294                          /* if they all worked then create the property */
1288 1295                          if (i == nsas)
1289 1296                                  (void) topo_prop_set_string_array(child,
1290 1297                                      TOPO_PGROUP_STORAGE,
1291 1298                                      TOPO_STORAGE_TARGET_PORT_L0IDS,
1292 1299                                      TOPO_PROP_IMMUTABLE, (const char **)tports,
1293 1300                                      nsas, &err);
1294 1301  
1295 1302                          for (i = 0; i < nsas; i++)
1296 1303                                  if (tports[i] != NULL)
1297 1304                                          scsi_free_wwnstr(tports[i]);
1298 1305                          topo_mod_free(mod, tports, sizeof (char *) * nsas);
1299 1306                  }
1300 1307          }
1301 1308  
1302 1309          for (s = 0; s < nsas; s++)
1303 1310                  topo_mod_free(mod, paths[s], strlen(paths[s]) + 1);
1304 1311          topo_mod_free(mod, paths, nsas * sizeof (char *));
1305 1312  
1306 1313          return (err);
1307 1314  }
1308 1315  
1309 1316  static int
1310 1317  ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp)
1311 1318  {
1312 1319          ses_alt_node_t *ap;
1313 1320          ses_node_t *np;
1314 1321          nvlist_t *props;
1315 1322  
1316 1323          nvlist_t **phys;
1317 1324          uint_t i, j, n_phys, all_phys = 0;
1318 1325          char **paths;
1319 1326          uint64_t addr;
1320 1327          size_t len;
1321 1328          int terr, err = -1;
1322 1329  
1323 1330          for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
1324 1331              ap = topo_list_next(ap)) {
1325 1332                  np = ap->san_node;
1326 1333                  props = ses_node_props(np);
1327 1334  
1328 1335                  if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
1329 1336                      &phys, &n_phys) != 0)
1330 1337                          continue;
1331 1338  
1332 1339                  all_phys += n_phys;
1333 1340          }
1334 1341  
1335 1342          if (all_phys == 0)
1336 1343                  return (0);
1337 1344  
1338 1345          if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL)
1339 1346                  return (-1);
1340 1347  
1341 1348          for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
1342 1349              ap = topo_list_next(ap)) {
1343 1350                  np = ap->san_node;
1344 1351                  props = ses_node_props(np);
1345 1352  
1346 1353                  if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
1347 1354                      &phys, &n_phys) != 0)
1348 1355                          continue;
1349 1356  
1350 1357                  for (j = 0; j < n_phys; j++) {
1351 1358                          if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR,
1352 1359                              &addr) != 0)
1353 1360                                  continue;
1354 1361  
1355 1362                          len = snprintf(NULL, 0, "%016llx", addr) + 1;
1356 1363                          if ((paths[i] = topo_mod_alloc(mod, len)) == NULL)
1357 1364                                  goto error;
1358 1365  
1359 1366                          (void) snprintf(paths[i], len, "%016llx", addr);
1360 1367  
1361 1368                          ++i;
1362 1369                  }
1363 1370          }
1364 1371  
1365 1372          err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
1366 1373              TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE,
1367 1374              (const char **)paths, i, &terr);
1368 1375          if (err != 0)
1369 1376                  err = topo_mod_seterrno(mod, terr);
1370 1377  
1371 1378  error:
1372 1379          for (i = 0; i < all_phys && paths[i] != NULL; i++)
1373 1380                  topo_mod_free(mod, paths[i], strlen(paths[i]) + 1);
1374 1381          topo_mod_free(mod, paths, all_phys * sizeof (char *));
1375 1382  
1376 1383          return (err);
1377 1384  }
1378 1385  
1379 1386  /*
1380 1387   * Callback to create a basic node (bay, psu, fan, or controller and expander).
1381 1388   */
1382 1389  static int
1383 1390  ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp, tnode_t *pnode,
1384 1391      tnode_t *frutn, const char *nodename, const char *labelname,
1385 1392      tnode_t **node)
1386 1393  {
1387 1394          ses_node_t *np = snp->sen_node;
1388 1395          ses_node_t *parent;
1389 1396          uint64_t instance = snp->sen_instance;
1390 1397          topo_mod_t *mod = sdp->sed_mod;
1391 1398          nvlist_t *props, *aprops;
1392 1399          nvlist_t *auth = NULL, *fmri = NULL;
1393 1400          tnode_t *tn = NULL;
1394 1401          char label[128];
1395 1402          int err;
1396 1403          char *part = NULL, *serial = NULL, *revision = NULL;
1397 1404          char *desc;
1398 1405          boolean_t report;
1399 1406  
1400 1407          props = ses_node_props(np);
1401 1408  
1402 1409          (void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part);
1403 1410          (void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial);
1404 1411  
1405 1412          topo_mod_dprintf(mod, "adding %s %llu", nodename, instance);
1406 1413  
1407 1414          /*
1408 1415           * Create the node.  The interesting information is all copied from the
1409 1416           * parent enclosure node, so there is not much to do.
1410 1417           */
1411 1418          if ((auth = topo_mod_auth(mod, pnode)) == NULL)
1412 1419                  goto error;
1413 1420  
1414 1421          /*
1415 1422           * We want to report revision information for the controller nodes, but
1416 1423           * we do not get per-element revision information.  However, we do have
1417 1424           * revision information for the entire enclosure, and we can use the
1418 1425           * 'reported-via' property to know that this controller corresponds to
1419 1426           * the given revision information.  This means we cannot get revision
1420 1427           * information for targets we are not explicitly connected to, but
1421 1428           * there is little we can do about the situation.
1422 1429           */
1423 1430          if (strcmp(nodename, CONTROLLER) == 0 &&
1424 1431              nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 &&
1425 1432              report) {
1426 1433                  for (parent = ses_node_parent(np); parent != NULL;
1427 1434                      parent = ses_node_parent(parent)) {
1428 1435                          if (ses_node_type(parent) == SES_NODE_ENCLOSURE) {
1429 1436                                  (void) nvlist_lookup_string(
1430 1437                                      ses_node_props(parent),
1431 1438                                      SES_EN_PROP_REV, &revision);
1432 1439                                  break;
1433 1440                          }
1434 1441                  }
1435 1442          }
1436 1443  
1437 1444          if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
1438 1445              nodename, (topo_instance_t)instance, NULL, auth, part, revision,
1439 1446              serial)) == NULL) {
1440 1447                  topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
1441 1448                      topo_mod_errmsg(mod));
1442 1449                  goto error;
1443 1450          }
1444 1451  
1445 1452          if ((tn = topo_node_bind(mod, pnode, nodename,
1446 1453              instance, fmri)) == NULL) {
1447 1454                  topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
1448 1455                      topo_mod_errmsg(mod));
1449 1456                  goto error;
1450 1457          }
1451 1458  
1452 1459          /*
1453 1460           * For the node label, we look for the following in order:
1454 1461           *
1455 1462           *      <ses-description>
1456 1463           *      <ses-class-description> <instance>
1457 1464           *      <default-type-label> <instance>
1458 1465           */
1459 1466          if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
1460 1467              desc[0] == '\0') {
1461 1468                  parent = ses_node_parent(np);
1462 1469                  aprops = ses_node_props(parent);
1463 1470                  if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION,
1464 1471                      &desc) != 0 || desc[0] == '\0')
1465 1472                          desc = (char *)labelname;
1466 1473                  (void) snprintf(label, sizeof (label), "%s %llu", desc,
1467 1474                      instance);
1468 1475                  desc = label;
1469 1476          }
1470 1477  
1471 1478          if (topo_node_label_set(tn, desc, &err) != 0)
1472 1479                  goto error;
1473 1480  
1474 1481          if (ses_set_standard_props(mod, frutn, tn, NULL, ses_node_id(np),
1475 1482              snp->sen_target->set_devpath) != 0)
1476 1483                  goto error;
1477 1484  
1478 1485          if (strcmp(nodename, BAY) == 0) {
1479 1486                  if (ses_add_bay_props(mod, tn, snp) != 0)
1480 1487                          goto error;
1481 1488  
1482 1489                  if (ses_create_disk(sdp, tn, props) != 0)
1483 1490                          goto error;
1484 1491  
1485 1492                  if (topo_method_register(mod, tn, ses_bay_methods) != 0) {
1486 1493                          topo_mod_dprintf(mod,
1487 1494                              "topo_method_register() failed: %s",
1488 1495                              topo_mod_errmsg(mod));
1489 1496                          goto error;
1490 1497                  }
1491 1498          } else if ((strcmp(nodename, FAN) == 0) ||
1492 1499              (strcmp(nodename, PSU) == 0) ||
1493 1500              (strcmp(nodename, CONTROLLER) == 0)) {
1494 1501                  /*
1495 1502                   * Only fan, psu, and controller nodes have a 'present' method.
1496 1503                   * Bay nodes are always present, and disk nodes are present by
1497 1504                   * virtue of being enumerated and SAS expander nodes and
1498 1505                   * SAS connector nodes are also always present once
1499 1506                   * the parent controller is found.
1500 1507                   */
1501 1508                  if (topo_method_register(mod, tn, ses_component_methods) != 0) {
1502 1509                          topo_mod_dprintf(mod,
1503 1510                              "topo_method_register() failed: %s",
1504 1511                              topo_mod_errmsg(mod));
1505 1512                          goto error;
1506 1513                  }
1507 1514  
1508 1515          }
1509 1516  
1510 1517          snp->sen_target->set_refcount++;
1511 1518          topo_node_setspecific(tn, snp->sen_target);
1512 1519  
1513 1520          nvlist_free(auth);
1514 1521          nvlist_free(fmri);
1515 1522          if (node != NULL) *node = tn;
1516 1523          return (0);
1517 1524  
1518 1525  error:
1519 1526          nvlist_free(auth);
1520 1527          nvlist_free(fmri);
1521 1528          return (-1);
1522 1529  }
1523 1530  
1524 1531  /*
1525 1532   * Create SAS expander specific props.
1526 1533   */
1527 1534  /*ARGSUSED*/
1528 1535  static int
1529 1536  ses_set_expander_props(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1530 1537      tnode_t *ptnode, tnode_t *tnode, int *phycount, int64_t *connlist)
1531 1538  {
1532 1539          ses_node_t *np = snp->sen_node;
1533 1540          topo_mod_t *mod = sdp->sed_mod;
1534 1541          nvlist_t *auth = NULL, *fmri = NULL;
1535 1542          nvlist_t *props, **phylist;
1536 1543          int err, i;
1537 1544          uint_t pcount;
1538 1545          uint64_t sasaddr, connidx;
1539 1546          char sasaddr_str[17];
1540 1547          boolean_t found = B_FALSE, ses_found = B_FALSE;
1541 1548          dev_di_node_t *dnode, *sesdnode;
1542 1549  
1543 1550          props = ses_node_props(np);
1544 1551  
1545 1552          /*
1546 1553           * the uninstalled expander is not enumerated by checking
1547 1554           * the element status code.  No present present' method provided.
1548 1555           */
1549 1556          /*
1550 1557           * Get the Expander SAS address.  It should exist.
1551 1558           */
1552 1559          if (nvlist_lookup_uint64(props, SES_EXP_PROP_SAS_ADDR,
1553 1560              &sasaddr) != 0) {
1554 1561                  topo_mod_dprintf(mod,
1555 1562                      "Failed to get prop %s.", SES_EXP_PROP_SAS_ADDR);
1556 1563                  goto error;
1557 1564          }
1558 1565  
1559 1566          (void) sprintf(sasaddr_str, "%llx", sasaddr);
1560 1567  
1561 1568          /* search matching dev_di_node. */
1562 1569          for (dnode = topo_list_next(&sdp->sed_devs); dnode != NULL;
1563 1570              dnode = topo_list_next(dnode)) {
1564 1571                  for (i = 0; i < dnode->ddn_ppath_count; i++) {
1565 1572                          if ((dnode->ddn_target_port[i] != NULL) &&
1566 1573                              (strstr(dnode->ddn_target_port[i],
1567 1574                              sasaddr_str) != NULL)) {
1568 1575                                  found = B_TRUE;
1569 1576                                  break;
1570 1577                          }
1571 1578                  }
1572 1579                  if (found)
1573 1580                          break;
1574 1581          }
1575 1582  
1576 1583          if (!found) {
1577 1584                  topo_mod_dprintf(mod,
1578 1585                      "ses_set_expander_props: Failed to find matching "
1579 1586                      "devinfo node for Exapnder SAS address %s",
1580 1587                      SES_EXP_PROP_SAS_ADDR);
1581 1588                  /* continue on to get storage group props. */
1582 1589          } else {
1583 1590                  /* create/set the devfs-path and devid in the smp group */
1584 1591                  if (topo_pgroup_create(tnode, &smp_pgroup, &err) != 0) {
1585 1592                          topo_mod_dprintf(mod, "ses_set_expander_props: "
1586 1593                              "failed to create smp property group %s\n",
1587 1594                              topo_strerror(err));
1588 1595                          goto error;
1589 1596                  } else {
1590 1597                          if (topo_prop_set_string(tnode, TOPO_PGROUP_SMP,
1591 1598                              TOPO_PROP_SMP_TARGET_PORT, TOPO_PROP_IMMUTABLE,
1592 1599                              dnode->ddn_target_port[i], &err) != 0) {
1593 1600                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1594 1601                                      "set %S error %s\n", TOPO_PROP_SAS_ADDR,
1595 1602                                      topo_strerror(err));
1596 1603                          }
1597 1604                          if (topo_prop_set_string(tnode, TOPO_PGROUP_SMP,
1598 1605                              TOPO_PROP_SMP_DEV_PATH, TOPO_PROP_IMMUTABLE,
1599 1606                              dnode->ddn_dpath, &err) != 0) {
1600 1607                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1601 1608                                      "set dev error %s\n", topo_strerror(err));
1602 1609                          }
1603 1610                          if (topo_prop_set_string(tnode, TOPO_PGROUP_SMP,
1604 1611                              TOPO_PROP_SMP_DEVID, TOPO_PROP_IMMUTABLE,
1605 1612                              dnode->ddn_devid, &err) != 0) {
1606 1613                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1607 1614                                      "set devid error %s\n", topo_strerror(err));
1608 1615                          }
1609 1616                          if (dnode->ddn_ppath_count != 0 &&
1610 1617                              topo_prop_set_string_array(tnode, TOPO_PGROUP_SMP,
1611 1618                              TOPO_PROP_SMP_PHYS_PATH, TOPO_PROP_IMMUTABLE,
1612 1619                              (const char **)dnode->ddn_ppath,
1613 1620                              dnode->ddn_ppath_count, &err) != 0) {
1614 1621                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1615 1622                                      "set phys-path error %s\n",
1616 1623                                      topo_strerror(err));
1617 1624                          }
1618 1625                  }
1619 1626          }
1620 1627  
1621 1628          /* update the ses property group with SES target info */
1622 1629          if ((topo_pgroup_create(tnode, &ses_pgroup, &err) != 0) &&
1623 1630              (err != ETOPO_PROP_DEFD)) {
1624 1631                  /* SES prop group doesn't exist but failed to be created. */
1625 1632                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1626 1633                      "ses pgroup create error %s\n", topo_strerror(err));
1627 1634                  goto error;
1628 1635          } else {
1629 1636                  /* locate assciated enclosure dev_di_node. */
1630 1637                  for (sesdnode = topo_list_next(&sdp->sed_devs);
1631 1638                      sesdnode != NULL; sesdnode = topo_list_next(sesdnode)) {
1632 1639                          for (i = 0; i < sesdnode->ddn_ppath_count; i++) {
1633 1640                                  /*
1634 1641                                   * check if attached port exists and
1635 1642                                   * its node type is enclosure and
1636 1643                                   * attached port is same as sas address of
1637 1644                                   * the expander and
1638 1645                                   * bridge port for virtual phy indication
1639 1646                                   * exist.
1640 1647                                   */
1641 1648                                  if ((sesdnode->ddn_attached_port[i] != NULL) &&
1642 1649                                      (sesdnode->ddn_dtype == DTYPE_ESI) &&
1643 1650                                      (strstr(sesdnode->ddn_attached_port[i],
1644 1651                                      sasaddr_str) != NULL) &&
1645 1652                                      (sesdnode->ddn_bridge_port[i] != NULL)) {
1646 1653                                          ses_found = B_TRUE;
1647 1654                                          break;
1648 1655                                  }
1649 1656                          }
1650 1657                          if (ses_found) break;
1651 1658                  }
1652 1659  
1653 1660                  if (ses_found) {
1654 1661                          if (topo_prop_set_string(tnode, TOPO_PGROUP_SES,
1655 1662                              TOPO_PROP_SES_TARGET_PORT, TOPO_PROP_IMMUTABLE,
1656 1663                              sesdnode->ddn_target_port[i], &err) != 0) {
1657 1664                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1658 1665                                      "set ses %S error %s\n", TOPO_PROP_SAS_ADDR,
1659 1666                                      topo_strerror(err));
1660 1667                          }
1661 1668                          if (topo_prop_set_string(tnode, TOPO_PGROUP_SES,
1662 1669                              TOPO_PROP_SES_DEV_PATH, TOPO_PROP_IMMUTABLE,
1663 1670                              sesdnode->ddn_dpath, &err) != 0) {
1664 1671                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1665 1672                                      "set ses dev error %s\n",
1666 1673                                      topo_strerror(err));
1667 1674                          }
1668 1675                          if (topo_prop_set_string(tnode, TOPO_PGROUP_SES,
1669 1676                              TOPO_PROP_SES_DEVID, TOPO_PROP_IMMUTABLE,
1670 1677                              sesdnode->ddn_devid, &err) != 0) {
1671 1678                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1672 1679                                      "set ses devid error %s\n",
1673 1680                                      topo_strerror(err));
1674 1681                          }
1675 1682                          if (sesdnode->ddn_ppath_count != 0 &&
1676 1683                              topo_prop_set_string_array(tnode, TOPO_PGROUP_SES,
1677 1684                              TOPO_PROP_SES_PHYS_PATH, TOPO_PROP_IMMUTABLE,
1678 1685                              (const char **)sesdnode->ddn_ppath,
1679 1686                              sesdnode->ddn_ppath_count, &err) != 0) {
1680 1687                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1681 1688                                      "set ses phys-path error %s\n",
1682 1689                                      topo_strerror(err));
1683 1690                          }
1684 1691  
1685 1692                  }
1686 1693          }
1687 1694  
1688 1695          /* create the storage group */
1689 1696          if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) {
1690 1697                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1691 1698                      "create storage error %s\n", topo_strerror(err));
1692 1699                  goto error;
1693 1700          } else {
1694 1701                  /* set the SAS address prop out of expander element status. */
1695 1702                  if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1696 1703                      TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, sasaddr_str,
1697 1704                      &err) != 0) {
1698 1705                          topo_mod_dprintf(mod, "ses_set_expander_props: "
1699 1706                              "set %S error %s\n", TOPO_PROP_SAS_ADDR,
1700 1707                              topo_strerror(err));
1701 1708                  }
1702 1709  
1703 1710                  /* Get the phy information for the expander */
1704 1711                  if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
1705 1712                      &phylist, &pcount) != 0) {
1706 1713                          topo_mod_dprintf(mod,
1707 1714                              "Failed to get prop %s.", SES_SAS_PROP_PHYS);
1708 1715                  } else {
1709 1716                          /*
1710 1717                           * For each phy, get the connector element index and
1711 1718                           * stores into connector element index array.
1712 1719                           */
1713 1720                          *phycount = pcount;
1714 1721                          for (i = 0; i < pcount; i++) {
1715 1722                                  if (nvlist_lookup_uint64(phylist[i],
1716 1723                                      SES_PROP_CE_IDX, &connidx) == 0) {
1717 1724                                          if (connidx != 0xff) {
1718 1725                                                  connlist[i] = connidx;
1719 1726                                          } else {
1720 1727                                                  connlist[i] = -1;
1721 1728                                          }
1722 1729                                  } else {
1723 1730                                          /* Fail to get the index. set to -1. */
1724 1731                                          connlist[i] = -1;
1725 1732                                  }
1726 1733                          }
1727 1734  
1728 1735                          /* set the phy count prop of the expander. */
1729 1736                          if (topo_prop_set_uint64(tnode, TOPO_PGROUP_STORAGE,
1730 1737                              TOPO_PROP_PHY_COUNT, TOPO_PROP_IMMUTABLE, pcount,
1731 1738                              &err) != 0) {
1732 1739                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1733 1740                                      "set %S error %s\n", TOPO_PROP_PHY_COUNT,
1734 1741                                      topo_strerror(err));
1735 1742                          }
1736 1743  
1737 1744                          /*
1738 1745                           * set the connector element index of
1739 1746                           * the expander phys.
1740 1747                           */
1741 1748                  }
1742 1749  
1743 1750                  /* populate other misc storage group properties */
1744 1751                  if (found) {
1745 1752                          if (dnode->ddn_mfg && (topo_prop_set_string(tnode,
1746 1753                              TOPO_PGROUP_STORAGE, TOPO_STORAGE_MANUFACTURER,
1747 1754                              TOPO_PROP_IMMUTABLE, dnode->ddn_mfg, &err) != 0)) {
1748 1755                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1749 1756                                      "set mfg error %s\n", topo_strerror(err));
1750 1757                          }
1751 1758  
1752 1759                          if (dnode->ddn_model && (topo_prop_set_string(tnode,
1753 1760                              TOPO_PGROUP_STORAGE, TOPO_STORAGE_MODEL,
1754 1761                              TOPO_PROP_IMMUTABLE,
1755 1762                              dnode->ddn_model, &err) != 0)) {
1756 1763                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1757 1764                                      "set model error %s\n", topo_strerror(err));
1758 1765                          }
1759 1766  
1760 1767                          if (dnode->ddn_serial && (topo_prop_set_string(tnode,
1761 1768                              TOPO_PGROUP_STORAGE, TOPO_STORAGE_SERIAL_NUM,
1762 1769                              TOPO_PROP_IMMUTABLE,
1763 1770                              dnode->ddn_serial, &err) != 0)) {
1764 1771                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1765 1772                                      "set serial error %s\n",
1766 1773                                      topo_strerror(err));
1767 1774                          }
1768 1775  
1769 1776                          if (dnode->ddn_firm && (topo_prop_set_string(tnode,
1770 1777                              TOPO_PGROUP_STORAGE,
1771 1778                              TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE,
1772 1779                              dnode->ddn_firm, &err) != 0)) {
1773 1780                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1774 1781                                      "set firm error %s\n", topo_strerror(err));
1775 1782                          }
1776 1783                  }
1777 1784          }
1778 1785  
1779 1786          return (0);
1780 1787  
1781 1788  error:
1782 1789          nvlist_free(auth);
1783 1790          nvlist_free(fmri);
1784 1791          return (-1);
1785 1792  }
1786 1793  
1787 1794  /*
1788 1795   * Create SAS expander specific props.
1789 1796   */
1790 1797  /*ARGSUSED*/
1791 1798  static int
1792 1799  ses_set_connector_props(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1793 1800      tnode_t *tnode, int64_t phy_mask)
1794 1801  {
1795 1802          ses_node_t *np = snp->sen_node;
1796 1803          topo_mod_t *mod = sdp->sed_mod;
1797 1804          nvlist_t *props;
1798 1805          int err, i;
1799 1806          uint64_t conntype;
1800 1807          char phymask_str[17], *conntype_str;
1801 1808          boolean_t   found;
1802 1809  
1803 1810          props = ses_node_props(np);
1804 1811  
1805 1812          /*
1806 1813           * convert phy mask to string.
1807 1814           */
1808 1815          (void) snprintf(phymask_str, 17, "%llx", phy_mask);
1809 1816  
1810 1817          /* create the storage group */
1811 1818          if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) {
1812 1819                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1813 1820                      "create storage error %s\n", topo_strerror(err));
1814 1821                  return (-1);
1815 1822          } else {
1816 1823                  /* set the SAS address prop of the expander. */
1817 1824                  if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1818 1825                      TOPO_STORAGE_SAS_PHY_MASK, TOPO_PROP_IMMUTABLE,
1819 1826                      phymask_str, &err) != 0) {
1820 1827                          topo_mod_dprintf(mod, "ses_set_expander_props: "
1821 1828                              "set %S error %s\n", TOPO_STORAGE_SAS_PHY_MASK,
1822 1829                              topo_strerror(err));
1823 1830                  }
1824 1831  
1825 1832                  /* Get the connector type information for the expander */
1826 1833                  if (nvlist_lookup_uint64(props,
1827 1834                      SES_SC_PROP_CONNECTOR_TYPE, &conntype) != 0) {
1828 1835                          topo_mod_dprintf(mod, "Failed to get prop %s.",
1829 1836                              TOPO_STORAGE_SAS_PHY_MASK);
1830 1837                  } else {
1831 1838                          found = B_FALSE;
1832 1839                          for (i = 0; ; i++) {
1833 1840                                  if (sas_connector_type_list[i].sct_type ==
1834 1841                                      SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) {
1835 1842                                          break;
1836 1843                                  }
1837 1844                                  if (sas_connector_type_list[i].sct_type ==
1838 1845                                      conntype) {
1839 1846                                          conntype_str =
1840 1847                                              sas_connector_type_list[i].sct_name;
1841 1848                                          found = B_TRUE;
1842 1849                                          break;
1843 1850                                  }
1844 1851                          }
1845 1852  
1846 1853                          if (!found) {
1847 1854                                  if (conntype <
1848 1855                                      SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) {
1849 1856                                          conntype_str =
1850 1857                                              SAS_CONNECTOR_TYPE_RESERVED;
1851 1858                                  } else {
1852 1859                                          conntype_str =
1853 1860                                              SAS_CONNECTOR_TYPE_NOT_DEFINED;
1854 1861                                  }
1855 1862                          }
1856 1863  
1857 1864                          /* set the phy count prop of the expander. */
1858 1865                          if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1859 1866                              TOPO_STORAGE_SAS_CONNECTOR_TYPE,
1860 1867                              TOPO_PROP_IMMUTABLE, conntype_str, &err) != 0) {
1861 1868                                  topo_mod_dprintf(mod, "ses_set_expander_props: "
1862 1869                                      "set %S error %s\n", TOPO_PROP_PHY_COUNT,
1863 1870                                      topo_strerror(err));
1864 1871                          }
1865 1872                  }
1866 1873          }
1867 1874  
1868 1875          return (0);
1869 1876  }
1870 1877  
1871 1878  /*
1872 1879   * Instantiate SAS expander nodes for a given ESC Electronics node(controller)
1873 1880   * nodes.
1874 1881   */
1875 1882  /*ARGSUSED*/
1876 1883  static int
1877 1884  ses_create_esc_sasspecific(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1878 1885      tnode_t *pnode, ses_enum_chassis_t *cp,
1879 1886      boolean_t dorange)
1880 1887  {
1881 1888          topo_mod_t *mod = sdp->sed_mod;
1882 1889          tnode_t *exptn, *contn;
1883 1890          boolean_t found;
1884 1891          sas_connector_phy_data_t connectors[64] = {NULL};
1885 1892          uint64_t max;
1886 1893          ses_enum_node_t *ctlsnp, *xsnp, *consnp;
1887 1894          ses_node_t *np = snp->sen_node;
1888 1895          nvlist_t *props, *psprops;
1889 1896          uint64_t index, psindex, conindex, psstatus, i, j, count;
1890 1897          int64_t cidxlist[256] = {NULL};
1891 1898          int phycount;
1892 1899  
1893 1900          props = ses_node_props(np);
1894 1901  
1895 1902          if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_ONLY_INDEX,
1896 1903              &index) != 0)
1897 1904                  return (-1);
1898 1905  
1899 1906          /*
1900 1907           * For SES constroller node, check to see if there are
1901 1908           * associated SAS expanders.
1902 1909           */
1903 1910          found = B_FALSE;
1904 1911          max = 0;
1905 1912          for (ctlsnp = topo_list_next(&cp->sec_nodes); ctlsnp != NULL;
1906 1913              ctlsnp = topo_list_next(ctlsnp)) {
1907 1914                  if (ctlsnp->sen_type == SES_ET_SAS_EXPANDER) {
1908 1915                          found = B_TRUE;
1909 1916                          if (ctlsnp->sen_instance > max)
1910 1917                                  max = ctlsnp->sen_instance;
1911 1918                  }
1912 1919          }
1913 1920  
1914 1921          /*
1915 1922           * No SAS expander found notthing to process.
1916 1923           */
1917 1924          if (!found)
1918 1925                  return (0);
1919 1926  
1920 1927          topo_mod_dprintf(mod, "%s Controller %d: creating "
1921 1928              "%llu %s nodes", cp->sec_csn, index, max + 1, SASEXPANDER);
1922 1929  
1923 1930          /*
1924 1931           * The max number represent the number of elements
1925 1932           * deducted from the highest SES_PROP_ELEMENT_CLASS_INDEX
1926 1933           * of SET_ET_SAS_EXPANDER type element.
1927 1934           *
1928 1935           * There may be multiple ESC Electronics element(controllers)
1929 1936           * within JBOD(typicall two for redundancy) and SAS expander
1930 1937           * elements are associated with only one of them.  We are
1931 1938           * still creating the range based max number here.
1932 1939           * That will cover the case that all expanders are associated
1933 1940           * with one SES controller.
1934 1941           */
1935 1942          if (dorange && topo_node_range_create(mod, pnode,
1936 1943              SASEXPANDER, 0, max) != 0) {
1937 1944                  topo_mod_dprintf(mod,
1938 1945                      "topo_node_create_range() failed: %s",
1939 1946                      topo_mod_errmsg(mod));
1940 1947                  return (-1);
1941 1948          }
1942 1949  
1943 1950          /*
1944 1951           * Search exapnders with the parent index matching with
1945 1952           * ESC Electronics element index.
1946 1953           * Note the index used here is a global index across
1947 1954           * SES elements.
1948 1955           */
1949 1956          for (xsnp = topo_list_next(&cp->sec_nodes); xsnp != NULL;
1950 1957              xsnp = topo_list_next(xsnp)) {
1951 1958                  if (xsnp->sen_type == SES_ET_SAS_EXPANDER) {
1952 1959                          /*
1953 1960                           * get the parent ESC controller.
1954 1961                           */
1955 1962                          psprops = ses_node_props(xsnp->sen_node);
1956 1963                          if (nvlist_lookup_uint64(psprops,
1957 1964                              SES_PROP_STATUS_CODE, &psstatus) == 0) {
1958 1965                                  if (psstatus == SES_ESC_NOT_INSTALLED) {
1959 1966                                          /*
1960 1967                                           * Not installed.
1961 1968                                           * Don't create a ndoe.
1962 1969                                           */
1963 1970                                          continue;
1964 1971                                  }
1965 1972                          } else {
1966 1973                                  /*
1967 1974                                   * The element should have status code.
1968 1975                                   * If not there is no way to find
1969 1976                                   * out if the expander element exist or
1970 1977                                   * not.
1971 1978                                   */
1972 1979                                  continue;
1973 1980                          }
1974 1981  
1975 1982                          /* Get the physical parent index to compare. */
1976 1983                          if (nvlist_lookup_uint64(psprops,
1977 1984                              LIBSES_PROP_PHYS_PARENT, &psindex) == 0) {
1978 1985                                  if (index == psindex) {
1979 1986                  /* indentation moved forward */
1980 1987                  /*
1981 1988                   * Handle basic node information of SAS expander
1982 1989                   * element - binding to parent node and
1983 1990                   * allocating FMRI...
1984 1991                   */
1985 1992                  if (ses_create_generic(sdp, xsnp, pnode, pnode, SASEXPANDER,
1986 1993                      "SAS-EXPANDER", &exptn) != 0)
1987 1994                          continue;
1988 1995                  /*
1989 1996                   * Now handle SAS expander unique portion of node creation.
1990 1997                   * The max nubmer of the phy count is 256 since SES-2
1991 1998                   * defines as 1 byte field.  The cidxlist has the same
1992 1999                   * number of elements.
1993 2000                   *
1994 2001                   * We use size 64 array to store the connectors.
1995 2002                   * Typically a connectors associated with 4 phys so that
1996 2003                   * matches with the max number of connecters associated
1997 2004                   * with an expander.
1998 2005                   * The phy count goes up to 38 for Sun supported
1999 2006                   * JBOD.
2000 2007                   */
2001 2008                  (void) memset(cidxlist, 0, sizeof (int64_t) * 64);
2002 2009                  if (ses_set_expander_props(sdp, xsnp, pnode, exptn, &phycount,
2003 2010                      cidxlist) != 0) {
2004 2011                          /*
2005 2012                           * error on getting specific prop failed.
2006 2013                           * continue on.  Note that the node is
2007 2014                           * left bound.
2008 2015                           */
2009 2016                          continue;
2010 2017                  }
2011 2018  
2012 2019                  /*
2013 2020                   * count represetns the number of connectors discovered so far.
2014 2021                   */
2015 2022                  count = 0;
2016 2023                  (void) memset(connectors, 0,
2017 2024                      sizeof (sas_connector_phy_data_t) * 64);
2018 2025                  for (i = 0; i < phycount; i++) {
2019 2026                          if (cidxlist[i] != -1) {
2020 2027                                  /* connector index is valid. */
2021 2028                                  for (j = 0; j < count; j++) {
2022 2029                                          if (connectors[j].scpd_index ==
2023 2030                                              cidxlist[i]) {
2024 2031                                                  /*
2025 2032                                                   * Just update phy mask.
2026 2033                                                   * The postion for connector
2027 2034                                                   * index lists(cidxlist index)
2028 2035                                                   * is set.
2029 2036                                                   */
2030 2037                                                  connectors[j].scpd_pm =
2031 2038                                                      connectors[j].scpd_pm |
2032 2039                                                      (1ULL << i);
2033 2040                                                  break;
2034 2041                                          }
2035 2042                                  }
2036 2043                                  /*
2037 2044                                   * If j and count matche a  new connector
2038 2045                                   * index is found.
2039 2046                                   */
2040 2047                                  if (j == count) {
2041 2048                                          /* add a new index and phy mask. */
2042 2049                                          connectors[count].scpd_index =
2043 2050                                              cidxlist[i];
2044 2051                                          connectors[count].scpd_pm =
2045 2052                                              connectors[count].scpd_pm |
2046 2053                                              (1ULL << i);
2047 2054                                          count++;
2048 2055                                  }
2049 2056                          }
2050 2057                  }
2051 2058  
2052 2059                  /*
2053 2060                   * create range for the connector nodes.
2054 2061                   * The class index of the ses connector element
2055 2062                   * is set as the instance nubmer for the node.
2056 2063                   * Even though one expander may not have all connectors
2057 2064                   * are associated with we are creating the range with
2058 2065                   * max possible instance number.
2059 2066                   */
2060 2067                  found = B_FALSE;
2061 2068                  max = 0;
2062 2069                  for (consnp = topo_list_next(&cp->sec_nodes);
2063 2070                      consnp != NULL; consnp = topo_list_next(consnp)) {
2064 2071                          if (consnp->sen_type == SES_ET_SAS_CONNECTOR) {
2065 2072                                  psprops = ses_node_props(consnp->sen_node);
2066 2073                                  found = B_TRUE;
2067 2074                                  if (consnp->sen_instance > max)
2068 2075                                          max = consnp->sen_instance;
2069 2076                          }
2070 2077                  }
2071 2078  
2072 2079                  /*
2073 2080                   * No SAS connector found nothing to process.
2074 2081                   */
2075 2082                  if (!found)
2076 2083                          return (0);
2077 2084  
2078 2085                  if (dorange && topo_node_range_create(mod, exptn,
2079 2086                      RECEPTACLE, 0, max) != 0) {
2080 2087                          topo_mod_dprintf(mod,
2081 2088                              "topo_node_create_range() failed: %s",
2082 2089                              topo_mod_errmsg(mod));
2083 2090                          return (-1);
2084 2091                  }
2085 2092  
2086 2093                  /* search matching connector element using the index. */
2087 2094                  for (i = 0; i < count; i++) {
2088 2095                          found = B_FALSE;
2089 2096                          for (consnp = topo_list_next(&cp->sec_nodes);
2090 2097                              consnp != NULL; consnp = topo_list_next(consnp)) {
2091 2098                                  if (consnp->sen_type == SES_ET_SAS_CONNECTOR) {
2092 2099                                          psprops = ses_node_props(
2093 2100                                              consnp->sen_node);
2094 2101                                          /*
2095 2102                                           * Get the physical parent index to
2096 2103                                           * compare.
2097 2104                                           * The connector elements are children
2098 2105                                           * of ESC Electronics element even
2099 2106                                           * though we enumerate them under
2100 2107                                           * an expander in libtopo.
2101 2108                                           */
2102 2109                                          if (nvlist_lookup_uint64(psprops,
2103 2110                                              SES_PROP_ELEMENT_ONLY_INDEX,
2104 2111                                              &conindex) == 0) {
2105 2112                                                  if (conindex ==
2106 2113                                                      connectors[i].scpd_index) {
2107 2114                                                          found = B_TRUE;
2108 2115                                                          break;
2109 2116                                                  }
2110 2117                                          }
2111 2118                                  }
2112 2119                          }
2113 2120  
2114 2121                          /* now create a libtopo node. */
2115 2122                          if (found) {
2116 2123                                  /* Create generic props. */
2117 2124                                  if (ses_create_generic(sdp, consnp, exptn,
2118 2125                                      topo_node_parent(exptn),
2119 2126                                      RECEPTACLE, "RECEPTACLE", &contn) !=
2120 2127                                      0) {
2121 2128                                          continue;
2122 2129                                  }
2123 2130                                  /* Create connector specific props. */
2124 2131                                  if (ses_set_connector_props(sdp, consnp,
2125 2132                                      contn, connectors[i].scpd_pm) != 0) {
2126 2133                                          continue;
2127 2134                                  }
2128 2135                          }
2129 2136                  }
2130 2137                  /* end indentation change */
2131 2138                                  }
2132 2139                          }
2133 2140                  }
2134 2141          }
2135 2142  
2136 2143          return (0);
2137 2144  }
2138 2145  
2139 2146  /*
2140 2147   * Instantiate any protocol specific portion of a node.
2141 2148   */
2142 2149  /*ARGSUSED*/
2143 2150  static int
2144 2151  ses_create_protocol_specific(ses_enum_data_t *sdp, ses_enum_node_t *snp,
2145 2152      tnode_t *pnode, uint64_t type, ses_enum_chassis_t *cp,
2146 2153      boolean_t dorange)
2147 2154  {
2148 2155  
2149 2156          if (type == SES_ET_ESC_ELECTRONICS) {
2150 2157                  /* create SAS specific children(expanders and connectors. */
2151 2158                  return (ses_create_esc_sasspecific(sdp, snp, pnode, cp,
2152 2159                      dorange));
2153 2160          }
2154 2161  
2155 2162          return (0);
2156 2163  }
2157 2164  
2158 2165  /*
2159 2166   * Instantiate any children of a given type.
2160 2167   */
2161 2168  static int
2162 2169  ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type,
2163 2170      const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp,
2164 2171      boolean_t dorange)
2165 2172  {
2166 2173          topo_mod_t *mod = sdp->sed_mod;
2167 2174          boolean_t found;
2168 2175          uint64_t max;
2169 2176          ses_enum_node_t *snp;
2170 2177          tnode_t *tn;
2171 2178  
2172 2179          /*
2173 2180           * First go through and count how many matching nodes we have.
2174 2181           */
2175 2182          max = 0;
2176 2183          found = B_FALSE;
2177 2184          for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2178 2185              snp = topo_list_next(snp)) {
2179 2186                  if (snp->sen_type == type) {
2180 2187                          found = B_TRUE;
2181 2188                          if (snp->sen_instance > max)
2182 2189                                  max = snp->sen_instance;
2183 2190                  }
2184 2191          }
2185 2192  
2186 2193          /*
2187 2194           * No enclosure should export both DEVICE and ARRAY_DEVICE elements.
2188 2195           * Since we map both of these to 'disk', if an enclosure does this, we
2189 2196           * just ignore the array elements.
2190 2197           */
2191 2198          if (!found ||
2192 2199              (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev))
2193 2200                  return (0);
2194 2201  
2195 2202          topo_mod_dprintf(mod, "%s: creating %llu %s nodes",
2196 2203              cp->sec_csn, max + 1, nodename);
2197 2204  
2198 2205          if (dorange && topo_node_range_create(mod, pnode,
2199 2206              nodename, 0, max) != 0) {
2200 2207                  topo_mod_dprintf(mod,
2201 2208                      "topo_node_create_range() failed: %s",
2202 2209                      topo_mod_errmsg(mod));
2203 2210                  return (-1);
2204 2211          }
2205 2212  
2206 2213          for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2207 2214              snp = topo_list_next(snp)) {
2208 2215                  if (snp->sen_type == type) {
2209 2216                          /*
2210 2217                           * With flat layout of ses nodes there is no
2211 2218                           * way to find out the direct FRU for a node.
2212 2219                           * Passing NULL for fru topo node.  Note that
2213 2220                           * ses_create_children_from_phys_tree() provides
2214 2221                           * the actual direct FRU for a node.
2215 2222                           */
2216 2223                          if (ses_create_generic(sdp, snp, pnode, NULL,
2217 2224                              nodename, defaultlabel, &tn) != 0)
2218 2225                                  return (-1);
2219 2226                          /*
2220 2227                           * For some SES element there may be protocol specific
2221 2228                           * information to process.   Here we are processing
2222 2229                           * the association between enclosure controller and
2223 2230                           * SAS expanders.
2224 2231                           */
2225 2232                          if (type == SES_ET_ESC_ELECTRONICS) {
2226 2233                                  /* create SAS expander node */
2227 2234                                  if (ses_create_protocol_specific(sdp, snp,
2228 2235                                      tn, type, cp, dorange) != 0) {
2229 2236                                          return (-1);
2230 2237                                  }
2231 2238                          }
2232 2239  
2233 2240                  }
2234 2241          }
2235 2242  
2236 2243          return (0);
2237 2244  }
2238 2245  
2239 2246  /*
2240 2247   * Instantiate a new subchassis instance in the topology.
2241 2248   */
2242 2249  static int
2243 2250  ses_create_subchassis(ses_enum_data_t *sdp, tnode_t *pnode,
2244 2251      ses_enum_chassis_t *scp)
2245 2252  {
2246 2253          topo_mod_t *mod = sdp->sed_mod;
2247 2254          tnode_t *tn;
2248 2255          nvlist_t *props;
2249 2256          nvlist_t *auth = NULL, *fmri = NULL;
2250 2257          uint64_t instance = scp->sec_instance;
2251 2258          char *desc;
2252 2259          char label[128];
2253 2260          char **paths;
2254 2261          int i, err;
2255 2262          ses_enum_target_t *stp;
2256 2263          int ret = -1;
2257 2264  
2258 2265          /*
2259 2266           * Copy authority information from parent enclosure node
2260 2267           */
2261 2268          if ((auth = topo_mod_auth(mod, pnode)) == NULL)
2262 2269                  goto error;
2263 2270  
2264 2271          /*
2265 2272           * Record the subchassis serial number in the FMRI.
2266 2273           * For now, we assume that logical id is the subchassis serial number.
2267 2274           * If this assumption changes in future, then the following
2268 2275           * piece of code will need to be updated via an RFE.
2269 2276           */
2270 2277          if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
2271 2278              SUBCHASSIS, (topo_instance_t)instance, NULL, auth, NULL, NULL,
2272 2279              NULL)) == NULL) {
2273 2280                  topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
2274 2281                      topo_mod_errmsg(mod));
2275 2282                  goto error;
2276 2283          }
2277 2284  
2278 2285          if ((tn = topo_node_bind(mod, pnode, SUBCHASSIS,
2279 2286              instance, fmri)) == NULL) {
2280 2287                  topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
2281 2288                      topo_mod_errmsg(mod));
2282 2289                  goto error;
2283 2290          }
2284 2291  
2285 2292          props = ses_node_props(scp->sec_enclosure);
2286 2293  
2287 2294          /*
2288 2295           * Look for the subchassis label in the following order:
2289 2296           *      <ses-description>
2290 2297           *      <ses-class-description> <instance>
2291 2298           *      <default-type-label> <instance>
2292 2299           *
2293 2300           * For subchassis, the default label is "SUBCHASSIS"
2294 2301           */
2295 2302          if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
2296 2303              desc[0] == '\0') {
2297 2304                  if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION,
2298 2305                      &desc) == 0 && desc[0] != '\0')
2299 2306                          (void) snprintf(label, sizeof (label), "%s %llu", desc,
2300 2307                              instance);
2301 2308                  else
2302 2309                          (void) snprintf(label, sizeof (label),
2303 2310                              "SUBCHASSIS %llu", instance);
2304 2311                  desc = label;
2305 2312          }
2306 2313  
2307 2314          if (topo_node_label_set(tn, desc, &err) != 0)
2308 2315                  goto error;
2309 2316  
2310 2317          if (ses_set_standard_props(mod, NULL, tn, NULL,
2311 2318              ses_node_id(scp->sec_enclosure), scp->sec_target->set_devpath) != 0)
2312 2319                  goto error;
2313 2320  
2314 2321          /*
2315 2322           * Set the 'chassis-type' property for this subchassis.  This is either
2316 2323           * 'ses-class-description' or 'subchassis'.
2317 2324           */
2318 2325          if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION, &desc) != 0)
2319 2326                  desc = "subchassis";
2320 2327  
2321 2328          if (topo_prop_set_string(tn, TOPO_PGROUP_SES,
2322 2329              TOPO_PROP_CHASSIS_TYPE, TOPO_PROP_IMMUTABLE, desc, &err) != 0) {
2323 2330                  topo_mod_dprintf(mod, "failed to create property %s: %s\n",
2324 2331                      TOPO_PROP_CHASSIS_TYPE, topo_strerror(err));
2325 2332                  goto error;
2326 2333          }
2327 2334  
2328 2335          /*
2329 2336           * For enclosures, we want to include all possible targets (for upgrade
2330 2337           * purposes).
2331 2338           */
2332 2339          for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
2333 2340              stp = topo_list_next(stp), i++)
2334 2341                  ;
2335 2342  
2336 2343          verify(i != 0);
2337 2344          paths = alloca(i * sizeof (char *));
2338 2345  
2339 2346          for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
2340 2347              stp = topo_list_next(stp), i++)
2341 2348                  paths[i] = stp->set_devpath;
2342 2349  
2343 2350          if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
2344 2351              TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
2345 2352              i, &err) != 0) {
2346 2353                  topo_mod_dprintf(mod, "failed to create property %s: %s\n",
2347 2354                      TOPO_PROP_PATHS, topo_strerror(err));
2348 2355                  goto error;
2349 2356          }
2350 2357  
2351 2358          if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
2352 2359                  topo_mod_dprintf(mod, "topo_method_register() failed: %s",
2353 2360                      topo_mod_errmsg(mod));
2354 2361                  goto error;
2355 2362          }
2356 2363  
2357 2364          /*
2358 2365           * Create the nodes for controllers and bays.
2359 2366           */
2360 2367          if (ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
2361 2368              CONTROLLER, "CONTROLLER", scp, B_TRUE) != 0 ||
2362 2369              ses_create_children(sdp, tn, SES_ET_DEVICE,
2363 2370              BAY, "BAY", scp, B_TRUE) != 0 ||
2364 2371              ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
2365 2372              BAY, "BAY", scp, B_TRUE) != 0)
2366 2373                  goto error;
2367 2374  
2368 2375          ret = 0;
2369 2376  
2370 2377  error:
2371 2378          nvlist_free(auth);
2372 2379          nvlist_free(fmri);
2373 2380          return (ret);
2374 2381  }
2375 2382  
2376 2383  /*
2377 2384   * Function we use to insert a node.
2378 2385   */
2379 2386  static int
2380 2387  ses_phys_tree_insert(topo_mod_t *mod, ses_phys_tree_t **sproot,
2381 2388      ses_phys_tree_t *child)
2382 2389  {
2383 2390          uint64_t ppindex, eindex, pindex;
2384 2391          ses_phys_tree_t *node_ptr;
2385 2392          int ret = 0;
2386 2393  
2387 2394          assert(sproot != NULL);
2388 2395          assert(child != NULL);
2389 2396  
2390 2397          if (*sproot == NULL) {
2391 2398                  *sproot = child;
2392 2399                  return (0);
2393 2400          }
2394 2401  
2395 2402          pindex = child->spt_pindex;
2396 2403          ppindex = (*sproot)->spt_pindex;
2397 2404          eindex = (*sproot)->spt_eonlyindex;
2398 2405  
2399 2406          /*
2400 2407           * If the element only index of the root is same as the physical
2401 2408           * parent index of a node to be added, add the node as a child of
2402 2409           * the current root.
2403 2410           */
2404 2411          if (eindex == pindex) {
2405 2412                  (void) ses_phys_tree_insert(mod, &(*sproot)->spt_child, child);
2406 2413                  child->spt_parent = *sproot;
2407 2414          } else if (ppindex == pindex) {
2408 2415                  /*
2409 2416                   * if the physical parent of the current root and the child
2410 2417                   * is same, then this should be a sibling node.
2411 2418                   * Siblings can be different element types and arrange
2412 2419                   * them by group.
2413 2420                   */
2414 2421                  if ((*sproot)->spt_senumnode->sen_type ==
2415 2422                      child->spt_senumnode->sen_type) {
2416 2423                          child->spt_sibling = *sproot;
2417 2424                          *sproot = child;
2418 2425                  } else {
2419 2426                          /* add a node in front of matching element type. */
2420 2427                          node_ptr = *sproot;
2421 2428                          while (node_ptr->spt_sibling != NULL) {
2422 2429                                  if (node_ptr->spt_sibling->
2423 2430                                      spt_senumnode->sen_type ==
2424 2431                                      child->spt_senumnode->sen_type) {
2425 2432                                          child->spt_sibling =
2426 2433                                              node_ptr->spt_sibling;
2427 2434                                          node_ptr->spt_sibling = child;
2428 2435                                          break;
2429 2436                                  }
2430 2437                                  node_ptr = node_ptr->spt_sibling;
2431 2438                          }
2432 2439                          /* no matching.  Add the child at the end. */
2433 2440                          if (node_ptr->spt_sibling == NULL) {
2434 2441                                  node_ptr->spt_sibling = child;
2435 2442                          }
2436 2443                  }
2437 2444                  child->spt_parent = (*sproot)->spt_parent;
2438 2445          } else {
2439 2446                  /*
2440 2447                   * The root and the node is not directly related.
2441 2448                   * Try to insert to the child sub-tree first and then try to
2442 2449                   * insert to the sibling sub-trees.  If fails for both
2443 2450                   * the caller will retry insertion later.
2444 2451                   */
2445 2452                  if ((*sproot)->spt_child) {
2446 2453                          ret = ses_phys_tree_insert(mod, &(*sproot)->spt_child,
2447 2454                              child);
2448 2455                  }
2449 2456                  if ((*sproot)->spt_child == NULL || ret != 0) {
2450 2457                          if ((*sproot)->spt_sibling) {
2451 2458                                  ret = ses_phys_tree_insert(mod,
2452 2459                                      &(*sproot)->spt_sibling, child);
2453 2460                          } else {
2454 2461                                  ret = 1;
2455 2462                          }
2456 2463                  }
2457 2464                  return (ret);
2458 2465          }
2459 2466          return (0);
2460 2467  }
2461 2468  
2462 2469  /*
2463 2470   * Construct tree view of ses elements through parent phyiscal element index.
2464 2471   * The root of tree is already constructed using the enclosure element.
2465 2472   */
2466 2473  static int
2467 2474  ses_construct_phys_tree(ses_enum_data_t *sdp, ses_enum_chassis_t *cp,
2468 2475      ses_phys_tree_t *sproot)
2469 2476  {
2470 2477          ses_enum_node_t *snp;
2471 2478          ses_phys_tree_t *child;
2472 2479          ses_phys_tree_t *u_watch = NULL;
2473 2480          ses_phys_tree_t *u_head = NULL;
2474 2481          ses_phys_tree_t *u_tail = NULL;
2475 2482          int u_inserted = 0, u_left = 0;
2476 2483          nvlist_t *props;
2477 2484          topo_mod_t *mod = sdp->sed_mod;
2478 2485  
2479 2486          for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2480 2487              snp = topo_list_next(snp)) {
2481 2488                  if ((child = topo_mod_zalloc(mod,
2482 2489                      sizeof (ses_phys_tree_t))) == NULL) {
2483 2490                          topo_mod_dprintf(mod,
2484 2491                              "failed to allocate root.");
2485 2492                          return (-1);
2486 2493                  }
2487 2494                  child->spt_snode = snp->sen_node;
2488 2495                  props = ses_node_props(snp->sen_node);
2489 2496                  if (nvlist_lookup_uint64(props,
2490 2497                      LIBSES_PROP_PHYS_PARENT, &child->spt_pindex) != 0) {
2491 2498                          /*
2492 2499                           * the prop should exist. continue to see if
2493 2500                           * we can build a partial tree with other elements.
2494 2501                           */
2495 2502                          topo_mod_dprintf(mod,
2496 2503                              "ses_construct_phys_tree(): Failed to find prop %s "
2497 2504                              "on ses element type %d and instance %d "
2498 2505                              "(CSN %s).", LIBSES_PROP_PHYS_PARENT,
2499 2506                              snp->sen_type, snp->sen_instance, cp->sec_csn);
2500 2507                          topo_mod_free(mod, child, sizeof (ses_phys_tree_t));
2501 2508                          continue;
2502 2509                  } else {
2503 2510                          if (nvlist_lookup_boolean_value(props,
2504 2511                              LIBSES_PROP_FRU, &child->spt_isfru) != 0) {
2505 2512                                  topo_mod_dprintf(mod,
2506 2513                                      "ses_construct_phys_tree(): Failed to "
2507 2514                                      "find prop %s on ses element type %d "
2508 2515                                      "and instance %d (CSN %s).",
2509 2516                                      LIBSES_PROP_FRU,
2510 2517                                      snp->sen_type, snp->sen_instance,
2511 2518                                      cp->sec_csn);
2512 2519                                  /*
2513 2520                                   * Ignore if the prop doesn't exist.
2514 2521                                   * Note that the enclosure itself should be
2515 2522                                   * a FRU so if no FRU found the enclosure FRU
2516 2523                                   * can be a direct FRU.
2517 2524                                   */
2518 2525                          }
2519 2526                          verify(nvlist_lookup_uint64(props,
2520 2527                              SES_PROP_ELEMENT_ONLY_INDEX,
2521 2528                              &child->spt_eonlyindex) == 0);
2522 2529                          verify(nvlist_lookup_uint64(props,
2523 2530                              SES_PROP_ELEMENT_CLASS_INDEX,
2524 2531                              &child->spt_cindex) == 0);
2525 2532                  }
2526 2533                  child->spt_senumnode = snp;
2527 2534                  if (ses_phys_tree_insert(mod, &sproot, child) != 0) {
2528 2535                          /* collect unresolved element to process later. */
2529 2536                          if (u_head == NULL) {
2530 2537                                  u_head = child;
2531 2538                                  u_tail = child;
2532 2539                          } else {
2533 2540                                  child->spt_sibling = u_head;
2534 2541                                  u_head = child;
2535 2542                          }
2536 2543                  }
2537 2544          }
2538 2545  
2539 2546          /*
2540 2547           * The parent of a child node may not be inserted yet.
2541 2548           * Trying to insert the child until no child is left or
2542 2549           * no child is not added further.  For the latter
2543 2550           * the hierarchical relationship between elements
2544 2551           * should be checked through SUNW,FRUID page.
2545 2552           * u_watch is a watch dog to check the prgress of unresolved
2546 2553           * node.
2547 2554           */
2548 2555          u_watch = u_tail;
2549 2556          while (u_head) {
2550 2557                  child = u_head;
2551 2558                  u_head = u_head->spt_sibling;
2552 2559                  if (u_head == NULL)
2553 2560                          u_tail = NULL;
2554 2561                  child->spt_sibling = NULL;
2555 2562                  if (ses_phys_tree_insert(mod, &sproot, child) != 0) {
2556 2563                          u_tail->spt_sibling = child;
2557 2564                          u_tail = child;
2558 2565                          if (child == u_watch) {
2559 2566                                  /*
2560 2567                                   * We just scanned one round for the
2561 2568                                   * unresolved list. Check to see whether we
2562 2569                                   * have nodes inserted, if none, we should
2563 2570                                   * break in case of an indefinite loop.
2564 2571                                   */
2565 2572                                  if (u_inserted == 0) {
2566 2573                                          /*
2567 2574                                           * Indicate there is unhandled node.
2568 2575                                           * Chain free the whole unsolved
2569 2576                                           * list here.
2570 2577                                           */
2571 2578                                          u_left++;
2572 2579                                          break;
2573 2580                                  } else {
2574 2581                                          u_inserted = 0;
2575 2582                                          u_watch = u_tail;
2576 2583                                  }
2577 2584                          }
2578 2585                  } else {
2579 2586                          /*
2580 2587                           * We just inserted one rpnode, increment the
2581 2588                           * unsolved_inserted counter. We will utilize this
2582 2589                           * counter to detect an indefinite insertion loop.
2583 2590                           */
2584 2591                          u_inserted++;
2585 2592                          if (child == u_watch) {
2586 2593                                  /*
2587 2594                                   * watch dog node itself is inserted.
2588 2595                                   * Set it to the tail and refresh the watching.
2589 2596                                   */
2590 2597                                  u_watch = u_tail;
2591 2598                                  u_inserted = 0;
2592 2599                                  u_left = 0;
2593 2600                          }
2594 2601                  }
2595 2602          }
2596 2603  
2597 2604          /* check if there is left out unresolved nodes. */
2598 2605          if (u_left) {
2599 2606                  topo_mod_dprintf(mod, "ses_construct_phys_tree(): "
2600 2607                      "Failed to construct physical view of the following "
2601 2608                      "ses elements of Chassis CSN %s.", cp->sec_csn);
2602 2609                  while (u_head) {
2603 2610                          u_tail = u_head->spt_sibling;
2604 2611                          topo_mod_dprintf(mod,
2605 2612                              "\telement type (%d) and instance (%d)",
2606 2613                              u_head->spt_senumnode->sen_type,
2607 2614                              u_head->spt_senumnode->sen_instance);
2608 2615                          topo_mod_free(mod, u_head, sizeof (ses_phys_tree_t));
2609 2616                          u_head = u_tail;
2610 2617                  }
2611 2618                  return (-1);
2612 2619          }
2613 2620  
2614 2621          return (0);
2615 2622  }
2616 2623  
2617 2624  /*
2618 2625   * Free the whole phys tree.
2619 2626   */
2620 2627  static void ses_phys_tree_free(topo_mod_t *mod, ses_phys_tree_t *sproot)
2621 2628  {
2622 2629          if (sproot == NULL)
2623 2630                  return;
2624 2631  
2625 2632          /* Free child tree. */
2626 2633          if (sproot->spt_child) {
2627 2634                  ses_phys_tree_free(mod, sproot->spt_child);
2628 2635          }
2629 2636  
2630 2637          /* Free sibling trees. */
2631 2638          if (sproot->spt_sibling) {
2632 2639                  ses_phys_tree_free(mod, sproot->spt_sibling);
2633 2640          }
2634 2641  
2635 2642          /* Free root node itself. */
2636 2643          topo_mod_free(mod, sproot, sizeof (ses_phys_tree_t));
2637 2644  }
2638 2645  
2639 2646  /*
2640 2647   * Parses phys_enum_type table to get the index of the given type.
2641 2648   */
2642 2649  static boolean_t
2643 2650  is_type_enumerated(ses_phys_tree_t *node, int *index)
2644 2651  {
2645 2652          int i;
2646 2653  
2647 2654          for (i = 0; i < N_PHYS_ENUM_TYPES; i++) {
2648 2655                  if (node->spt_senumnode->sen_type ==
2649 2656                      phys_enum_type_list[i].pet_type) {
2650 2657                          *index = i;
2651 2658                          return (B_TRUE);
2652 2659                  }
2653 2660          }
2654 2661          return (B_FALSE);
2655 2662  }
2656 2663  
2657 2664  /*
2658 2665   * Recusrive routine for top-down enumeration of the tree.
2659 2666   */
2660 2667  static int
2661 2668  ses_enumerate_node(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp,
2662 2669      ses_phys_tree_t *parent, int mrange[])
2663 2670  {
2664 2671          topo_mod_t *mod = sdp->sed_mod;
2665 2672          ses_phys_tree_t *child = NULL;
2666 2673          int i, ret = 0, ret_ch;
2667 2674          uint64_t prevtype = SES_ET_UNSPECIFIED;
2668 2675          ses_phys_tree_t *dirfru = NULL;
2669 2676          tnode_t *tn = NULL, *frutn = NULL;
2670 2677  
2671 2678          if (parent == NULL) {
2672 2679                  return (0);
2673 2680          }
2674 2681  
2675 2682          for (child = parent->spt_child; child != NULL;
2676 2683              child = child->spt_sibling) {
2677 2684                  if (is_type_enumerated(child, &i)) {
2678 2685                          if (prevtype != phys_enum_type_list[i].pet_type) {
2679 2686                                  /* check if range needs to be created. */
2680 2687                                  if (phys_enum_type_list[i].pet_dorange &&
2681 2688                                      topo_node_range_create(mod, pnode,
2682 2689                                      phys_enum_type_list[i].pet_nodename, 0,
2683 2690                                      mrange[i]) != 0) {
2684 2691                                          topo_mod_dprintf(mod,
2685 2692                                              "topo_node_create_range() failed: "
2686 2693                                              "%s", topo_mod_errmsg(mod));
2687 2694                                          return (-1);
2688 2695                                  }
2689 2696                                  prevtype = phys_enum_type_list[i].pet_type;
2690 2697                          }
2691 2698  
2692 2699                          if (!(child->spt_isfru)) {
2693 2700                                  for (dirfru = parent; dirfru != NULL;
2694 2701                                      dirfru = dirfru->spt_parent) {
2695 2702                                          if (dirfru->spt_isfru) {
2696 2703                                                  break;
2697 2704                                          }
2698 2705                                  }
2699 2706                                  /* found direct FRU node. */
2700 2707                                  if (dirfru) {
2701 2708                                          frutn = dirfru->spt_tnode;
2702 2709                                  } else {
2703 2710                                          frutn = NULL;
2704 2711                                  }
2705 2712                          } else {
2706 2713                                  frutn = NULL;
2707 2714                          }
2708 2715  
2709 2716                          if (ses_create_generic(sdp, child->spt_senumnode,
2710 2717                              pnode, frutn, phys_enum_type_list[i].pet_nodename,
2711 2718                              phys_enum_type_list[i].pet_defaultlabel, &tn) != 0)
2712 2719                                  return (-1);
2713 2720  
2714 2721                          child->spt_tnode = tn;
2715 2722                          /*
2716 2723                           * For some SES element there may be protocol specific
2717 2724                           * information to process.   Here we are processing
2718 2725                           * the association between enclosure controller and
2719 2726                           * SAS expanders.
2720 2727                           */
2721 2728                          if (phys_enum_type_list[i].pet_type ==
2722 2729                              SES_ET_ESC_ELECTRONICS) {
2723 2730                                  /* create SAS expander node */
2724 2731                                  if (ses_create_protocol_specific(sdp,
2725 2732                                      child->spt_senumnode, tn,
2726 2733                                      phys_enum_type_list[i].pet_type,
2727 2734                                      cp, phys_enum_type_list[i].pet_dorange) !=
2728 2735                                      0) {
2729 2736                                          return (-1);
2730 2737                                  }
2731 2738                          }
2732 2739                  } else {
2733 2740                          continue;
2734 2741                  }
2735 2742                  ret_ch = ses_enumerate_node(sdp, tn, cp, child, mrange);
2736 2743                  if (ret_ch)
2737 2744                          ret = ret_ch; /* there was an error and set the ret. */
2738 2745          }
2739 2746  
2740 2747          return (ret);
2741 2748  }
2742 2749  
2743 2750  /*
2744 2751   * Instantiate types of nodes that are specified in the hierarchy
2745 2752   * element type list.
2746 2753   */
2747 2754  static int
2748 2755  ses_create_children_from_phys_tree(ses_enum_data_t *sdp, tnode_t *pnode,
2749 2756      ses_enum_chassis_t *cp, ses_phys_tree_t *phys_tree)
2750 2757  {
2751 2758          topo_mod_t *mod = sdp->sed_mod;
2752 2759          int mrange[N_PHYS_ENUM_TYPES] = { 0 };
2753 2760          ses_enum_node_t *snp;
2754 2761          int i, ret;
2755 2762  
2756 2763          /*
2757 2764           * First get max range for each type of element to be enumerated.
2758 2765           */
2759 2766          for (i = 0; i < N_PHYS_ENUM_TYPES; i++) {
2760 2767                  if (phys_enum_type_list[i].pet_dorange) {
2761 2768                          for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2762 2769                              snp = topo_list_next(snp)) {
2763 2770                                  if (snp->sen_type ==
2764 2771                                      phys_enum_type_list[i].pet_type) {
2765 2772                                          if (snp->sen_instance > mrange[i])
2766 2773                                                  mrange[i] =
2767 2774                                                      snp->sen_instance;
2768 2775                                  }
2769 2776                          }
2770 2777                  }
2771 2778          }
2772 2779  
2773 2780          topo_mod_dprintf(mod, "%s: creating nodes from FRU hierarchy tree.",
2774 2781              cp->sec_csn);
2775 2782  
2776 2783          if ((ret = ses_enumerate_node(sdp, pnode, cp, phys_tree, mrange)) !=
2777 2784              0) {
2778 2785                  topo_mod_dprintf(mod,
2779 2786                      "ses_create_children_from_phys_tree() failed: ");
2780 2787                  return (ret);
2781 2788          }
2782 2789  
2783 2790          return (0);
2784 2791  }
2785 2792  
2786 2793  /*
2787 2794   * Instantiate a new chassis instance in the topology.
2788 2795   */
2789 2796  static int
2790 2797  ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp)
2791 2798  {
2792 2799          topo_mod_t *mod = sdp->sed_mod;
2793 2800          nvlist_t *props;
2794 2801          char *raw_manufacturer, *raw_model, *raw_revision;
2795 2802          char *manufacturer = NULL, *model = NULL, *product = NULL;
2796 2803          char *revision = NULL;
2797 2804          char *serial;
2798 2805          char **paths;
2799 2806          size_t prodlen;
2800 2807          tnode_t *tn;
2801 2808          nvlist_t *fmri = NULL, *auth = NULL;
2802 2809          int ret = -1;
2803 2810          ses_enum_node_t *snp;
2804 2811          ses_enum_target_t *stp;
2805 2812          ses_enum_chassis_t *scp;
2806 2813          int i, err;
2807 2814          uint64_t sc_count = 0, pindex;
2808 2815          ses_phys_tree_t *sproot = NULL;
2809 2816          hrtime_t start;
2810 2817          hrtime_t end;
2811 2818          double duration;
2812 2819  
2813 2820          /*
2814 2821           * Ignore any internal enclosures.
2815 2822           */
2816 2823          if (cp->sec_internal)
2817 2824                  return (0);
2818 2825  
2819 2826          /*
2820 2827           * Check to see if there are any devices presennt in the chassis.  If
2821 2828           * not, ignore the chassis alltogether.  This is most useful for
2822 2829           * ignoring internal HBAs that present a SES target but don't actually
2823 2830           * manage any of the devices.
2824 2831           */
2825 2832          for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2826 2833              snp = topo_list_next(snp)) {
2827 2834                  if (snp->sen_type == SES_ET_DEVICE ||
2828 2835                      snp->sen_type == SES_ET_ARRAY_DEVICE)
2829 2836                          break;
2830 2837          }
2831 2838  
2832 2839          if (snp == NULL)
2833 2840                  return (0);
2834 2841  
2835 2842          props = ses_node_props(cp->sec_enclosure);
2836 2843  
2837 2844          /*
2838 2845           * We use the following property mappings:
2839 2846           *
2840 2847           *      manufacturer            vendor-id
2841 2848           *      model                   product-id
2842 2849           *      serial-number           libses-chassis-serial
2843 2850           */
2844 2851          verify(nvlist_lookup_string(props, SES_EN_PROP_VID,
2845 2852              &raw_manufacturer) == 0);
2846 2853          verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0);
2847 2854          verify(nvlist_lookup_string(props, SES_EN_PROP_REV,
2848 2855              &raw_revision) == 0);
2849 2856          verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0);
2850 2857  
2851 2858          /*
2852 2859           * To construct the authority information, we 'clean' each string by
2853 2860           * removing any offensive characters and trimmming whitespace.  For the
2854 2861           * 'product-id', we use a concatenation of 'manufacturer-model'.  We
2855 2862           * also take the numerical serial number and convert it to a string.
2856 2863           */
2857 2864          if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL ||
2858 2865              (model = disk_auth_clean(mod, raw_model)) == NULL ||
2859 2866              (revision = disk_auth_clean(mod, raw_revision)) == NULL) {
2860 2867                  goto error;
2861 2868          }
2862 2869  
2863 2870          prodlen = strlen(manufacturer) + strlen(model) + 2;
2864 2871          if ((product = topo_mod_alloc(mod, prodlen)) == NULL)
2865 2872                  goto error;
2866 2873  
2867 2874          (void) snprintf(product, prodlen, "%s-%s", manufacturer, model);
2868 2875  
2869 2876          /*
2870 2877           * Construct the topo node and bind it to our parent.
2871 2878           */
2872 2879          if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0)
2873 2880                  goto error;
2874 2881  
2875 2882          if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 ||
2876 2883              nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) {
2877 2884                  (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
2878 2885                  goto error;
2879 2886          }
2880 2887  
2881 2888          /*
2882 2889           * We pass NULL for the parent FMRI because there is no resource
2883 2890           * associated with it.  For the toplevel enclosure, we leave the
2884 2891           * serial/part/revision portions empty, which are reserved for
2885 2892           * individual components within the chassis.
2886 2893           */
2887 2894          if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION,
2888 2895              SES_ENCLOSURE, cp->sec_instance, NULL, auth,
2889 2896              model, revision, serial)) == NULL) {
2890 2897                  topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
2891 2898                      topo_mod_errmsg(mod));
2892 2899                  goto error;
2893 2900          }
2894 2901  
2895 2902          if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE,
2896 2903              cp->sec_instance, fmri)) == NULL) {
2897 2904                  topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
2898 2905                      topo_mod_errmsg(mod));
2899 2906                  goto error;
2900 2907          }
2901 2908  
2902 2909          if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
2903 2910                  topo_mod_dprintf(mod,
2904 2911                      "topo_method_register() failed: %s",
2905 2912                      topo_mod_errmsg(mod));
2906 2913                  goto error;
2907 2914          }
2908 2915  
2909 2916          if (ses_set_standard_props(mod, NULL, tn, auth,
2910 2917              ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0)
2911 2918                  goto error;
2912 2919  
2913 2920          /*
2914 2921           * For enclosures, we want to include all possible targets (for upgrade
2915 2922           * purposes).
2916 2923           */
2917 2924          for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
2918 2925              stp = topo_list_next(stp), i++)
2919 2926                  ;
2920 2927  
2921 2928          verify(i != 0);
2922 2929          paths = alloca(i * sizeof (char *));
2923 2930  
2924 2931          for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
2925 2932              stp = topo_list_next(stp), i++)
2926 2933                  paths[i] = stp->set_devpath;
2927 2934  
2928 2935  
2929 2936          if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
2930 2937              TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
2931 2938              i, &err) != 0) {
2932 2939                  topo_mod_dprintf(mod,
2933 2940                      "failed to create property %s: %s\n",
2934 2941                      TOPO_PROP_PATHS, topo_strerror(err));
2935 2942                  goto error;
2936 2943          }
2937 2944  
2938 2945          if (nvlist_lookup_uint64(props,
2939 2946              LIBSES_PROP_PHYS_PARENT, &pindex) == 0) {
2940 2947                  start = gethrtime(); /* to mearusre performance */
2941 2948                  /*
2942 2949                   * The enclosure is supported through SUNW,FRUID.
2943 2950                   * Need to enumerate the nodes through hierarchical order.
2944 2951                   */
2945 2952                  if ((sproot = topo_mod_zalloc(mod,
2946 2953                      sizeof (ses_phys_tree_t))) == NULL) {
2947 2954                          topo_mod_dprintf(mod,
2948 2955                              "failed to allocate root: %s\n",
2949 2956                              topo_strerror(err));
2950 2957                          goto error;
2951 2958                  }
2952 2959                  sproot->spt_pindex = pindex;
2953 2960                  if (nvlist_lookup_boolean_value(props,
2954 2961                      LIBSES_PROP_FRU, &sproot->spt_isfru) != 0) {
2955 2962                          topo_mod_dprintf(mod,
2956 2963                              "ses_create_chassis(): Failed to find prop %s "
2957 2964                              "on enclosure element (CSN %s).",
2958 2965                              LIBSES_PROP_FRU, cp->sec_csn);
2959 2966                          /* an enclosure should be a FRU. continue to process. */
2960 2967                          sproot->spt_isfru = B_TRUE;
2961 2968                  }
2962 2969                  if (nvlist_lookup_uint64(props,
2963 2970                      SES_PROP_ELEMENT_ONLY_INDEX,
2964 2971                      &sproot->spt_eonlyindex) != 0) {
2965 2972                          topo_mod_dprintf(mod,
2966 2973                              "ses_create_chassis(): Failed to find prop %s "
2967 2974                              "on enclosure element (CSN %s).",
2968 2975                              LIBSES_PROP_PHYS_PARENT, cp->sec_csn);
2969 2976                          topo_mod_free(mod, sproot, sizeof (ses_phys_tree_t));
2970 2977                          goto error;
2971 2978                  }
2972 2979                  if (sproot->spt_pindex != sproot->spt_eonlyindex) {
2973 2980                          topo_mod_dprintf(mod, "ses_create_chassis(): "
2974 2981                              "Enclosure element(CSN %s) should have "
2975 2982                              "itself as the parent to be the root node "
2976 2983                              "of FRU hierarchical tree.)", cp->sec_csn);
2977 2984                          topo_mod_free(mod, sproot, sizeof (ses_phys_tree_t));
2978 2985                          goto error;
2979 2986                  } else {
2980 2987                          sproot->spt_snode = cp->sec_enclosure;
2981 2988                          sproot->spt_tnode = tn;
2982 2989                          /* construct a tree. */
2983 2990                          if (ses_construct_phys_tree(sdp, cp, sproot) != 0) {
2984 2991                                  topo_mod_dprintf(mod, "ses_create_chassis(): "
2985 2992                                      "Failed to construct FRU hierarchical "
2986 2993                                      "tree on enclosure (CSN %s.)",
2987 2994                                      cp->sec_csn);
2988 2995                          }
2989 2996  
2990 2997                          /* enumerate elements from the tree. */
2991 2998                          if (ses_create_children_from_phys_tree(sdp, tn, cp,
2992 2999                              sproot) != 0) {
2993 3000                                  topo_mod_dprintf(mod, "ses_create_chassis(): "
2994 3001                                      "Failed to create children topo nodes out "
2995 3002                                      "of FRU hierarchical tree on enclosure "
2996 3003                                      "(CSN %s).", cp->sec_csn);
2997 3004                          }
2998 3005                          /* destroy the phys tree. */
2999 3006                          ses_phys_tree_free(mod, sproot);
3000 3007                  }
3001 3008  
3002 3009                  end = gethrtime();
3003 3010                  duration = end - start;
3004 3011                  duration /= HR_SECOND;
3005 3012                  topo_mod_dprintf(mod,
3006 3013                      "FRU boundary tree based enumeration: %.6f seconds",
3007 3014                      duration);
3008 3015          } else {
3009 3016                  /*
3010 3017                   * Create the nodes for power supplies, fans, controllers and
3011 3018                   * devices.  Note that SAS exopander nodes and connector nodes
3012 3019                   * are handled through protocol specific processing of
3013 3020                   * controllers.
3014 3021                   */
3015 3022                  if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY,
3016 3023                      PSU, "PSU", cp, B_TRUE) != 0 ||
3017 3024                      ses_create_children(sdp, tn, SES_ET_COOLING,
3018 3025                      FAN, "FAN", cp, B_TRUE) != 0 ||
3019 3026                      ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
3020 3027                      CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 ||
3021 3028                      ses_create_children(sdp, tn, SES_ET_DEVICE,
3022 3029                      BAY, "BAY", cp, B_TRUE) != 0 ||
3023 3030                      ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
3024 3031                      BAY, "BAY", cp, B_TRUE) != 0)
3025 3032                          goto error;
3026 3033          }
3027 3034  
3028 3035          if (cp->sec_maxinstance >= 0 &&
3029 3036              (topo_node_range_create(mod, tn, SUBCHASSIS, 0,
3030 3037              cp->sec_maxinstance) != 0)) {
3031 3038                  topo_mod_dprintf(mod, "topo_node_create_range() failed: %s",
3032 3039                      topo_mod_errmsg(mod));
3033 3040                  goto error;
3034 3041          }
3035 3042  
3036 3043          for (scp = topo_list_next(&cp->sec_subchassis); scp != NULL;
3037 3044              scp = topo_list_next(scp)) {
3038 3045  
3039 3046                  if (ses_create_subchassis(sdp, tn, scp) != 0)
3040 3047                          goto error;
3041 3048  
3042 3049                  topo_mod_dprintf(mod, "created Subchassis node with "
3043 3050                      "instance %u\nand target (%s) under Chassis with CSN %s",
3044 3051                      scp->sec_instance, scp->sec_target->set_devpath,
3045 3052                      cp->sec_csn);
3046 3053  
3047 3054                  sc_count++;
3048 3055          }
3049 3056  
3050 3057          topo_mod_dprintf(mod, "%s: created %llu %s nodes",
3051 3058              cp->sec_csn, sc_count, SUBCHASSIS);
3052 3059  
3053 3060          cp->sec_target->set_refcount++;
3054 3061          topo_node_setspecific(tn, cp->sec_target);
3055 3062  
3056 3063          ret = 0;
3057 3064  error:
3058 3065          topo_mod_strfree(mod, manufacturer);
3059 3066          topo_mod_strfree(mod, model);
3060 3067          topo_mod_strfree(mod, revision);
3061 3068          topo_mod_strfree(mod, product);
3062 3069  
3063 3070          nvlist_free(fmri);
3064 3071          nvlist_free(auth);
3065 3072          return (ret);
3066 3073  }
3067 3074  
3068 3075  /*
3069 3076   * Create a bay node explicitly enumerated via XML.
3070 3077   */
3071 3078  static int
3072 3079  ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode)
3073 3080  {
3074 3081          topo_mod_t *mod = sdp->sed_mod;
3075 3082          ses_enum_chassis_t *cp;
3076 3083  
3077 3084          /*
3078 3085           * Iterate over chassis looking for an internal enclosure.  This
3079 3086           * property is set via a vendor-specific plugin, and there should only
3080 3087           * ever be a single internal chassis in a system.
3081 3088           */
3082 3089          for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
3083 3090              cp = topo_list_next(cp)) {
3084 3091                  if (cp->sec_internal)
3085 3092                          break;
3086 3093          }
3087 3094  
3088 3095          if (cp == NULL) {
3089 3096                  topo_mod_dprintf(mod, "failed to find internal chassis\n");
3090 3097                  return (-1);
3091 3098          }
3092 3099  
3093 3100          if (ses_create_children(sdp, pnode, SES_ET_DEVICE,
3094 3101              BAY, "BAY", cp, B_FALSE) != 0 ||
3095 3102              ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE,
3096 3103              BAY, "BAY", cp, B_FALSE) != 0)
3097 3104                  return (-1);
3098 3105  
3099 3106          return (0);
3100 3107  }
3101 3108  
3102 3109  /*
3103 3110   * Initialize chassis or subchassis.
3104 3111   */
3105 3112  static int
3106 3113  ses_init_chassis(topo_mod_t *mod, ses_enum_data_t *sdp, ses_enum_chassis_t *pcp,
3107 3114      ses_enum_chassis_t *cp, ses_node_t *np, nvlist_t *props,
3108 3115      uint64_t subchassis, ses_chassis_type_e flags)
3109 3116  {
3110 3117          boolean_t internal, ident;
3111 3118  
3112 3119          assert((flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS |
3113 3120              SES_DUP_CHASSIS | SES_DUP_SUBCHASSIS)) != 0);
3114 3121  
3115 3122          assert(cp != NULL);
3116 3123          assert(np != NULL);
3117 3124          assert(props != NULL);
3118 3125  
3119 3126          if (flags & (SES_NEW_SUBCHASSIS | SES_DUP_SUBCHASSIS))
3120 3127                  assert(pcp != NULL);
3121 3128  
3122 3129          topo_mod_dprintf(mod, "ses_init_chassis: %s: index %llu, flags (%d)",
3123 3130              sdp->sed_name, subchassis, flags);
3124 3131  
3125 3132          if (flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS)) {
3126 3133  
3127 3134                  topo_mod_dprintf(mod, "new chassis/subchassis");
3128 3135                  if (nvlist_lookup_boolean_value(props,
3129 3136                      LIBSES_EN_PROP_INTERNAL, &internal) == 0)
3130 3137                          cp->sec_internal = internal;
3131 3138  
3132 3139                  cp->sec_enclosure = np;
3133 3140                  cp->sec_target = sdp->sed_target;
3134 3141  
3135 3142                  if (flags & SES_NEW_CHASSIS) {
3136 3143                          if (!cp->sec_internal)
3137 3144                                  cp->sec_instance = sdp->sed_instance++;
3138 3145                          topo_list_append(&sdp->sed_chassis, cp);
3139 3146                  } else {
3140 3147                          if (subchassis != NO_SUBCHASSIS)
3141 3148                                  cp->sec_instance = subchassis;
3142 3149                          else
3143 3150                                  cp->sec_instance = pcp->sec_scinstance++;
3144 3151  
3145 3152                          if (cp->sec_instance > pcp->sec_maxinstance)
3146 3153                                  pcp->sec_maxinstance = cp->sec_instance;
3147 3154  
3148 3155                          topo_list_append(&pcp->sec_subchassis, cp);
3149 3156                  }
3150 3157  
3151 3158          } else {
3152 3159                  topo_mod_dprintf(mod, "dup chassis/subchassis");
3153 3160                  if (nvlist_lookup_boolean_value(props,
3154 3161                      SES_PROP_IDENT, &ident) == 0) {
3155 3162                          topo_mod_dprintf(mod,  "overriding enclosure node");
3156 3163  
3157 3164                          cp->sec_enclosure = np;
3158 3165                          cp->sec_target = sdp->sed_target;
3159 3166                  }
3160 3167          }
3161 3168  
3162 3169          topo_list_append(&cp->sec_targets, sdp->sed_target);
3163 3170          sdp->sed_current = cp;
3164 3171  
3165 3172          return (0);
3166 3173  }
3167 3174  
3168 3175  /*
3169 3176   * Gather nodes from the current SES target into our chassis list, merging the
3170 3177   * results if necessary.
3171 3178   */
3172 3179  static ses_walk_action_t
3173 3180  ses_enum_gather(ses_node_t *np, void *data)
3174 3181  {
3175 3182          nvlist_t *props = ses_node_props(np);
3176 3183          ses_enum_data_t *sdp = data;
3177 3184          topo_mod_t *mod = sdp->sed_mod;
3178 3185          ses_enum_chassis_t *cp, *scp;
3179 3186          ses_enum_node_t *snp;
3180 3187          ses_alt_node_t *sap;
3181 3188          char *csn;
3182 3189          uint64_t instance, type;
3183 3190          uint64_t prevstatus, status;
3184 3191          boolean_t report;
3185 3192          uint64_t subchassis = NO_SUBCHASSIS;
3186 3193  
3187 3194          if (ses_node_type(np) == SES_NODE_ENCLOSURE) {
3188 3195                  /*
3189 3196                   * If we have already identified the chassis for this target,
3190 3197                   * then this is a secondary enclosure and we should ignore it,
3191 3198                   * along with the rest of the tree (since this is depth-first).
3192 3199                   */
3193 3200                  if (sdp->sed_current != NULL)
3194 3201                          return (SES_WALK_ACTION_TERMINATE);
3195 3202  
3196 3203                  /*
3197 3204                   * Go through the list of chassis we have seen so far and see
3198 3205                   * if this serial number matches one of the known values.
3199 3206                   * If so, check whether this enclosure is a subchassis.
3200 3207                   */
3201 3208                  if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN,
3202 3209                      &csn) != 0)
3203 3210                          return (SES_WALK_ACTION_TERMINATE);
3204 3211  
3205 3212                  (void) nvlist_lookup_uint64(props, LIBSES_EN_PROP_SUBCHASSIS_ID,
3206 3213                      &subchassis);
3207 3214  
3208 3215                  topo_mod_dprintf(mod, "ses_enum_gather: Enclosure Node (%s) "
3209 3216                      "CSN (%s), subchassis (%llu)", sdp->sed_name, csn,
3210 3217                      subchassis);
3211 3218  
3212 3219                  /*
3213 3220                   * We need to determine whether this enclosure node
3214 3221                   * represents a chassis or a subchassis. Since we may
3215 3222                   * receive the enclosure nodes in a non-deterministic
3216 3223                   * manner, we need to account for all possible combinations:
3217 3224                   *      1. Chassis for the current CSN has not yet been
3218 3225                   *         allocated
3219 3226                   *              1.1 This is a new chassis:
3220 3227                   *                      allocate and instantiate the chassis
3221 3228                   *              1.2 This is a new subchassis:
3222 3229                   *                      allocate a placeholder chassis
3223 3230                   *                      allocate and instantiate the subchassis
3224 3231                   *                      link the subchassis to the chassis
3225 3232                   *      2. Chassis for the current CSN has been allocated
3226 3233                   *              2.1 This is a duplicate chassis enclosure
3227 3234                   *                      check whether to override old chassis
3228 3235                   *                      append to chassis' target list
3229 3236                   *              2.2 Only placeholder chassis exists
3230 3237                   *                      fill in the chassis fields
3231 3238                   *              2.3 This is a new subchassis
3232 3239                   *                      allocate and instantiate the subchassis
3233 3240                   *                      link the subchassis to the chassis
3234 3241                   *              2.4 This is a duplicate subchassis enclosure
3235 3242                   *                       check whether to override old chassis
3236 3243                   *                       append to chassis' target list
3237 3244                   */
3238 3245  
3239 3246                  for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
3240 3247                      cp = topo_list_next(cp))
3241 3248                          if (strcmp(cp->sec_csn, csn) == 0)
3242 3249                                  break;
3243 3250  
3244 3251                  if (cp == NULL) {
3245 3252                          /* 1. Haven't seen a chassis with this CSN before */
3246 3253  
3247 3254                          if ((cp = topo_mod_zalloc(mod,
3248 3255                              sizeof (ses_enum_chassis_t))) == NULL)
3249 3256                                  goto error;
3250 3257  
3251 3258                          cp->sec_scinstance = SES_STARTING_SUBCHASSIS;
3252 3259                          cp->sec_maxinstance = -1;
3253 3260                          cp->sec_csn = csn;
3254 3261  
3255 3262                          if (subchassis == NO_SUBCHASSIS) {
3256 3263                                  /* 1.1 This is a new chassis */
3257 3264  
3258 3265                                  topo_mod_dprintf(mod, "%s: Initialize new "
3259 3266                                      "chassis with CSN %s", sdp->sed_name, csn);
3260 3267  
3261 3268                                  if (ses_init_chassis(mod, sdp, NULL, cp,
3262 3269                                      np, props, NO_SUBCHASSIS,
3263 3270                                      SES_NEW_CHASSIS) < 0)
3264 3271                                          goto error;
3265 3272                          } else {
3266 3273                                  /* 1.2 This is a new subchassis */
3267 3274  
3268 3275                                  topo_mod_dprintf(mod, "%s: Initialize new "
3269 3276                                      "subchassis with CSN %s and index %llu",
3270 3277                                      sdp->sed_name, csn, subchassis);
3271 3278  
3272 3279                                  if ((scp = topo_mod_zalloc(mod,
3273 3280                                      sizeof (ses_enum_chassis_t))) == NULL)
3274 3281                                          goto error;
3275 3282  
3276 3283                                  scp->sec_csn = csn;
3277 3284  
3278 3285                                  if (ses_init_chassis(mod, sdp, cp, scp, np,
3279 3286                                      props, subchassis, SES_NEW_SUBCHASSIS) < 0)
3280 3287                                          goto error;
3281 3288                          }
3282 3289                  } else {
3283 3290                          /*
3284 3291                           * We have a chassis or subchassis with this CSN.  If
3285 3292                           * it's a chassis, we must check to see whether it is
3286 3293                           * a placeholder previously created because we found a
3287 3294                           * subchassis with this CSN.  We will know that because
3288 3295                           * the sec_target value will not be set; it is set only
3289 3296                           * in ses_init_chassis().  In that case, initialise it
3290 3297                           * as a new chassis; otherwise, it's a duplicate and we
3291 3298                           * need to append only.
3292 3299                           */
3293 3300                          if (subchassis == NO_SUBCHASSIS) {
3294 3301                                  if (cp->sec_target != NULL) {
3295 3302                                          /* 2.1 This is a duplicate chassis */
3296 3303  
3297 3304                                          topo_mod_dprintf(mod, "%s: Append "
3298 3305                                              "duplicate chassis with CSN (%s)",
3299 3306                                              sdp->sed_name, csn);
3300 3307  
3301 3308                                          if (ses_init_chassis(mod, sdp, NULL, cp,
3302 3309                                              np, props, NO_SUBCHASSIS,
3303 3310                                              SES_DUP_CHASSIS) < 0)
3304 3311                                                  goto error;
3305 3312                                  } else {
3306 3313                                          /* Placeholder chassis - init it up */
3307 3314                                          topo_mod_dprintf(mod, "%s: Initialize"
3308 3315                                              "placeholder chassis with CSN %s",
3309 3316                                              sdp->sed_name, csn);
3310 3317  
3311 3318                                          if (ses_init_chassis(mod, sdp, NULL,
3312 3319                                              cp, np, props, NO_SUBCHASSIS,
3313 3320                                              SES_NEW_CHASSIS) < 0)
3314 3321                                                  goto error;
3315 3322  
3316 3323                                  }
3317 3324                          } else {
3318 3325                                  /* This is a subchassis */
3319 3326  
3320 3327                                  for (scp = topo_list_next(&cp->sec_subchassis);
3321 3328                                      scp != NULL; scp = topo_list_next(scp))
3322 3329                                          if (scp->sec_instance == subchassis)
3323 3330                                                  break;
3324 3331  
3325 3332                                  if (scp == NULL) {
3326 3333                                          /* 2.3 This is a new subchassis */
3327 3334  
3328 3335                                          topo_mod_dprintf(mod, "%s: Initialize "
3329 3336                                              "new subchassis with CSN (%s) "
3330 3337                                              "and LID (%s)",
3331 3338                                              sdp->sed_name, csn);
3332 3339  
3333 3340                                          if ((scp = topo_mod_zalloc(mod,
3334 3341                                              sizeof (ses_enum_chassis_t)))
3335 3342                                              == NULL)
3336 3343                                                  goto error;
3337 3344  
3338 3345                                          scp->sec_csn = csn;
3339 3346  
3340 3347                                          if (ses_init_chassis(mod, sdp, cp, scp,
3341 3348                                              np, props, subchassis,
3342 3349                                              SES_NEW_SUBCHASSIS) < 0)
3343 3350                                                  goto error;
3344 3351                                  } else {
3345 3352                                          /* 2.4 This is a duplicate subchassis */
3346 3353  
3347 3354                                          topo_mod_dprintf(mod, "%s: Append "
3348 3355                                              "duplicate subchassis with "
3349 3356                                              "CSN (%s)", sdp->sed_name, csn);
3350 3357  
3351 3358                                          if (ses_init_chassis(mod, sdp, cp, scp,
3352 3359                                              np, props, subchassis,
3353 3360                                              SES_DUP_SUBCHASSIS) < 0)
3354 3361                                                  goto error;
3355 3362                                  }
3356 3363                          }
3357 3364                  }
3358 3365          } else if (ses_node_type(np) == SES_NODE_ELEMENT) {
3359 3366                  /*
3360 3367                   * If we haven't yet seen an enclosure node and identified the
3361 3368                   * current chassis, something is very wrong; bail out.
3362 3369                   */
3363 3370                  if (sdp->sed_current == NULL)
3364 3371                          return (SES_WALK_ACTION_TERMINATE);
3365 3372  
3366 3373                  /*
3367 3374                   * If this isn't one of the element types we care about, then
3368 3375                   * ignore it.
3369 3376                   */
3370 3377                  verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE,
3371 3378                      &type) == 0);
3372 3379                  if (type != SES_ET_DEVICE &&
3373 3380                      type != SES_ET_ARRAY_DEVICE &&
3374 3381                      type != SES_ET_SUNW_FANBOARD &&
3375 3382                      type != SES_ET_SUNW_FANMODULE &&
3376 3383                      type != SES_ET_COOLING &&
3377 3384                      type != SES_ET_SUNW_POWERBOARD &&
3378 3385                      type != SES_ET_SUNW_POWERMODULE &&
3379 3386                      type != SES_ET_POWER_SUPPLY &&
3380 3387                      type != SES_ET_ESC_ELECTRONICS &&
3381 3388                      type != SES_ET_SAS_EXPANDER &&
3382 3389                      type != SES_ET_SAS_CONNECTOR)
3383 3390                          return (SES_WALK_ACTION_CONTINUE);
3384 3391  
3385 3392                  /*
3386 3393                   * Get the current instance number and see if we already know
3387 3394                   * about this element.  If so, it means we have multiple paths
3388 3395                   * to the same elements, and we should ignore the current path.
3389 3396                   */
3390 3397                  verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX,
3391 3398                      &instance) == 0);
3392 3399                  if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE)
3393 3400                          (void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER,
3394 3401                              &instance);
3395 3402  
3396 3403                  cp = sdp->sed_current;
3397 3404  
3398 3405                  for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
3399 3406                      snp = topo_list_next(snp)) {
3400 3407                          if (snp->sen_type == type &&
3401 3408                              snp->sen_instance == instance)
3402 3409                                  break;
3403 3410                  }
3404 3411  
3405 3412                  /*
3406 3413                   * We prefer the new element under the following circumstances:
3407 3414                   *
3408 3415                   * - The currently known element's status is unknown or not
3409 3416                   *   available, but the new element has a known status.  This
3410 3417                   *   occurs if a given element is only available through a
3411 3418                   *   particular target.
3412 3419                   *
3413 3420                   * - This is an ESC_ELECTRONICS element, and the 'reported-via'
3414 3421                   *   property is set.  This allows us to get reliable firmware
3415 3422                   *   revision information from the enclosure node.
3416 3423                   */
3417 3424                  if (snp != NULL) {
3418 3425                          if (nvlist_lookup_uint64(
3419 3426                              ses_node_props(snp->sen_node),
3420 3427                              SES_PROP_STATUS_CODE, &prevstatus) != 0)
3421 3428                                  prevstatus = SES_ESC_UNSUPPORTED;
3422 3429                          if (nvlist_lookup_uint64(
3423 3430                              props, SES_PROP_STATUS_CODE, &status) != 0)
3424 3431                                  status = SES_ESC_UNSUPPORTED;
3425 3432                          if (nvlist_lookup_boolean_value(
3426 3433                              props, SES_PROP_REPORT, &report) != 0)
3427 3434                                  report = B_FALSE;
3428 3435  
3429 3436                          if ((SES_STATUS_UNAVAIL(prevstatus) &&
3430 3437                              !SES_STATUS_UNAVAIL(status)) ||
3431 3438                              (type == SES_ET_ESC_ELECTRONICS &&
3432 3439                              report)) {
3433 3440                                  snp->sen_node = np;
3434 3441                                  snp->sen_target = sdp->sed_target;
3435 3442                          }
3436 3443  
3437 3444                          if ((sap = topo_mod_zalloc(mod,
3438 3445                              sizeof (ses_alt_node_t))) == NULL)
3439 3446                                  goto error;
3440 3447  
3441 3448                          sap->san_node = np;
3442 3449                          topo_list_append(&snp->sen_alt_nodes, sap);
3443 3450  
3444 3451                          return (SES_WALK_ACTION_CONTINUE);
3445 3452                  }
3446 3453  
3447 3454                  if ((snp = topo_mod_zalloc(mod,
3448 3455                      sizeof (ses_enum_node_t))) == NULL)
3449 3456                          goto error;
3450 3457  
3451 3458                  if ((sap = topo_mod_zalloc(mod,
3452 3459                      sizeof (ses_alt_node_t))) == NULL) {
3453 3460                          topo_mod_free(mod, snp, sizeof (ses_enum_node_t));
3454 3461                          goto error;
3455 3462                  }
3456 3463  
3457 3464                  topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)",
3458 3465                      sdp->sed_name, type, instance);
3459 3466                  snp->sen_node = np;
3460 3467                  snp->sen_type = type;
3461 3468                  snp->sen_instance = instance;
3462 3469                  snp->sen_target = sdp->sed_target;
3463 3470                  sap->san_node = np;
3464 3471                  topo_list_append(&snp->sen_alt_nodes, sap);
3465 3472                  topo_list_append(&cp->sec_nodes, snp);
3466 3473  
3467 3474                  if (type == SES_ET_DEVICE)
3468 3475                          cp->sec_hasdev = B_TRUE;
3469 3476          }
3470 3477  
3471 3478          return (SES_WALK_ACTION_CONTINUE);
3472 3479  
3473 3480  error:
3474 3481          sdp->sed_errno = -1;
3475 3482          return (SES_WALK_ACTION_TERMINATE);
3476 3483  }
3477 3484  
3478 3485  static int
3479 3486  ses_process_dir(const char *dirpath, ses_enum_data_t *sdp)
3480 3487  {
3481 3488          topo_mod_t *mod = sdp->sed_mod;
3482 3489          DIR *dir;
3483 3490          struct dirent *dp;
3484 3491          char path[PATH_MAX];
3485 3492          ses_enum_target_t *stp;
3486 3493          int err = -1;
3487 3494  
3488 3495          /*
3489 3496           * Open the SES target directory and iterate over any available
3490 3497           * targets.
3491 3498           */
3492 3499          if ((dir = opendir(dirpath)) == NULL) {
3493 3500                  /*
3494 3501                   * If the SES target directory does not exist, then return as if
3495 3502                   * there are no active targets.
3496 3503                   */
3497 3504                  topo_mod_dprintf(mod, "failed to open ses "
3498 3505                      "directory '%s'", dirpath);
3499 3506                  return (0);
3500 3507          }
3501 3508  
3502 3509          while ((dp = readdir(dir)) != NULL) {
3503 3510                  if (strcmp(dp->d_name, ".") == 0 ||
3504 3511                      strcmp(dp->d_name, "..") == 0)
3505 3512                          continue;
3506 3513  
3507 3514                  /*
3508 3515                   * Create a new target instance and take a snapshot.
3509 3516                   */
3510 3517                  if ((stp = topo_mod_zalloc(mod,
3511 3518                      sizeof (ses_enum_target_t))) == NULL)
3512 3519                          goto error;
3513 3520  
3514 3521                  (void) pthread_mutex_init(&stp->set_lock, NULL);
3515 3522  
3516 3523                  (void) snprintf(path, sizeof (path), "%s/%s", dirpath,
3517 3524                      dp->d_name);
3518 3525  
3519 3526                  /*
3520 3527                   * We keep track of the SES device path and export it on a
3521 3528                   * per-node basis to allow higher level software to get to the
3522 3529                   * corresponding SES state.
3523 3530                   */
3524 3531                  if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) {
3525 3532                          topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
3526 3533                          goto error;
3527 3534                  }
3528 3535  
3529 3536                  if ((stp->set_target =
3530 3537                      ses_open(LIBSES_VERSION, path)) == NULL) {
3531 3538                          topo_mod_dprintf(mod, "failed to open ses target "
3532 3539                              "'%s': %s", dp->d_name, ses_errmsg());
3533 3540                          ses_sof_alloc(mod, stp->set_devpath);
3534 3541                          topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
3535 3542                          continue;
3536 3543                  }
3537 3544                  topo_mod_dprintf(mod, "open contract");
3538 3545                  ses_ssl_alloc(mod, stp);
3539 3546                  ses_create_contract(mod, stp);
3540 3547  
3541 3548                  stp->set_refcount = 1;
3542 3549                  sdp->sed_target = stp;
3543 3550                  stp->set_snap = ses_snap_hold(stp->set_target);
3544 3551                  stp->set_snaptime = gethrtime();
3545 3552  
3546 3553                  /*
3547 3554                   * Enumerate over all SES elements and merge them into the
3548 3555                   * correct ses_enum_chassis_t.
3549 3556                   */
3550 3557                  sdp->sed_current = NULL;
3551 3558                  sdp->sed_errno = 0;
3552 3559                  sdp->sed_name = dp->d_name;
3553 3560                  (void) ses_walk(stp->set_snap, ses_enum_gather, sdp);
3554 3561  
3555 3562                  if (sdp->sed_errno != 0)
3556 3563                          goto error;
3557 3564          }
3558 3565  
3559 3566          err = 0;
3560 3567  error:
3561 3568          (void) closedir(dir);
3562 3569          return (err);
3563 3570  }
3564 3571  
3565 3572  static void
3566 3573  ses_release(topo_mod_t *mod, tnode_t *tn)
3567 3574  {
3568 3575          ses_enum_target_t *stp;
3569 3576  
3570 3577          if ((stp = topo_node_getspecific(tn)) != NULL) {
3571 3578                  topo_node_setspecific(tn, NULL);
3572 3579                  ses_target_free(mod, stp);
3573 3580          }
3574 3581  }
3575 3582  
3576 3583  /*ARGSUSED*/
3577 3584  static int
3578 3585  ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
3579 3586      topo_instance_t min, topo_instance_t max, void *arg, void *notused)
3580 3587  {
3581 3588          ses_enum_chassis_t *cp;
3582 3589          ses_enum_data_t *data;
3583 3590  
3584 3591          /*
3585 3592           * Check to make sure we're being invoked sensibly, and that we're not
3586 3593           * being invoked as part of a post-processing step.
3587 3594           */
3588 3595          if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0)
3589 3596                  return (0);
3590 3597  
3591 3598          /*
3592 3599           * If this is the first time we've called our enumeration method, then
3593 3600           * gather information about any available enclosures.
3594 3601           */
3595 3602          if ((data = topo_mod_getspecific(mod)) == NULL) {
3596 3603                  ses_sof_freeall(mod);
3597 3604                  if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) ==
3598 3605                      NULL)
3599 3606                          return (-1);
3600 3607  
3601 3608                  data->sed_mod = mod;
3602 3609                  topo_mod_setspecific(mod, data);
3603 3610  
3604 3611                  if (dev_list_gather(mod, &data->sed_devs) != 0)
3605 3612                          goto error;
3606 3613  
3607 3614                  /*
3608 3615                   * We search both the ses(7D) and sgen(7D) locations, so we are
3609 3616                   * independent of any particular driver class bindings.
3610 3617                   */
3611 3618                  if (ses_process_dir("/dev/es", data) != 0 ||
3612 3619                      ses_process_dir("/dev/scsi/ses", data) != 0)
3613 3620                          goto error;
3614 3621          }
3615 3622  
3616 3623          if (strcmp(name, SES_ENCLOSURE) == 0) {
3617 3624                  /*
3618 3625                   * This is a request to enumerate external enclosures.  Go
3619 3626                   * through all the targets and create chassis nodes where
3620 3627                   * necessary.
3621 3628                   */
3622 3629                  for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
3623 3630                      cp = topo_list_next(cp)) {
3624 3631                          if (ses_create_chassis(data, rnode, cp) != 0)
3625 3632                                  goto error;
3626 3633                  }
3627 3634          } else {
3628 3635                  /*
3629 3636                   * This is a request to enumerate a specific bay underneath the
3630 3637                   * root chassis (for internal disks).
3631 3638                   */
3632 3639                  if (ses_create_bays(data, rnode) != 0)
3633 3640                          goto error;
3634 3641          }
3635 3642  
3636 3643          /*
3637 3644           * This is a bit of a kludge.  In order to allow internal disks to be
3638 3645           * enumerated and share snapshot-specific information with the external
3639 3646           * enclosure enumeration, we rely on the fact that we will be invoked
3640 3647           * for the 'ses-enclosure' node last.
3641 3648           */
3642 3649          if (strcmp(name, SES_ENCLOSURE) == 0) {
3643 3650                  for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
3644 3651                      cp = topo_list_next(cp))
3645 3652                          ses_data_free(data, cp);
3646 3653                  ses_data_free(data, NULL);
3647 3654                  topo_mod_setspecific(mod, NULL);
3648 3655          }
3649 3656          return (0);
3650 3657  
3651 3658  error:
3652 3659          for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
3653 3660              cp = topo_list_next(cp))
3654 3661                  ses_data_free(data, cp);
3655 3662          ses_data_free(data, NULL);
3656 3663          topo_mod_setspecific(mod, NULL);
3657 3664          return (-1);
3658 3665  }
3659 3666  
3660 3667  static const topo_modops_t ses_ops =
3661 3668          { ses_enum, ses_release };
3662 3669  
3663 3670  static topo_modinfo_t ses_info =
3664 3671          { SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops };
3665 3672  
3666 3673  /*ARGSUSED*/
3667 3674  int
3668 3675  _topo_init(topo_mod_t *mod, topo_version_t version)
3669 3676  {
3670 3677          int rval;
3671 3678  
3672 3679          if (getenv("TOPOSESDEBUG") != NULL)
3673 3680                  topo_mod_setdebug(mod);
3674 3681  
3675 3682          topo_mod_dprintf(mod, "initializing %s enumerator\n",
3676 3683              SES_ENCLOSURE);
3677 3684  
3678 3685          if ((rval = topo_mod_register(mod, &ses_info, TOPO_VERSION)) == 0)
3679 3686                  ses_thread_init(mod);
3680 3687  
3681 3688          return (rval);
3682 3689  }
3683 3690  
3684 3691  void
3685 3692  _topo_fini(topo_mod_t *mod)
3686 3693  {
3687 3694          ses_thread_fini(mod);
3688 3695          ses_sof_freeall(mod);
3689 3696          topo_mod_unregister(mod);
3690 3697  }
  
    | 
      ↓ open down ↓ | 
    2658 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX