/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #undef NDEBUG #include typedef struct rio_path { char rpt_path[PATH_MAX]; struct rio_path *rpt_next; } rio_path_t; typedef struct rcm_arg { char *rcm_root; di_node_t rcm_node; int rcm_supp; rcm_handle_t *rcm_handle; int rcm_retcode; di_retire_t *rcm_dp; rio_path_t *rcm_cons_nodes; rio_path_t *rcm_rsrc_minors; int (*rcm_offline)(); int (*rcm_online)(); int (*rcm_remove)(); } rcm_arg_t; typedef struct selector { char *sel_name; int (*sel_selector)(di_node_t node, rcm_arg_t *rp); } di_selector_t; static void rio_assert(di_retire_t *dp, const char *EXstr, int line, const char *file); #define LIBRCM_PATH "/usr/lib/librcm.so" #define RIO_ASSERT(d, x) \ {if (!(x)) rio_assert(d, #x, __LINE__, __FILE__); } static int disk_select(di_node_t node, rcm_arg_t *rp); static int nexus_select(di_node_t node, rcm_arg_t *rp); static int enclosure_select(di_node_t node, rcm_arg_t *rp); static int smp_select(di_node_t node, rcm_arg_t *rp); di_selector_t supported_devices[] = { {"disk", disk_select}, {"nexus", nexus_select}, {"enclosure", enclosure_select}, {"smp", smp_select}, {NULL, NULL} }; void * s_calloc(size_t nelem, size_t elsize, int fail) { if (fail) { errno = ENOMEM; return (NULL); } else { return (calloc(nelem, elsize)); } } static void rio_assert(di_retire_t *dp, const char *EXstr, int line, const char *file) { char buf[PATH_MAX]; if (dp->rt_abort == NULL) assert(0); (void) snprintf(buf, sizeof (buf), "Assertion failed: %s, file %s, line %d\n", EXstr, file, line); dp->rt_abort(dp->rt_hdl, buf); } /*ARGSUSED*/ static int enclosure_minor(di_node_t node, di_minor_t minor, void *arg) { rcm_arg_t *rp = (rcm_arg_t *)arg; di_retire_t *dp = rp->rcm_dp; rp->rcm_supp = 1; dp->rt_debug(dp->rt_hdl, "[INFO]: enclosure_minor: " "IDed this node as enclosure\n"); return (DI_WALK_TERMINATE); } static int enclosure_select(di_node_t node, rcm_arg_t *rp) { rcm_arg_t rarg; di_retire_t *dp = rp->rcm_dp; rarg.rcm_dp = dp; /* * Check if this is an enclosure minor. If any one minor is DDI_NT_SGEN * or DDI_NT_SCSI_ENCLOSURE we assume it is an enclosure. */ rarg.rcm_supp = 0; if (di_walk_minor(node, DDI_NT_SCSI_ENCLOSURE, 0, &rarg, enclosure_minor) != 0) { dp->rt_debug(dp->rt_hdl, "[INFO]: enclosure_select:" "di_walk_minor failed. Returning NOTSUP\n"); return (0); } if (di_walk_minor(node, "ddi_generic:scsi", 0, &rarg, enclosure_minor) != 0) { dp->rt_debug(dp->rt_hdl, "[INFO]: enclosure_select:" "di_walk_minor failed. Returning NOTSUP\n"); return (0); } return (rarg.rcm_supp); } /*ARGSUSED*/ static int smp_minor(di_node_t node, di_minor_t minor, void *arg) { rcm_arg_t *rp = (rcm_arg_t *)arg; di_retire_t *dp = rp->rcm_dp; rp->rcm_supp = 1; dp->rt_debug(dp->rt_hdl, "[INFO]: smp_minor: " "IDed this node as smp\n"); return (DI_WALK_TERMINATE); } static int smp_select(di_node_t node, rcm_arg_t *rp) { rcm_arg_t rarg; di_retire_t *dp = rp->rcm_dp; rarg.rcm_dp = dp; /* * Check if this is an smp minor. If any one minor is DDI_NT_SMP * we assume it is an smp. */ rarg.rcm_supp = 0; if (di_walk_minor(node, DDI_NT_SMP, 0, &rarg, smp_minor) != 0) { dp->rt_debug(dp->rt_hdl, "[INFO]: smp_select:" "di_walk_minor failed. Returning NOTSUP\n"); return (0); } return (rarg.rcm_supp); } /*ARGSUSED*/ static int disk_minor(di_node_t node, di_minor_t minor, void *arg) { rcm_arg_t *rp = (rcm_arg_t *)arg; di_retire_t *dp = rp->rcm_dp; if (di_minor_spectype(minor) == S_IFBLK) { rp->rcm_supp = 1; dp->rt_debug(dp->rt_hdl, "[INFO]: disk_minor: is disk minor. " "IDed this node as disk\n"); return (DI_WALK_TERMINATE); } dp->rt_debug(dp->rt_hdl, "[INFO]: disk_minor: Not a disk minor. " "Continuing minor walk\n"); return (DI_WALK_CONTINUE); } static int disk_select(di_node_t node, rcm_arg_t *rp) { rcm_arg_t rarg; di_retire_t *dp = rp->rcm_dp; rarg.rcm_dp = dp; /* * Check if this is a disk minor. If any one minor is DDI_NT_BLOCK * we assume it is a disk */ rarg.rcm_supp = 0; if (di_walk_minor(node, DDI_NT_BLOCK, 0, &rarg, disk_minor) != 0) { dp->rt_debug(dp->rt_hdl, "[INFO]: disk_select: di_walk_minor " "failed. Returning NOTSUP\n"); return (0); } return (rarg.rcm_supp); } static int nexus_select(di_node_t node, rcm_arg_t *rp) { int select; char *path; di_retire_t *dp = rp->rcm_dp; path = di_devfs_path(node); if (path == NULL) { dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: " "di_devfs_path() is NULL. Returning NOTSUP\n"); return (0); } /* * Check if it is a nexus */ if (di_driver_ops(node) & DI_BUS_OPS) { dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: is nexus %s\n", path); select = 1; } else { dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: not nexus %s\n", path); select = 0; } di_devfs_path_free(path); return (select); } static int node_select(di_node_t node, void *arg) { rcm_arg_t *rp = (rcm_arg_t *)arg; di_retire_t *dp; int sel; int i; char *path; uint_t state; dp = rp->rcm_dp; /* skip pseudo nodes - we only retire real hardware */ path = di_devfs_path(node); if (strncmp(path, "/pseudo/", strlen("/pseudo/")) == 0 || strcmp(path, "/pseudo") == 0) { dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: " "pseudo device in subtree - returning NOTSUP: %s\n", path); rp->rcm_supp = 0; di_devfs_path_free(path); return (DI_WALK_TERMINATE); } di_devfs_path_free(path); /* * If a device is offline/detached/down it is * retireable irrespective of the type of device, * presumably the system is able to function without * it. */ state = di_state(node); if ((state & DI_DRIVER_DETACHED) || (state & DI_DEVICE_OFFLINE) || (state & DI_BUS_DOWN)) { dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: device " "is offline/detached. Assuming retire supported\n"); return (DI_WALK_CONTINUE); } sel = 0; for (i = 0; supported_devices[i].sel_name != NULL; i++) { sel = supported_devices[i].sel_selector(node, rp); if (sel == 1) { dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: " "found supported device: %s\n", supported_devices[i].sel_name); break; } } if (sel != 1) { /* * This node is not a supported device. Retire cannot proceed */ dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: found " "unsupported device. Returning NOTSUP\n"); rp->rcm_supp = 0; return (DI_WALK_TERMINATE); } /* * This node is supported. Check other nodes in this subtree. */ dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: This node supported. " "Checking other nodes in subtree: %s\n", rp->rcm_root); return (DI_WALK_CONTINUE); } /* * when in doubt assume that retire is not supported for this device. */ static int retire_supported(rcm_arg_t *rp) { di_retire_t *dp; di_node_t rnode = rp->rcm_node; dp = rp->rcm_dp; /* * We should not be here if devinfo snapshot is NULL. */ RIO_ASSERT(dp, rnode != DI_NODE_NIL); /* * Note: We initally set supported to 1, then walk the * subtree rooted at devpath, allowing each node the * opportunity to veto the support. We cannot do things * the other way around i.e. assume "not supported" and * let individual nodes indicate that they are supported. * In the latter case, the supported flag would be set * if any one node in the subtree was supported which is * not what we want. */ rp->rcm_supp = 1; if (di_walk_node(rnode, DI_WALK_CLDFIRST, rp, node_select) != 0) { dp->rt_debug(dp->rt_hdl, "[ERROR]: retire_supported: " "di_walk_node: failed. Returning NOTSUP\n"); rp->rcm_supp = 0; } if (rp->rcm_supp) { dp->rt_debug(dp->rt_hdl, "[INFO]: retire IS supported\n"); } return (rp->rcm_supp); } static void rcm_finalize(rcm_arg_t *rp, int retcode) { rio_path_t *p; rio_path_t *tmp; int flags = RCM_RETIRE_NOTIFY; int retval; int error; di_retire_t *dp; dp = rp->rcm_dp; RIO_ASSERT(dp, retcode == 0 || retcode == -1); dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: retcode=%d: dev=%s\n", retcode, rp->rcm_root); for (p = rp->rcm_cons_nodes; p; ) { tmp = p; p = tmp->rpt_next; free(tmp); } rp->rcm_cons_nodes = NULL; dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: cons_nodes NULL\n"); for (p = rp->rcm_rsrc_minors; p; ) { tmp = p; p = tmp->rpt_next; if (retcode == 0) { retval = rp->rcm_remove(rp->rcm_handle, tmp->rpt_path, flags, NULL); error = errno; } else { RIO_ASSERT(dp, retcode == -1); retval = rp->rcm_online(rp->rcm_handle, tmp->rpt_path, flags, NULL); error = errno; } if (retval != RCM_SUCCESS) { dp->rt_debug(dp->rt_hdl, "[ERROR]: rcm_finalize: " "rcm_%s: retval=%d: error=%s: path=%s\n", retcode == 0 ? "remove" : "online", retval, strerror(error), tmp->rpt_path); } else { dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: " "rcm_%s: SUCCESS: path=%s\n", retcode == 0 ? "remove" : "online", tmp->rpt_path); } free(tmp); } rp->rcm_rsrc_minors = NULL; } /*ARGSUSED*/ static int call_offline(di_node_t node, di_minor_t minor, void *arg) { rcm_arg_t *rp = (rcm_arg_t *)arg; di_retire_t *dp = rp->rcm_dp; char *mnp; rio_path_t *rpt; int retval; mnp = di_devfs_minor_path(minor); if (mnp == NULL) { dp->rt_debug(dp->rt_hdl, "[ERROR]: di_devfs_minor_path " "failed. Returning RCM FAILURE: %s\n", rp->rcm_root); rp->rcm_retcode = RCM_FAILURE; return (DI_WALK_TERMINATE); } rpt = s_calloc(1, sizeof (rio_path_t), 0); if (rpt == NULL) { dp->rt_debug(dp->rt_hdl, "[ERROR]: calloc failed. " "Returning RCM FAILURE: %s\n", rp->rcm_root); di_devfs_path_free(mnp); rp->rcm_retcode = RCM_FAILURE; return (DI_WALK_TERMINATE); } (void) snprintf(rpt->rpt_path, sizeof (rpt->rpt_path), "/devices%s", mnp); di_devfs_path_free(mnp); retval = rp->rcm_offline(rp->rcm_handle, rpt->rpt_path, RCM_RETIRE_REQUEST, NULL); rpt->rpt_next = rp->rcm_rsrc_minors; rp->rcm_rsrc_minors = rpt; if (retval == RCM_FAILURE) { dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM OFFLINE failed " "for: %s\n", rpt->rpt_path); rp->rcm_retcode = RCM_FAILURE; return (DI_WALK_TERMINATE); } else if (retval == RCM_SUCCESS) { rp->rcm_retcode = RCM_SUCCESS; dp->rt_debug(dp->rt_hdl, "[INFO]: RCM OFFLINE returned " "RCM_SUCCESS: %s\n", rpt->rpt_path); } else if (retval != RCM_NO_CONSTRAINT) { dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM OFFLINE returned " "invalid value for: %s\n", rpt->rpt_path); rp->rcm_retcode = RCM_FAILURE; return (DI_WALK_TERMINATE); } else { dp->rt_debug(dp->rt_hdl, "[INFO]: RCM OFFLINE returned " "RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path); } return (DI_WALK_CONTINUE); } static int offline_one(di_node_t node, void *arg) { rcm_arg_t *rp = (rcm_arg_t *)arg; rio_path_t *rpt; di_retire_t *dp = rp->rcm_dp; char *path; /* * We should already have terminated the walk * in case of failure */ RIO_ASSERT(dp, rp->rcm_retcode == RCM_SUCCESS || rp->rcm_retcode == RCM_NO_CONSTRAINT); dp->rt_debug(dp->rt_hdl, "[INFO]: offline_one: entered\n"); rp->rcm_retcode = RCM_NO_CONSTRAINT; rpt = s_calloc(1, sizeof (rio_path_t), 0); if (rpt == NULL) { dp->rt_debug(dp->rt_hdl, "[ERROR]: rio_path_t calloc " "failed: error: %s\n", strerror(errno)); goto fail; } path = di_devfs_path(node); if (path == NULL) { dp->rt_debug(dp->rt_hdl, "[ERROR]: di_devfs_path " "failed: error: %s\n", strerror(errno)); free(rpt); goto fail; } (void) strlcpy(rpt->rpt_path, path, sizeof (rpt->rpt_path)); di_devfs_path_free(path); if (di_walk_minor(node, NULL, 0, rp, call_offline) != 0) { dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor " "failed: error: %s: %s\n", strerror(errno), path); free(rpt); goto fail; } if (rp->rcm_retcode == RCM_FAILURE) { dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor " "returned: RCM_FAILURE: %s\n", rpt->rpt_path); free(rpt); goto fail; } else if (rp->rcm_retcode == RCM_SUCCESS) { dp->rt_debug(dp->rt_hdl, "[INFO]: di_walk_minor " "returned: RCM_SUCCESS: %s\n", rpt->rpt_path); rpt->rpt_next = rp->rcm_cons_nodes; rp->rcm_cons_nodes = rpt; } else if (rp->rcm_retcode != RCM_NO_CONSTRAINT) { dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor " "returned: unknown RCM error code: %d, %s\n", rp->rcm_retcode, rpt->rpt_path); free(rpt); goto fail; } else { dp->rt_debug(dp->rt_hdl, "[INFO]: di_walk_minor " "returned: RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path); free(rpt); } /* * RCM_SUCCESS or RCM_NO_CONSTRAINT. * RCM_SUCCESS implies we overcame a constraint, so keep walking. * RCM_NO_CONSTRAINT implies no constraints applied via RCM. * Continue walking in the hope that contracts or LDI will * apply constraints * set retcode to RCM_SUCCESS to show that at least 1 node * completely walked */ rp->rcm_retcode = RCM_SUCCESS; return (DI_WALK_CONTINUE); fail: rp->rcm_retcode = RCM_FAILURE; return (DI_WALK_TERMINATE); } /* * Returns: * RCM_SUCCESS: RCM constraints (if any) were applied. The * device paths for which constraints were applied is passed * back via the pp argument * * RCM_FAILURE: Either RCM constraints prevent a retire or * an error occurred */ static int rcm_notify(rcm_arg_t *rp, char **pp, size_t *clen) { size_t len; rio_path_t *p; rio_path_t *tmp; char *plistp; char *s; di_retire_t *dp; di_node_t rnode; dp = rp->rcm_dp; dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_notify() entered\n"); RIO_ASSERT(dp, rp->rcm_root); *pp = NULL; rnode = rp->rcm_node; if (rnode == DI_NODE_NIL) { dp->rt_debug(dp->rt_hdl, "[ERROR]: devinfo snapshot " "NULL. Returning no RCM constraint: %s\n", rp->rcm_root); return (RCM_NO_CONSTRAINT); } rp->rcm_retcode = RCM_NO_CONSTRAINT; rp->rcm_cons_nodes = NULL; rp->rcm_rsrc_minors = NULL; if (di_walk_node(rnode, DI_WALK_CLDFIRST, rp, offline_one) != 0) { dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_node " "failed: error: %s: %s\n", strerror(errno), rp->rcm_root); /* online is idempotent - safe to online non-offlined nodes */ rcm_finalize(rp, -1); rp->rcm_retcode = RCM_FAILURE; goto out; } if (rp->rcm_retcode == RCM_FAILURE) { dp->rt_debug(dp->rt_hdl, "[ERROR]: walk_node " "returned retcode of RCM_FAILURE: %s\n", rp->rcm_root); rcm_finalize(rp, -1); goto out; } if (rp->rcm_retcode == RCM_NO_CONSTRAINT) { dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_node " " - no nodes walked: RCM_NO_CONSTRAINT: %s\n", rp->rcm_root); } else { dp->rt_debug(dp->rt_hdl, "[INFO]: walk_node: RCM_SUCCESS\n"); } /* * Convert to a sequence of NUL separated strings terminated by '\0'\0' */ for (len = 0, p = rp->rcm_cons_nodes; p; p = p->rpt_next) { RIO_ASSERT(dp, p->rpt_path); RIO_ASSERT(dp, strlen(p->rpt_path) > 0); len += (strlen(p->rpt_path) + 1); } len++; /* list terminating '\0' */ dp->rt_debug(dp->rt_hdl, "[INFO]: len of constraint str = %lu\n", len); plistp = s_calloc(1, len, 0); if (plistp == NULL) { dp->rt_debug(dp->rt_hdl, "[ERROR]: fail to alloc " "constraint list: error: %s: %s\n", strerror(errno), rp->rcm_root); rcm_finalize(rp, -1); rp->rcm_retcode = RCM_FAILURE; goto out; } for (s = plistp, p = rp->rcm_cons_nodes; p; ) { tmp = p; p = tmp->rpt_next; (void) strcpy(s, tmp->rpt_path); s += strlen(s) + 1; RIO_ASSERT(dp, s - plistp < len); free(tmp); } rp->rcm_cons_nodes = NULL; RIO_ASSERT(dp, s - plistp == len - 1); *s = '\0'; dp->rt_debug(dp->rt_hdl, "[INFO]: constraint str = %p\n", plistp); *pp = plistp; *clen = len; 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; size_t clen; void *librcm_hdl; rcm_arg_t rarg = {0}; int (*librcm_alloc_handle)(); int (*librcm_free_handle)(); if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL) return (EINVAL); if (devpath == NULL || devpath[0] == '\0') { dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL argument(s)\n"); return (EINVAL); } if (devpath[0] != '/' || strlen(devpath) >= PATH_MAX || strncmp(devpath, "/devices/", strlen("/devices/")) == 0 || strstr(devpath, "../devices/") || strrchr(devpath, ':')) { dp->rt_debug(dp->rt_hdl, "[ERROR]: invalid devpath: %s\n", devpath); return (EINVAL); } if (flags != 0) { dp->rt_debug(dp->rt_hdl, "[ERROR]: flags should be 0: %d\n", flags); return (EINVAL); } /* * dlopen rather than link against librcm since libdevinfo * resides in / and librcm resides in /usr. The dlopen is * safe to do since fmd which invokes the retire code * resides on /usr and will not come here until /usr is * mounted. */ librcm_hdl = dlopen(LIBRCM_PATH, RTLD_LAZY); if (librcm_hdl == NULL) { char *errstr = dlerror(); dp->rt_debug(dp->rt_hdl, "[ERROR]: Cannot dlopen librcm: %s\n", errstr ? errstr : "Unknown error"); return (ENOSYS); } librcm_alloc_handle = (int (*)())dlsym(librcm_hdl, "rcm_alloc_handle"); rarg.rcm_offline = (int (*)())dlsym(librcm_hdl, "rcm_request_offline"); rarg.rcm_online = (int (*)())dlsym(librcm_hdl, "rcm_notify_online"); rarg.rcm_remove = (int (*)())dlsym(librcm_hdl, "rcm_notify_remove"); librcm_free_handle = (int (*)())dlsym(librcm_hdl, "rcm_free_handle"); if (librcm_alloc_handle == NULL || rarg.rcm_offline == NULL || rarg.rcm_online == NULL || rarg.rcm_remove == NULL || librcm_free_handle == NULL) { dp->rt_debug(dp->rt_hdl, "[ERROR]: dlsym failed\n"); retval = ENOSYS; goto out; } /* * Take a libdevinfo snapshot here because we cannot do so * after device is retired. If device doesn't attach, we retire * anyway i.e. it is not fatal. */ rarg.rcm_node = di_init(devpath, DINFOCPYALL); if (rarg.rcm_node == DI_NODE_NIL) { dp->rt_debug(dp->rt_hdl, "[ERROR]: device doesn't attach, " "retiring anyway: %s\n", devpath); } rarg.rcm_handle = NULL; if (librcm_alloc_handle(NULL, 0, NULL, &rarg.rcm_handle) != RCM_SUCCESS) { retval = errno; dp->rt_debug(dp->rt_hdl, "[ERROR]: failed to alloc " "RCM handle. Returning RCM failure: %s\n", devpath); rarg.rcm_handle = NULL; goto out; } rarg.rcm_root = devpath; rarg.rcm_dp = dp; /* * If device is already detached/nonexistent and cannot be * attached, allow retire without checking device type. * XXX * Else, check if retire is supported for this device type. */ (void) snprintf(path, sizeof (path), "/devices%s", devpath); if (stat(path, &sb) == -1 || !S_ISDIR(sb.st_mode)) { dp->rt_debug(dp->rt_hdl, "[ERROR]: detached or nonexistent " "device. Bypassing retire_supported: %s\n", devpath); } else if (!retire_supported(&rarg)) { dp->rt_debug(dp->rt_hdl, "[ERROR]: retire not supported for " "device type: %s\n", devpath); retval = ENOTSUP; goto out; } clen = 0; constraint = NULL; retval = rcm_notify(&rarg, &constraint, &clen); if (retval == RCM_FAILURE) { /* retire not permitted */ dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM constraints block " "retire: %s\n", devpath); retval = EBUSY; goto out; } else if (retval == RCM_SUCCESS) { dp->rt_debug(dp->rt_hdl, "[INFO]: RCM constraints applied" ": %s\n", devpath); } else if (retval == RCM_NO_CONSTRAINT) { dp->rt_debug(dp->rt_hdl, "[INFO]: No RCM constraints applied" ": %s\n", devpath); } else { dp->rt_debug(dp->rt_hdl, "[ERROR]: notify returned unknown " "return code: %d: %s\n", retval, devpath); retval = ESRCH; goto out; } if (modctl(MODRETIRE, devpath, constraint, clen) != 0) { retval = errno; dp->rt_debug(dp->rt_hdl, "[ERROR]: retire modctl() failed: " "%s: %s\n", devpath, strerror(retval)); rcm_finalize(&rarg, -1); goto out; } dp->rt_debug(dp->rt_hdl, "[INFO]: retire modctl() succeeded: %s\n", devpath); rcm_finalize(&rarg, 0); retval = 0; out: if (rarg.rcm_handle) (void) librcm_free_handle(rarg.rcm_handle); RIO_ASSERT(dp, rarg.rcm_cons_nodes == NULL); RIO_ASSERT(dp, rarg.rcm_rsrc_minors == NULL); (void) dlclose(librcm_hdl); free(constraint); if (rarg.rcm_node != DI_NODE_NIL) di_fini(rarg.rcm_node); 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') { dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL devpath\n"); return (EINVAL); } if (devpath[0] != '/' || strlen(devpath) >= PATH_MAX || strncmp(devpath, "/devices/", strlen("/devices/")) == 0 || strstr(devpath, "../devices/") || strrchr(devpath, ':')) { dp->rt_debug(dp->rt_hdl, "[ERROR]: invalid devpath: %s\n", devpath); return (EINVAL); } if (modctl(MODUNRETIRE, devpath) != 0) { int err = errno; dp->rt_debug(dp->rt_hdl, "[ERROR]: unretire modctl() failed: " "%s: %s\n", devpath, strerror(err)); return (err); } dp->rt_debug(dp->rt_hdl, "[INFO]: unretire modctl() done: %s\n", devpath); return (0); }