Print this page
    
OS-66 Retired devices may still get attached leading to ndi_devi_online errors
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/lib/libdevinfo/devinfo_retire.c
          +++ new/usr/src/lib/libdevinfo/devinfo_retire.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
  
    | 
      ↓ open down ↓ | 
    13 lines elided | 
    
      ↑ open up ↑ | 
  
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  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) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
       24 + * Copyright (c) 2013, Nexenta Systemc, Inc.  All rights reserved.
  24   25   */
  25   26  
  26   27  #include <libdevinfo.h>
  27   28  #include <sys/modctl.h>
  28   29  #include <sys/stat.h>
  29   30  #include <string.h>
  30   31  #include <librcm.h>
  31   32  #include <dlfcn.h>
       33 +#include <sys/scsi/scsi_address.h>
       34 +#include <limits.h>
       35 +#include <errno.h>
  32   36  
  33   37  #undef  NDEBUG
  34   38  #include <assert.h>
  35   39  
  36   40  typedef struct rio_path {
  37   41          char            rpt_path[PATH_MAX];
  38   42          struct rio_path *rpt_next;
  39   43  } rio_path_t;
  40   44  
  41   45  typedef struct rcm_arg {
  42   46          char            *rcm_root;
  43   47          di_node_t       rcm_node;
  44   48          int             rcm_supp;
  45   49          rcm_handle_t    *rcm_handle;
  46   50          int             rcm_retcode;
  47   51          di_retire_t     *rcm_dp;
  48   52          rio_path_t      *rcm_cons_nodes;
  49   53          rio_path_t      *rcm_rsrc_minors;
  50   54          int             (*rcm_offline)();
  51   55          int             (*rcm_online)();
  52   56          int             (*rcm_remove)();
  53   57  } rcm_arg_t;
  54   58  
  55   59  typedef struct selector {
  56   60          char    *sel_name;
  57   61          int     (*sel_selector)(di_node_t node, rcm_arg_t *rp);
  58   62  } di_selector_t;
  59   63  
  60   64  static void rio_assert(di_retire_t *dp, const char *EXstr, int line,
  61   65      const char *file);
  62   66  
  63   67  #define LIBRCM_PATH     "/usr/lib/librcm.so"
  64   68  #define RIO_ASSERT(d, x)        \
  65   69                  {if (!(x)) rio_assert(d, #x, __LINE__, __FILE__); }
  66   70  
  67   71  static int disk_select(di_node_t node, rcm_arg_t *rp);
  68   72  static int nexus_select(di_node_t node, rcm_arg_t *rp);
  69   73  static int enclosure_select(di_node_t node, rcm_arg_t *rp);
  70   74  static int smp_select(di_node_t node, rcm_arg_t *rp);
  71   75  
  72   76  di_selector_t supported_devices[] = {
  73   77          {"disk",        disk_select},
  74   78          {"nexus",       nexus_select},
  75   79          {"enclosure",   enclosure_select},
  76   80          {"smp",         smp_select},
  77   81          {NULL,          NULL}
  78   82  };
  79   83  
  80   84  void *
  81   85  s_calloc(size_t nelem, size_t elsize, int fail)
  82   86  {
  83   87          if (fail) {
  84   88                  errno = ENOMEM;
  85   89                  return (NULL);
  86   90          } else {
  87   91                  return (calloc(nelem, elsize));
  88   92          }
  89   93  }
  90   94  
  91   95  static void
  92   96  rio_assert(di_retire_t *dp, const char *EXstr, int line, const char *file)
  93   97  {
  94   98          char    buf[PATH_MAX];
  95   99  
  96  100          if (dp->rt_abort == NULL)
  97  101                  assert(0);
  98  102  
  99  103          (void) snprintf(buf, sizeof (buf),
 100  104              "Assertion failed: %s, file %s, line %d\n",
 101  105              EXstr, file, line);
 102  106          dp->rt_abort(dp->rt_hdl, buf);
 103  107  }
 104  108  
 105  109  /*ARGSUSED*/
 106  110  static int
 107  111  enclosure_minor(di_node_t node, di_minor_t minor, void *arg)
 108  112  {
 109  113          rcm_arg_t *rp = (rcm_arg_t *)arg;
 110  114          di_retire_t *dp = rp->rcm_dp;
 111  115  
 112  116          rp->rcm_supp = 1;
 113  117          dp->rt_debug(dp->rt_hdl, "[INFO]: enclosure_minor: "
 114  118              "IDed this node as enclosure\n");
 115  119          return (DI_WALK_TERMINATE);
 116  120  }
 117  121  
 118  122  static int
 119  123  enclosure_select(di_node_t node, rcm_arg_t *rp)
 120  124  {
 121  125          rcm_arg_t rarg;
 122  126          di_retire_t     *dp = rp->rcm_dp;
 123  127  
 124  128          rarg.rcm_dp = dp;
 125  129  
 126  130          /*
 127  131           * Check if this is an enclosure minor. If any one minor is DDI_NT_SGEN
 128  132           * or DDI_NT_SCSI_ENCLOSURE we assume it is an enclosure.
 129  133           */
 130  134          rarg.rcm_supp = 0;
 131  135          if (di_walk_minor(node, DDI_NT_SCSI_ENCLOSURE, 0, &rarg,
 132  136              enclosure_minor) != 0) {
 133  137                  dp->rt_debug(dp->rt_hdl, "[INFO]: enclosure_select:"
 134  138                      "di_walk_minor failed. Returning NOTSUP\n");
 135  139                  return (0);
 136  140          }
 137  141          if (di_walk_minor(node, "ddi_generic:scsi", 0, &rarg,
 138  142              enclosure_minor) != 0) {
 139  143                  dp->rt_debug(dp->rt_hdl, "[INFO]: enclosure_select:"
 140  144                      "di_walk_minor failed. Returning NOTSUP\n");
 141  145                  return (0);
 142  146          }
 143  147  
 144  148          return (rarg.rcm_supp);
 145  149  }
 146  150  
 147  151  /*ARGSUSED*/
 148  152  static int
 149  153  smp_minor(di_node_t node, di_minor_t minor, void *arg)
 150  154  {
 151  155          rcm_arg_t *rp = (rcm_arg_t *)arg;
 152  156          di_retire_t *dp = rp->rcm_dp;
 153  157  
 154  158          rp->rcm_supp = 1;
 155  159          dp->rt_debug(dp->rt_hdl, "[INFO]: smp_minor: "
 156  160              "IDed this node as smp\n");
 157  161          return (DI_WALK_TERMINATE);
 158  162  }
 159  163  
 160  164  static int
 161  165  smp_select(di_node_t node, rcm_arg_t *rp)
 162  166  {
 163  167          rcm_arg_t rarg;
 164  168          di_retire_t     *dp = rp->rcm_dp;
 165  169  
 166  170          rarg.rcm_dp = dp;
 167  171  
 168  172          /*
 169  173           * Check if this is an smp minor. If any one minor is DDI_NT_SMP
 170  174           * we assume it is an smp.
 171  175           */
 172  176          rarg.rcm_supp = 0;
 173  177          if (di_walk_minor(node, DDI_NT_SMP, 0, &rarg, smp_minor) != 0) {
 174  178                  dp->rt_debug(dp->rt_hdl, "[INFO]: smp_select:"
 175  179                      "di_walk_minor failed. Returning NOTSUP\n");
 176  180                  return (0);
 177  181          }
 178  182  
 179  183          return (rarg.rcm_supp);
 180  184  }
 181  185  
 182  186  /*ARGSUSED*/
 183  187  static int
 184  188  disk_minor(di_node_t node, di_minor_t minor, void *arg)
 185  189  {
 186  190          rcm_arg_t *rp = (rcm_arg_t *)arg;
 187  191          di_retire_t *dp = rp->rcm_dp;
 188  192  
 189  193          if (di_minor_spectype(minor) == S_IFBLK) {
 190  194                  rp->rcm_supp = 1;
 191  195                  dp->rt_debug(dp->rt_hdl, "[INFO]: disk_minor: is disk minor. "
 192  196                      "IDed this node as disk\n");
 193  197                  return (DI_WALK_TERMINATE);
 194  198          }
 195  199  
 196  200          dp->rt_debug(dp->rt_hdl, "[INFO]: disk_minor: Not a disk minor. "
 197  201              "Continuing minor walk\n");
 198  202          return (DI_WALK_CONTINUE);
 199  203  }
 200  204  
 201  205  static int
 202  206  disk_select(di_node_t node, rcm_arg_t *rp)
 203  207  {
 204  208          rcm_arg_t rarg;
 205  209          di_retire_t     *dp = rp->rcm_dp;
 206  210  
 207  211          rarg.rcm_dp = dp;
 208  212  
 209  213          /*
 210  214           * Check if this is a disk minor. If any one minor is DDI_NT_BLOCK
 211  215           * we assume it is a disk
 212  216           */
 213  217          rarg.rcm_supp = 0;
 214  218          if (di_walk_minor(node, DDI_NT_BLOCK, 0, &rarg, disk_minor) != 0) {
 215  219                  dp->rt_debug(dp->rt_hdl, "[INFO]: disk_select: di_walk_minor "
 216  220                      "failed. Returning NOTSUP\n");
 217  221                  return (0);
 218  222          }
 219  223  
 220  224          return (rarg.rcm_supp);
 221  225  }
 222  226  
 223  227  static int
 224  228  nexus_select(di_node_t node, rcm_arg_t *rp)
 225  229  {
 226  230          int select;
 227  231          char *path;
 228  232  
 229  233          di_retire_t *dp = rp->rcm_dp;
 230  234  
 231  235          path = di_devfs_path(node);
 232  236          if (path == NULL) {
 233  237                  dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: "
 234  238                      "di_devfs_path() is NULL. Returning NOTSUP\n");
 235  239                  return (0);
 236  240          }
 237  241  
 238  242          /*
 239  243           * Check if it is a nexus
 240  244           */
 241  245          if (di_driver_ops(node) & DI_BUS_OPS) {
 242  246                  dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: is nexus %s\n",
 243  247                      path);
 244  248                  select = 1;
 245  249          } else {
 246  250                  dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: not nexus %s\n",
 247  251                      path);
 248  252                  select = 0;
 249  253          }
 250  254  
 251  255          di_devfs_path_free(path);
 252  256  
 253  257          return (select);
 254  258  }
 255  259  
 256  260  static int
 257  261  node_select(di_node_t node, void *arg)
 258  262  {
 259  263          rcm_arg_t *rp = (rcm_arg_t *)arg;
 260  264          di_retire_t *dp;
 261  265          int     sel;
 262  266          int     i;
 263  267          char    *path;
 264  268          uint_t  state;
 265  269  
 266  270          dp = rp->rcm_dp;
 267  271  
 268  272          /* skip pseudo nodes - we only retire real hardware */
 269  273          path = di_devfs_path(node);
 270  274          if (strncmp(path, "/pseudo/", strlen("/pseudo/")) == 0 ||
 271  275              strcmp(path, "/pseudo") == 0) {
 272  276                  dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: "
 273  277                      "pseudo device in subtree - returning NOTSUP: %s\n",
 274  278                      path);
 275  279                  rp->rcm_supp = 0;
 276  280                  di_devfs_path_free(path);
 277  281                  return (DI_WALK_TERMINATE);
 278  282          }
 279  283          di_devfs_path_free(path);
 280  284  
 281  285          /*
 282  286           * If a device is offline/detached/down it is
 283  287           * retireable irrespective of the type of device,
 284  288           * presumably the system is able to function without
 285  289           * it.
 286  290           */
 287  291          state = di_state(node);
 288  292          if ((state & DI_DRIVER_DETACHED) || (state & DI_DEVICE_OFFLINE) ||
 289  293              (state & DI_BUS_DOWN)) {
 290  294                  dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: device "
 291  295                      "is offline/detached. Assuming retire supported\n");
 292  296                  return (DI_WALK_CONTINUE);
 293  297          }
 294  298  
 295  299          sel = 0;
 296  300          for (i = 0; supported_devices[i].sel_name != NULL; i++) {
 297  301                  sel = supported_devices[i].sel_selector(node, rp);
 298  302                  if (sel == 1) {
 299  303                          dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: "
 300  304                              "found supported device: %s\n",
 301  305                              supported_devices[i].sel_name);
 302  306                          break;
 303  307                  }
 304  308          }
 305  309  
 306  310          if (sel != 1) {
 307  311                  /*
 308  312                   * This node is not a supported device. Retire cannot proceed
 309  313                   */
 310  314                  dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: found "
 311  315                      "unsupported device. Returning NOTSUP\n");
 312  316                  rp->rcm_supp = 0;
 313  317                  return (DI_WALK_TERMINATE);
 314  318          }
 315  319  
 316  320          /*
 317  321           * This node is supported. Check other nodes in this subtree.
 318  322           */
 319  323          dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: This node supported. "
 320  324              "Checking other nodes in subtree: %s\n", rp->rcm_root);
 321  325          return (DI_WALK_CONTINUE);
 322  326  }
 323  327  
 324  328  
 325  329  
 326  330  /*
 327  331   * when in doubt assume that retire is not supported for this device.
 328  332   */
 329  333  static int
 330  334  retire_supported(rcm_arg_t *rp)
 331  335  {
 332  336          di_retire_t     *dp;
 333  337          di_node_t rnode = rp->rcm_node;
 334  338  
 335  339          dp = rp->rcm_dp;
 336  340  
 337  341          /*
 338  342           * We should not be here if devinfo snapshot is NULL.
 339  343           */
 340  344          RIO_ASSERT(dp, rnode != DI_NODE_NIL);
 341  345  
 342  346          /*
 343  347           * Note: We initally set supported to 1, then walk the
 344  348           * subtree rooted at devpath, allowing each node the
 345  349           * opportunity to veto the support. We cannot do things
 346  350           * the other way around i.e. assume "not supported" and
 347  351           * let individual nodes indicate that they are supported.
 348  352           * In the latter case, the supported flag would be set
 349  353           * if any one node in the subtree was supported which is
 350  354           * not what we want.
 351  355           */
 352  356          rp->rcm_supp = 1;
 353  357          if (di_walk_node(rnode, DI_WALK_CLDFIRST, rp, node_select) != 0) {
 354  358                  dp->rt_debug(dp->rt_hdl, "[ERROR]: retire_supported: "
 355  359                      "di_walk_node: failed. Returning NOTSUP\n");
 356  360                  rp->rcm_supp = 0;
 357  361          }
 358  362  
 359  363          if (rp->rcm_supp) {
 360  364                  dp->rt_debug(dp->rt_hdl, "[INFO]: retire IS supported\n");
 361  365          }
 362  366  
 363  367          return (rp->rcm_supp);
 364  368  }
 365  369  
 366  370  static void
 367  371  rcm_finalize(rcm_arg_t *rp, int retcode)
 368  372  {
 369  373          rio_path_t      *p;
 370  374          rio_path_t      *tmp;
 371  375          int             flags = RCM_RETIRE_NOTIFY;
 372  376          int             retval;
 373  377          int             error;
 374  378          di_retire_t     *dp;
 375  379  
 376  380          dp = rp->rcm_dp;
 377  381  
 378  382          RIO_ASSERT(dp, retcode == 0 || retcode == -1);
 379  383  
 380  384          dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: retcode=%d: dev=%s\n",
 381  385              retcode, rp->rcm_root);
 382  386  
 383  387          for (p = rp->rcm_cons_nodes; p; ) {
 384  388                  tmp = p;
 385  389                  p = tmp->rpt_next;
 386  390                  free(tmp);
 387  391          }
 388  392          rp->rcm_cons_nodes = NULL;
 389  393  
 390  394          dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: cons_nodes NULL\n");
 391  395  
 392  396          for (p = rp->rcm_rsrc_minors; p; ) {
 393  397                  tmp = p;
 394  398                  p = tmp->rpt_next;
 395  399                  if (retcode == 0) {
 396  400                          retval = rp->rcm_remove(rp->rcm_handle,
 397  401                              tmp->rpt_path, flags, NULL);
 398  402                          error = errno;
 399  403                  } else {
 400  404                          RIO_ASSERT(dp, retcode == -1);
 401  405                          retval = rp->rcm_online(rp->rcm_handle,
 402  406                              tmp->rpt_path, flags, NULL);
 403  407                          error = errno;
 404  408                  }
 405  409                  if (retval != RCM_SUCCESS) {
 406  410                          dp->rt_debug(dp->rt_hdl, "[ERROR]: rcm_finalize: "
 407  411                              "rcm_%s: retval=%d: error=%s: path=%s\n",
 408  412                              retcode == 0 ? "remove" : "online", retval,
 409  413                              strerror(error), tmp->rpt_path);
 410  414                  } else {
 411  415                          dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: "
 412  416                              "rcm_%s: SUCCESS: path=%s\n",
 413  417                              retcode == 0 ? "remove" : "online", tmp->rpt_path);
 414  418                  }
 415  419                  free(tmp);
 416  420          }
 417  421          rp->rcm_rsrc_minors = NULL;
 418  422  }
 419  423  /*ARGSUSED*/
 420  424  static int
 421  425  call_offline(di_node_t node, di_minor_t minor, void *arg)
 422  426  {
 423  427          rcm_arg_t       *rp = (rcm_arg_t *)arg;
 424  428          di_retire_t     *dp = rp->rcm_dp;
 425  429          char            *mnp;
 426  430          rio_path_t      *rpt;
 427  431          int             retval;
 428  432  
 429  433          mnp = di_devfs_minor_path(minor);
 430  434          if (mnp == NULL) {
 431  435                  dp->rt_debug(dp->rt_hdl, "[ERROR]: di_devfs_minor_path "
 432  436                      "failed. Returning RCM FAILURE: %s\n", rp->rcm_root);
 433  437                  rp->rcm_retcode = RCM_FAILURE;
 434  438                  return (DI_WALK_TERMINATE);
 435  439          }
 436  440  
 437  441          rpt = s_calloc(1, sizeof (rio_path_t), 0);
 438  442          if (rpt == NULL) {
 439  443                  dp->rt_debug(dp->rt_hdl, "[ERROR]: calloc failed. "
 440  444                      "Returning RCM FAILURE: %s\n", rp->rcm_root);
 441  445                  di_devfs_path_free(mnp);
 442  446                  rp->rcm_retcode = RCM_FAILURE;
 443  447                  return (DI_WALK_TERMINATE);
 444  448          }
 445  449  
 446  450          (void) snprintf(rpt->rpt_path, sizeof (rpt->rpt_path),
 447  451              "/devices%s", mnp);
 448  452  
 449  453          di_devfs_path_free(mnp);
 450  454  
 451  455          retval = rp->rcm_offline(rp->rcm_handle, rpt->rpt_path,
 452  456              RCM_RETIRE_REQUEST, NULL);
 453  457  
 454  458          rpt->rpt_next = rp->rcm_rsrc_minors;
 455  459          rp->rcm_rsrc_minors = rpt;
 456  460  
 457  461          if (retval == RCM_FAILURE) {
 458  462                  dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM OFFLINE failed "
 459  463                      "for: %s\n", rpt->rpt_path);
 460  464                  rp->rcm_retcode = RCM_FAILURE;
 461  465                  return (DI_WALK_TERMINATE);
 462  466          } else if (retval == RCM_SUCCESS) {
 463  467                  rp->rcm_retcode = RCM_SUCCESS;
 464  468                  dp->rt_debug(dp->rt_hdl, "[INFO]: RCM OFFLINE returned "
 465  469                      "RCM_SUCCESS: %s\n", rpt->rpt_path);
 466  470          } else if (retval != RCM_NO_CONSTRAINT) {
 467  471                  dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM OFFLINE returned "
 468  472                      "invalid value for: %s\n", rpt->rpt_path);
 469  473                  rp->rcm_retcode = RCM_FAILURE;
 470  474                  return (DI_WALK_TERMINATE);
 471  475          } else {
 472  476                  dp->rt_debug(dp->rt_hdl, "[INFO]: RCM OFFLINE returned "
 473  477                      "RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path);
 474  478          }
 475  479  
 476  480          return (DI_WALK_CONTINUE);
 477  481  }
 478  482  
 479  483  static int
 480  484  offline_one(di_node_t node, void *arg)
 481  485  {
 482  486          rcm_arg_t       *rp = (rcm_arg_t *)arg;
 483  487          rio_path_t      *rpt;
 484  488          di_retire_t     *dp = rp->rcm_dp;
 485  489          char            *path;
 486  490  
 487  491          /*
 488  492           * We should already have terminated the walk
 489  493           * in case of failure
 490  494           */
 491  495          RIO_ASSERT(dp, rp->rcm_retcode == RCM_SUCCESS ||
 492  496              rp->rcm_retcode == RCM_NO_CONSTRAINT);
 493  497  
 494  498          dp->rt_debug(dp->rt_hdl, "[INFO]: offline_one: entered\n");
 495  499  
 496  500          rp->rcm_retcode = RCM_NO_CONSTRAINT;
 497  501  
 498  502          rpt = s_calloc(1, sizeof (rio_path_t), 0);
 499  503          if (rpt == NULL) {
 500  504                  dp->rt_debug(dp->rt_hdl, "[ERROR]: rio_path_t calloc "
 501  505                      "failed: error: %s\n", strerror(errno));
 502  506                  goto fail;
 503  507          }
 504  508  
 505  509          path = di_devfs_path(node);
 506  510          if (path == NULL) {
 507  511                  dp->rt_debug(dp->rt_hdl, "[ERROR]: di_devfs_path "
 508  512                      "failed: error: %s\n", strerror(errno));
 509  513                  free(rpt);
 510  514                  goto fail;
 511  515          }
 512  516  
 513  517          (void) strlcpy(rpt->rpt_path, path, sizeof (rpt->rpt_path));
 514  518  
 515  519          di_devfs_path_free(path);
 516  520  
 517  521          if (di_walk_minor(node, NULL, 0, rp, call_offline) != 0) {
 518  522                  dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor "
 519  523                      "failed: error: %s: %s\n", strerror(errno), path);
 520  524                  free(rpt);
 521  525                  goto fail;
 522  526          }
 523  527  
 524  528          if (rp->rcm_retcode == RCM_FAILURE) {
 525  529                  dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor "
 526  530                      "returned: RCM_FAILURE: %s\n", rpt->rpt_path);
 527  531                  free(rpt);
 528  532                  goto fail;
 529  533          } else if (rp->rcm_retcode == RCM_SUCCESS) {
 530  534                  dp->rt_debug(dp->rt_hdl, "[INFO]: di_walk_minor "
 531  535                      "returned: RCM_SUCCESS: %s\n", rpt->rpt_path);
 532  536                  rpt->rpt_next = rp->rcm_cons_nodes;
 533  537                  rp->rcm_cons_nodes = rpt;
 534  538          } else if (rp->rcm_retcode != RCM_NO_CONSTRAINT) {
 535  539                  dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor "
 536  540                      "returned: unknown RCM error code: %d, %s\n",
 537  541                      rp->rcm_retcode, rpt->rpt_path);
 538  542                  free(rpt);
 539  543                  goto fail;
 540  544          } else {
 541  545                  dp->rt_debug(dp->rt_hdl, "[INFO]: di_walk_minor "
 542  546                      "returned: RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path);
 543  547                  free(rpt);
 544  548          }
 545  549  
 546  550          /*
 547  551           * RCM_SUCCESS or RCM_NO_CONSTRAINT.
 548  552           * RCM_SUCCESS implies we overcame a constraint, so keep walking.
 549  553           * RCM_NO_CONSTRAINT implies no constraints applied via RCM.
 550  554           *      Continue walking in the hope that contracts or LDI will
 551  555           *      apply constraints
 552  556           * set retcode to RCM_SUCCESS to show that at least 1 node
 553  557           * completely walked
 554  558           */
 555  559          rp->rcm_retcode = RCM_SUCCESS;
 556  560          return (DI_WALK_CONTINUE);
 557  561  
 558  562  fail:
 559  563          rp->rcm_retcode = RCM_FAILURE;
 560  564          return (DI_WALK_TERMINATE);
 561  565  }
 562  566  
 563  567  /*
 564  568   * Returns:
 565  569   *      RCM_SUCCESS:  RCM constraints (if any) were applied. The
 566  570   *      device paths for which constraints were applied is passed
 567  571   *      back via the pp argument
 568  572   *
 569  573   *      RCM_FAILURE: Either RCM constraints prevent a retire or
 570  574   *      an error occurred
 571  575   */
 572  576  static int
 573  577  rcm_notify(rcm_arg_t *rp, char **pp, size_t *clen)
 574  578  {
 575  579          size_t  len;
 576  580          rio_path_t *p;
 577  581          rio_path_t *tmp;
 578  582          char *plistp;
 579  583          char *s;
 580  584          di_retire_t *dp;
 581  585          di_node_t rnode;
 582  586  
 583  587          dp = rp->rcm_dp;
 584  588  
 585  589          dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_notify() entered\n");
 586  590  
 587  591          RIO_ASSERT(dp, rp->rcm_root);
 588  592  
 589  593          *pp = NULL;
 590  594  
 591  595          rnode = rp->rcm_node;
 592  596          if (rnode == DI_NODE_NIL) {
 593  597                  dp->rt_debug(dp->rt_hdl, "[ERROR]: devinfo snapshot "
 594  598                      "NULL. Returning no RCM constraint: %s\n", rp->rcm_root);
 595  599                  return (RCM_NO_CONSTRAINT);
 596  600          }
 597  601  
 598  602          rp->rcm_retcode = RCM_NO_CONSTRAINT;
 599  603          rp->rcm_cons_nodes = NULL;
 600  604          rp->rcm_rsrc_minors = NULL;
 601  605          if (di_walk_node(rnode, DI_WALK_CLDFIRST, rp, offline_one) != 0) {
 602  606                  dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_node "
 603  607                      "failed: error: %s: %s\n", strerror(errno), rp->rcm_root);
 604  608                  /* online is idempotent - safe to online non-offlined nodes */
 605  609                  rcm_finalize(rp, -1);
 606  610                  rp->rcm_retcode = RCM_FAILURE;
 607  611                  goto out;
 608  612          }
 609  613  
 610  614          if (rp->rcm_retcode == RCM_FAILURE) {
 611  615                  dp->rt_debug(dp->rt_hdl, "[ERROR]: walk_node "
 612  616                      "returned retcode of RCM_FAILURE: %s\n", rp->rcm_root);
 613  617                  rcm_finalize(rp, -1);
 614  618                  goto out;
 615  619          }
 616  620  
 617  621          if (rp->rcm_retcode == RCM_NO_CONSTRAINT) {
 618  622                  dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_node "
 619  623                      " - no nodes walked: RCM_NO_CONSTRAINT: %s\n",
 620  624                      rp->rcm_root);
 621  625          } else {
 622  626                  dp->rt_debug(dp->rt_hdl, "[INFO]: walk_node: RCM_SUCCESS\n");
 623  627          }
 624  628  
 625  629          /*
 626  630           * Convert to a sequence of NUL separated strings terminated by '\0'\0'
 627  631           */
 628  632          for (len = 0, p = rp->rcm_cons_nodes; p; p = p->rpt_next) {
 629  633                  RIO_ASSERT(dp, p->rpt_path);
 630  634                  RIO_ASSERT(dp, strlen(p->rpt_path) > 0);
 631  635                  len += (strlen(p->rpt_path) + 1);
 632  636          }
 633  637          len++;  /* list terminating '\0' */
 634  638  
 635  639          dp->rt_debug(dp->rt_hdl, "[INFO]: len of constraint str = %lu\n", len);
 636  640  
 637  641          plistp = s_calloc(1, len, 0);
 638  642          if (plistp == NULL) {
 639  643                  dp->rt_debug(dp->rt_hdl, "[ERROR]: fail to alloc "
 640  644                      "constraint list: error: %s: %s\n", strerror(errno),
 641  645                      rp->rcm_root);
 642  646                  rcm_finalize(rp, -1);
 643  647                  rp->rcm_retcode = RCM_FAILURE;
 644  648                  goto out;
 645  649          }
 646  650  
 647  651          for (s = plistp, p = rp->rcm_cons_nodes; p; ) {
 648  652                  tmp = p;
 649  653                  p = tmp->rpt_next;
 650  654                  (void) strcpy(s, tmp->rpt_path);
 651  655                  s += strlen(s) + 1;
 652  656                  RIO_ASSERT(dp, s - plistp < len);
 653  657                  free(tmp);
 654  658          }
 655  659          rp->rcm_cons_nodes = NULL;
 656  660          RIO_ASSERT(dp, s - plistp == len - 1);
 657  661          *s = '\0';
 658  662  
  
    | 
      ↓ open down ↓ | 
    617 lines elided | 
    
      ↑ open up ↑ | 
  
 659  663          dp->rt_debug(dp->rt_hdl, "[INFO]: constraint str = %p\n", plistp);
 660  664  
 661  665          *pp = plistp;
 662  666          *clen = len;
 663  667  
 664  668          rp->rcm_retcode = RCM_SUCCESS;
 665  669  out:
 666  670          return (rp->rcm_retcode);
 667  671  }
 668  672  
 669      -
 670  673  /*ARGSUSED*/
 671      -int
 672      -di_retire_device(char *devpath, di_retire_t *dp, int flags)
      674 +static int
      675 +do_di_retire_device(char *devpath, di_retire_t *dp, int flags)
 673  676  {
 674  677          char path[PATH_MAX];
 675  678          struct stat sb;
 676  679          int retval = EINVAL;
 677  680          char *constraint = NULL;
 678  681          size_t clen;
 679  682          void *librcm_hdl;
 680  683          rcm_arg_t rarg = {0};
 681  684          int (*librcm_alloc_handle)();
 682  685          int (*librcm_free_handle)();
 683  686  
 684  687          if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL)
 685  688                  return (EINVAL);
 686  689  
 687  690          if (devpath == NULL || devpath[0] == '\0') {
 688  691                  dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL argument(s)\n");
 689  692                  return (EINVAL);
 690  693          }
 691  694  
 692  695          if (devpath[0] != '/' || strlen(devpath) >= PATH_MAX ||
 693  696              strncmp(devpath, "/devices/", strlen("/devices/")) == 0 ||
 694  697              strstr(devpath, "../devices/") || strrchr(devpath, ':')) {
 695  698                  dp->rt_debug(dp->rt_hdl, "[ERROR]: invalid devpath: %s\n",
 696  699                      devpath);
 697  700                  return (EINVAL);
 698  701          }
 699  702  
 700  703          if (flags != 0) {
 701  704                  dp->rt_debug(dp->rt_hdl, "[ERROR]: flags should be 0: %d\n",
 702  705                      flags);
 703  706                  return (EINVAL);
 704  707          }
 705  708  
 706  709          /*
 707  710           * dlopen rather than link against librcm since libdevinfo
 708  711           * resides in / and librcm resides in /usr. The dlopen is
 709  712           * safe to do since fmd which invokes the retire code
 710  713           * resides on /usr and will not come here until /usr is
 711  714           * mounted.
 712  715           */
 713  716          librcm_hdl = dlopen(LIBRCM_PATH, RTLD_LAZY);
 714  717          if (librcm_hdl == NULL) {
 715  718                  char *errstr = dlerror();
 716  719                  dp->rt_debug(dp->rt_hdl, "[ERROR]: Cannot dlopen librcm: %s\n",
 717  720                      errstr ? errstr : "Unknown error");
 718  721                  return (ENOSYS);
 719  722          }
 720  723  
 721  724          librcm_alloc_handle = (int (*)())dlsym(librcm_hdl, "rcm_alloc_handle");
 722  725          rarg.rcm_offline = (int (*)())dlsym(librcm_hdl, "rcm_request_offline");
 723  726          rarg.rcm_online = (int (*)())dlsym(librcm_hdl, "rcm_notify_online");
 724  727          rarg.rcm_remove = (int (*)())dlsym(librcm_hdl, "rcm_notify_remove");
 725  728          librcm_free_handle = (int (*)())dlsym(librcm_hdl, "rcm_free_handle");
 726  729  
 727  730          if (librcm_alloc_handle == NULL ||
 728  731              rarg.rcm_offline == NULL ||
 729  732              rarg.rcm_online == NULL ||
 730  733              rarg.rcm_remove == NULL ||
 731  734              librcm_free_handle == NULL) {
 732  735                  dp->rt_debug(dp->rt_hdl, "[ERROR]: dlsym failed\n");
 733  736                  retval = ENOSYS;
 734  737                  goto out;
 735  738          }
 736  739  
 737  740          /*
 738  741           * Take a libdevinfo snapshot here because we cannot do so
 739  742           * after device is retired. If device doesn't attach, we retire
 740  743           * anyway i.e. it is not fatal.
 741  744           */
 742  745          rarg.rcm_node = di_init(devpath, DINFOCPYALL);
 743  746          if (rarg.rcm_node == DI_NODE_NIL) {
 744  747                  dp->rt_debug(dp->rt_hdl, "[ERROR]: device doesn't attach, "
 745  748                      "retiring anyway: %s\n", devpath);
 746  749          }
 747  750  
 748  751          rarg.rcm_handle = NULL;
 749  752          if (librcm_alloc_handle(NULL, 0,  NULL, &rarg.rcm_handle)
 750  753              != RCM_SUCCESS) {
 751  754                  retval = errno;
 752  755                  dp->rt_debug(dp->rt_hdl, "[ERROR]: failed to alloc "
 753  756                      "RCM handle. Returning RCM failure: %s\n", devpath);
 754  757                  rarg.rcm_handle = NULL;
 755  758                  goto out;
 756  759          }
 757  760  
 758  761          rarg.rcm_root = devpath;
 759  762          rarg.rcm_dp = dp;
 760  763  
 761  764          /*
 762  765           * If device is already detached/nonexistent and cannot be
 763  766           * attached, allow retire without checking device type.
 764  767           * XXX
 765  768           * Else, check if retire is supported for this device type.
 766  769           */
 767  770          (void) snprintf(path, sizeof (path), "/devices%s", devpath);
 768  771          if (stat(path, &sb) == -1 || !S_ISDIR(sb.st_mode)) {
 769  772                  dp->rt_debug(dp->rt_hdl, "[ERROR]: detached or nonexistent "
 770  773                      "device. Bypassing retire_supported: %s\n", devpath);
 771  774          } else if (!retire_supported(&rarg)) {
 772  775                  dp->rt_debug(dp->rt_hdl, "[ERROR]: retire not supported for "
 773  776                      "device type: %s\n", devpath);
 774  777                  retval = ENOTSUP;
 775  778                  goto out;
 776  779          }
 777  780  
 778  781          clen = 0;
 779  782          constraint = NULL;
 780  783          retval = rcm_notify(&rarg, &constraint, &clen);
 781  784          if (retval == RCM_FAILURE) {
 782  785                  /* retire not permitted */
 783  786                  dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM constraints block "
 784  787                      "retire: %s\n", devpath);
 785  788                  retval = EBUSY;
 786  789                  goto out;
 787  790          } else if (retval == RCM_SUCCESS) {
 788  791                  dp->rt_debug(dp->rt_hdl, "[INFO]: RCM constraints applied"
 789  792                      ": %s\n", devpath);
 790  793          } else if (retval == RCM_NO_CONSTRAINT) {
 791  794                  dp->rt_debug(dp->rt_hdl, "[INFO]: No RCM constraints applied"
 792  795                      ": %s\n", devpath);
 793  796          } else {
 794  797                  dp->rt_debug(dp->rt_hdl, "[ERROR]: notify returned unknown "
 795  798                      "return code: %d: %s\n", retval, devpath);
 796  799                  retval = ESRCH;
 797  800                  goto out;
 798  801          }
 799  802  
 800  803          if (modctl(MODRETIRE, devpath, constraint, clen) != 0) {
 801  804                  retval = errno;
 802  805                  dp->rt_debug(dp->rt_hdl, "[ERROR]: retire modctl() failed: "
 803  806                      "%s: %s\n", devpath, strerror(retval));
 804  807                  rcm_finalize(&rarg, -1);
 805  808                  goto out;
 806  809          }
 807  810  
 808  811          dp->rt_debug(dp->rt_hdl, "[INFO]: retire modctl() succeeded: %s\n",
 809  812              devpath);
 810  813  
 811  814          rcm_finalize(&rarg, 0);
 812  815  
 813  816          retval = 0;
 814  817  
 815  818  out:
 816  819          if (rarg.rcm_handle)
 817  820                  (void) librcm_free_handle(rarg.rcm_handle);
 818  821  
 819  822          RIO_ASSERT(dp, rarg.rcm_cons_nodes == NULL);
 820  823          RIO_ASSERT(dp, rarg.rcm_rsrc_minors == NULL);
 821  824  
 822  825          (void) dlclose(librcm_hdl);
  
    | 
      ↓ open down ↓ | 
    140 lines elided | 
    
      ↑ open up ↑ | 
  
 823  826  
 824  827          free(constraint);
 825  828  
 826  829          if (rarg.rcm_node != DI_NODE_NIL)
 827  830                  di_fini(rarg.rcm_node);
 828  831  
 829  832          return (retval);
 830  833  }
 831  834  
 832  835  /*ARGSUSED*/
 833      -int
 834      -di_unretire_device(char *devpath, di_retire_t *dp)
      836 +static int
      837 +do_di_unretire_device(char *devpath, di_retire_t *dp)
 835  838  {
 836  839          if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL)
 837  840                  return (EINVAL);
 838  841  
 839  842          if (devpath == NULL || devpath[0] == '\0') {
 840  843                  dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL devpath\n");
 841  844                  return (EINVAL);
 842  845          }
 843  846  
 844  847          if (devpath[0] != '/' || strlen(devpath) >= PATH_MAX ||
 845  848              strncmp(devpath, "/devices/", strlen("/devices/")) == 0 ||
 846  849              strstr(devpath, "../devices/") || strrchr(devpath, ':')) {
 847  850                  dp->rt_debug(dp->rt_hdl, "[ERROR]: invalid devpath: %s\n",
 848  851                      devpath);
 849  852                  return (EINVAL);
 850  853          }
 851  854  
 852  855          if (modctl(MODUNRETIRE, devpath) != 0) {
  
    | 
      ↓ open down ↓ | 
    8 lines elided | 
    
      ↑ open up ↑ | 
  
 853  856                  int err = errno;
 854  857                  dp->rt_debug(dp->rt_hdl, "[ERROR]: unretire modctl() failed: "
 855  858                      "%s: %s\n", devpath, strerror(err));
 856  859                  return (err);
 857  860          }
 858  861  
 859  862          dp->rt_debug(dp->rt_hdl, "[INFO]: unretire modctl() done: %s\n",
 860  863              devpath);
 861  864  
 862  865          return (0);
      866 +}
      867 +
      868 +/* Structure that holds physical path instance. */
      869 +struct retire_mpath_info {
      870 +        char *pathname;
      871 +        struct retire_mpath_info *next;
      872 +        char nodename[PATH_MAX];
      873 +};
      874 +
      875 +static int
      876 +retire_walk_nodes(di_node_t node, void *arg)
      877 +{
      878 +        char *dn = NULL;
      879 +        struct retire_mpath_info **mpinfo = (struct retire_mpath_info **)arg;
      880 +        di_node_t pnode;
      881 +        char *baddr;
      882 +        di_path_t path;
      883 +
      884 +        if (node == NULL || ((baddr = di_bus_addr(node)) == NULL) ||
      885 +            baddr[0] == '\0') {
      886 +                return (DI_WALK_CONTINUE);
      887 +        }
      888 +
      889 +        if (((dn = strstr((*mpinfo)->pathname, baddr)) == NULL) ||
      890 +            /* Make sure bus address matches completely. */
      891 +            (strlen(dn) != strlen(baddr))) {
      892 +                return (DI_WALK_CONTINUE);
      893 +        }
      894 +
      895 +        if ((path = di_path_client_next_path(node, DI_PATH_NIL)) == NULL) {
      896 +                return (DI_WALK_CONTINUE);
      897 +        }
      898 +
      899 +        for (; path != NULL; path = di_path_client_next_path(node, path)) {
      900 +                struct retire_mpath_info *ri, *prev;
      901 +                char *port_id = NULL;
      902 +                char *p;
      903 +
      904 +                if ((pnode = di_path_phci_node(path)) == DI_NODE_NIL) {
      905 +                        continue;
      906 +                }
      907 +
      908 +                if ((p = di_devfs_path(pnode)) == NULL) {
      909 +                        continue;
      910 +                }
      911 +
      912 +                if (di_path_prop_lookup_strings(path,
      913 +                    SCSI_ADDR_PROP_TARGET_PORT, &port_id) == 1) {
      914 +
      915 +                        ri = malloc(sizeof (*ri) + PATH_MAX);
      916 +                        if (ri != NULL) {
      917 +                                prev = *mpinfo;
      918 +
      919 +                                ri->next = prev;
      920 +
      921 +                                /* Preserve nodename */
      922 +                                ri->pathname = prev->pathname;
      923 +                                (void) snprintf(&ri->nodename[0], PATH_MAX,
      924 +                                    "%s/disk@%s,0", p, port_id);
      925 +
      926 +                                *mpinfo = ri;
      927 +                        }
      928 +                }
      929 +
      930 +                di_devfs_path_free(p);
      931 +        }
      932 +
      933 +        return (DI_WALK_CONTINUE);
      934 +}
      935 +
      936 +int
      937 +do_di_retire_device_mp(char *devpath, di_retire_t *dp, int flags,
      938 +    boolean_t retire)
      939 +{
      940 +        int err = 0;
      941 +        struct retire_mpath_info mpinfo, *pmpinfo, *pcurr;
      942 +        char *path;
      943 +        di_node_t root_node;
      944 +
      945 +        /* First, retire the device itself. */
      946 +        err = retire ?
      947 +            do_di_retire_device(devpath, dp, flags) :
      948 +            do_di_unretire_device(devpath, dp);
      949 +
      950 +        if (err != 0) {
      951 +                dp->rt_debug(dp->rt_hdl, "di_%sretire_device failed to"
      952 +                    " %sretire device: %d %s", retire ? "" : "un",
      953 +                    retire ? "" : "un", err, devpath);
      954 +                return (err);
      955 +        }
      956 +
      957 +        /* Next, try to retire all physical paths, if possible. */
      958 +        root_node = di_init("/", DINFOCPYALL | DINFOPATH | DINFOLYR);
      959 +        if (root_node == DI_NODE_NIL) {
      960 +                dp->rt_debug(dp->rt_hdl, "di_%sretire_device can't access"
      961 +                    " device tree, MPxIO checks ignored for %s",
      962 +                    retire ? "" : "un", devpath);
      963 +                return (0);
      964 +        }
      965 +
      966 +        /* Obtain multipath information. */
      967 +        (void) memset(&mpinfo, 0, sizeof (mpinfo));
      968 +        mpinfo.pathname = devpath;
      969 +
      970 +        pmpinfo = &mpinfo;
      971 +
      972 +        (void) di_walk_node(root_node, DI_WALK_CLDFIRST, &pmpinfo,
      973 +            retire_walk_nodes);
      974 +
      975 +        /* Next, retire all possible physical paths. */
      976 +        for (; err == 0 && pmpinfo != &mpinfo; ) {
      977 +                pcurr = pmpinfo;
      978 +                pmpinfo = pmpinfo->next;
      979 +
      980 +                path = &pcurr->nodename[0];
      981 +
      982 +                dp->rt_debug(dp->rt_hdl,
      983 +                    "di_%sretire_device %sretiring physical path %s\n",
      984 +                    retire ? "" : "un", retire ? "" : "un", path);
      985 +
      986 +                err = retire ?
      987 +                    do_di_retire_device(path, dp, flags) :
      988 +                    do_di_unretire_device(path, dp);
      989 +
      990 +                if (err != 0)
      991 +                        dp->rt_debug(dp->rt_hdl,
      992 +                            "di_%sretire_device failed to %sretire physical"
      993 +                            " path %s, %d\n", retire ? "" : "un",
      994 +                            retire ? "" : "un", path, err);
      995 +
      996 +                free(pcurr);
      997 +        }
      998 +
      999 +        return (0);
     1000 +}
     1001 +
     1002 +/*ARGSUSED*/
     1003 +int
     1004 +di_retire_device(char *devpath, di_retire_t *dp, int flags)
     1005 +{
     1006 +        return (do_di_retire_device_mp(devpath, dp, flags, B_TRUE));
     1007 +}
     1008 +
     1009 +/*ARGSUSED*/
     1010 +int
     1011 +di_unretire_device(char *devpath, di_retire_t *dp)
     1012 +{
     1013 +        return (do_di_retire_device_mp(devpath, dp, 0, B_FALSE));
 863 1014  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX