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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 #include "cfga_fp.h" 28 29 static fpcfga_ret_t fp_rcm_init(char *, cfga_flags_t, char **, uint_t *, 30 char **rsrc_fixed); 31 static int fp_rcm_process_node(di_node_t, void *); 32 static fpcfga_ret_t fp_rcm_info_table(rcm_info_t *, char **); 33 static char *chop_minor(char *); 34 35 #define MAX_FORMAT 80 36 #define DEVICES "/devices" 37 38 typedef struct { 39 char *bus_path; 40 char *filter; 41 char **errstring; 42 fpcfga_ret_t ret; 43 cfga_flags_t flags; 44 fpcfga_ret_t (*func)(char *, char *, char **, cfga_flags_t); 45 } walkargs_t; 46 47 static fpcfga_ret_t fp_rcm_info_table(rcm_info_t *, char **); 48 static int fp_rcm_process_node(di_node_t, void *); 49 static fpcfga_ret_t fp_rcm_init(char *, cfga_flags_t, char **, uint_t *, 50 char **); 51 static char *chop_minor(char *); 52 53 static rcm_handle_t *rcm_handle = NULL; 54 static mutex_t rcm_handle_lock; 55 56 /* 57 * fp_rcm_offline() 58 * 59 * Offline FP resource consumers. 60 */ 61 fpcfga_ret_t 62 fp_rcm_offline(char *rsrc, char **errstring, cfga_flags_t flags) 63 { 64 int rret; 65 uint_t rflags = 0; 66 char *rsrc_fixed; 67 rcm_info_t *rinfo = NULL; 68 fpcfga_ret_t ret = FPCFGA_OK; 69 70 if ((ret = fp_rcm_init(rsrc, flags, errstring, &rflags, &rsrc_fixed)) 71 != FPCFGA_OK) 72 return (ret); 73 74 if ((rret = rcm_request_offline(rcm_handle, rsrc_fixed, rflags, &rinfo)) 75 != RCM_SUCCESS) { 76 cfga_err(errstring, 0, ERRARG_RCM_OFFLINE, rsrc_fixed, 0); 77 if (rinfo) { 78 (void) fp_rcm_info_table(rinfo, errstring); 79 rcm_free_info(rinfo); 80 } 81 if (rret == RCM_FAILURE) 82 (void) fp_rcm_online(rsrc, errstring, flags); 83 ret = FPCFGA_BUSY; 84 } 85 86 S_FREE(rsrc_fixed); 87 88 return (ret); 89 } 90 91 /* 92 * fp_rcm_online() 93 * 94 * Online FP resource consumers that were previously offlined. 95 */ 96 fpcfga_ret_t 97 fp_rcm_online(char *rsrc, char **errstring, cfga_flags_t flags) 98 { 99 char *rsrc_fixed; 100 rcm_info_t *rinfo = NULL; 101 fpcfga_ret_t ret = FPCFGA_OK; 102 103 if ((ret = fp_rcm_init(rsrc, flags, errstring, NULL, &rsrc_fixed)) 104 != FPCFGA_OK) 105 return (ret); 106 107 if (rcm_notify_online(rcm_handle, rsrc_fixed, 0, &rinfo) 108 != RCM_SUCCESS && rinfo != NULL) { 109 cfga_err(errstring, 0, ERRARG_RCM_ONLINE, rsrc_fixed, 0); 110 (void) fp_rcm_info_table(rinfo, errstring); 111 rcm_free_info(rinfo); 112 ret = FPCFGA_ERR; 113 } 114 115 S_FREE(rsrc_fixed); 116 117 return (ret); 118 } 119 120 /* 121 * fp_rcm_remove() 122 * 123 * Remove FP resource consumers after their kernel removal. 124 */ 125 fpcfga_ret_t 126 fp_rcm_remove(char *rsrc, char **errstring, cfga_flags_t flags) 127 { 128 char *rsrc_fixed; 129 rcm_info_t *rinfo = NULL; 130 fpcfga_ret_t ret = FPCFGA_OK; 131 132 if ((ret = fp_rcm_init(rsrc, flags, errstring, NULL, &rsrc_fixed)) 133 != FPCFGA_OK) 134 return (ret); 135 136 if (rcm_notify_remove(rcm_handle, rsrc_fixed, 0, &rinfo) 137 != RCM_SUCCESS) { 138 cfga_err(errstring, 0, ERRARG_RCM_REMOVE, rsrc_fixed, 0); 139 if (rinfo) { 140 (void) fp_rcm_info_table(rinfo, errstring); 141 rcm_free_info(rinfo); 142 } 143 ret = FPCFGA_ERR; 144 } 145 146 S_FREE(rsrc_fixed); 147 148 return (ret); 149 } 150 151 /* 152 * fp_rcm_suspend() 153 * 154 * Suspend FP resource consumers before a bus quiesce. 155 */ 156 fpcfga_ret_t 157 fp_rcm_suspend(char *rsrc, char *filter, char **errstring, cfga_flags_t flags) 158 { 159 int rret; 160 uint_t rflags = 0; 161 char *rsrc_fixed; 162 char *filter_fixed; 163 char *rsrc_devpath; 164 rcm_info_t *rinfo = NULL; 165 di_node_t node; 166 fpcfga_ret_t ret = FPCFGA_OK; 167 walkargs_t walkargs; 168 timespec_t zerotime = { 0, 0 }; 169 170 if ((ret = fp_rcm_init(rsrc, flags, errstring, &rflags, &rsrc_fixed)) 171 != FPCFGA_OK) 172 return (ret); 173 174 /* If a filter is provided, ensure that it makes sense */ 175 if (filter != NULL && strstr(filter, rsrc) != filter) { 176 S_FREE(rsrc_fixed); 177 cfga_err(errstring, 0, ERR_APID_INVAL, 0); 178 return (FPCFGA_ERR); 179 } 180 181 /* 182 * If no filter is specified: attempt a suspension on the resource, 183 * directly. 184 */ 185 if (filter == NULL) { 186 if ((rret = rcm_request_suspend(rcm_handle, rsrc_fixed, rflags, 187 &zerotime, &rinfo)) != RCM_SUCCESS) { 188 cfga_err(errstring, 0, ERRARG_RCM_SUSPEND, rsrc_fixed, 189 0); 190 if (rinfo) { 191 (void) fp_rcm_info_table(rinfo, errstring); 192 rcm_free_info(rinfo); 193 } 194 if (rret == RCM_FAILURE) 195 (void) fp_rcm_resume(rsrc, filter, errstring, 196 (flags & (~CFGA_FLAG_FORCE))); 197 ret = FPCFGA_BUSY; 198 } 199 S_FREE(rsrc_fixed); 200 return (ret); 201 } 202 203 /* 204 * If a filter is specified: open the resource with libdevinfo, walk 205 * through its nodes, and attempt a suspension of each node that 206 * mismatches the filter. 207 */ 208 209 /* Chop off the filter's minor name */ 210 if ((filter_fixed = chop_minor(filter)) == NULL) 211 return (FPCFGA_ERR); 212 213 /* get a libdevinfo snapshot of the resource's subtree */ 214 rsrc_devpath = rsrc_fixed; 215 if (strstr(rsrc_fixed, DEVICES) != NULL) 216 rsrc_devpath += strlen(DEVICES); 217 node = di_init(rsrc_devpath, DINFOSUBTREE | DINFOMINOR); 218 if (node == DI_NODE_NIL) { 219 cfga_err(errstring, 0, ERRARG_DEVINFO, rsrc_fixed, 0); 220 ret = FPCFGA_ERR; 221 } 222 223 /* apply the filter, and suspend all resources not filtered out */ 224 if (ret == FPCFGA_OK) { 225 226 walkargs.bus_path = rsrc_fixed; 227 walkargs.filter = filter_fixed; 228 walkargs.errstring = errstring; 229 walkargs.ret = FPCFGA_OK; 230 walkargs.flags = rflags; 231 walkargs.func = fp_rcm_suspend; 232 233 if (di_walk_node(node, 0, &walkargs, fp_rcm_process_node) < 0) 234 cfga_err(errstring, 0, ERRARG_DEVINFO, rsrc_fixed, 0); 235 236 ret = walkargs.ret; 237 } 238 239 if (node != DI_NODE_NIL) 240 di_fini(node); 241 242 S_FREE(rsrc_fixed); 243 S_FREE(filter_fixed); 244 245 if (ret != FPCFGA_OK) 246 (void) fp_rcm_resume(rsrc, filter, errstring, 247 (flags & (~CFGA_FLAG_FORCE))); 248 249 return (ret); 250 } 251 252 /* 253 * fp_rcm_resume() 254 * 255 * Resume FP resource consumers after a bus has been unquiesced. 256 */ 257 fpcfga_ret_t 258 fp_rcm_resume(char *rsrc, char *filter, char **errstring, cfga_flags_t flags) 259 { 260 uint_t rflags = 0; 261 char *rsrc_fixed; 262 char *filter_fixed; 263 char *rsrc_devpath; 264 rcm_info_t *rinfo = NULL; 265 di_node_t node; 266 fpcfga_ret_t ret = FPCFGA_OK; 267 walkargs_t walkargs; 268 269 if ((ret = fp_rcm_init(rsrc, flags, errstring, &rflags, &rsrc_fixed)) 270 != FPCFGA_OK) 271 return (ret); 272 273 /* If a filter is provided, ensure that it makes sense */ 274 if (filter != NULL && strstr(filter, rsrc) != filter) { 275 S_FREE(rsrc_fixed); 276 cfga_err(errstring, 0, ERR_APID_INVAL, 0); 277 return (FPCFGA_ERR); 278 } 279 280 /* 281 * If no filter is specified: resume the resource directly. 282 */ 283 if (filter == NULL) { 284 if (rcm_notify_resume(rcm_handle, rsrc_fixed, rflags, &rinfo) 285 != RCM_SUCCESS && rinfo != NULL) { 286 cfga_err(errstring, 0, ERRARG_RCM_RESUME, rsrc_fixed, 287 0); 288 (void) fp_rcm_info_table(rinfo, errstring); 289 rcm_free_info(rinfo); 290 ret = FPCFGA_BUSY; 291 } 292 S_FREE(rsrc_fixed); 293 return (ret); 294 } 295 296 /* 297 * If a filter is specified: open the resource with libdevinfo, walk 298 * through its nodes, and resume each of its nodes that mismatches 299 * the filter. 300 */ 301 302 /* Chop off the filter's minor name */ 303 if ((filter_fixed = chop_minor(filter)) == NULL) 304 return (FPCFGA_ERR); 305 306 /* get a libdevinfo snapshot of the resource's subtree */ 307 rsrc_devpath = rsrc_fixed; 308 if (strstr(rsrc_fixed, DEVICES) != NULL) 309 rsrc_devpath += strlen(DEVICES); 310 node = di_init(rsrc_devpath, DINFOSUBTREE | DINFOMINOR); 311 if (node == DI_NODE_NIL) { 312 cfga_err(errstring, 0, ERRARG_DEVINFO, rsrc_fixed, 0); 313 ret = FPCFGA_ERR; 314 } 315 316 /* apply the filter, and resume all resources not filtered out */ 317 if (ret == FPCFGA_OK) { 318 319 walkargs.bus_path = rsrc_fixed; 320 walkargs.filter = filter_fixed; 321 walkargs.errstring = errstring; 322 walkargs.ret = FPCFGA_OK; 323 walkargs.flags = rflags; 324 walkargs.func = fp_rcm_resume; 325 326 if (di_walk_node(node, 0, &walkargs, fp_rcm_process_node) < 0) 327 cfga_err(errstring, 0, ERRARG_DEVINFO, rsrc_fixed, 0); 328 329 ret = walkargs.ret; 330 } 331 332 if (node != DI_NODE_NIL) 333 di_fini(node); 334 335 S_FREE(rsrc_fixed); 336 S_FREE(filter_fixed); 337 338 return (ret); 339 } 340 341 /* 342 * fp_rcm_info 343 * 344 * Queries RCM information for resources, and formats it into a table. 345 * The table is appended to the info argument. If the info argument is a 346 * null pointer, then a new string is malloc'ed. If the info argument is 347 * not a null pointer, then it is realloc'ed to the required size. 348 */ 349 fpcfga_ret_t 350 fp_rcm_info(char *rsrc, char **errstring, char **info) 351 { 352 char *rsrc_fixed; 353 rcm_info_t *rinfo = NULL; 354 fpcfga_ret_t ret = FPCFGA_OK; 355 356 if ((ret = fp_rcm_init(rsrc, 0, errstring, NULL, &rsrc_fixed)) 357 != FPCFGA_OK) 358 return (ret); 359 360 if (info == NULL) { 361 S_FREE(rsrc_fixed); 362 return (FPCFGA_ERR); 363 } 364 365 if (rcm_get_info(rcm_handle, rsrc_fixed, 0, &rinfo) 366 != RCM_SUCCESS) { 367 cfga_err(errstring, 0, ERRARG_RCM_INFO, rsrc_fixed, 0); 368 ret = FPCFGA_ERR; 369 } else if (rinfo == NULL) 370 ret = FPCFGA_OK; 371 372 if (rinfo) { 373 if ((ret = fp_rcm_info_table(rinfo, info)) != FPCFGA_OK) 374 cfga_err(errstring, 0, ERRARG_RCM_INFO, rsrc_fixed, 0); 375 rcm_free_info(rinfo); 376 } 377 378 S_FREE(rsrc_fixed); 379 380 return (ret); 381 } 382 383 /* 384 * fp_rcm_init() 385 * 386 * Contains common initialization code for entering a fp_rcm_xx() 387 * routine. 388 */ 389 static fpcfga_ret_t 390 fp_rcm_init(char *rsrc, cfga_flags_t flags, char **errstring, uint_t *rflags, 391 char **rsrc_fixed) 392 { 393 /* Validate the rsrc argument */ 394 if (rsrc == NULL) { 395 cfga_err(errstring, 0, ERR_APID_INVAL, 0); 396 return (FPCFGA_ERR); 397 } 398 399 /* Translate the cfgadm flags to RCM flags */ 400 if (rflags && (flags & CFGA_FLAG_FORCE)) 401 *rflags |= RCM_FORCE; 402 403 /* Get a handle for the RCM operations */ 404 (void) mutex_lock(&rcm_handle_lock); 405 if (rcm_handle == NULL) { 406 if (rcm_alloc_handle(NULL, RCM_NOPID, NULL, &rcm_handle) != 407 RCM_SUCCESS) { 408 cfga_err(errstring, 0, ERR_RCM_HANDLE, 0); 409 (void) mutex_unlock(&rcm_handle_lock); 410 return (FPCFGA_LIB_ERR); 411 } 412 } 413 (void) mutex_unlock(&rcm_handle_lock); 414 415 /* Chop off the rsrc's minor, if it has one */ 416 if ((*rsrc_fixed = chop_minor(rsrc)) == NULL) 417 return (FPCFGA_ERR); 418 419 return (FPCFGA_OK); 420 } 421 422 /* 423 * fp_rcm_process_node 424 * 425 * Helper routine for fp_rcm_{suspend,resume}. This is a di_walk_node() 426 * callback that will apply a filter to every node it sees, and either suspend 427 * or resume it if it doesn't match the filter. 428 */ 429 static int 430 fp_rcm_process_node(di_node_t node, void *argp) 431 { 432 char *devfs_path; 433 walkargs_t *walkargs; 434 fpcfga_ret_t ret = FPCFGA_OK; 435 char disk_path[MAXPATHLEN]; 436 437 /* Guard against bad arguments */ 438 if ((walkargs = (walkargs_t *)argp) == NULL) 439 return (DI_WALK_TERMINATE); 440 if (walkargs->filter == NULL || walkargs->errstring == NULL) { 441 walkargs->ret = FPCFGA_ERR; 442 return (DI_WALK_TERMINATE); 443 } 444 445 /* If the node has no minors, then skip it */ 446 if (di_minor_next(node, DI_MINOR_NIL) == DI_MINOR_NIL) 447 return (DI_WALK_CONTINUE); 448 449 /* Construct the devices path */ 450 if ((devfs_path = di_devfs_path(node)) == NULL) 451 return (DI_WALK_CONTINUE); 452 (void) snprintf(disk_path, MAXPATHLEN, "%s%s", DEVICES, devfs_path); 453 di_devfs_path_free(devfs_path); 454 455 /* 456 * If the node does not correspond to the targeted FP bus or the 457 * disk being filtered out, then use the appropriate suspend/resume 458 * function. 459 */ 460 if (strcmp(disk_path, walkargs->bus_path) != 0 && 461 strcmp(disk_path, walkargs->filter) != 0) 462 ret = (*walkargs->func)(disk_path, NULL, walkargs->errstring, 463 walkargs->flags); 464 465 /* Stop the walk early if the above operation failed */ 466 if (ret != FPCFGA_OK) { 467 walkargs->ret = ret; 468 return (DI_WALK_TERMINATE); 469 } 470 471 return (DI_WALK_CONTINUE); 472 } 473 474 /* 475 * fp_rcm_info_table 476 * 477 * Takes an opaque rcm_info_t pointer and a character pointer, and appends 478 * the rcm_info_t data in the form of a table to the given character pointer. 479 */ 480 static fpcfga_ret_t 481 fp_rcm_info_table(rcm_info_t *rinfo, char **table) 482 { 483 int i; 484 size_t w; 485 size_t width = 0; 486 size_t w_rsrc = 0; 487 size_t w_info = 0; 488 size_t table_size = 0; 489 uint_t tuples = 0; 490 rcm_info_tuple_t *tuple = NULL; 491 char *rsrc; 492 char *info; 493 char *newtable; 494 static char format[MAX_FORMAT]; 495 const char *info_info_str, *info_rsrc_str; 496 497 /* Protect against invalid arguments */ 498 if (rinfo == NULL || table == NULL) 499 return (FPCFGA_ERR); 500 501 /* Set localized table header strings */ 502 rsrc = gettext("Resource"); 503 info = gettext("Information"); 504 505 /* A first pass, to size up the RCM information */ 506 while (tuple = rcm_info_next(rinfo, tuple)) { 507 info_info_str = rcm_info_info(tuple); 508 info_rsrc_str = rcm_info_rsrc(tuple); 509 if ((info_info_str != NULL) && (info_rsrc_str != NULL)) { 510 tuples++; 511 if ((w = strlen(info_rsrc_str)) > w_rsrc) 512 w_rsrc = w; 513 if ((w = strlen(info_info_str)) > w_info) 514 w_info = w; 515 } 516 } 517 518 /* If nothing was sized up above, stop early */ 519 if (tuples == 0) 520 return (FPCFGA_OK); 521 522 /* Adjust column widths for column headings */ 523 if ((w = strlen(rsrc)) > w_rsrc) 524 w_rsrc = w; 525 else if ((w_rsrc - w) % 2) 526 w_rsrc++; 527 if ((w = strlen(info)) > w_info) 528 w_info = w; 529 else if ((w_info - w) % 2) 530 w_info++; 531 532 /* 533 * Compute the total line width of each line, 534 * accounting for intercolumn spacing. 535 */ 536 width = w_info + w_rsrc + 4; 537 538 /* Allocate space for the table */ 539 table_size = (2 + tuples) * (width + 1) + 2; 540 if (*table == NULL) 541 *table = malloc(table_size); 542 else { 543 newtable = realloc(*table, strlen(*table) + table_size); 544 if (newtable != NULL) 545 *table = newtable; 546 } 547 if (*table == NULL) 548 return (FPCFGA_ERR); 549 550 /* Place a table header into the string */ 551 552 /* The resource header */ 553 (void) strcat(*table, "\n"); 554 w = strlen(rsrc); 555 for (i = 0; i < ((w_rsrc - w) / 2); i++) 556 (void) strcat(*table, " "); 557 (void) strcat(*table, rsrc); 558 for (i = 0; i < ((w_rsrc - w) / 2); i++) 559 (void) strcat(*table, " "); 560 561 /* The information header */ 562 (void) strcat(*table, " "); 563 w = strlen(info); 564 for (i = 0; i < ((w_info - w) / 2); i++) 565 (void) strcat(*table, " "); 566 (void) strcat(*table, info); 567 for (i = 0; i < ((w_info - w) / 2); i++) 568 (void) strcat(*table, " "); 569 570 /* Underline the headers */ 571 (void) strcat(*table, "\n"); 572 for (i = 0; i < w_rsrc; i++) 573 (void) strcat(*table, "-"); 574 (void) strcat(*table, " "); 575 for (i = 0; i < w_info; i++) 576 (void) strcat(*table, "-"); 577 578 /* Construct the format string */ 579 (void) snprintf(format, MAX_FORMAT, "%%-%ds %%-%ds", w_rsrc, w_info); 580 581 /* Add the tuples to the table string */ 582 tuple = NULL; 583 while ((tuple = rcm_info_next(rinfo, tuple)) != NULL) { 584 info_info_str = rcm_info_info(tuple); 585 info_rsrc_str = rcm_info_rsrc(tuple); 586 if ((info_info_str != NULL) && (info_rsrc_str != NULL)) { 587 (void) strcat(*table, "\n"); 588 (void) sprintf(&((*table)[strlen(*table)]), 589 format, info_rsrc_str, info_info_str); 590 } 591 } 592 593 return (FPCFGA_OK); 594 } 595 596 /* 597 * chop_minor() 598 * 599 * Chops off the minor name portion of a resource. Allocates storage for 600 * the returned string. Caller must free the storage if return is non-NULL. 601 */ 602 static char * 603 chop_minor(char *rsrc) 604 { 605 char *rsrc_fixed; 606 char *cp; 607 608 if ((rsrc_fixed = strdup(rsrc)) == NULL) 609 return (NULL); 610 if ((cp = strrchr(rsrc_fixed, ':')) != NULL) 611 *cp = '\0'; 612 return (rsrc_fixed); 613 } 614