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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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