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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <libdevinfo.h>
27 #include <sys/modctl.h>
28 #include <sys/stat.h>
29 #include <string.h>
30 #include <librcm.h>
31 #include <dlfcn.h>
32
33 #undef NDEBUG
34 #include <assert.h>
35
36 typedef struct rio_path {
37 char rpt_path[PATH_MAX];
38 struct rio_path *rpt_next;
39 } rio_path_t;
40
41 typedef struct rcm_arg {
42 char *rcm_root;
43 di_node_t rcm_node;
44 int rcm_supp;
45 rcm_handle_t *rcm_handle;
46 int rcm_retcode;
47 di_retire_t *rcm_dp;
48 rio_path_t *rcm_cons_nodes;
49 rio_path_t *rcm_rsrc_minors;
50 int (*rcm_offline)();
51 int (*rcm_online)();
52 int (*rcm_remove)();
53 } rcm_arg_t;
54
55 typedef struct selector {
56 char *sel_name;
57 int (*sel_selector)(di_node_t node, rcm_arg_t *rp);
58 } di_selector_t;
59
60 static void rio_assert(di_retire_t *dp, const char *EXstr, int line,
61 const char *file);
62
63 #define LIBRCM_PATH "/usr/lib/librcm.so"
64 #define RIO_ASSERT(d, x) \
65 {if (!(x)) rio_assert(d, #x, __LINE__, __FILE__); }
66
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);
71
72 di_selector_t supported_devices[] = {
73 {"disk", disk_select},
74 {"nexus", nexus_select},
75 {"enclosure", enclosure_select},
76 {"smp", smp_select},
77 {NULL, NULL}
78 };
79
80 void *
s_calloc(size_t nelem,size_t elsize,int fail)81 s_calloc(size_t nelem, size_t elsize, int fail)
82 {
83 if (fail) {
84 errno = ENOMEM;
85 return (NULL);
86 } else {
87 return (calloc(nelem, elsize));
88 }
89 }
90
91 static void
rio_assert(di_retire_t * dp,const char * EXstr,int line,const char * file)92 rio_assert(di_retire_t *dp, const char *EXstr, int line, const char *file)
93 {
94 char buf[PATH_MAX];
95
96 if (dp->rt_abort == NULL)
97 assert(0);
98
99 (void) snprintf(buf, sizeof (buf),
100 "Assertion failed: %s, file %s, line %d\n",
101 EXstr, file, line);
102 dp->rt_abort(dp->rt_hdl, buf);
103 }
104
105 /*ARGSUSED*/
106 static int
enclosure_minor(di_node_t node,di_minor_t minor,void * arg)107 enclosure_minor(di_node_t node, di_minor_t minor, void *arg)
108 {
109 rcm_arg_t *rp = (rcm_arg_t *)arg;
110 di_retire_t *dp = rp->rcm_dp;
111
112 rp->rcm_supp = 1;
113 dp->rt_debug(dp->rt_hdl, "[INFO]: enclosure_minor: "
114 "IDed this node as enclosure\n");
115 return (DI_WALK_TERMINATE);
116 }
117
118 static int
enclosure_select(di_node_t node,rcm_arg_t * rp)119 enclosure_select(di_node_t node, rcm_arg_t *rp)
120 {
121 rcm_arg_t rarg;
122 di_retire_t *dp = rp->rcm_dp;
123
124 rarg.rcm_dp = dp;
125
126 /*
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.
129 */
130 rarg.rcm_supp = 0;
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");
135 return (0);
136 }
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");
141 return (0);
142 }
143
144 return (rarg.rcm_supp);
145 }
146
147 /*ARGSUSED*/
148 static int
smp_minor(di_node_t node,di_minor_t minor,void * arg)149 smp_minor(di_node_t node, di_minor_t minor, void *arg)
150 {
151 rcm_arg_t *rp = (rcm_arg_t *)arg;
152 di_retire_t *dp = rp->rcm_dp;
153
154 rp->rcm_supp = 1;
155 dp->rt_debug(dp->rt_hdl, "[INFO]: smp_minor: "
156 "IDed this node as smp\n");
157 return (DI_WALK_TERMINATE);
158 }
159
160 static int
smp_select(di_node_t node,rcm_arg_t * rp)161 smp_select(di_node_t node, rcm_arg_t *rp)
162 {
163 rcm_arg_t rarg;
164 di_retire_t *dp = rp->rcm_dp;
165
166 rarg.rcm_dp = dp;
167
168 /*
169 * Check if this is an smp minor. If any one minor is DDI_NT_SMP
170 * we assume it is an smp.
171 */
172 rarg.rcm_supp = 0;
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");
176 return (0);
177 }
178
179 return (rarg.rcm_supp);
180 }
181
182 /*ARGSUSED*/
183 static int
disk_minor(di_node_t node,di_minor_t minor,void * arg)184 disk_minor(di_node_t node, di_minor_t minor, void *arg)
185 {
186 rcm_arg_t *rp = (rcm_arg_t *)arg;
187 di_retire_t *dp = rp->rcm_dp;
188
189 if (di_minor_spectype(minor) == S_IFBLK) {
190 rp->rcm_supp = 1;
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);
194 }
195
196 dp->rt_debug(dp->rt_hdl, "[INFO]: disk_minor: Not a disk minor. "
197 "Continuing minor walk\n");
198 return (DI_WALK_CONTINUE);
199 }
200
201 static int
disk_select(di_node_t node,rcm_arg_t * rp)202 disk_select(di_node_t node, rcm_arg_t *rp)
203 {
204 rcm_arg_t rarg;
205 di_retire_t *dp = rp->rcm_dp;
206
207 rarg.rcm_dp = dp;
208
209 /*
210 * Check if this is a disk minor. If any one minor is DDI_NT_BLOCK
211 * we assume it is a disk
212 */
213 rarg.rcm_supp = 0;
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");
217 return (0);
218 }
219
220 return (rarg.rcm_supp);
221 }
222
223 static int
nexus_select(di_node_t node,rcm_arg_t * rp)224 nexus_select(di_node_t node, rcm_arg_t *rp)
225 {
226 int select;
227 char *path;
228
229 di_retire_t *dp = rp->rcm_dp;
230
231 path = di_devfs_path(node);
232 if (path == NULL) {
233 dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: "
234 "di_devfs_path() is NULL. Returning NOTSUP\n");
235 return (0);
236 }
237
238 /*
239 * Check if it is a nexus
240 */
241 if (di_driver_ops(node) & DI_BUS_OPS) {
242 dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: is nexus %s\n",
243 path);
244 select = 1;
245 } else {
246 dp->rt_debug(dp->rt_hdl, "[INFO]: nexus_select: not nexus %s\n",
247 path);
248 select = 0;
249 }
250
251 di_devfs_path_free(path);
252
253 return (select);
254 }
255
256 static int
node_select(di_node_t node,void * arg)257 node_select(di_node_t node, void *arg)
258 {
259 rcm_arg_t *rp = (rcm_arg_t *)arg;
260 di_retire_t *dp;
261 int sel;
262 int i;
263 char *path;
264 uint_t state;
265
266 dp = rp->rcm_dp;
267
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",
274 path);
275 rp->rcm_supp = 0;
276 di_devfs_path_free(path);
277 return (DI_WALK_TERMINATE);
278 }
279 di_devfs_path_free(path);
280
281 /*
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
285 * it.
286 */
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);
293 }
294
295 sel = 0;
296 for (i = 0; supported_devices[i].sel_name != NULL; i++) {
297 sel = supported_devices[i].sel_selector(node, rp);
298 if (sel == 1) {
299 dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: "
300 "found supported device: %s\n",
301 supported_devices[i].sel_name);
302 break;
303 }
304 }
305
306 if (sel != 1) {
307 /*
308 * This node is not a supported device. Retire cannot proceed
309 */
310 dp->rt_debug(dp->rt_hdl, "[INFO]: node_select: found "
311 "unsupported device. Returning NOTSUP\n");
312 rp->rcm_supp = 0;
313 return (DI_WALK_TERMINATE);
314 }
315
316 /*
317 * This node is supported. Check other nodes in this subtree.
318 */
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);
322 }
323
324
325
326 /*
327 * when in doubt assume that retire is not supported for this device.
328 */
329 static int
retire_supported(rcm_arg_t * rp)330 retire_supported(rcm_arg_t *rp)
331 {
332 di_retire_t *dp;
333 di_node_t rnode = rp->rcm_node;
334
335 dp = rp->rcm_dp;
336
337 /*
338 * We should not be here if devinfo snapshot is NULL.
339 */
340 RIO_ASSERT(dp, rnode != DI_NODE_NIL);
341
342 /*
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
350 * not what we want.
351 */
352 rp->rcm_supp = 1;
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");
356 rp->rcm_supp = 0;
357 }
358
359 if (rp->rcm_supp) {
360 dp->rt_debug(dp->rt_hdl, "[INFO]: retire IS supported\n");
361 }
362
363 return (rp->rcm_supp);
364 }
365
366 static void
rcm_finalize(rcm_arg_t * rp,int retcode)367 rcm_finalize(rcm_arg_t *rp, int retcode)
368 {
369 rio_path_t *p;
370 rio_path_t *tmp;
371 int flags = RCM_RETIRE_NOTIFY;
372 int retval;
373 int error;
374 di_retire_t *dp;
375
376 dp = rp->rcm_dp;
377
378 RIO_ASSERT(dp, retcode == 0 || retcode == -1);
379
380 dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: retcode=%d: dev=%s\n",
381 retcode, rp->rcm_root);
382
383 for (p = rp->rcm_cons_nodes; p; ) {
384 tmp = p;
385 p = tmp->rpt_next;
386 free(tmp);
387 }
388 rp->rcm_cons_nodes = NULL;
389
390 dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_finalize: cons_nodes NULL\n");
391
392 for (p = rp->rcm_rsrc_minors; p; ) {
393 tmp = p;
394 p = tmp->rpt_next;
395 if (retcode == 0) {
396 retval = rp->rcm_remove(rp->rcm_handle,
397 tmp->rpt_path, flags, NULL);
398 error = errno;
399 } else {
400 RIO_ASSERT(dp, retcode == -1);
401 retval = rp->rcm_online(rp->rcm_handle,
402 tmp->rpt_path, flags, NULL);
403 error = errno;
404 }
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);
410 } else {
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);
414 }
415 free(tmp);
416 }
417 rp->rcm_rsrc_minors = NULL;
418 }
419 /*ARGSUSED*/
420 static int
call_offline(di_node_t node,di_minor_t minor,void * arg)421 call_offline(di_node_t node, di_minor_t minor, void *arg)
422 {
423 rcm_arg_t *rp = (rcm_arg_t *)arg;
424 di_retire_t *dp = rp->rcm_dp;
425 char *mnp;
426 rio_path_t *rpt;
427 int retval;
428
429 mnp = di_devfs_minor_path(minor);
430 if (mnp == NULL) {
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);
435 }
436
437 rpt = s_calloc(1, sizeof (rio_path_t), 0);
438 if (rpt == NULL) {
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);
444 }
445
446 (void) snprintf(rpt->rpt_path, sizeof (rpt->rpt_path),
447 "/devices%s", mnp);
448
449 di_devfs_path_free(mnp);
450
451 retval = rp->rcm_offline(rp->rcm_handle, rpt->rpt_path,
452 RCM_RETIRE_REQUEST, NULL);
453
454 rpt->rpt_next = rp->rcm_rsrc_minors;
455 rp->rcm_rsrc_minors = rpt;
456
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);
471 } else {
472 dp->rt_debug(dp->rt_hdl, "[INFO]: RCM OFFLINE returned "
473 "RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path);
474 }
475
476 return (DI_WALK_CONTINUE);
477 }
478
479 static int
offline_one(di_node_t node,void * arg)480 offline_one(di_node_t node, void *arg)
481 {
482 rcm_arg_t *rp = (rcm_arg_t *)arg;
483 rio_path_t *rpt;
484 di_retire_t *dp = rp->rcm_dp;
485 char *path;
486
487 /*
488 * We should already have terminated the walk
489 * in case of failure
490 */
491 RIO_ASSERT(dp, rp->rcm_retcode == RCM_SUCCESS ||
492 rp->rcm_retcode == RCM_NO_CONSTRAINT);
493
494 dp->rt_debug(dp->rt_hdl, "[INFO]: offline_one: entered\n");
495
496 rp->rcm_retcode = RCM_NO_CONSTRAINT;
497
498 rpt = s_calloc(1, sizeof (rio_path_t), 0);
499 if (rpt == NULL) {
500 dp->rt_debug(dp->rt_hdl, "[ERROR]: rio_path_t calloc "
501 "failed: error: %s\n", strerror(errno));
502 goto fail;
503 }
504
505 path = di_devfs_path(node);
506 if (path == NULL) {
507 dp->rt_debug(dp->rt_hdl, "[ERROR]: di_devfs_path "
508 "failed: error: %s\n", strerror(errno));
509 free(rpt);
510 goto fail;
511 }
512
513 (void) strlcpy(rpt->rpt_path, path, sizeof (rpt->rpt_path));
514
515 di_devfs_path_free(path);
516
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);
520 free(rpt);
521 goto fail;
522 }
523
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);
527 free(rpt);
528 goto fail;
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);
538 free(rpt);
539 goto fail;
540 } else {
541 dp->rt_debug(dp->rt_hdl, "[INFO]: di_walk_minor "
542 "returned: RCM_NO_CONSTRAINT: %s\n", rpt->rpt_path);
543 free(rpt);
544 }
545
546 /*
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
551 * apply constraints
552 * set retcode to RCM_SUCCESS to show that at least 1 node
553 * completely walked
554 */
555 rp->rcm_retcode = RCM_SUCCESS;
556 return (DI_WALK_CONTINUE);
557
558 fail:
559 rp->rcm_retcode = RCM_FAILURE;
560 return (DI_WALK_TERMINATE);
561 }
562
563 /*
564 * Returns:
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
568 *
569 * RCM_FAILURE: Either RCM constraints prevent a retire or
570 * an error occurred
571 */
572 static int
rcm_notify(rcm_arg_t * rp,char ** pp,size_t * clen)573 rcm_notify(rcm_arg_t *rp, char **pp, size_t *clen)
574 {
575 size_t len;
576 rio_path_t *p;
577 rio_path_t *tmp;
578 char *plistp;
579 char *s;
580 di_retire_t *dp;
581 di_node_t rnode;
582
583 dp = rp->rcm_dp;
584
585 dp->rt_debug(dp->rt_hdl, "[INFO]: rcm_notify() entered\n");
586
587 RIO_ASSERT(dp, rp->rcm_root);
588
589 *pp = NULL;
590
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);
596 }
597
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;
607 goto out;
608 }
609
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);
614 goto out;
615 }
616
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",
620 rp->rcm_root);
621 } else {
622 dp->rt_debug(dp->rt_hdl, "[INFO]: walk_node: RCM_SUCCESS\n");
623 }
624
625 /*
626 * Convert to a sequence of NUL separated strings terminated by '\0'\0'
627 */
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);
632 }
633 len++; /* list terminating '\0' */
634
635 dp->rt_debug(dp->rt_hdl, "[INFO]: len of constraint str = %lu\n", len);
636
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),
641 rp->rcm_root);
642 rcm_finalize(rp, -1);
643 rp->rcm_retcode = RCM_FAILURE;
644 goto out;
645 }
646
647 for (s = plistp, p = rp->rcm_cons_nodes; p; ) {
648 tmp = p;
649 p = tmp->rpt_next;
650 (void) strcpy(s, tmp->rpt_path);
651 s += strlen(s) + 1;
652 RIO_ASSERT(dp, s - plistp < len);
653 free(tmp);
654 }
655 rp->rcm_cons_nodes = NULL;
656 RIO_ASSERT(dp, s - plistp == len - 1);
657 *s = '\0';
658
659 dp->rt_debug(dp->rt_hdl, "[INFO]: constraint str = %p\n", plistp);
660
661 *pp = plistp;
662 *clen = len;
663
664 rp->rcm_retcode = RCM_SUCCESS;
665 out:
666 return (rp->rcm_retcode);
667 }
668
669
670 /*ARGSUSED*/
671 int
di_retire_device(char * devpath,di_retire_t * dp,int flags)672 di_retire_device(char *devpath, di_retire_t *dp, int flags)
673 {
674 char path[PATH_MAX];
675 struct stat sb;
676 int retval = EINVAL;
677 char *constraint = NULL;
678 size_t clen;
679 void *librcm_hdl;
680 rcm_arg_t rarg = {0};
681 int (*librcm_alloc_handle)();
682 int (*librcm_free_handle)();
683
684 if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL)
685 return (EINVAL);
686
687 if (devpath == NULL || devpath[0] == '\0') {
688 dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL argument(s)\n");
689 return (EINVAL);
690 }
691
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",
696 devpath);
697 return (EINVAL);
698 }
699
700 if (flags != 0) {
701 dp->rt_debug(dp->rt_hdl, "[ERROR]: flags should be 0: %d\n",
702 flags);
703 return (EINVAL);
704 }
705
706 /*
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
711 * mounted.
712 */
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");
718 return (ENOSYS);
719 }
720
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");
726
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");
733 retval = ENOSYS;
734 goto out;
735 }
736
737 /*
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.
741 */
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);
746 }
747
748 rarg.rcm_handle = NULL;
749 if (librcm_alloc_handle(NULL, 0, NULL, &rarg.rcm_handle)
750 != RCM_SUCCESS) {
751 retval = errno;
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;
755 goto out;
756 }
757
758 rarg.rcm_root = devpath;
759 rarg.rcm_dp = dp;
760
761 /*
762 * If device is already detached/nonexistent and cannot be
763 * attached, allow retire without checking device type.
764 * XXX
765 * Else, check if retire is supported for this device type.
766 */
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);
774 retval = ENOTSUP;
775 goto out;
776 }
777
778 clen = 0;
779 constraint = NULL;
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);
785 retval = EBUSY;
786 goto out;
787 } else if (retval == RCM_SUCCESS) {
788 dp->rt_debug(dp->rt_hdl, "[INFO]: RCM constraints applied"
789 ": %s\n", devpath);
790 } else if (retval == RCM_NO_CONSTRAINT) {
791 dp->rt_debug(dp->rt_hdl, "[INFO]: No RCM constraints applied"
792 ": %s\n", devpath);
793 } else {
794 dp->rt_debug(dp->rt_hdl, "[ERROR]: notify returned unknown "
795 "return code: %d: %s\n", retval, devpath);
796 retval = ESRCH;
797 goto out;
798 }
799
800 if (modctl(MODRETIRE, devpath, constraint, clen) != 0) {
801 retval = errno;
802 dp->rt_debug(dp->rt_hdl, "[ERROR]: retire modctl() failed: "
803 "%s: %s\n", devpath, strerror(retval));
804 rcm_finalize(&rarg, -1);
805 goto out;
806 }
807
808 dp->rt_debug(dp->rt_hdl, "[INFO]: retire modctl() succeeded: %s\n",
809 devpath);
810
811 rcm_finalize(&rarg, 0);
812
813 retval = 0;
814
815 out:
816 if (rarg.rcm_handle)
817 (void) librcm_free_handle(rarg.rcm_handle);
818
819 RIO_ASSERT(dp, rarg.rcm_cons_nodes == NULL);
820 RIO_ASSERT(dp, rarg.rcm_rsrc_minors == NULL);
821
822 (void) dlclose(librcm_hdl);
823
824 free(constraint);
825
826 if (rarg.rcm_node != DI_NODE_NIL)
827 di_fini(rarg.rcm_node);
828
829 return (retval);
830 }
831
832 /*ARGSUSED*/
833 int
di_unretire_device(char * devpath,di_retire_t * dp)834 di_unretire_device(char *devpath, di_retire_t *dp)
835 {
836 if (dp == NULL || dp->rt_debug == NULL || dp->rt_hdl == NULL)
837 return (EINVAL);
838
839 if (devpath == NULL || devpath[0] == '\0') {
840 dp->rt_debug(dp->rt_hdl, "[ERROR]: NULL devpath\n");
841 return (EINVAL);
842 }
843
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",
848 devpath);
849 return (EINVAL);
850 }
851
852 if (modctl(MODUNRETIRE, devpath) != 0) {
853 int err = errno;
854 dp->rt_debug(dp->rt_hdl, "[ERROR]: unretire modctl() failed: "
855 "%s: %s\n", devpath, strerror(err));
856 return (err);
857 }
858
859 dp->rt_debug(dp->rt_hdl, "[INFO]: unretire modctl() done: %s\n",
860 devpath);
861
862 return (0);
863 }
864