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