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.
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.
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]
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <libdevinfo.h>
27 #include <sys/modctl.h>
36 typedef struct rio_path
{
37 char rpt_path
[PATH_MAX
];
38 struct rio_path
*rpt_next
;
41 typedef struct rcm_arg
{
45 rcm_handle_t
*rcm_handle
;
48 rio_path_t
*rcm_cons_nodes
;
49 rio_path_t
*rcm_rsrc_minors
;
55 typedef struct selector
{
57 int (*sel_selector
)(di_node_t node
, rcm_arg_t
*rp
);
60 static void rio_assert(di_retire_t
*dp
, const char *EXstr
, int line
,
63 #define LIBRCM_PATH "/usr/lib/librcm.so"
64 #define RIO_ASSERT(d, x) \
65 {if (!(x)) rio_assert(d, #x, __LINE__, __FILE__); }
67 static int disk_select(di_node_t node
, rcm_arg_t
*rp
);
68 static int nexus_select(di_node_t node
, rcm_arg_t
*rp
);
69 static int enclosure_select(di_node_t node
, rcm_arg_t
*rp
);
70 static int smp_select(di_node_t node
, rcm_arg_t
*rp
);
72 di_selector_t supported_devices
[] = {
73 {"disk", disk_select
},
74 {"nexus", nexus_select
},
75 {"enclosure", enclosure_select
},
81 s_calloc(size_t nelem
, size_t elsize
, int fail
)
87 return (calloc(nelem
, elsize
));
92 rio_assert(di_retire_t
*dp
, const char *EXstr
, int line
, const char *file
)
96 if (dp
->rt_abort
== NULL
)
99 (void) snprintf(buf
, sizeof (buf
),
100 "Assertion failed: %s, file %s, line %d\n",
102 dp
->rt_abort(dp
->rt_hdl
, buf
);
107 enclosure_minor(di_node_t node
, di_minor_t minor
, void *arg
)
109 rcm_arg_t
*rp
= (rcm_arg_t
*)arg
;
110 di_retire_t
*dp
= rp
->rcm_dp
;
113 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: enclosure_minor: "
114 "IDed this node as enclosure\n");
115 return (DI_WALK_TERMINATE
);
119 enclosure_select(di_node_t node
, rcm_arg_t
*rp
)
122 di_retire_t
*dp
= rp
->rcm_dp
;
127 * Check if this is an enclosure minor. If any one minor is DDI_NT_SGEN
128 * or DDI_NT_SCSI_ENCLOSURE we assume it is an enclosure.
131 if (di_walk_minor(node
, DDI_NT_SCSI_ENCLOSURE
, 0, &rarg
,
132 enclosure_minor
) != 0) {
133 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: enclosure_select:"
134 "di_walk_minor failed. Returning NOTSUP\n");
137 if (di_walk_minor(node
, "ddi_generic:scsi", 0, &rarg
,
138 enclosure_minor
) != 0) {
139 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: enclosure_select:"
140 "di_walk_minor failed. Returning NOTSUP\n");
144 return (rarg
.rcm_supp
);
149 smp_minor(di_node_t node
, di_minor_t minor
, void *arg
)
151 rcm_arg_t
*rp
= (rcm_arg_t
*)arg
;
152 di_retire_t
*dp
= rp
->rcm_dp
;
155 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: smp_minor: "
156 "IDed this node as smp\n");
157 return (DI_WALK_TERMINATE
);
161 smp_select(di_node_t node
, rcm_arg_t
*rp
)
164 di_retire_t
*dp
= rp
->rcm_dp
;
169 * Check if this is an smp minor. If any one minor is DDI_NT_SMP
170 * we assume it is an smp.
173 if (di_walk_minor(node
, DDI_NT_SMP
, 0, &rarg
, smp_minor
) != 0) {
174 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: smp_select:"
175 "di_walk_minor failed. Returning NOTSUP\n");
179 return (rarg
.rcm_supp
);
184 disk_minor(di_node_t node
, di_minor_t minor
, void *arg
)
186 rcm_arg_t
*rp
= (rcm_arg_t
*)arg
;
187 di_retire_t
*dp
= rp
->rcm_dp
;
189 if (di_minor_spectype(minor
) == S_IFBLK
) {
191 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: disk_minor: is disk minor. "
192 "IDed this node as disk\n");
193 return (DI_WALK_TERMINATE
);
196 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: disk_minor: Not a disk minor. "
197 "Continuing minor walk\n");
198 return (DI_WALK_CONTINUE
);
202 disk_select(di_node_t node
, rcm_arg_t
*rp
)
205 di_retire_t
*dp
= rp
->rcm_dp
;
210 * Check if this is a disk minor. If any one minor is DDI_NT_BLOCK
211 * we assume it is a disk
214 if (di_walk_minor(node
, DDI_NT_BLOCK
, 0, &rarg
, disk_minor
) != 0) {
215 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: disk_select: di_walk_minor "
216 "failed. Returning NOTSUP\n");
220 return (rarg
.rcm_supp
);
224 nexus_select(di_node_t node
, rcm_arg_t
*rp
)
229 di_retire_t
*dp
= rp
->rcm_dp
;
231 path
= di_devfs_path(node
);
233 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: nexus_select: "
234 "di_devfs_path() is NULL. Returning NOTSUP\n");
239 * Check if it is a nexus
241 if (di_driver_ops(node
) & DI_BUS_OPS
) {
242 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: nexus_select: is nexus %s\n",
246 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: nexus_select: not nexus %s\n",
251 di_devfs_path_free(path
);
257 node_select(di_node_t node
, void *arg
)
259 rcm_arg_t
*rp
= (rcm_arg_t
*)arg
;
268 /* skip pseudo nodes - we only retire real hardware */
269 path
= di_devfs_path(node
);
270 if (strncmp(path
, "/pseudo/", strlen("/pseudo/")) == 0 ||
271 strcmp(path
, "/pseudo") == 0) {
272 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: node_select: "
273 "pseudo device in subtree - returning NOTSUP: %s\n",
276 di_devfs_path_free(path
);
277 return (DI_WALK_TERMINATE
);
279 di_devfs_path_free(path
);
282 * If a device is offline/detached/down it is
283 * retireable irrespective of the type of device,
284 * presumably the system is able to function without
287 state
= di_state(node
);
288 if ((state
& DI_DRIVER_DETACHED
) || (state
& DI_DEVICE_OFFLINE
) ||
289 (state
& DI_BUS_DOWN
)) {
290 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: node_select: device "
291 "is offline/detached. Assuming retire supported\n");
292 return (DI_WALK_CONTINUE
);
296 for (i
= 0; supported_devices
[i
].sel_name
!= NULL
; i
++) {
297 sel
= supported_devices
[i
].sel_selector(node
, rp
);
299 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: node_select: "
300 "found supported device: %s\n",
301 supported_devices
[i
].sel_name
);
308 * This node is not a supported device. Retire cannot proceed
310 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: node_select: found "
311 "unsupported device. Returning NOTSUP\n");
313 return (DI_WALK_TERMINATE
);
317 * This node is supported. Check other nodes in this subtree.
319 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: node_select: This node supported. "
320 "Checking other nodes in subtree: %s\n", rp
->rcm_root
);
321 return (DI_WALK_CONTINUE
);
327 * when in doubt assume that retire is not supported for this device.
330 retire_supported(rcm_arg_t
*rp
)
333 di_node_t rnode
= rp
->rcm_node
;
338 * We should not be here if devinfo snapshot is NULL.
340 RIO_ASSERT(dp
, rnode
!= DI_NODE_NIL
);
343 * Note: We initally set supported to 1, then walk the
344 * subtree rooted at devpath, allowing each node the
345 * opportunity to veto the support. We cannot do things
346 * the other way around i.e. assume "not supported" and
347 * let individual nodes indicate that they are supported.
348 * In the latter case, the supported flag would be set
349 * if any one node in the subtree was supported which is
353 if (di_walk_node(rnode
, DI_WALK_CLDFIRST
, rp
, node_select
) != 0) {
354 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: retire_supported: "
355 "di_walk_node: failed. Returning NOTSUP\n");
360 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: retire IS supported\n");
363 return (rp
->rcm_supp
);
367 rcm_finalize(rcm_arg_t
*rp
, int retcode
)
371 int flags
= RCM_RETIRE_NOTIFY
;
378 RIO_ASSERT(dp
, retcode
== 0 || retcode
== -1);
380 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: rcm_finalize: retcode=%d: dev=%s\n",
381 retcode
, rp
->rcm_root
);
383 for (p
= rp
->rcm_cons_nodes
; p
; ) {
388 rp
->rcm_cons_nodes
= NULL
;
390 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: rcm_finalize: cons_nodes NULL\n");
392 for (p
= rp
->rcm_rsrc_minors
; p
; ) {
396 retval
= rp
->rcm_remove(rp
->rcm_handle
,
397 tmp
->rpt_path
, flags
, NULL
);
400 RIO_ASSERT(dp
, retcode
== -1);
401 retval
= rp
->rcm_online(rp
->rcm_handle
,
402 tmp
->rpt_path
, flags
, NULL
);
405 if (retval
!= RCM_SUCCESS
) {
406 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: rcm_finalize: "
407 "rcm_%s: retval=%d: error=%s: path=%s\n",
408 retcode
== 0 ? "remove" : "online", retval
,
409 strerror(error
), tmp
->rpt_path
);
411 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: rcm_finalize: "
412 "rcm_%s: SUCCESS: path=%s\n",
413 retcode
== 0 ? "remove" : "online", tmp
->rpt_path
);
417 rp
->rcm_rsrc_minors
= NULL
;
421 call_offline(di_node_t node
, di_minor_t minor
, void *arg
)
423 rcm_arg_t
*rp
= (rcm_arg_t
*)arg
;
424 di_retire_t
*dp
= rp
->rcm_dp
;
429 mnp
= di_devfs_minor_path(minor
);
431 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: di_devfs_minor_path "
432 "failed. Returning RCM FAILURE: %s\n", rp
->rcm_root
);
433 rp
->rcm_retcode
= RCM_FAILURE
;
434 return (DI_WALK_TERMINATE
);
437 rpt
= s_calloc(1, sizeof (rio_path_t
), 0);
439 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: calloc failed. "
440 "Returning RCM FAILURE: %s\n", rp
->rcm_root
);
441 di_devfs_path_free(mnp
);
442 rp
->rcm_retcode
= RCM_FAILURE
;
443 return (DI_WALK_TERMINATE
);
446 (void) snprintf(rpt
->rpt_path
, sizeof (rpt
->rpt_path
),
449 di_devfs_path_free(mnp
);
451 retval
= rp
->rcm_offline(rp
->rcm_handle
, rpt
->rpt_path
,
452 RCM_RETIRE_REQUEST
, NULL
);
454 rpt
->rpt_next
= rp
->rcm_rsrc_minors
;
455 rp
->rcm_rsrc_minors
= rpt
;
457 if (retval
== RCM_FAILURE
) {
458 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: RCM OFFLINE failed "
459 "for: %s\n", rpt
->rpt_path
);
460 rp
->rcm_retcode
= RCM_FAILURE
;
461 return (DI_WALK_TERMINATE
);
462 } else if (retval
== RCM_SUCCESS
) {
463 rp
->rcm_retcode
= RCM_SUCCESS
;
464 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: RCM OFFLINE returned "
465 "RCM_SUCCESS: %s\n", rpt
->rpt_path
);
466 } else if (retval
!= RCM_NO_CONSTRAINT
) {
467 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: RCM OFFLINE returned "
468 "invalid value for: %s\n", rpt
->rpt_path
);
469 rp
->rcm_retcode
= RCM_FAILURE
;
470 return (DI_WALK_TERMINATE
);
472 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: RCM OFFLINE returned "
473 "RCM_NO_CONSTRAINT: %s\n", rpt
->rpt_path
);
476 return (DI_WALK_CONTINUE
);
480 offline_one(di_node_t node
, void *arg
)
482 rcm_arg_t
*rp
= (rcm_arg_t
*)arg
;
484 di_retire_t
*dp
= rp
->rcm_dp
;
488 * We should already have terminated the walk
491 RIO_ASSERT(dp
, rp
->rcm_retcode
== RCM_SUCCESS
||
492 rp
->rcm_retcode
== RCM_NO_CONSTRAINT
);
494 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: offline_one: entered\n");
496 rp
->rcm_retcode
= RCM_NO_CONSTRAINT
;
498 rpt
= s_calloc(1, sizeof (rio_path_t
), 0);
500 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: rio_path_t calloc "
501 "failed: error: %s\n", strerror(errno
));
505 path
= di_devfs_path(node
);
507 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: di_devfs_path "
508 "failed: error: %s\n", strerror(errno
));
513 (void) strlcpy(rpt
->rpt_path
, path
, sizeof (rpt
->rpt_path
));
515 di_devfs_path_free(path
);
517 if (di_walk_minor(node
, NULL
, 0, rp
, call_offline
) != 0) {
518 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: di_walk_minor "
519 "failed: error: %s: %s\n", strerror(errno
), path
);
524 if (rp
->rcm_retcode
== RCM_FAILURE
) {
525 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: di_walk_minor "
526 "returned: RCM_FAILURE: %s\n", rpt
->rpt_path
);
529 } else if (rp
->rcm_retcode
== RCM_SUCCESS
) {
530 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: di_walk_minor "
531 "returned: RCM_SUCCESS: %s\n", rpt
->rpt_path
);
532 rpt
->rpt_next
= rp
->rcm_cons_nodes
;
533 rp
->rcm_cons_nodes
= rpt
;
534 } else if (rp
->rcm_retcode
!= RCM_NO_CONSTRAINT
) {
535 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: di_walk_minor "
536 "returned: unknown RCM error code: %d, %s\n",
537 rp
->rcm_retcode
, rpt
->rpt_path
);
541 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: di_walk_minor "
542 "returned: RCM_NO_CONSTRAINT: %s\n", rpt
->rpt_path
);
547 * RCM_SUCCESS or RCM_NO_CONSTRAINT.
548 * RCM_SUCCESS implies we overcame a constraint, so keep walking.
549 * RCM_NO_CONSTRAINT implies no constraints applied via RCM.
550 * Continue walking in the hope that contracts or LDI will
552 * set retcode to RCM_SUCCESS to show that at least 1 node
555 rp
->rcm_retcode
= RCM_SUCCESS
;
556 return (DI_WALK_CONTINUE
);
559 rp
->rcm_retcode
= RCM_FAILURE
;
560 return (DI_WALK_TERMINATE
);
565 * RCM_SUCCESS: RCM constraints (if any) were applied. The
566 * device paths for which constraints were applied is passed
567 * back via the pp argument
569 * RCM_FAILURE: Either RCM constraints prevent a retire or
573 rcm_notify(rcm_arg_t
*rp
, char **pp
, size_t *clen
)
585 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: rcm_notify() entered\n");
587 RIO_ASSERT(dp
, rp
->rcm_root
);
591 rnode
= rp
->rcm_node
;
592 if (rnode
== DI_NODE_NIL
) {
593 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: devinfo snapshot "
594 "NULL. Returning no RCM constraint: %s\n", rp
->rcm_root
);
595 return (RCM_NO_CONSTRAINT
);
598 rp
->rcm_retcode
= RCM_NO_CONSTRAINT
;
599 rp
->rcm_cons_nodes
= NULL
;
600 rp
->rcm_rsrc_minors
= NULL
;
601 if (di_walk_node(rnode
, DI_WALK_CLDFIRST
, rp
, offline_one
) != 0) {
602 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: di_walk_node "
603 "failed: error: %s: %s\n", strerror(errno
), rp
->rcm_root
);
604 /* online is idempotent - safe to online non-offlined nodes */
605 rcm_finalize(rp
, -1);
606 rp
->rcm_retcode
= RCM_FAILURE
;
610 if (rp
->rcm_retcode
== RCM_FAILURE
) {
611 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: walk_node "
612 "returned retcode of RCM_FAILURE: %s\n", rp
->rcm_root
);
613 rcm_finalize(rp
, -1);
617 if (rp
->rcm_retcode
== RCM_NO_CONSTRAINT
) {
618 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: di_walk_node "
619 " - no nodes walked: RCM_NO_CONSTRAINT: %s\n",
622 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: walk_node: RCM_SUCCESS\n");
626 * Convert to a sequence of NUL separated strings terminated by '\0'\0'
628 for (len
= 0, p
= rp
->rcm_cons_nodes
; p
; p
= p
->rpt_next
) {
629 RIO_ASSERT(dp
, p
->rpt_path
);
630 RIO_ASSERT(dp
, strlen(p
->rpt_path
) > 0);
631 len
+= (strlen(p
->rpt_path
) + 1);
633 len
++; /* list terminating '\0' */
635 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: len of constraint str = %lu\n", len
);
637 plistp
= s_calloc(1, len
, 0);
638 if (plistp
== NULL
) {
639 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: fail to alloc "
640 "constraint list: error: %s: %s\n", strerror(errno
),
642 rcm_finalize(rp
, -1);
643 rp
->rcm_retcode
= RCM_FAILURE
;
647 for (s
= plistp
, p
= rp
->rcm_cons_nodes
; p
; ) {
650 (void) strcpy(s
, tmp
->rpt_path
);
652 RIO_ASSERT(dp
, s
- plistp
< len
);
655 rp
->rcm_cons_nodes
= NULL
;
656 RIO_ASSERT(dp
, s
- plistp
== len
- 1);
659 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: constraint str = %p\n", plistp
);
664 rp
->rcm_retcode
= RCM_SUCCESS
;
666 return (rp
->rcm_retcode
);
672 di_retire_device(char *devpath
, di_retire_t
*dp
, int flags
)
677 char *constraint
= NULL
;
680 rcm_arg_t rarg
= {0};
681 int (*librcm_alloc_handle
)();
682 int (*librcm_free_handle
)();
684 if (dp
== NULL
|| dp
->rt_debug
== NULL
|| dp
->rt_hdl
== NULL
)
687 if (devpath
== NULL
|| devpath
[0] == '\0') {
688 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: NULL argument(s)\n");
692 if (devpath
[0] != '/' || strlen(devpath
) >= PATH_MAX
||
693 strncmp(devpath
, "/devices/", strlen("/devices/")) == 0 ||
694 strstr(devpath
, "../devices/") || strrchr(devpath
, ':')) {
695 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: invalid devpath: %s\n",
701 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: flags should be 0: %d\n",
707 * dlopen rather than link against librcm since libdevinfo
708 * resides in / and librcm resides in /usr. The dlopen is
709 * safe to do since fmd which invokes the retire code
710 * resides on /usr and will not come here until /usr is
713 librcm_hdl
= dlopen(LIBRCM_PATH
, RTLD_LAZY
);
714 if (librcm_hdl
== NULL
) {
715 char *errstr
= dlerror();
716 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: Cannot dlopen librcm: %s\n",
717 errstr
? errstr
: "Unknown error");
721 librcm_alloc_handle
= (int (*)())dlsym(librcm_hdl
, "rcm_alloc_handle");
722 rarg
.rcm_offline
= (int (*)())dlsym(librcm_hdl
, "rcm_request_offline");
723 rarg
.rcm_online
= (int (*)())dlsym(librcm_hdl
, "rcm_notify_online");
724 rarg
.rcm_remove
= (int (*)())dlsym(librcm_hdl
, "rcm_notify_remove");
725 librcm_free_handle
= (int (*)())dlsym(librcm_hdl
, "rcm_free_handle");
727 if (librcm_alloc_handle
== NULL
||
728 rarg
.rcm_offline
== NULL
||
729 rarg
.rcm_online
== NULL
||
730 rarg
.rcm_remove
== NULL
||
731 librcm_free_handle
== NULL
) {
732 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: dlsym failed\n");
738 * Take a libdevinfo snapshot here because we cannot do so
739 * after device is retired. If device doesn't attach, we retire
740 * anyway i.e. it is not fatal.
742 rarg
.rcm_node
= di_init(devpath
, DINFOCPYALL
);
743 if (rarg
.rcm_node
== DI_NODE_NIL
) {
744 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: device doesn't attach, "
745 "retiring anyway: %s\n", devpath
);
748 rarg
.rcm_handle
= NULL
;
749 if (librcm_alloc_handle(NULL
, 0, NULL
, &rarg
.rcm_handle
)
752 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: failed to alloc "
753 "RCM handle. Returning RCM failure: %s\n", devpath
);
754 rarg
.rcm_handle
= NULL
;
758 rarg
.rcm_root
= devpath
;
762 * If device is already detached/nonexistent and cannot be
763 * attached, allow retire without checking device type.
765 * Else, check if retire is supported for this device type.
767 (void) snprintf(path
, sizeof (path
), "/devices%s", devpath
);
768 if (stat(path
, &sb
) == -1 || !S_ISDIR(sb
.st_mode
)) {
769 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: detached or nonexistent "
770 "device. Bypassing retire_supported: %s\n", devpath
);
771 } else if (!retire_supported(&rarg
)) {
772 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: retire not supported for "
773 "device type: %s\n", devpath
);
780 retval
= rcm_notify(&rarg
, &constraint
, &clen
);
781 if (retval
== RCM_FAILURE
) {
782 /* retire not permitted */
783 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: RCM constraints block "
784 "retire: %s\n", devpath
);
787 } else if (retval
== RCM_SUCCESS
) {
788 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: RCM constraints applied"
790 } else if (retval
== RCM_NO_CONSTRAINT
) {
791 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: No RCM constraints applied"
794 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: notify returned unknown "
795 "return code: %d: %s\n", retval
, devpath
);
800 if (modctl(MODRETIRE
, devpath
, constraint
, clen
) != 0) {
802 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: retire modctl() failed: "
803 "%s: %s\n", devpath
, strerror(retval
));
804 rcm_finalize(&rarg
, -1);
808 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: retire modctl() succeeded: %s\n",
811 rcm_finalize(&rarg
, 0);
817 (void) librcm_free_handle(rarg
.rcm_handle
);
819 RIO_ASSERT(dp
, rarg
.rcm_cons_nodes
== NULL
);
820 RIO_ASSERT(dp
, rarg
.rcm_rsrc_minors
== NULL
);
822 (void) dlclose(librcm_hdl
);
826 if (rarg
.rcm_node
!= DI_NODE_NIL
)
827 di_fini(rarg
.rcm_node
);
834 di_unretire_device(char *devpath
, di_retire_t
*dp
)
836 if (dp
== NULL
|| dp
->rt_debug
== NULL
|| dp
->rt_hdl
== NULL
)
839 if (devpath
== NULL
|| devpath
[0] == '\0') {
840 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: NULL devpath\n");
844 if (devpath
[0] != '/' || strlen(devpath
) >= PATH_MAX
||
845 strncmp(devpath
, "/devices/", strlen("/devices/")) == 0 ||
846 strstr(devpath
, "../devices/") || strrchr(devpath
, ':')) {
847 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: invalid devpath: %s\n",
852 if (modctl(MODUNRETIRE
, devpath
) != 0) {
854 dp
->rt_debug(dp
->rt_hdl
, "[ERROR]: unretire modctl() failed: "
855 "%s: %s\n", devpath
, strerror(err
));
859 dp
->rt_debug(dp
->rt_hdl
, "[INFO]: unretire modctl() done: %s\n",