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));
}