1*25e8c5aaSvikram /* 2*25e8c5aaSvikram * CDDL HEADER START 3*25e8c5aaSvikram * 4*25e8c5aaSvikram * The contents of this file are subject to the terms of the 5*25e8c5aaSvikram * Common Development and Distribution License (the "License"). 6*25e8c5aaSvikram * You may not use this file except in compliance with the License. 7*25e8c5aaSvikram * 8*25e8c5aaSvikram * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*25e8c5aaSvikram * or http://www.opensolaris.org/os/licensing. 10*25e8c5aaSvikram * See the License for the specific language governing permissions 11*25e8c5aaSvikram * and limitations under the License. 12*25e8c5aaSvikram * 13*25e8c5aaSvikram * When distributing Covered Code, include this CDDL HEADER in each 14*25e8c5aaSvikram * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*25e8c5aaSvikram * If applicable, add the following below this CDDL HEADER, with the 16*25e8c5aaSvikram * fields enclosed by brackets "[]" replaced with your own identifying 17*25e8c5aaSvikram * information: Portions Copyright [yyyy] [name of copyright owner] 18*25e8c5aaSvikram * 19*25e8c5aaSvikram * CDDL HEADER END 20*25e8c5aaSvikram */ 21*25e8c5aaSvikram 22*25e8c5aaSvikram /* 23*25e8c5aaSvikram * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24*25e8c5aaSvikram * Use is subject to license terms. 25*25e8c5aaSvikram */ 26*25e8c5aaSvikram 27*25e8c5aaSvikram #pragma ident "%Z%%M% %I% %E% SMI" 28*25e8c5aaSvikram 29*25e8c5aaSvikram #include <libdevinfo.h> 30*25e8c5aaSvikram #include <sys/modctl.h> 31*25e8c5aaSvikram #include <sys/stat.h> 32*25e8c5aaSvikram #include <string.h> 33*25e8c5aaSvikram #include <librcm.h> 34*25e8c5aaSvikram #include <dlfcn.h> 35*25e8c5aaSvikram 36*25e8c5aaSvikram #undef NDEBUG 37*25e8c5aaSvikram #include <assert.h> 38*25e8c5aaSvikram 39*25e8c5aaSvikram typedef struct rio_path { 40*25e8c5aaSvikram char rpt_path[PATH_MAX]; 41*25e8c5aaSvikram struct rio_path *rpt_next; 42*25e8c5aaSvikram } rio_path_t; 43*25e8c5aaSvikram 44*25e8c5aaSvikram typedef struct rcm_arg { 45*25e8c5aaSvikram char *rcm_root; 46*25e8c5aaSvikram di_node_t rcm_node; 47*25e8c5aaSvikram int rcm_supp; 48*25e8c5aaSvikram rcm_handle_t *rcm_handle; 49*25e8c5aaSvikram int rcm_retcode; 50*25e8c5aaSvikram di_retire_t *rcm_dp; 51*25e8c5aaSvikram rio_path_t *rcm_cons_nodes; 52*25e8c5aaSvikram rio_path_t *rcm_rsrc_minors; 53*25e8c5aaSvikram int (*rcm_offline)(); 54*25e8c5aaSvikram int (*rcm_online)(); 55*25e8c5aaSvikram int (*rcm_remove)(); 56*25e8c5aaSvikram } rcm_arg_t; 57*25e8c5aaSvikram 58*25e8c5aaSvikram typedef struct selector { 59*25e8c5aaSvikram char *sel_name; 60*25e8c5aaSvikram int (*sel_selector)(di_node_t node, rcm_arg_t *rp); 61*25e8c5aaSvikram } di_selector_t; 62*25e8c5aaSvikram 63*25e8c5aaSvikram static void rio_assert(di_retire_t *dp, const char *EXstr, int line, 64*25e8c5aaSvikram const char *file); 65*25e8c5aaSvikram 66*25e8c5aaSvikram #define LIBRCM_PATH "/usr/lib/librcm.so" 67*25e8c5aaSvikram #define RIO_ASSERT(d, x) \ 68*25e8c5aaSvikram {if (!(x)) rio_assert(d, #x, __LINE__, __FILE__); } 69*25e8c5aaSvikram 70*25e8c5aaSvikram static int disk_select(di_node_t node, rcm_arg_t *rp); 71*25e8c5aaSvikram static int nexus_select(di_node_t node, rcm_arg_t *rp); 72*25e8c5aaSvikram 73*25e8c5aaSvikram di_selector_t supported_devices[] = { 74*25e8c5aaSvikram {"disk", disk_select}, 75*25e8c5aaSvikram {"nexus", nexus_select}, 76*25e8c5aaSvikram {NULL, NULL} 77*25e8c5aaSvikram }; 78*25e8c5aaSvikram 79*25e8c5aaSvikram void * 80*25e8c5aaSvikram s_calloc(size_t nelem, size_t elsize, int fail) 81*25e8c5aaSvikram { 82*25e8c5aaSvikram if (fail) { 83*25e8c5aaSvikram errno = ENOMEM; 84*25e8c5aaSvikram return (NULL); 85*25e8c5aaSvikram } else { 86*25e8c5aaSvikram return (calloc(nelem, elsize)); 87*25e8c5aaSvikram } 88*25e8c5aaSvikram } 89*25e8c5aaSvikram 90*25e8c5aaSvikram static void 91*25e8c5aaSvikram rio_assert(di_retire_t *dp, const char *EXstr, int line, const char *file) 92*25e8c5aaSvikram { 93*25e8c5aaSvikram char buf[PATH_MAX]; 94*25e8c5aaSvikram 95*25e8c5aaSvikram if (dp->rt_abort == NULL) 96*25e8c5aaSvikram assert(0); 97*25e8c5aaSvikram 98*25e8c5aaSvikram (void) snprintf(buf, sizeof (buf), 99*25e8c5aaSvikram "Assertion failed: %s, file %s, line %d\n", 100*25e8c5aaSvikram EXstr, file, line); 101*25e8c5aaSvikram dp->rt_abort(dp->rt_hdl, buf); 102*25e8c5aaSvikram } 103*25e8c5aaSvikram 104*25e8c5aaSvikram /*ARGSUSED*/ 105*25e8c5aaSvikram static int 106*25e8c5aaSvikram disk_minor(di_node_t node, di_minor_t minor, void *arg) 107*25e8c5aaSvikram { 108*25e8c5aaSvikram rcm_arg_t *rp = (rcm_arg_t *)arg; 109*25e8c5aaSvikram di_retire_t *dp = rp->rcm_dp; 110*25e8c5aaSvikram 111*25e8c5aaSvikram if (di_minor_spectype(minor) == S_IFBLK) { 112*25e8c5aaSvikram rp->rcm_supp = 1; 113*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: disk_minor: is disk minor. " 114*25e8c5aaSvikram "IDed this node as disk\n"); 115*25e8c5aaSvikram return (DI_WALK_TERMINATE); 116*25e8c5aaSvikram } 117*25e8c5aaSvikram 118*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: disk_minor: Not a disk minor. " 119*25e8c5aaSvikram "Continuing minor walk\n"); 120*25e8c5aaSvikram return (DI_WALK_CONTINUE); 121*25e8c5aaSvikram } 122*25e8c5aaSvikram 123*25e8c5aaSvikram static int 124*25e8c5aaSvikram disk_select(di_node_t node, rcm_arg_t *rp) 125*25e8c5aaSvikram { 126*25e8c5aaSvikram rcm_arg_t rarg; 127*25e8c5aaSvikram di_retire_t *dp = rp->rcm_dp; 128*25e8c5aaSvikram 129*25e8c5aaSvikram rarg.rcm_dp = dp; 130*25e8c5aaSvikram 131*25e8c5aaSvikram /* 132*25e8c5aaSvikram * Check if this is a disk minor. If any one minor is DDI_NT_BLOCK 133*25e8c5aaSvikram * we assume it is a disk 134*25e8c5aaSvikram */ 135*25e8c5aaSvikram rarg.rcm_supp = 0; 136*25e8c5aaSvikram if (di_walk_minor(node, DDI_NT_BLOCK, 0, &rarg, disk_minor) != 0) { 137*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: disk_select: di_walk_minor " 138*25e8c5aaSvikram "failed. Returning NOTSUP\n"); 139*25e8c5aaSvikram return (0); 140*25e8c5aaSvikram } 141*25e8c5aaSvikram 142*25e8c5aaSvikram return (rarg.rcm_supp); 143*25e8c5aaSvikram } 144*25e8c5aaSvikram 145*25e8c5aaSvikram static int 146*25e8c5aaSvikram nexus_select(di_node_t node, rcm_arg_t *rp) 147*25e8c5aaSvikram { 148*25e8c5aaSvikram int select; 149*25e8c5aaSvikram char *path; 150*25e8c5aaSvikram 151*25e8c5aaSvikram di_retire_t *dp = rp->rcm_dp; 152*25e8c5aaSvikram 153*25e8c5aaSvikram path = di_devfs_path(node); 154*25e8c5aaSvikram if (path == NULL) { 155*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: " 156*25e8c5aaSvikram "di_devfs_path() is NULL. Returning NOTSUP\n"); 157*25e8c5aaSvikram return (0); 158*25e8c5aaSvikram } 159*25e8c5aaSvikram 160*25e8c5aaSvikram /* 161*25e8c5aaSvikram * Check if it is a nexus 162*25e8c5aaSvikram */ 163*25e8c5aaSvikram if (di_driver_ops(node) & DI_BUS_OPS) { 164*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: is nexus %s\n", 165*25e8c5aaSvikram path); 166*25e8c5aaSvikram select = 1; 167*25e8c5aaSvikram } else { 168*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: not nexus %s\n", 169*25e8c5aaSvikram path); 170*25e8c5aaSvikram select = 0; 171*25e8c5aaSvikram } 172*25e8c5aaSvikram 173*25e8c5aaSvikram di_devfs_path_free(path); 174*25e8c5aaSvikram 175*25e8c5aaSvikram return (select); 176*25e8c5aaSvikram } 177*25e8c5aaSvikram 178*25e8c5aaSvikram static int 179*25e8c5aaSvikram node_select(di_node_t node, void *arg) 180*25e8c5aaSvikram { 181*25e8c5aaSvikram rcm_arg_t *rp = (rcm_arg_t *)arg; 182*25e8c5aaSvikram di_retire_t *dp; 183*25e8c5aaSvikram int sel; 184*25e8c5aaSvikram int i; 185*25e8c5aaSvikram char *path; 186*25e8c5aaSvikram uint_t state; 187*25e8c5aaSvikram 188*25e8c5aaSvikram dp = rp->rcm_dp; 189*25e8c5aaSvikram 190*25e8c5aaSvikram /* skip pseudo nodes - we only retire real hardware */ 191*25e8c5aaSvikram path = di_devfs_path(node); 192*25e8c5aaSvikram if (strncmp(path, "/pseudo/", strlen("/pseudo/")) == 0 || 193*25e8c5aaSvikram strcmp(path, "/pseudo") == 0) { 194*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: " 195*25e8c5aaSvikram "pseudo device in subtree - returning NOTSUP: %s\n", 196*25e8c5aaSvikram path); 197*25e8c5aaSvikram rp->rcm_supp = 0; 198*25e8c5aaSvikram di_devfs_path_free(path); 199*25e8c5aaSvikram return (DI_WALK_TERMINATE); 200*25e8c5aaSvikram } 201*25e8c5aaSvikram di_devfs_path_free(path); 202*25e8c5aaSvikram 203*25e8c5aaSvikram /* 204*25e8c5aaSvikram * If a device is offline/detached/down it is 205*25e8c5aaSvikram * retireable irrespective of the type of device, 206*25e8c5aaSvikram * presumably the system is able to function without 207*25e8c5aaSvikram * it. 208*25e8c5aaSvikram */ 209*25e8c5aaSvikram state = di_state(node); 210*25e8c5aaSvikram if ((state & DI_DRIVER_DETACHED) || (state & DI_DEVICE_OFFLINE) || 211*25e8c5aaSvikram (state & DI_BUS_DOWN)) { 212*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: device " 213*25e8c5aaSvikram "is offline/detached. Assuming retire supported\n"); 214*25e8c5aaSvikram return (DI_WALK_CONTINUE); 215*25e8c5aaSvikram } 216*25e8c5aaSvikram 217*25e8c5aaSvikram sel = 0; 218*25e8c5aaSvikram for (i = 0; supported_devices[i].sel_name != NULL; i++) { 219*25e8c5aaSvikram sel = supported_devices[i].sel_selector(node, rp); 220*25e8c5aaSvikram if (sel == 1) { 221*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: " 222*25e8c5aaSvikram "found supported device: %s\n", 223*25e8c5aaSvikram supported_devices[i].sel_name); 224*25e8c5aaSvikram break; 225*25e8c5aaSvikram } 226*25e8c5aaSvikram } 227*25e8c5aaSvikram 228*25e8c5aaSvikram if (sel != 1) { 229*25e8c5aaSvikram /* 230*25e8c5aaSvikram * This node is not a supported device. Retire cannot proceed 231*25e8c5aaSvikram */ 232*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: found " 233*25e8c5aaSvikram "unsupported device. Returning NOTSUP\n"); 234*25e8c5aaSvikram rp->rcm_supp = 0; 235*25e8c5aaSvikram return (DI_WALK_TERMINATE); 236*25e8c5aaSvikram } 237*25e8c5aaSvikram 238*25e8c5aaSvikram /* 239*25e8c5aaSvikram * This node is supported. Check other nodes in this subtree. 240*25e8c5aaSvikram */ 241*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: This node supported. " 242*25e8c5aaSvikram "Checking other nodes in subtree: %s\n", rp->rcm_root); 243*25e8c5aaSvikram return (DI_WALK_CONTINUE); 244*25e8c5aaSvikram } 245*25e8c5aaSvikram 246*25e8c5aaSvikram 247*25e8c5aaSvikram 248*25e8c5aaSvikram /* 249*25e8c5aaSvikram * when in doubt assume that retire is not supported for this device. 250*25e8c5aaSvikram */ 251*25e8c5aaSvikram static int 252*25e8c5aaSvikram retire_supported(rcm_arg_t *rp) 253*25e8c5aaSvikram { 254*25e8c5aaSvikram di_retire_t *dp; 255*25e8c5aaSvikram di_node_t rnode = rp->rcm_node; 256*25e8c5aaSvikram 257*25e8c5aaSvikram dp = rp->rcm_dp; 258*25e8c5aaSvikram 259*25e8c5aaSvikram /* 260*25e8c5aaSvikram * We should not be here if devinfo snapshot is NULL. 261*25e8c5aaSvikram */ 262*25e8c5aaSvikram RIO_ASSERT(dp, rnode != DI_NODE_NIL); 263*25e8c5aaSvikram 264*25e8c5aaSvikram /* 265*25e8c5aaSvikram * Note: We initally set supported to 1, then walk the 266*25e8c5aaSvikram * subtree rooted at devpath, allowing each node the 267*25e8c5aaSvikram * opportunity to veto the support. We cannot do things 268*25e8c5aaSvikram * the other way around i.e. assume "not supported" and 269*25e8c5aaSvikram * let individual nodes indicate that they are supported. 270*25e8c5aaSvikram * In the latter case, the supported flag would be set 271*25e8c5aaSvikram * if any one node in the subtree was supported which is 272*25e8c5aaSvikram * not what we want. 273*25e8c5aaSvikram */ 274*25e8c5aaSvikram rp->rcm_supp = 1; 275*25e8c5aaSvikram if (di_walk_node(rnode, DI_WALK_CLDFIRST, rp, node_select) != 0) { 276*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: retire_supported: " 277*25e8c5aaSvikram "di_walk_node: failed. Returning NOTSUP\n"); 278*25e8c5aaSvikram rp->rcm_supp = 0; 279*25e8c5aaSvikram } 280*25e8c5aaSvikram 281*25e8c5aaSvikram if (rp->rcm_supp) { 282*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: retire IS supported\n"); 283*25e8c5aaSvikram } 284*25e8c5aaSvikram 285*25e8c5aaSvikram return (rp->rcm_supp); 286*25e8c5aaSvikram } 287*25e8c5aaSvikram 288*25e8c5aaSvikram static void 289*25e8c5aaSvikram rcm_finalize(rcm_arg_t *rp, int retcode) 290*25e8c5aaSvikram { 291*25e8c5aaSvikram rio_path_t *p; 292*25e8c5aaSvikram rio_path_t *tmp; 293*25e8c5aaSvikram int flags = RCM_RETIRE_NOTIFY; 294*25e8c5aaSvikram int retval; 295*25e8c5aaSvikram int error; 296*25e8c5aaSvikram di_retire_t *dp; 297*25e8c5aaSvikram 298*25e8c5aaSvikram dp = rp->rcm_dp; 299*25e8c5aaSvikram 300*25e8c5aaSvikram RIO_ASSERT(dp, retcode == 0 || retcode == -1); 301*25e8c5aaSvikram 302*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: retcode=%d: dev=%s\n", 303*25e8c5aaSvikram retcode, rp->rcm_root); 304*25e8c5aaSvikram 305*25e8c5aaSvikram for (p = rp->rcm_cons_nodes; p; ) { 306*25e8c5aaSvikram tmp = p; 307*25e8c5aaSvikram p = tmp->rpt_next; 308*25e8c5aaSvikram free(tmp); 309*25e8c5aaSvikram } 310*25e8c5aaSvikram rp->rcm_cons_nodes = NULL; 311*25e8c5aaSvikram 312*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: cons_nodes NULL\n"); 313*25e8c5aaSvikram 314*25e8c5aaSvikram for (p = rp->rcm_rsrc_minors; p; ) { 315*25e8c5aaSvikram tmp = p; 316*25e8c5aaSvikram p = tmp->rpt_next; 317*25e8c5aaSvikram if (retcode == 0) { 318*25e8c5aaSvikram retval = rp->rcm_remove(rp->rcm_handle, 319*25e8c5aaSvikram tmp->rpt_path, flags, NULL); 320*25e8c5aaSvikram error = errno; 321*25e8c5aaSvikram } else { 322*25e8c5aaSvikram RIO_ASSERT(dp, retcode == -1); 323*25e8c5aaSvikram retval = rp->rcm_online(rp->rcm_handle, 324*25e8c5aaSvikram tmp->rpt_path, flags, NULL); 325*25e8c5aaSvikram error = errno; 326*25e8c5aaSvikram } 327*25e8c5aaSvikram if (retval != RCM_SUCCESS) { 328*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: rcm_finalize: " 329*25e8c5aaSvikram "rcm_%s: retval=%d: error=%s: path=%s\n", 330*25e8c5aaSvikram retcode == 0 ? "remove" : "online", retval, 331*25e8c5aaSvikram strerror(error), tmp->rpt_path); 332*25e8c5aaSvikram } else { 333*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: " 334*25e8c5aaSvikram "rcm_%s: SUCCESS: path=%s\n", 335*25e8c5aaSvikram retcode == 0 ? "remove" : "online", tmp->rpt_path); 336*25e8c5aaSvikram } 337*25e8c5aaSvikram free(tmp); 338*25e8c5aaSvikram } 339*25e8c5aaSvikram rp->rcm_rsrc_minors = NULL; 340*25e8c5aaSvikram } 341*25e8c5aaSvikram /*ARGSUSED*/ 342*25e8c5aaSvikram static int 343*25e8c5aaSvikram call_offline(di_node_t node, di_minor_t minor, void *arg) 344*25e8c5aaSvikram { 345*25e8c5aaSvikram rcm_arg_t *rp = (rcm_arg_t *)arg; 346*25e8c5aaSvikram di_retire_t *dp = rp->rcm_dp; 347*25e8c5aaSvikram char *mnp; 348*25e8c5aaSvikram rio_path_t *rpt; 349*25e8c5aaSvikram int retval; 350*25e8c5aaSvikram 351*25e8c5aaSvikram mnp = di_devfs_minor_path(minor); 352*25e8c5aaSvikram if (mnp == NULL) { 353*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_devfs_minor_path " 354*25e8c5aaSvikram "failed. Returning RCM FAILURE: %s\n", rp->rcm_root); 355*25e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 356*25e8c5aaSvikram return (DI_WALK_TERMINATE); 357*25e8c5aaSvikram } 358*25e8c5aaSvikram 359*25e8c5aaSvikram rpt = s_calloc(1, sizeof (rio_path_t), 0); 360*25e8c5aaSvikram if (rpt == NULL) { 361*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: calloc failed. " 362*25e8c5aaSvikram "Returning RCM FAILURE: %s\n", rp->rcm_root); 363*25e8c5aaSvikram di_devfs_path_free(mnp); 364*25e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 365*25e8c5aaSvikram return (DI_WALK_TERMINATE); 366*25e8c5aaSvikram } 367*25e8c5aaSvikram 368*25e8c5aaSvikram (void) snprintf(rpt->rpt_path, sizeof (rpt->rpt_path), 369*25e8c5aaSvikram "/devices%s", mnp); 370*25e8c5aaSvikram 371*25e8c5aaSvikram di_devfs_path_free(mnp); 372*25e8c5aaSvikram 373*25e8c5aaSvikram retval = rp->rcm_offline(rp->rcm_handle, rpt->rpt_path, 374*25e8c5aaSvikram RCM_RETIRE_REQUEST, NULL); 375*25e8c5aaSvikram 376*25e8c5aaSvikram rpt->rpt_next = rp->rcm_rsrc_minors; 377*25e8c5aaSvikram rp->rcm_rsrc_minors = rpt; 378*25e8c5aaSvikram 379*25e8c5aaSvikram if (retval == RCM_FAILURE) { 380*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM OFFLINE failed " 381*25e8c5aaSvikram "for: %s\n", rpt->rpt_path); 382*25e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 383*25e8c5aaSvikram return (DI_WALK_TERMINATE); 384*25e8c5aaSvikram } else if (retval == RCM_SUCCESS) { 385*25e8c5aaSvikram rp->rcm_retcode = RCM_SUCCESS; 386*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: RCM OFFLINE returned " 387*25e8c5aaSvikram "RCM_SUCCESS: %s\n", rpt->rpt_path); 388*25e8c5aaSvikram } else if (retval != RCM_NO_CONSTRAINT) { 389*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM OFFLINE returned " 390*25e8c5aaSvikram "invalid value for: %s\n", rpt->rpt_path); 391*25e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 392*25e8c5aaSvikram return (DI_WALK_TERMINATE); 393*25e8c5aaSvikram } else { 394*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: RCM OFFLINE returned " 395*25e8c5aaSvikram "RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path); 396*25e8c5aaSvikram } 397*25e8c5aaSvikram 398*25e8c5aaSvikram return (DI_WALK_CONTINUE); 399*25e8c5aaSvikram } 400*25e8c5aaSvikram 401*25e8c5aaSvikram static int 402*25e8c5aaSvikram offline_one(di_node_t node, void *arg) 403*25e8c5aaSvikram { 404*25e8c5aaSvikram rcm_arg_t *rp = (rcm_arg_t *)arg; 405*25e8c5aaSvikram rio_path_t *rpt; 406*25e8c5aaSvikram di_retire_t *dp = rp->rcm_dp; 407*25e8c5aaSvikram char *path; 408*25e8c5aaSvikram 409*25e8c5aaSvikram /* 410*25e8c5aaSvikram * We should already have terminated the walk 411*25e8c5aaSvikram * in case of failure 412*25e8c5aaSvikram */ 413*25e8c5aaSvikram RIO_ASSERT(dp, rp->rcm_retcode == RCM_SUCCESS || 414*25e8c5aaSvikram rp->rcm_retcode == RCM_NO_CONSTRAINT); 415*25e8c5aaSvikram 416*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: offline_one: entered\n"); 417*25e8c5aaSvikram 418*25e8c5aaSvikram rp->rcm_retcode = RCM_NO_CONSTRAINT; 419*25e8c5aaSvikram 420*25e8c5aaSvikram rpt = s_calloc(1, sizeof (rio_path_t), 0); 421*25e8c5aaSvikram if (rpt == NULL) { 422*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: rio_path_t calloc " 423*25e8c5aaSvikram "failed: error: %s\n", strerror(errno)); 424*25e8c5aaSvikram goto fail; 425*25e8c5aaSvikram } 426*25e8c5aaSvikram 427*25e8c5aaSvikram path = di_devfs_path(node); 428*25e8c5aaSvikram if (path == NULL) { 429*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_devfs_path " 430*25e8c5aaSvikram "failed: error: %s\n", strerror(errno)); 431*25e8c5aaSvikram free(rpt); 432*25e8c5aaSvikram goto fail; 433*25e8c5aaSvikram } 434*25e8c5aaSvikram 435*25e8c5aaSvikram (void) strlcpy(rpt->rpt_path, path, sizeof (rpt->rpt_path)); 436*25e8c5aaSvikram 437*25e8c5aaSvikram di_devfs_path_free(path); 438*25e8c5aaSvikram 439*25e8c5aaSvikram if (di_walk_minor(node, NULL, 0, rp, call_offline) != 0) { 440*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor " 441*25e8c5aaSvikram "failed: error: %s: %s\n", strerror(errno), path); 442*25e8c5aaSvikram free(rpt); 443*25e8c5aaSvikram goto fail; 444*25e8c5aaSvikram } 445*25e8c5aaSvikram 446*25e8c5aaSvikram if (rp->rcm_retcode == RCM_FAILURE) { 447*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor " 448*25e8c5aaSvikram "returned: RCM_FAILURE: %s\n", rpt->rpt_path); 449*25e8c5aaSvikram free(rpt); 450*25e8c5aaSvikram goto fail; 451*25e8c5aaSvikram } else if (rp->rcm_retcode == RCM_SUCCESS) { 452*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: di_walk_minor " 453*25e8c5aaSvikram "returned: RCM_SUCCESS: %s\n", rpt->rpt_path); 454*25e8c5aaSvikram rpt->rpt_next = rp->rcm_cons_nodes; 455*25e8c5aaSvikram rp->rcm_cons_nodes = rpt; 456*25e8c5aaSvikram } else if (rp->rcm_retcode != RCM_NO_CONSTRAINT) { 457*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_minor " 458*25e8c5aaSvikram "returned: unknown RCM error code: %d, %s\n", 459*25e8c5aaSvikram rp->rcm_retcode, rpt->rpt_path); 460*25e8c5aaSvikram free(rpt); 461*25e8c5aaSvikram goto fail; 462*25e8c5aaSvikram } else { 463*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: di_walk_minor " 464*25e8c5aaSvikram "returned: RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path); 465*25e8c5aaSvikram free(rpt); 466*25e8c5aaSvikram } 467*25e8c5aaSvikram 468*25e8c5aaSvikram /* 469*25e8c5aaSvikram * RCM_SUCCESS or RCM_NO_CONSTRAINT. 470*25e8c5aaSvikram * RCM_SUCCESS implies we overcame a constraint, so keep walking. 471*25e8c5aaSvikram * RCM_NO_CONSTRAINT implies no constraints applied via RCM. 472*25e8c5aaSvikram * Continue walking in the hope that contracts or LDI will 473*25e8c5aaSvikram * apply constraints 474*25e8c5aaSvikram * set retcode to RCM_SUCCESS to show that at least 1 node 475*25e8c5aaSvikram * completely walked 476*25e8c5aaSvikram */ 477*25e8c5aaSvikram rp->rcm_retcode = RCM_SUCCESS; 478*25e8c5aaSvikram return (DI_WALK_CONTINUE); 479*25e8c5aaSvikram 480*25e8c5aaSvikram fail: 481*25e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 482*25e8c5aaSvikram return (DI_WALK_TERMINATE); 483*25e8c5aaSvikram } 484*25e8c5aaSvikram 485*25e8c5aaSvikram /* 486*25e8c5aaSvikram * Returns: 487*25e8c5aaSvikram * RCM_SUCCESS: RCM constraints (if any) were applied. The 488*25e8c5aaSvikram * device paths for which constraints were applied is passed 489*25e8c5aaSvikram * back via the pp argument 490*25e8c5aaSvikram * 491*25e8c5aaSvikram * RCM_FAILURE: Either RCM constraints prevent a retire or 492*25e8c5aaSvikram * an error occurred 493*25e8c5aaSvikram */ 494*25e8c5aaSvikram static int 495*25e8c5aaSvikram rcm_notify(rcm_arg_t *rp, char **pp, size_t *clen) 496*25e8c5aaSvikram { 497*25e8c5aaSvikram size_t len; 498*25e8c5aaSvikram rio_path_t *p; 499*25e8c5aaSvikram rio_path_t *tmp; 500*25e8c5aaSvikram char *plistp; 501*25e8c5aaSvikram char *s; 502*25e8c5aaSvikram di_retire_t *dp; 503*25e8c5aaSvikram di_node_t rnode; 504*25e8c5aaSvikram 505*25e8c5aaSvikram dp = rp->rcm_dp; 506*25e8c5aaSvikram 507*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_notify() entered\n"); 508*25e8c5aaSvikram 509*25e8c5aaSvikram RIO_ASSERT(dp, rp->rcm_root); 510*25e8c5aaSvikram 511*25e8c5aaSvikram *pp = NULL; 512*25e8c5aaSvikram 513*25e8c5aaSvikram rnode = rp->rcm_node; 514*25e8c5aaSvikram if (rnode == DI_NODE_NIL) { 515*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: devinfo snapshot " 516*25e8c5aaSvikram "NULL. Returning no RCM constraint: %s\n", rp->rcm_root); 517*25e8c5aaSvikram return (RCM_NO_CONSTRAINT); 518*25e8c5aaSvikram } 519*25e8c5aaSvikram 520*25e8c5aaSvikram rp->rcm_retcode = RCM_NO_CONSTRAINT; 521*25e8c5aaSvikram rp->rcm_cons_nodes = NULL; 522*25e8c5aaSvikram rp->rcm_rsrc_minors = NULL; 523*25e8c5aaSvikram if (di_walk_node(rnode, DI_WALK_CLDFIRST, rp, offline_one) != 0) { 524*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_node " 525*25e8c5aaSvikram "failed: error: %s: %s\n", strerror(errno), rp->rcm_root); 526*25e8c5aaSvikram /* online is idempotent - safe to online non-offlined nodes */ 527*25e8c5aaSvikram rcm_finalize(rp, -1); 528*25e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 529*25e8c5aaSvikram goto out; 530*25e8c5aaSvikram } 531*25e8c5aaSvikram 532*25e8c5aaSvikram if (rp->rcm_retcode == RCM_FAILURE) { 533*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: walk_node " 534*25e8c5aaSvikram "returned retcode of RCM_FAILURE: %s\n", rp->rcm_root); 535*25e8c5aaSvikram rcm_finalize(rp, -1); 536*25e8c5aaSvikram goto out; 537*25e8c5aaSvikram } 538*25e8c5aaSvikram 539*25e8c5aaSvikram if (rp->rcm_retcode == RCM_NO_CONSTRAINT) { 540*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: di_walk_node " 541*25e8c5aaSvikram " - no nodes walked: RCM_NO_CONSTRAINT: %s\n", 542*25e8c5aaSvikram rp->rcm_root); 543*25e8c5aaSvikram } else { 544*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: walk_node: RCM_SUCCESS\n"); 545*25e8c5aaSvikram } 546*25e8c5aaSvikram 547*25e8c5aaSvikram /* 548*25e8c5aaSvikram * Convert to a sequence of NUL separated strings terminated by '\0'\0' 549*25e8c5aaSvikram */ 550*25e8c5aaSvikram for (len = 0, p = rp->rcm_cons_nodes; p; p = p->rpt_next) { 551*25e8c5aaSvikram RIO_ASSERT(dp, p->rpt_path); 552*25e8c5aaSvikram RIO_ASSERT(dp, strlen(p->rpt_path) > 0); 553*25e8c5aaSvikram len += (strlen(p->rpt_path) + 1); 554*25e8c5aaSvikram } 555*25e8c5aaSvikram len++; /* list terminating '\0' */ 556*25e8c5aaSvikram 557*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: len of constraint str = %lu\n", len); 558*25e8c5aaSvikram 559*25e8c5aaSvikram plistp = s_calloc(1, len, 0); 560*25e8c5aaSvikram if (plistp == NULL) { 561*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: fail to alloc " 562*25e8c5aaSvikram "constraint list: error: %s: %s\n", strerror(errno), 563*25e8c5aaSvikram rp->rcm_root); 564*25e8c5aaSvikram rcm_finalize(rp, -1); 565*25e8c5aaSvikram rp->rcm_retcode = RCM_FAILURE; 566*25e8c5aaSvikram goto out; 567*25e8c5aaSvikram } 568*25e8c5aaSvikram 569*25e8c5aaSvikram for (s = plistp, p = rp->rcm_cons_nodes; p; ) { 570*25e8c5aaSvikram tmp = p; 571*25e8c5aaSvikram p = tmp->rpt_next; 572*25e8c5aaSvikram (void) strcpy(s, tmp->rpt_path); 573*25e8c5aaSvikram s += strlen(s) + 1; 574*25e8c5aaSvikram RIO_ASSERT(dp, s - plistp < len); 575*25e8c5aaSvikram free(tmp); 576*25e8c5aaSvikram } 577*25e8c5aaSvikram rp->rcm_cons_nodes = NULL; 578*25e8c5aaSvikram RIO_ASSERT(dp, s - plistp == len - 1); 579*25e8c5aaSvikram *s = '\0'; 580*25e8c5aaSvikram 581*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: constraint str = %p\n", plistp); 582*25e8c5aaSvikram 583*25e8c5aaSvikram *pp = plistp; 584*25e8c5aaSvikram *clen = len; 585*25e8c5aaSvikram 586*25e8c5aaSvikram rp->rcm_retcode = RCM_SUCCESS; 587*25e8c5aaSvikram out: 588*25e8c5aaSvikram return (rp->rcm_retcode); 589*25e8c5aaSvikram } 590*25e8c5aaSvikram 591*25e8c5aaSvikram 592*25e8c5aaSvikram /*ARGSUSED*/ 593*25e8c5aaSvikram int 594*25e8c5aaSvikram di_retire_device(char *devpath, di_retire_t *dp, int flags) 595*25e8c5aaSvikram { 596*25e8c5aaSvikram char path[PATH_MAX]; 597*25e8c5aaSvikram struct stat sb; 598*25e8c5aaSvikram int retval = EINVAL; 599*25e8c5aaSvikram char *constraint = NULL; 600*25e8c5aaSvikram size_t clen; 601*25e8c5aaSvikram void *librcm_hdl; 602*25e8c5aaSvikram rcm_arg_t rarg = {0}; 603*25e8c5aaSvikram int (*librcm_alloc_handle)(); 604*25e8c5aaSvikram int (*librcm_free_handle)(); 605*25e8c5aaSvikram 606*25e8c5aaSvikram if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL) 607*25e8c5aaSvikram return (EINVAL); 608*25e8c5aaSvikram 609*25e8c5aaSvikram if (devpath == NULL || devpath[0] == '\0') { 610*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL argument(s)\n"); 611*25e8c5aaSvikram return (EINVAL); 612*25e8c5aaSvikram } 613*25e8c5aaSvikram 614*25e8c5aaSvikram if (devpath[0] != '/' || strlen(devpath) >= PATH_MAX || 615*25e8c5aaSvikram strncmp(devpath, "/devices/", strlen("/devices/")) == 0 || 616*25e8c5aaSvikram strstr(devpath, "../devices/") || strrchr(devpath, ':')) { 617*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: invalid devpath: %s\n", 618*25e8c5aaSvikram devpath); 619*25e8c5aaSvikram return (EINVAL); 620*25e8c5aaSvikram } 621*25e8c5aaSvikram 622*25e8c5aaSvikram if (flags != 0) { 623*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: flags should be 0: %d\n", 624*25e8c5aaSvikram flags); 625*25e8c5aaSvikram return (EINVAL); 626*25e8c5aaSvikram } 627*25e8c5aaSvikram 628*25e8c5aaSvikram /* 629*25e8c5aaSvikram * dlopen rather than link against librcm since libdevinfo 630*25e8c5aaSvikram * resides in / and librcm resides in /usr. The dlopen is 631*25e8c5aaSvikram * safe to do since fmd which invokes the retire code 632*25e8c5aaSvikram * resides on /usr and will not come here until /usr is 633*25e8c5aaSvikram * mounted. 634*25e8c5aaSvikram */ 635*25e8c5aaSvikram librcm_hdl = dlopen(LIBRCM_PATH, RTLD_LAZY); 636*25e8c5aaSvikram if (librcm_hdl == NULL) { 637*25e8c5aaSvikram char *errstr = dlerror(); 638*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: Cannot dlopen librcm: %s\n", 639*25e8c5aaSvikram errstr ? errstr : "Unknown error"); 640*25e8c5aaSvikram return (ENOSYS); 641*25e8c5aaSvikram } 642*25e8c5aaSvikram 643*25e8c5aaSvikram librcm_alloc_handle = (int (*)())dlsym(librcm_hdl, "rcm_alloc_handle"); 644*25e8c5aaSvikram rarg.rcm_offline = (int (*)())dlsym(librcm_hdl, "rcm_request_offline"); 645*25e8c5aaSvikram rarg.rcm_online = (int (*)())dlsym(librcm_hdl, "rcm_notify_online"); 646*25e8c5aaSvikram rarg.rcm_remove = (int (*)())dlsym(librcm_hdl, "rcm_notify_remove"); 647*25e8c5aaSvikram librcm_free_handle = (int (*)())dlsym(librcm_hdl, "rcm_free_handle"); 648*25e8c5aaSvikram 649*25e8c5aaSvikram if (librcm_alloc_handle == NULL || 650*25e8c5aaSvikram rarg.rcm_offline == NULL || 651*25e8c5aaSvikram rarg.rcm_online == NULL || 652*25e8c5aaSvikram rarg.rcm_remove == NULL || 653*25e8c5aaSvikram librcm_free_handle == NULL) { 654*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: dlsym failed\n"); 655*25e8c5aaSvikram retval = ENOSYS; 656*25e8c5aaSvikram goto out; 657*25e8c5aaSvikram } 658*25e8c5aaSvikram 659*25e8c5aaSvikram /* 660*25e8c5aaSvikram * Take a libdevinfo snapshot here because we cannot do so 661*25e8c5aaSvikram * after device is retired. If device doesn't attach, we retire 662*25e8c5aaSvikram * anyway i.e. it is not fatal. 663*25e8c5aaSvikram */ 664*25e8c5aaSvikram rarg.rcm_node = di_init(devpath, DINFOCPYALL); 665*25e8c5aaSvikram if (rarg.rcm_node == DI_NODE_NIL) { 666*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: device doesn't attach, " 667*25e8c5aaSvikram "retiring anyway: %s\n", devpath); 668*25e8c5aaSvikram } 669*25e8c5aaSvikram 670*25e8c5aaSvikram rarg.rcm_handle = NULL; 671*25e8c5aaSvikram if (librcm_alloc_handle(NULL, 0, NULL, &rarg.rcm_handle) 672*25e8c5aaSvikram != RCM_SUCCESS) { 673*25e8c5aaSvikram retval = errno; 674*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: failed to alloc " 675*25e8c5aaSvikram "RCM handle. Returning RCM failure: %s\n", devpath); 676*25e8c5aaSvikram rarg.rcm_handle = NULL; 677*25e8c5aaSvikram goto out; 678*25e8c5aaSvikram } 679*25e8c5aaSvikram 680*25e8c5aaSvikram rarg.rcm_root = devpath; 681*25e8c5aaSvikram rarg.rcm_dp = dp; 682*25e8c5aaSvikram 683*25e8c5aaSvikram /* 684*25e8c5aaSvikram * If device is already detached/nonexistent and cannot be 685*25e8c5aaSvikram * attached, allow retire without checking device type. 686*25e8c5aaSvikram * XXX 687*25e8c5aaSvikram * Else, check if retire is supported for this device type. 688*25e8c5aaSvikram */ 689*25e8c5aaSvikram (void) snprintf(path, sizeof (path), "/devices%s", devpath); 690*25e8c5aaSvikram if (stat(path, &sb) == -1 || !S_ISDIR(sb.st_mode)) { 691*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: detached or nonexistent " 692*25e8c5aaSvikram "device. Bypassing retire_supported: %s\n", devpath); 693*25e8c5aaSvikram } else if (!retire_supported(&rarg)) { 694*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: retire not supported for " 695*25e8c5aaSvikram "device type: %s\n", devpath); 696*25e8c5aaSvikram retval = ENOTSUP; 697*25e8c5aaSvikram goto out; 698*25e8c5aaSvikram } 699*25e8c5aaSvikram 700*25e8c5aaSvikram clen = 0; 701*25e8c5aaSvikram constraint = NULL; 702*25e8c5aaSvikram retval = rcm_notify(&rarg, &constraint, &clen); 703*25e8c5aaSvikram if (retval == RCM_FAILURE) { 704*25e8c5aaSvikram /* retire not permitted */ 705*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: RCM constraints block " 706*25e8c5aaSvikram "retire: %s\n", devpath); 707*25e8c5aaSvikram retval = EBUSY; 708*25e8c5aaSvikram goto out; 709*25e8c5aaSvikram } else if (retval == RCM_SUCCESS) { 710*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: RCM constraints applied" 711*25e8c5aaSvikram ": %s\n", devpath); 712*25e8c5aaSvikram } else if (retval == RCM_NO_CONSTRAINT) { 713*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: No RCM constraints applied" 714*25e8c5aaSvikram ": %s\n", devpath); 715*25e8c5aaSvikram } else { 716*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: notify returned unknown " 717*25e8c5aaSvikram "return code: %d: %s\n", retval, devpath); 718*25e8c5aaSvikram retval = ESRCH; 719*25e8c5aaSvikram goto out; 720*25e8c5aaSvikram } 721*25e8c5aaSvikram 722*25e8c5aaSvikram if (modctl(MODRETIRE, devpath, constraint, clen) != 0) { 723*25e8c5aaSvikram retval = errno; 724*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: retire modctl() failed: " 725*25e8c5aaSvikram "%s: %s\n", devpath, strerror(retval)); 726*25e8c5aaSvikram rcm_finalize(&rarg, -1); 727*25e8c5aaSvikram goto out; 728*25e8c5aaSvikram } 729*25e8c5aaSvikram 730*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: retire modctl() succeeded: %s\n", 731*25e8c5aaSvikram devpath); 732*25e8c5aaSvikram 733*25e8c5aaSvikram rcm_finalize(&rarg, 0); 734*25e8c5aaSvikram 735*25e8c5aaSvikram retval = 0; 736*25e8c5aaSvikram 737*25e8c5aaSvikram out: 738*25e8c5aaSvikram if (rarg.rcm_handle) 739*25e8c5aaSvikram (void) librcm_free_handle(rarg.rcm_handle); 740*25e8c5aaSvikram 741*25e8c5aaSvikram RIO_ASSERT(dp, rarg.rcm_cons_nodes == NULL); 742*25e8c5aaSvikram RIO_ASSERT(dp, rarg.rcm_rsrc_minors == NULL); 743*25e8c5aaSvikram 744*25e8c5aaSvikram (void) dlclose(librcm_hdl); 745*25e8c5aaSvikram 746*25e8c5aaSvikram free(constraint); 747*25e8c5aaSvikram 748*25e8c5aaSvikram if (rarg.rcm_node != DI_NODE_NIL) 749*25e8c5aaSvikram di_fini(rarg.rcm_node); 750*25e8c5aaSvikram 751*25e8c5aaSvikram return (retval); 752*25e8c5aaSvikram } 753*25e8c5aaSvikram 754*25e8c5aaSvikram /*ARGSUSED*/ 755*25e8c5aaSvikram int 756*25e8c5aaSvikram di_unretire_device(char *devpath, di_retire_t *dp) 757*25e8c5aaSvikram { 758*25e8c5aaSvikram if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL) 759*25e8c5aaSvikram return (EINVAL); 760*25e8c5aaSvikram 761*25e8c5aaSvikram if (devpath == NULL || devpath[0] == '\0') { 762*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL devpath\n"); 763*25e8c5aaSvikram return (EINVAL); 764*25e8c5aaSvikram } 765*25e8c5aaSvikram 766*25e8c5aaSvikram if (devpath[0] != '/' || strlen(devpath) >= PATH_MAX || 767*25e8c5aaSvikram strncmp(devpath, "/devices/", strlen("/devices/")) == 0 || 768*25e8c5aaSvikram strstr(devpath, "../devices/") || strrchr(devpath, ':')) { 769*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: invalid devpath: %s\n", 770*25e8c5aaSvikram devpath); 771*25e8c5aaSvikram return (EINVAL); 772*25e8c5aaSvikram } 773*25e8c5aaSvikram 774*25e8c5aaSvikram if (modctl(MODUNRETIRE, devpath) != 0) { 775*25e8c5aaSvikram int err = errno; 776*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[ERROR]: unretire modctl() failed: " 777*25e8c5aaSvikram "%s: %s\n", devpath, strerror(err)); 778*25e8c5aaSvikram return (err); 779*25e8c5aaSvikram } 780*25e8c5aaSvikram 781*25e8c5aaSvikram dp->rt_debug(dp->rt_hdl, "[INFO]: unretire modctl() done: %s\n", 782*25e8c5aaSvikram devpath); 783*25e8c5aaSvikram 784*25e8c5aaSvikram return (0); 785*25e8c5aaSvikram } 786