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