Print this page
OS-66 Retired devices may still get attached leading to ndi_devi_online errors
        
*** 19,36 ****
--- 19,40 ----
   * CDDL HEADER END
   */
  
  /*
   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+  * Copyright (c) 2013, Nexenta Systemc, Inc.  All rights reserved.
   */
  
  #include <libdevinfo.h>
  #include <sys/modctl.h>
  #include <sys/stat.h>
  #include <string.h>
  #include <librcm.h>
  #include <dlfcn.h>
+ #include <sys/scsi/scsi_address.h>
+ #include <limits.h>
+ #include <errno.h>
  
  #undef  NDEBUG
  #include <assert.h>
  
  typedef struct rio_path {
*** 664,677 ****
          rp->rcm_retcode = RCM_SUCCESS;
  out:
          return (rp->rcm_retcode);
  }
  
- 
  /*ARGSUSED*/
! int
! di_retire_device(char *devpath, di_retire_t *dp, int flags)
  {
          char path[PATH_MAX];
          struct stat sb;
          int retval = EINVAL;
          char *constraint = NULL;
--- 668,680 ----
          rp->rcm_retcode = RCM_SUCCESS;
  out:
          return (rp->rcm_retcode);
  }
  
  /*ARGSUSED*/
! static int
! do_di_retire_device(char *devpath, di_retire_t *dp, int flags)
  {
          char path[PATH_MAX];
          struct stat sb;
          int retval = EINVAL;
          char *constraint = NULL;
*** 828,839 ****
  
          return (retval);
  }
  
  /*ARGSUSED*/
! int
! di_unretire_device(char *devpath, di_retire_t *dp)
  {
          if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL)
                  return (EINVAL);
  
          if (devpath == NULL || devpath[0] == '\0') {
--- 831,842 ----
  
          return (retval);
  }
  
  /*ARGSUSED*/
! static int
! do_di_unretire_device(char *devpath, di_retire_t *dp)
  {
          if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL)
                  return (EINVAL);
  
          if (devpath == NULL || devpath[0] == '\0') {
*** 858,863 ****
--- 861,1014 ----
  
          dp->rt_debug(dp->rt_hdl, "[INFO]: unretire modctl() done: %s\n",
              devpath);
  
          return (0);
+ }
+ 
+ /* Structure that holds physical path instance. */
+ struct retire_mpath_info {
+         char *pathname;
+         struct retire_mpath_info *next;
+         char nodename[PATH_MAX];
+ };
+ 
+ static int
+ retire_walk_nodes(di_node_t node, void *arg)
+ {
+         char *dn = NULL;
+         struct retire_mpath_info **mpinfo = (struct retire_mpath_info **)arg;
+         di_node_t pnode;
+         char *baddr;
+         di_path_t path;
+ 
+         if (node == NULL || ((baddr = di_bus_addr(node)) == NULL) ||
+             baddr[0] == '\0') {
+                 return (DI_WALK_CONTINUE);
+         }
+ 
+         if (((dn = strstr((*mpinfo)->pathname, baddr)) == NULL) ||
+             /* Make sure bus address matches completely. */
+             (strlen(dn) != strlen(baddr))) {
+                 return (DI_WALK_CONTINUE);
+         }
+ 
+         if ((path = di_path_client_next_path(node, DI_PATH_NIL)) == NULL) {
+                 return (DI_WALK_CONTINUE);
+         }
+ 
+         for (; path != NULL; path = di_path_client_next_path(node, path)) {
+                 struct retire_mpath_info *ri, *prev;
+                 char *port_id = NULL;
+                 char *p;
+ 
+                 if ((pnode = di_path_phci_node(path)) == DI_NODE_NIL) {
+                         continue;
+                 }
+ 
+                 if ((p = di_devfs_path(pnode)) == NULL) {
+                         continue;
+                 }
+ 
+                 if (di_path_prop_lookup_strings(path,
+                     SCSI_ADDR_PROP_TARGET_PORT, &port_id) == 1) {
+ 
+                         ri = malloc(sizeof (*ri) + PATH_MAX);
+                         if (ri != NULL) {
+                                 prev = *mpinfo;
+ 
+                                 ri->next = prev;
+ 
+                                 /* Preserve nodename */
+                                 ri->pathname = prev->pathname;
+                                 (void) snprintf(&ri->nodename[0], PATH_MAX,
+                                     "%s/disk@%s,0", p, port_id);
+ 
+                                 *mpinfo = ri;
+                         }
+                 }
+ 
+                 di_devfs_path_free(p);
+         }
+ 
+         return (DI_WALK_CONTINUE);
+ }
+ 
+ int
+ do_di_retire_device_mp(char *devpath, di_retire_t *dp, int flags,
+     boolean_t retire)
+ {
+         int err = 0;
+         struct retire_mpath_info mpinfo, *pmpinfo, *pcurr;
+         char *path;
+         di_node_t root_node;
+ 
+         /* First, retire the device itself. */
+         err = retire ?
+             do_di_retire_device(devpath, dp, flags) :
+             do_di_unretire_device(devpath, dp);
+ 
+         if (err != 0) {
+                 dp->rt_debug(dp->rt_hdl, "di_%sretire_device failed to"
+                     " %sretire device: %d %s", retire ? "" : "un",
+                     retire ? "" : "un", err, devpath);
+                 return (err);
+         }
+ 
+         /* Next, try to retire all physical paths, if possible. */
+         root_node = di_init("/", DINFOCPYALL | DINFOPATH | DINFOLYR);
+         if (root_node == DI_NODE_NIL) {
+                 dp->rt_debug(dp->rt_hdl, "di_%sretire_device can't access"
+                     " device tree, MPxIO checks ignored for %s",
+                     retire ? "" : "un", devpath);
+                 return (0);
+         }
+ 
+         /* Obtain multipath information. */
+         (void) memset(&mpinfo, 0, sizeof (mpinfo));
+         mpinfo.pathname = devpath;
+ 
+         pmpinfo = &mpinfo;
+ 
+         (void) di_walk_node(root_node, DI_WALK_CLDFIRST, &pmpinfo,
+             retire_walk_nodes);
+ 
+         /* Next, retire all possible physical paths. */
+         for (; err == 0 && pmpinfo != &mpinfo; ) {
+                 pcurr = pmpinfo;
+                 pmpinfo = pmpinfo->next;
+ 
+                 path = &pcurr->nodename[0];
+ 
+                 dp->rt_debug(dp->rt_hdl,
+                     "di_%sretire_device %sretiring physical path %s\n",
+                     retire ? "" : "un", retire ? "" : "un", path);
+ 
+                 err = retire ?
+                     do_di_retire_device(path, dp, flags) :
+                     do_di_unretire_device(path, dp);
+ 
+                 if (err != 0)
+                         dp->rt_debug(dp->rt_hdl,
+                             "di_%sretire_device failed to %sretire physical"
+                             " path %s, %d\n", retire ? "" : "un",
+                             retire ? "" : "un", path, err);
+ 
+                 free(pcurr);
+         }
+ 
+         return (0);
+ }
+ 
+ /*ARGSUSED*/
+ int
+ di_retire_device(char *devpath, di_retire_t *dp, int flags)
+ {
+         return (do_di_retire_device_mp(devpath, dp, flags, B_TRUE));
+ }
+ 
+ /*ARGSUSED*/
+ int
+ di_unretire_device(char *devpath, di_retire_t *dp)
+ {
+         return (do_di_retire_device_mp(devpath, dp, 0, B_FALSE));
  }