xref: /titanic_52/usr/src/lib/libdevinfo/devinfo_retire.c (revision 25e8c5aa2b496d9026e958ac731a610167574f59)
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