125e8c5aaSvikram /* 225e8c5aaSvikram * CDDL HEADER START 325e8c5aaSvikram * 425e8c5aaSvikram * The contents of this file are subject to the terms of the 525e8c5aaSvikram * Common Development and Distribution License (the "License"). 625e8c5aaSvikram * You may not use this file except in compliance with the License. 725e8c5aaSvikram * 825e8c5aaSvikram * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 925e8c5aaSvikram * or http://www.opensolaris.org/os/licensing. 1025e8c5aaSvikram * See the License for the specific language governing permissions 1125e8c5aaSvikram * and limitations under the License. 1225e8c5aaSvikram * 1325e8c5aaSvikram * When distributing Covered Code, include this CDDL HEADER in each 1425e8c5aaSvikram * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1525e8c5aaSvikram * If applicable, add the following below this CDDL HEADER, with the 1625e8c5aaSvikram * fields enclosed by brackets "[]" replaced with your own identifying 1725e8c5aaSvikram * information: Portions Copyright [yyyy] [name of copyright owner] 1825e8c5aaSvikram * 1925e8c5aaSvikram * CDDL HEADER END 2025e8c5aaSvikram */ 2125e8c5aaSvikram 2225e8c5aaSvikram /* 23*f76de749SStephen Hanson * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 2425e8c5aaSvikram */ 2525e8c5aaSvikram 2625e8c5aaSvikram #include <libdevinfo.h> 2725e8c5aaSvikram #include <sys/modctl.h> 2825e8c5aaSvikram #include <sys/stat.h> 2925e8c5aaSvikram #include <string.h> 3025e8c5aaSvikram #include <librcm.h> 3125e8c5aaSvikram #include <dlfcn.h> 3225e8c5aaSvikram 3325e8c5aaSvikram #undef NDEBUG 3425e8c5aaSvikram #include <assert.h> 3525e8c5aaSvikram 3625e8c5aaSvikram typedef struct rio_path { 3725e8c5aaSvikram char rpt_path[PATH_MAX]; 3825e8c5aaSvikram struct rio_path *rpt_next; 3925e8c5aaSvikram } rio_path_t; 4025e8c5aaSvikram 4125e8c5aaSvikram typedef struct rcm_arg { 4225e8c5aaSvikram char *rcm_root; 4325e8c5aaSvikram di_node_t rcm_node; 4425e8c5aaSvikram int rcm_supp; 4525e8c5aaSvikram rcm_handle_t *rcm_handle; 4625e8c5aaSvikram int rcm_retcode; 4725e8c5aaSvikram di_retire_t *rcm_dp; 4825e8c5aaSvikram rio_path_t *rcm_cons_nodes; 4925e8c5aaSvikram rio_path_t *rcm_rsrc_minors; 5025e8c5aaSvikram int (*rcm_offline)(); 5125e8c5aaSvikram int (*rcm_online)(); 5225e8c5aaSvikram int (*rcm_remove)(); 5325e8c5aaSvikram } rcm_arg_t; 5425e8c5aaSvikram 5525e8c5aaSvikram typedef struct selector { 5625e8c5aaSvikram char *sel_name; 5725e8c5aaSvikram int (*sel_selector)(di_node_t node, rcm_arg_t *rp); 5825e8c5aaSvikram } di_selector_t; 5925e8c5aaSvikram 6025e8c5aaSvikram static void rio_assert(di_retire_t *dp, const char *EXstr, int line, 6125e8c5aaSvikram const char *file); 6225e8c5aaSvikram 6325e8c5aaSvikram #define LIBRCM_PATH "/usr/lib/librcm.so" 6425e8c5aaSvikram #define RIO_ASSERT(d, x) \ 6525e8c5aaSvikram {if (!(x)) rio_assert(d, #x, __LINE__, __FILE__); } 6625e8c5aaSvikram 6725e8c5aaSvikram static int disk_select(di_node_t node, rcm_arg_t *rp); 6825e8c5aaSvikram static int nexus_select(di_node_t node, rcm_arg_t *rp); 69*f76de749SStephen Hanson static int enclosure_select(di_node_t node, rcm_arg_t *rp); 70*f76de749SStephen Hanson static int smp_select(di_node_t node, rcm_arg_t *rp); 7125e8c5aaSvikram 7225e8c5aaSvikram di_selector_t supported_devices[] = { 7325e8c5aaSvikram {"disk", disk_select}, 7425e8c5aaSvikram {"nexus", nexus_select}, 75*f76de749SStephen Hanson {"enclosure", enclosure_select}, 76*f76de749SStephen Hanson {"smp", smp_select}, 7725e8c5aaSvikram {NULL, NULL} 7825e8c5aaSvikram }; 7925e8c5aaSvikram 8025e8c5aaSvikram void * 8125e8c5aaSvikram s_calloc(size_t nelem, size_t elsize, int fail) 8225e8c5aaSvikram { 8325e8c5aaSvikram if (fail) { 8425e8c5aaSvikram errno = ENOMEM; 8525e8c5aaSvikram return (NULL); 8625e8c5aaSvikram } else { 8725e8c5aaSvikram return (calloc(nelem, elsize)); 8825e8c5aaSvikram } 8925e8c5aaSvikram } 9025e8c5aaSvikram 9125e8c5aaSvikram static void 9225e8c5aaSvikram rio_assert(di_retire_t *dp, const char *EXstr, int line, const char *file) 9325e8c5aaSvikram { 9425e8c5aaSvikram char buf[PATH_MAX]; 9525e8c5aaSvikram 9625e8c5aaSvikram if (dp->rt_abort == NULL) 9725e8c5aaSvikram assert(0); 9825e8c5aaSvikram 9925e8c5aaSvikram (void) snprintf(buf, sizeof (buf), 10025e8c5aaSvikram "Assertion failed: %s, file %s, line %d\n", 10125e8c5aaSvikram EXstr, file, line); 10225e8c5aaSvikram dp->rt_abort(dp->rt_hdl, buf); 10325e8c5aaSvikram } 10425e8c5aaSvikram 10525e8c5aaSvikram /*ARGSUSED*/ 10625e8c5aaSvikram static int 107*f76de749SStephen Hanson enclosure_minor(di_node_t node, di_minor_t minor, void *arg) 108*f76de749SStephen Hanson { 109*f76de749SStephen Hanson rcm_arg_t *rp = (rcm_arg_t *)arg; 110*f76de749SStephen Hanson di_retire_t *dp = rp->rcm_dp; 111*f76de749SStephen Hanson 112*f76de749SStephen Hanson rp->rcm_supp = 1; 113*f76de749SStephen Hanson dp->rt_debug(dp->rt_hdl, "[INFO]: enclosure_minor: " 114*f76de749SStephen Hanson "IDed this node as enclosure\n"); 115*f76de749SStephen Hanson return (DI_WALK_TERMINATE); 116*f76de749SStephen Hanson } 117*f76de749SStephen Hanson 118*f76de749SStephen Hanson static int 119*f76de749SStephen Hanson enclosure_select(di_node_t node, rcm_arg_t *rp) 120*f76de749SStephen Hanson { 121*f76de749SStephen Hanson rcm_arg_t rarg; 122*f76de749SStephen Hanson di_retire_t *dp = rp->rcm_dp; 123*f76de749SStephen Hanson 124*f76de749SStephen Hanson rarg.rcm_dp = dp; 125*f76de749SStephen Hanson 126*f76de749SStephen Hanson /* 127*f76de749SStephen Hanson * Check if this is an enclosure minor. If any one minor is DDI_NT_SGEN 128*f76de749SStephen Hanson * or DDI_NT_SCSI_ENCLOSURE we assume it is an enclosure. 129*f76de749SStephen Hanson */ 130*f76de749SStephen Hanson rarg.rcm_supp = 0; 131*f76de749SStephen Hanson if (di_walk_minor(node, DDI_NT_SCSI_ENCLOSURE, 0, &rarg, 132*f76de749SStephen Hanson enclosure_minor) != 0) { 133*f76de749SStephen Hanson dp->rt_debug(dp->rt_hdl, "[INFO]: enclosure_select:" 134*f76de749SStephen Hanson "di_walk_minor failed. Returning NOTSUP\n"); 135*f76de749SStephen Hanson return (0); 136*f76de749SStephen Hanson } 137*f76de749SStephen Hanson if (di_walk_minor(node, "ddi_generic:scsi", 0, &rarg, 138*f76de749SStephen Hanson enclosure_minor) != 0) { 139*f76de749SStephen Hanson dp->rt_debug(dp->rt_hdl, "[INFO]: enclosure_select:" 140*f76de749SStephen Hanson "di_walk_minor failed. Returning NOTSUP\n"); 141*f76de749SStephen Hanson return (0); 142*f76de749SStephen Hanson } 143*f76de749SStephen Hanson 144*f76de749SStephen Hanson return (rarg.rcm_supp); 145*f76de749SStephen Hanson } 146*f76de749SStephen Hanson 147*f76de749SStephen Hanson /*ARGSUSED*/ 148*f76de749SStephen Hanson static int 149*f76de749SStephen Hanson smp_minor(di_node_t node, di_minor_t minor, void *arg) 150*f76de749SStephen Hanson { 151*f76de749SStephen Hanson rcm_arg_t *rp = (rcm_arg_t *)arg; 152*f76de749SStephen Hanson di_retire_t *dp = rp->rcm_dp; 153*f76de749SStephen Hanson 154*f76de749SStephen Hanson rp->rcm_supp = 1; 155*f76de749SStephen Hanson dp->rt_debug(dp->rt_hdl, "[INFO]: smp_minor: " 156*f76de749SStephen Hanson "IDed this node as smp\n"); 157*f76de749SStephen Hanson return (DI_WALK_TERMINATE); 158*f76de749SStephen Hanson } 159*f76de749SStephen Hanson 160*f76de749SStephen Hanson static int 161*f76de749SStephen Hanson smp_select(di_node_t node, rcm_arg_t *rp) 162*f76de749SStephen Hanson { 163*f76de749SStephen Hanson rcm_arg_t rarg; 164*f76de749SStephen Hanson di_retire_t *dp = rp->rcm_dp; 165*f76de749SStephen Hanson 166*f76de749SStephen Hanson rarg.rcm_dp = dp; 167*f76de749SStephen Hanson 168*f76de749SStephen Hanson /* 169*f76de749SStephen Hanson * Check if this is an smp minor. If any one minor is DDI_NT_SMP 170*f76de749SStephen Hanson * we assume it is an smp. 171*f76de749SStephen Hanson */ 172*f76de749SStephen Hanson rarg.rcm_supp = 0; 173*f76de749SStephen Hanson if (di_walk_minor(node, DDI_NT_SMP, 0, &rarg, smp_minor) != 0) { 174*f76de749SStephen Hanson dp->rt_debug(dp->rt_hdl, "[INFO]: smp_select:" 175*f76de749SStephen Hanson "di_walk_minor failed. Returning NOTSUP\n"); 176*f76de749SStephen Hanson return (0); 177*f76de749SStephen Hanson } 178*f76de749SStephen Hanson 179*f76de749SStephen Hanson return (rarg.rcm_supp); 180*f76de749SStephen Hanson } 181*f76de749SStephen Hanson 182*f76de749SStephen Hanson /*ARGSUSED*/ 183*f76de749SStephen Hanson static int 18425e8c5aaSvikram disk_minor(di_node_t node, di_minor_t minor, void *arg) 18525e8c5aaSvikram { 18625e8c5aaSvikram rcm_arg_t *rp = (rcm_arg_t *)arg; 18725e8c5aaSvikram di_retire_t *dp = rp->rcm_dp; 18825e8c5aaSvikram 18925e8c5aaSvikram if (di_minor_spectype(minor) == S_IFBLK) { 19025e8c5aaSvikram rp->rcm_supp = 1; 19125e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: disk_minor: is disk minor. " 19225e8c5aaSvikram "IDed this node as disk\n"); 19325e8c5aaSvikram return (DI_WALK_TERMINATE); 19425e8c5aaSvikram } 19525e8c5aaSvikram 19625e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: disk_minor: Not a disk minor. " 19725e8c5aaSvikram "Continuing minor walk\n"); 19825e8c5aaSvikram return (DI_WALK_CONTINUE); 19925e8c5aaSvikram } 20025e8c5aaSvikram 20125e8c5aaSvikram static int 20225e8c5aaSvikram disk_select(di_node_t node, rcm_arg_t *rp) 20325e8c5aaSvikram { 20425e8c5aaSvikram rcm_arg_t rarg; 20525e8c5aaSvikram di_retire_t *dp = rp->rcm_dp; 20625e8c5aaSvikram 20725e8c5aaSvikram rarg.rcm_dp = dp; 20825e8c5aaSvikram 20925e8c5aaSvikram /* 21025e8c5aaSvikram * Check if this is a disk minor. If any one minor is DDI_NT_BLOCK 21125e8c5aaSvikram * we assume it is a disk 21225e8c5aaSvikram */ 21325e8c5aaSvikram rarg.rcm_supp = 0; 21425e8c5aaSvikram if (di_walk_minor(node, DDI_NT_BLOCK, 0, &rarg, disk_minor) != 0) { 21525e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: disk_select: di_walk_minor " 21625e8c5aaSvikram "failed. Returning NOTSUP\n"); 21725e8c5aaSvikram return (0); 21825e8c5aaSvikram } 21925e8c5aaSvikram 22025e8c5aaSvikram return (rarg.rcm_supp); 22125e8c5aaSvikram } 22225e8c5aaSvikram 22325e8c5aaSvikram static int 22425e8c5aaSvikram nexus_select(di_node_t node, rcm_arg_t *rp) 22525e8c5aaSvikram { 22625e8c5aaSvikram int select; 22725e8c5aaSvikram char *path; 22825e8c5aaSvikram 22925e8c5aaSvikram di_retire_t *dp = rp->rcm_dp; 23025e8c5aaSvikram 23125e8c5aaSvikram path = di_devfs_path(node); 23225e8c5aaSvikram if (path == NULL) { 23325e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: " 23425e8c5aaSvikram "di_devfs_path() is NULL. Returning NOTSUP\n"); 23525e8c5aaSvikram return (0); 23625e8c5aaSvikram } 23725e8c5aaSvikram 23825e8c5aaSvikram /* 23925e8c5aaSvikram * Check if it is a nexus 24025e8c5aaSvikram */ 24125e8c5aaSvikram if (di_driver_ops(node) & DI_BUS_OPS) { 24225e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: is nexus %s\n", 24325e8c5aaSvikram path); 24425e8c5aaSvikram select = 1; 24525e8c5aaSvikram } else { 24625e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: not nexus %s\n", 24725e8c5aaSvikram path); 24825e8c5aaSvikram select = 0; 24925e8c5aaSvikram } 25025e8c5aaSvikram 25125e8c5aaSvikram di_devfs_path_free(path); 25225e8c5aaSvikram 25325e8c5aaSvikram return (select); 25425e8c5aaSvikram } 25525e8c5aaSvikram 25625e8c5aaSvikram static int 25725e8c5aaSvikram node_select(di_node_t node, void *arg) 25825e8c5aaSvikram { 25925e8c5aaSvikram rcm_arg_t *rp = (rcm_arg_t *)arg; 26025e8c5aaSvikram di_retire_t *dp; 26125e8c5aaSvikram int sel; 26225e8c5aaSvikram int i; 26325e8c5aaSvikram char *path; 26425e8c5aaSvikram uint_t state; 26525e8c5aaSvikram 26625e8c5aaSvikram dp = rp->rcm_dp; 26725e8c5aaSvikram 26825e8c5aaSvikram /* skip pseudo nodes - we only retire real hardware */ 26925e8c5aaSvikram path = di_devfs_path(node); 27025e8c5aaSvikram if (strncmp(path, "/pseudo/", strlen("/pseudo/")) == 0 || 27125e8c5aaSvikram strcmp(path, "/pseudo") == 0) { 27225e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: " 27325e8c5aaSvikram "pseudo device in subtree - returning NOTSUP: %s\n", 27425e8c5aaSvikram path); 27525e8c5aaSvikram rp->rcm_supp = 0; 27625e8c5aaSvikram di_devfs_path_free(path); 27725e8c5aaSvikram return (DI_WALK_TERMINATE); 27825e8c5aaSvikram } 27925e8c5aaSvikram di_devfs_path_free(path); 28025e8c5aaSvikram 28125e8c5aaSvikram /* 28225e8c5aaSvikram * If a device is offline/detached/down it is 28325e8c5aaSvikram * retireable irrespective of the type of device, 28425e8c5aaSvikram * presumably the system is able to function without 28525e8c5aaSvikram * it. 28625e8c5aaSvikram */ 28725e8c5aaSvikram state = di_state(node); 28825e8c5aaSvikram if ((state & DI_DRIVER_DETACHED) || (state & DI_DEVICE_OFFLINE) || 28925e8c5aaSvikram (state & DI_BUS_DOWN)) { 29025e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: device " 29125e8c5aaSvikram "is offline/detached. Assuming retire supported\n"); 29225e8c5aaSvikram return (DI_WALK_CONTINUE); 29325e8c5aaSvikram } 29425e8c5aaSvikram 29525e8c5aaSvikram sel = 0; 29625e8c5aaSvikram for (i = 0; supported_devices[i].sel_name != NULL; i++) { 29725e8c5aaSvikram sel = supported_devices[i].sel_selector(node, rp); 29825e8c5aaSvikram if (sel == 1) { 29925e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: " 30025e8c5aaSvikram "found supported device: %s\n", 30125e8c5aaSvikram supported_devices[i].sel_name); 30225e8c5aaSvikram break; 30325e8c5aaSvikram } 30425e8c5aaSvikram } 30525e8c5aaSvikram 30625e8c5aaSvikram if (sel != 1) { 30725e8c5aaSvikram /* 30825e8c5aaSvikram * This node is not a supported device. Retire cannot proceed 30925e8c5aaSvikram */ 31025e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: found " 31125e8c5aaSvikram "unsupported device. Returning NOTSUP\n"); 31225e8c5aaSvikram rp->rcm_supp = 0; 31325e8c5aaSvikram return (DI_WALK_TERMINATE); 31425e8c5aaSvikram } 31525e8c5aaSvikram 31625e8c5aaSvikram /* 31725e8c5aaSvikram * This node is supported. Check other nodes in this subtree. 31825e8c5aaSvikram */ 31925e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: This node supported. " 32025e8c5aaSvikram "Checking other nodes in subtree: %s\n", rp->rcm_root); 32125e8c5aaSvikram return (DI_WALK_CONTINUE); 32225e8c5aaSvikram } 32325e8c5aaSvikram 32425e8c5aaSvikram 32525e8c5aaSvikram 32625e8c5aaSvikram /* 32725e8c5aaSvikram * when in doubt assume that retire is not supported for this device. 32825e8c5aaSvikram */ 32925e8c5aaSvikram static int 33025e8c5aaSvikram retire_supported(rcm_arg_t *rp) 33125e8c5aaSvikram { 33225e8c5aaSvikram di_retire_t *dp; 33325e8c5aaSvikram di_node_t rnode = rp->rcm_node; 33425e8c5aaSvikram 33525e8c5aaSvikram dp = rp->rcm_dp; 33625e8c5aaSvikram 33725e8c5aaSvikram /* 33825e8c5aaSvikram * We should not be here if devinfo snapshot is NULL. 33925e8c5aaSvikram */ 34025e8c5aaSvikram RIO_ASSERT(dp, rnode != DI_NODE_NIL); 34125e8c5aaSvikram 34225e8c5aaSvikram /* 34325e8c5aaSvikram * Note: We initally set supported to 1, then walk the 34425e8c5aaSvikram * subtree rooted at devpath, allowing each node the 34525e8c5aaSvikram * opportunity to veto the support. We cannot do things 34625e8c5aaSvikram * the other way around i.e. assume "not supported" and 34725e8c5aaSvikram * let individual nodes indicate that they are supported. 34825e8c5aaSvikram * In the latter case, the supported flag would be set 34925e8c5aaSvikram * if any one node in the subtree was supported which is 35025e8c5aaSvikram * not what we want. 35125e8c5aaSvikram */ 35225e8c5aaSvikram rp->rcm_supp = 1; 35325e8c5aaSvikram if (di_walk_node(rnode, DI_WALK_CLDFIRST, rp, node_select) != 0) { 35425e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: retire_supported: " 35525e8c5aaSvikram "di_walk_node: failed. Returning NOTSUP\n"); 35625e8c5aaSvikram rp->rcm_supp = 0; 35725e8c5aaSvikram } 35825e8c5aaSvikram 35925e8c5aaSvikram if (rp->rcm_supp) { 36025e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: retire IS supported\n"); 36125e8c5aaSvikram } 36225e8c5aaSvikram 36325e8c5aaSvikram return (rp->rcm_supp); 36425e8c5aaSvikram } 36525e8c5aaSvikram 36625e8c5aaSvikram static void 36725e8c5aaSvikram rcm_finalize(rcm_arg_t *rp, int retcode) 36825e8c5aaSvikram { 36925e8c5aaSvikram rio_path_t *p; 37025e8c5aaSvikram rio_path_t *tmp; 37125e8c5aaSvikram int flags = RCM_RETIRE_NOTIFY; 37225e8c5aaSvikram int retval; 37325e8c5aaSvikram int error; 37425e8c5aaSvikram di_retire_t *dp; 37525e8c5aaSvikram 37625e8c5aaSvikram dp = rp->rcm_dp; 37725e8c5aaSvikram 37825e8c5aaSvikram RIO_ASSERT(dp, retcode == 0 || retcode == -1); 37925e8c5aaSvikram 38025e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: retcode=%d: dev=%s\n", 38125e8c5aaSvikram retcode, rp->rcm_root); 38225e8c5aaSvikram 38325e8c5aaSvikram for (p = rp->rcm_cons_nodes; p; ) { 38425e8c5aaSvikram tmp = p; 38525e8c5aaSvikram p = tmp->rpt_next; 38625e8c5aaSvikram free(tmp); 38725e8c5aaSvikram } 38825e8c5aaSvikram rp->rcm_cons_nodes = NULL; 38925e8c5aaSvikram 39025e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: cons_nodes NULL\n"); 39125e8c5aaSvikram 39225e8c5aaSvikram for (p = rp->rcm_rsrc_minors; p; ) { 39325e8c5aaSvikram tmp = p; 39425e8c5aaSvikram p = tmp->rpt_next; 39525e8c5aaSvikram if (retcode == 0) { 39625e8c5aaSvikram retval = rp->rcm_remove(rp->rcm_handle, 39725e8c5aaSvikram tmp->rpt_path, flags, NULL); 39825e8c5aaSvikram error = errno; 39925e8c5aaSvikram } else { 40025e8c5aaSvikram RIO_ASSERT(dp, retcode == -1); 40125e8c5aaSvikram retval = rp->rcm_online(rp->rcm_handle, 40225e8c5aaSvikram tmp->rpt_path, flags, NULL); 40325e8c5aaSvikram error = errno; 40425e8c5aaSvikram } 40525e8c5aaSvikram if (retval != RCM_SUCCESS) { 40625e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: rcm_finalize: " 40725e8c5aaSvikram "rcm_%s: retval=%d: error=%s: path=%s\n", 40825e8c5aaSvikram retcode == 0 ? "remove" : "online", retval, 40925e8c5aaSvikram strerror(error), tmp->rpt_path); 41025e8c5aaSvikram } else { 41125e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: " 41225e8c5aaSvikram "rcm_%s: SUCCESS: path=%s\n", 41325e8c5aaSvikram retcode == 0 ? "remove" : "online", tmp->rpt_path); 41425e8c5aaSvikram } 41525e8c5aaSvikram free(tmp); 41625e8c5aaSvikram } 41725e8c5aaSvikram rp->rcm_rsrc_minors = NULL; 41825e8c5aaSvikram } 41925e8c5aaSvikram /*ARGSUSED*/ 42025e8c5aaSvikram static int 42125e8c5aaSvikram call_offline(di_node_t node, di_minor_t minor, void *arg) 42225e8c5aaSvikram { 42325e8c5aaSvikram rcm_arg_t *rp = (rcm_arg_t *)arg; 42425e8c5aaSvikram di_retire_t *dp = rp->rcm_dp; 42525e8c5aaSvikram char *mnp; 42625e8c5aaSvikram rio_path_t *rpt; 42725e8c5aaSvikram int retval; 42825e8c5aaSvikram 42925e8c5aaSvikram mnp = di_devfs_minor_path(minor); 43025e8c5aaSvikram if (mnp == NULL) { 43125e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_devfs_minor_path " 43225e8c5aaSvikram "failed. Returning RCM FAILURE: %s\n", rp->rcm_root); 43325e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 43425e8c5aaSvikram return (DI_WALK_TERMINATE); 43525e8c5aaSvikram } 43625e8c5aaSvikram 43725e8c5aaSvikram rpt = s_calloc(1, sizeof (rio_path_t), 0); 43825e8c5aaSvikram if (rpt == NULL) { 43925e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: calloc failed. " 44025e8c5aaSvikram "Returning RCM FAILURE: %s\n", rp->rcm_root); 44125e8c5aaSvikram di_devfs_path_free(mnp); 44225e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 44325e8c5aaSvikram return (DI_WALK_TERMINATE); 44425e8c5aaSvikram } 44525e8c5aaSvikram 44625e8c5aaSvikram (void) snprintf(rpt->rpt_path, sizeof (rpt->rpt_path), 44725e8c5aaSvikram "/devices%s", mnp); 44825e8c5aaSvikram 44925e8c5aaSvikram di_devfs_path_free(mnp); 45025e8c5aaSvikram 45125e8c5aaSvikram retval = rp->rcm_offline(rp->rcm_handle, rpt->rpt_path, 45225e8c5aaSvikram RCM_RETIRE_REQUEST, NULL); 45325e8c5aaSvikram 45425e8c5aaSvikram rpt->rpt_next = rp->rcm_rsrc_minors; 45525e8c5aaSvikram rp->rcm_rsrc_minors = rpt; 45625e8c5aaSvikram 45725e8c5aaSvikram if (retval == RCM_FAILURE) { 45825e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM OFFLINE failed " 45925e8c5aaSvikram "for: %s\n", rpt->rpt_path); 46025e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 46125e8c5aaSvikram return (DI_WALK_TERMINATE); 46225e8c5aaSvikram } else if (retval == RCM_SUCCESS) { 46325e8c5aaSvikram rp->rcm_retcode = RCM_SUCCESS; 46425e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: RCM OFFLINE returned " 46525e8c5aaSvikram "RCM_SUCCESS: %s\n", rpt->rpt_path); 46625e8c5aaSvikram } else if (retval != RCM_NO_CONSTRAINT) { 46725e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM OFFLINE returned " 46825e8c5aaSvikram "invalid value for: %s\n", rpt->rpt_path); 46925e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 47025e8c5aaSvikram return (DI_WALK_TERMINATE); 47125e8c5aaSvikram } else { 47225e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: RCM OFFLINE returned " 47325e8c5aaSvikram "RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path); 47425e8c5aaSvikram } 47525e8c5aaSvikram 47625e8c5aaSvikram return (DI_WALK_CONTINUE); 47725e8c5aaSvikram } 47825e8c5aaSvikram 47925e8c5aaSvikram static int 48025e8c5aaSvikram offline_one(di_node_t node, void *arg) 48125e8c5aaSvikram { 48225e8c5aaSvikram rcm_arg_t *rp = (rcm_arg_t *)arg; 48325e8c5aaSvikram rio_path_t *rpt; 48425e8c5aaSvikram di_retire_t *dp = rp->rcm_dp; 48525e8c5aaSvikram char *path; 48625e8c5aaSvikram 48725e8c5aaSvikram /* 48825e8c5aaSvikram * We should already have terminated the walk 48925e8c5aaSvikram * in case of failure 49025e8c5aaSvikram */ 49125e8c5aaSvikram RIO_ASSERT(dp, rp->rcm_retcode == RCM_SUCCESS || 49225e8c5aaSvikram rp->rcm_retcode == RCM_NO_CONSTRAINT); 49325e8c5aaSvikram 49425e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: offline_one: entered\n"); 49525e8c5aaSvikram 49625e8c5aaSvikram rp->rcm_retcode = RCM_NO_CONSTRAINT; 49725e8c5aaSvikram 49825e8c5aaSvikram rpt = s_calloc(1, sizeof (rio_path_t), 0); 49925e8c5aaSvikram if (rpt == NULL) { 50025e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: rio_path_t calloc " 50125e8c5aaSvikram "failed: error: %s\n", strerror(errno)); 50225e8c5aaSvikram goto fail; 50325e8c5aaSvikram } 50425e8c5aaSvikram 50525e8c5aaSvikram path = di_devfs_path(node); 50625e8c5aaSvikram if (path == NULL) { 50725e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_devfs_path " 50825e8c5aaSvikram "failed: error: %s\n", strerror(errno)); 50925e8c5aaSvikram free(rpt); 51025e8c5aaSvikram goto fail; 51125e8c5aaSvikram } 51225e8c5aaSvikram 51325e8c5aaSvikram (void) strlcpy(rpt->rpt_path, path, sizeof (rpt->rpt_path)); 51425e8c5aaSvikram 51525e8c5aaSvikram di_devfs_path_free(path); 51625e8c5aaSvikram 51725e8c5aaSvikram if (di_walk_minor(node, NULL, 0, rp, call_offline) != 0) { 51825e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor " 51925e8c5aaSvikram "failed: error: %s: %s\n", strerror(errno), path); 52025e8c5aaSvikram free(rpt); 52125e8c5aaSvikram goto fail; 52225e8c5aaSvikram } 52325e8c5aaSvikram 52425e8c5aaSvikram if (rp->rcm_retcode == RCM_FAILURE) { 52525e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor " 52625e8c5aaSvikram "returned: RCM_FAILURE: %s\n", rpt->rpt_path); 52725e8c5aaSvikram free(rpt); 52825e8c5aaSvikram goto fail; 52925e8c5aaSvikram } else if (rp->rcm_retcode == RCM_SUCCESS) { 53025e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: di_walk_minor " 53125e8c5aaSvikram "returned: RCM_SUCCESS: %s\n", rpt->rpt_path); 53225e8c5aaSvikram rpt->rpt_next = rp->rcm_cons_nodes; 53325e8c5aaSvikram rp->rcm_cons_nodes = rpt; 53425e8c5aaSvikram } else if (rp->rcm_retcode != RCM_NO_CONSTRAINT) { 53525e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor " 53625e8c5aaSvikram "returned: unknown RCM error code: %d, %s\n", 53725e8c5aaSvikram rp->rcm_retcode, rpt->rpt_path); 53825e8c5aaSvikram free(rpt); 53925e8c5aaSvikram goto fail; 54025e8c5aaSvikram } else { 54125e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: di_walk_minor " 54225e8c5aaSvikram "returned: RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path); 54325e8c5aaSvikram free(rpt); 54425e8c5aaSvikram } 54525e8c5aaSvikram 54625e8c5aaSvikram /* 54725e8c5aaSvikram * RCM_SUCCESS or RCM_NO_CONSTRAINT. 54825e8c5aaSvikram * RCM_SUCCESS implies we overcame a constraint, so keep walking. 54925e8c5aaSvikram * RCM_NO_CONSTRAINT implies no constraints applied via RCM. 55025e8c5aaSvikram * Continue walking in the hope that contracts or LDI will 55125e8c5aaSvikram * apply constraints 55225e8c5aaSvikram * set retcode to RCM_SUCCESS to show that at least 1 node 55325e8c5aaSvikram * completely walked 55425e8c5aaSvikram */ 55525e8c5aaSvikram rp->rcm_retcode = RCM_SUCCESS; 55625e8c5aaSvikram return (DI_WALK_CONTINUE); 55725e8c5aaSvikram 55825e8c5aaSvikram fail: 55925e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 56025e8c5aaSvikram return (DI_WALK_TERMINATE); 56125e8c5aaSvikram } 56225e8c5aaSvikram 56325e8c5aaSvikram /* 56425e8c5aaSvikram * Returns: 56525e8c5aaSvikram * RCM_SUCCESS: RCM constraints (if any) were applied. The 56625e8c5aaSvikram * device paths for which constraints were applied is passed 56725e8c5aaSvikram * back via the pp argument 56825e8c5aaSvikram * 56925e8c5aaSvikram * RCM_FAILURE: Either RCM constraints prevent a retire or 57025e8c5aaSvikram * an error occurred 57125e8c5aaSvikram */ 57225e8c5aaSvikram static int 57325e8c5aaSvikram rcm_notify(rcm_arg_t *rp, char **pp, size_t *clen) 57425e8c5aaSvikram { 57525e8c5aaSvikram size_t len; 57625e8c5aaSvikram rio_path_t *p; 57725e8c5aaSvikram rio_path_t *tmp; 57825e8c5aaSvikram char *plistp; 57925e8c5aaSvikram char *s; 58025e8c5aaSvikram di_retire_t *dp; 58125e8c5aaSvikram di_node_t rnode; 58225e8c5aaSvikram 58325e8c5aaSvikram dp = rp->rcm_dp; 58425e8c5aaSvikram 58525e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_notify() entered\n"); 58625e8c5aaSvikram 58725e8c5aaSvikram RIO_ASSERT(dp, rp->rcm_root); 58825e8c5aaSvikram 58925e8c5aaSvikram *pp = NULL; 59025e8c5aaSvikram 59125e8c5aaSvikram rnode = rp->rcm_node; 59225e8c5aaSvikram if (rnode == DI_NODE_NIL) { 59325e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: devinfo snapshot " 59425e8c5aaSvikram "NULL. Returning no RCM constraint: %s\n", rp->rcm_root); 59525e8c5aaSvikram return (RCM_NO_CONSTRAINT); 59625e8c5aaSvikram } 59725e8c5aaSvikram 59825e8c5aaSvikram rp->rcm_retcode = RCM_NO_CONSTRAINT; 59925e8c5aaSvikram rp->rcm_cons_nodes = NULL; 60025e8c5aaSvikram rp->rcm_rsrc_minors = NULL; 60125e8c5aaSvikram if (di_walk_node(rnode, DI_WALK_CLDFIRST, rp, offline_one) != 0) { 60225e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_node " 60325e8c5aaSvikram "failed: error: %s: %s\n", strerror(errno), rp->rcm_root); 60425e8c5aaSvikram /* online is idempotent - safe to online non-offlined nodes */ 60525e8c5aaSvikram rcm_finalize(rp, -1); 60625e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 60725e8c5aaSvikram goto out; 60825e8c5aaSvikram } 60925e8c5aaSvikram 61025e8c5aaSvikram if (rp->rcm_retcode == RCM_FAILURE) { 61125e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: walk_node " 61225e8c5aaSvikram "returned retcode of RCM_FAILURE: %s\n", rp->rcm_root); 61325e8c5aaSvikram rcm_finalize(rp, -1); 61425e8c5aaSvikram goto out; 61525e8c5aaSvikram } 61625e8c5aaSvikram 61725e8c5aaSvikram if (rp->rcm_retcode == RCM_NO_CONSTRAINT) { 61825e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_node " 61925e8c5aaSvikram " - no nodes walked: RCM_NO_CONSTRAINT: %s\n", 62025e8c5aaSvikram rp->rcm_root); 62125e8c5aaSvikram } else { 62225e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: walk_node: RCM_SUCCESS\n"); 62325e8c5aaSvikram } 62425e8c5aaSvikram 62525e8c5aaSvikram /* 62625e8c5aaSvikram * Convert to a sequence of NUL separated strings terminated by '\0'\0' 62725e8c5aaSvikram */ 62825e8c5aaSvikram for (len = 0, p = rp->rcm_cons_nodes; p; p = p->rpt_next) { 62925e8c5aaSvikram RIO_ASSERT(dp, p->rpt_path); 63025e8c5aaSvikram RIO_ASSERT(dp, strlen(p->rpt_path) > 0); 63125e8c5aaSvikram len += (strlen(p->rpt_path) + 1); 63225e8c5aaSvikram } 63325e8c5aaSvikram len++; /* list terminating '\0' */ 63425e8c5aaSvikram 63525e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: len of constraint str = %lu\n", len); 63625e8c5aaSvikram 63725e8c5aaSvikram plistp = s_calloc(1, len, 0); 63825e8c5aaSvikram if (plistp == NULL) { 63925e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: fail to alloc " 64025e8c5aaSvikram "constraint list: error: %s: %s\n", strerror(errno), 64125e8c5aaSvikram rp->rcm_root); 64225e8c5aaSvikram rcm_finalize(rp, -1); 64325e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 64425e8c5aaSvikram goto out; 64525e8c5aaSvikram } 64625e8c5aaSvikram 64725e8c5aaSvikram for (s = plistp, p = rp->rcm_cons_nodes; p; ) { 64825e8c5aaSvikram tmp = p; 64925e8c5aaSvikram p = tmp->rpt_next; 65025e8c5aaSvikram (void) strcpy(s, tmp->rpt_path); 65125e8c5aaSvikram s += strlen(s) + 1; 65225e8c5aaSvikram RIO_ASSERT(dp, s - plistp < len); 65325e8c5aaSvikram free(tmp); 65425e8c5aaSvikram } 65525e8c5aaSvikram rp->rcm_cons_nodes = NULL; 65625e8c5aaSvikram RIO_ASSERT(dp, s - plistp == len - 1); 65725e8c5aaSvikram *s = '\0'; 65825e8c5aaSvikram 65925e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: constraint str = %p\n", plistp); 66025e8c5aaSvikram 66125e8c5aaSvikram *pp = plistp; 66225e8c5aaSvikram *clen = len; 66325e8c5aaSvikram 66425e8c5aaSvikram rp->rcm_retcode = RCM_SUCCESS; 66525e8c5aaSvikram out: 66625e8c5aaSvikram return (rp->rcm_retcode); 66725e8c5aaSvikram } 66825e8c5aaSvikram 66925e8c5aaSvikram 67025e8c5aaSvikram /*ARGSUSED*/ 67125e8c5aaSvikram int 67225e8c5aaSvikram di_retire_device(char *devpath, di_retire_t *dp, int flags) 67325e8c5aaSvikram { 67425e8c5aaSvikram char path[PATH_MAX]; 67525e8c5aaSvikram struct stat sb; 67625e8c5aaSvikram int retval = EINVAL; 67725e8c5aaSvikram char *constraint = NULL; 67825e8c5aaSvikram size_t clen; 67925e8c5aaSvikram void *librcm_hdl; 68025e8c5aaSvikram rcm_arg_t rarg = {0}; 68125e8c5aaSvikram int (*librcm_alloc_handle)(); 68225e8c5aaSvikram int (*librcm_free_handle)(); 68325e8c5aaSvikram 68425e8c5aaSvikram if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL) 68525e8c5aaSvikram return (EINVAL); 68625e8c5aaSvikram 68725e8c5aaSvikram if (devpath == NULL || devpath[0] == '\0') { 68825e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL argument(s)\n"); 68925e8c5aaSvikram return (EINVAL); 69025e8c5aaSvikram } 69125e8c5aaSvikram 69225e8c5aaSvikram if (devpath[0] != '/' || strlen(devpath) >= PATH_MAX || 69325e8c5aaSvikram strncmp(devpath, "/devices/", strlen("/devices/")) == 0 || 69425e8c5aaSvikram strstr(devpath, "../devices/") || strrchr(devpath, ':')) { 69525e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: invalid devpath: %s\n", 69625e8c5aaSvikram devpath); 69725e8c5aaSvikram return (EINVAL); 69825e8c5aaSvikram } 69925e8c5aaSvikram 70025e8c5aaSvikram if (flags != 0) { 70125e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: flags should be 0: %d\n", 70225e8c5aaSvikram flags); 70325e8c5aaSvikram return (EINVAL); 70425e8c5aaSvikram } 70525e8c5aaSvikram 70625e8c5aaSvikram /* 70725e8c5aaSvikram * dlopen rather than link against librcm since libdevinfo 70825e8c5aaSvikram * resides in / and librcm resides in /usr. The dlopen is 70925e8c5aaSvikram * safe to do since fmd which invokes the retire code 71025e8c5aaSvikram * resides on /usr and will not come here until /usr is 71125e8c5aaSvikram * mounted. 71225e8c5aaSvikram */ 71325e8c5aaSvikram librcm_hdl = dlopen(LIBRCM_PATH, RTLD_LAZY); 71425e8c5aaSvikram if (librcm_hdl == NULL) { 71525e8c5aaSvikram char *errstr = dlerror(); 71625e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: Cannot dlopen librcm: %s\n", 71725e8c5aaSvikram errstr ? errstr : "Unknown error"); 71825e8c5aaSvikram return (ENOSYS); 71925e8c5aaSvikram } 72025e8c5aaSvikram 72125e8c5aaSvikram librcm_alloc_handle = (int (*)())dlsym(librcm_hdl, "rcm_alloc_handle"); 72225e8c5aaSvikram rarg.rcm_offline = (int (*)())dlsym(librcm_hdl, "rcm_request_offline"); 72325e8c5aaSvikram rarg.rcm_online = (int (*)())dlsym(librcm_hdl, "rcm_notify_online"); 72425e8c5aaSvikram rarg.rcm_remove = (int (*)())dlsym(librcm_hdl, "rcm_notify_remove"); 72525e8c5aaSvikram librcm_free_handle = (int (*)())dlsym(librcm_hdl, "rcm_free_handle"); 72625e8c5aaSvikram 72725e8c5aaSvikram if (librcm_alloc_handle == NULL || 72825e8c5aaSvikram rarg.rcm_offline == NULL || 72925e8c5aaSvikram rarg.rcm_online == NULL || 73025e8c5aaSvikram rarg.rcm_remove == NULL || 73125e8c5aaSvikram librcm_free_handle == NULL) { 73225e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: dlsym failed\n"); 73325e8c5aaSvikram retval = ENOSYS; 73425e8c5aaSvikram goto out; 73525e8c5aaSvikram } 73625e8c5aaSvikram 73725e8c5aaSvikram /* 73825e8c5aaSvikram * Take a libdevinfo snapshot here because we cannot do so 73925e8c5aaSvikram * after device is retired. If device doesn't attach, we retire 74025e8c5aaSvikram * anyway i.e. it is not fatal. 74125e8c5aaSvikram */ 74225e8c5aaSvikram rarg.rcm_node = di_init(devpath, DINFOCPYALL); 74325e8c5aaSvikram if (rarg.rcm_node == DI_NODE_NIL) { 74425e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: device doesn't attach, " 74525e8c5aaSvikram "retiring anyway: %s\n", devpath); 74625e8c5aaSvikram } 74725e8c5aaSvikram 74825e8c5aaSvikram rarg.rcm_handle = NULL; 74925e8c5aaSvikram if (librcm_alloc_handle(NULL, 0, NULL, &rarg.rcm_handle) 75025e8c5aaSvikram != RCM_SUCCESS) { 75125e8c5aaSvikram retval = errno; 75225e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: failed to alloc " 75325e8c5aaSvikram "RCM handle. Returning RCM failure: %s\n", devpath); 75425e8c5aaSvikram rarg.rcm_handle = NULL; 75525e8c5aaSvikram goto out; 75625e8c5aaSvikram } 75725e8c5aaSvikram 75825e8c5aaSvikram rarg.rcm_root = devpath; 75925e8c5aaSvikram rarg.rcm_dp = dp; 76025e8c5aaSvikram 76125e8c5aaSvikram /* 76225e8c5aaSvikram * If device is already detached/nonexistent and cannot be 76325e8c5aaSvikram * attached, allow retire without checking device type. 76425e8c5aaSvikram * XXX 76525e8c5aaSvikram * Else, check if retire is supported for this device type. 76625e8c5aaSvikram */ 76725e8c5aaSvikram (void) snprintf(path, sizeof (path), "/devices%s", devpath); 76825e8c5aaSvikram if (stat(path, &sb) == -1 || !S_ISDIR(sb.st_mode)) { 76925e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: detached or nonexistent " 77025e8c5aaSvikram "device. Bypassing retire_supported: %s\n", devpath); 77125e8c5aaSvikram } else if (!retire_supported(&rarg)) { 77225e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: retire not supported for " 77325e8c5aaSvikram "device type: %s\n", devpath); 77425e8c5aaSvikram retval = ENOTSUP; 77525e8c5aaSvikram goto out; 77625e8c5aaSvikram } 77725e8c5aaSvikram 77825e8c5aaSvikram clen = 0; 77925e8c5aaSvikram constraint = NULL; 78025e8c5aaSvikram retval = rcm_notify(&rarg, &constraint, &clen); 78125e8c5aaSvikram if (retval == RCM_FAILURE) { 78225e8c5aaSvikram /* retire not permitted */ 78325e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM constraints block " 78425e8c5aaSvikram "retire: %s\n", devpath); 78525e8c5aaSvikram retval = EBUSY; 78625e8c5aaSvikram goto out; 78725e8c5aaSvikram } else if (retval == RCM_SUCCESS) { 78825e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: RCM constraints applied" 78925e8c5aaSvikram ": %s\n", devpath); 79025e8c5aaSvikram } else if (retval == RCM_NO_CONSTRAINT) { 79125e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: No RCM constraints applied" 79225e8c5aaSvikram ": %s\n", devpath); 79325e8c5aaSvikram } else { 79425e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: notify returned unknown " 79525e8c5aaSvikram "return code: %d: %s\n", retval, devpath); 79625e8c5aaSvikram retval = ESRCH; 79725e8c5aaSvikram goto out; 79825e8c5aaSvikram } 79925e8c5aaSvikram 80025e8c5aaSvikram if (modctl(MODRETIRE, devpath, constraint, clen) != 0) { 80125e8c5aaSvikram retval = errno; 80225e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: retire modctl() failed: " 80325e8c5aaSvikram "%s: %s\n", devpath, strerror(retval)); 80425e8c5aaSvikram rcm_finalize(&rarg, -1); 80525e8c5aaSvikram goto out; 80625e8c5aaSvikram } 80725e8c5aaSvikram 80825e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: retire modctl() succeeded: %s\n", 80925e8c5aaSvikram devpath); 81025e8c5aaSvikram 81125e8c5aaSvikram rcm_finalize(&rarg, 0); 81225e8c5aaSvikram 81325e8c5aaSvikram retval = 0; 81425e8c5aaSvikram 81525e8c5aaSvikram out: 81625e8c5aaSvikram if (rarg.rcm_handle) 81725e8c5aaSvikram (void) librcm_free_handle(rarg.rcm_handle); 81825e8c5aaSvikram 81925e8c5aaSvikram RIO_ASSERT(dp, rarg.rcm_cons_nodes == NULL); 82025e8c5aaSvikram RIO_ASSERT(dp, rarg.rcm_rsrc_minors == NULL); 82125e8c5aaSvikram 82225e8c5aaSvikram (void) dlclose(librcm_hdl); 82325e8c5aaSvikram 82425e8c5aaSvikram free(constraint); 82525e8c5aaSvikram 82625e8c5aaSvikram if (rarg.rcm_node != DI_NODE_NIL) 82725e8c5aaSvikram di_fini(rarg.rcm_node); 82825e8c5aaSvikram 82925e8c5aaSvikram return (retval); 83025e8c5aaSvikram } 83125e8c5aaSvikram 83225e8c5aaSvikram /*ARGSUSED*/ 83325e8c5aaSvikram int 83425e8c5aaSvikram di_unretire_device(char *devpath, di_retire_t *dp) 83525e8c5aaSvikram { 83625e8c5aaSvikram if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL) 83725e8c5aaSvikram return (EINVAL); 83825e8c5aaSvikram 83925e8c5aaSvikram if (devpath == NULL || devpath[0] == '\0') { 84025e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL devpath\n"); 84125e8c5aaSvikram return (EINVAL); 84225e8c5aaSvikram } 84325e8c5aaSvikram 84425e8c5aaSvikram if (devpath[0] != '/' || strlen(devpath) >= PATH_MAX || 84525e8c5aaSvikram strncmp(devpath, "/devices/", strlen("/devices/")) == 0 || 84625e8c5aaSvikram strstr(devpath, "../devices/") || strrchr(devpath, ':')) { 84725e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: invalid devpath: %s\n", 84825e8c5aaSvikram devpath); 84925e8c5aaSvikram return (EINVAL); 85025e8c5aaSvikram } 85125e8c5aaSvikram 85225e8c5aaSvikram if (modctl(MODUNRETIRE, devpath) != 0) { 85325e8c5aaSvikram int err = errno; 85425e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: unretire modctl() failed: " 85525e8c5aaSvikram "%s: %s\n", devpath, strerror(err)); 85625e8c5aaSvikram return (err); 85725e8c5aaSvikram } 85825e8c5aaSvikram 85925e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: unretire modctl() done: %s\n", 86025e8c5aaSvikram devpath); 86125e8c5aaSvikram 86225e8c5aaSvikram return (0); 86325e8c5aaSvikram } 864