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 *
s_calloc(size_t nelem,size_t elsize,int fail)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
rio_assert(di_retire_t * dp,const char * EXstr,int line,const char * file)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 
105*f76de749SStephen Hanson /*ARGSUSED*/
106*f76de749SStephen Hanson static int
enclosure_minor(di_node_t node,di_minor_t minor,void * arg)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
enclosure_select(di_node_t node,rcm_arg_t * rp)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
smp_minor(di_node_t node,di_minor_t minor,void * arg)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
smp_select(di_node_t node,rcm_arg_t * rp)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 
18225e8c5aaSvikram /*ARGSUSED*/
18325e8c5aaSvikram static int
disk_minor(di_node_t node,di_minor_t minor,void * arg)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
disk_select(di_node_t node,rcm_arg_t * rp)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
nexus_select(di_node_t node,rcm_arg_t * rp)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
node_select(di_node_t node,void * arg)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
retire_supported(rcm_arg_t * rp)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
rcm_finalize(rcm_arg_t * rp,int retcode)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
call_offline(di_node_t node,di_minor_t minor,void * arg)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
offline_one(di_node_t node,void * arg)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
rcm_notify(rcm_arg_t * rp,char ** pp,size_t * clen)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
di_retire_device(char * devpath,di_retire_t * dp,int flags)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
di_unretire_device(char * devpath,di_retire_t * dp)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