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 2008 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 /* 30 * RCM backend for the DR Daemon 31 */ 32 33 #include <unistd.h> 34 #include <strings.h> 35 #include <errno.h> 36 #include <kstat.h> 37 #include <libnvpair.h> 38 #include <librcm.h> 39 #include <locale.h> 40 41 #include "drd.h" 42 43 /* 44 * RCM Backend Support 45 */ 46 static int drd_rcm_init(void); 47 static int drd_rcm_fini(void); 48 static int drd_rcm_cpu_config_request(drctl_rsrc_t *rsrcs, int nrsrc); 49 static int drd_rcm_cpu_config_notify(drctl_rsrc_t *rsrcs, int nrsrc); 50 static int drd_rcm_cpu_unconfig_request(drctl_rsrc_t *rsrcs, int nrsrc); 51 static int drd_rcm_cpu_unconfig_notify(drctl_rsrc_t *rsrcs, int nrsrc); 52 static int drd_rcm_io_config_request(drctl_rsrc_t *rsrc, int nrsrc); 53 static int drd_rcm_io_config_notify(drctl_rsrc_t *rsrc, int nrsrc); 54 static int drd_rcm_io_unconfig_request(drctl_rsrc_t *rsrc, int nrsrc); 55 static int drd_rcm_io_unconfig_notify(drctl_rsrc_t *rsrc, int nrsrc); 56 57 drd_backend_t drd_rcm_backend = { 58 drd_rcm_init, /* init */ 59 drd_rcm_fini, /* fini */ 60 drd_rcm_cpu_config_request, /* cpu_config_request */ 61 drd_rcm_cpu_config_notify, /* cpu_config_notify */ 62 drd_rcm_cpu_unconfig_request, /* cpu_unconfig_request */ 63 drd_rcm_cpu_unconfig_notify, /* cpu_unconfig_notify */ 64 drd_rcm_io_config_request, /* io_config_request */ 65 drd_rcm_io_config_notify, /* io_config_notify */ 66 drd_rcm_io_unconfig_request, /* io_unconfig_request */ 67 drd_rcm_io_unconfig_notify /* io_unconfig_notify */ 68 }; 69 70 #define RCM_CPU_ALL "SUNW_cpu" 71 #define RCM_CPU RCM_CPU_ALL"/cpu" 72 #define RCM_CPU_MAX_LEN (32) 73 74 /* global RCM handle used in all RCM operations */ 75 static rcm_handle_t *rcm_hdl; 76 77 /* functions that call into RCM */ 78 static int drd_rcm_online_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc); 79 static int drd_rcm_add_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc); 80 static int drd_rcm_del_cpu_request(drctl_rsrc_t *rsrcs, int nrsrc); 81 static int drd_rcm_offline_cpu_request(drctl_rsrc_t *rsrcs, int nrsrc); 82 static int drd_rcm_remove_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc); 83 static int drd_rcm_restore_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc); 84 static int drd_rcm_del_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc); 85 86 /* utility functions */ 87 static char **drd_rcm_cpu_rlist_init(drctl_rsrc_t *, int nrsrc, int status); 88 static void drd_rcm_cpu_rlist_fini(char **rlist); 89 static drctl_rsrc_t *cpu_rsrcstr_to_rsrc(const char *, drctl_rsrc_t *, int); 90 static int get_sys_cpuids(cpuid_t **cpuids, int *ncpuids); 91 static boolean_t is_cpu_in_list(cpuid_t cpuid, cpuid_t *list, int len); 92 static char *rcm_info_table(rcm_info_t *rinfo); 93 94 /* debugging utility functions */ 95 static void dump_cpu_list(char *prefix, cpuid_t *cpuids, int ncpuids); 96 static void dump_cpu_rsrc_list(char *prefix, drctl_rsrc_t *, int nrsrc); 97 static void dump_cpu_rlist(char **rlist); 98 99 static int 100 drd_rcm_init(void) 101 { 102 int rv; 103 104 drd_dbg("drd_rcm_init..."); 105 106 rv = rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl); 107 if (rv == RCM_FAILURE) { 108 drd_err("unable to allocate RCM handle: %s", strerror(errno)); 109 return (-1); 110 } 111 112 return (0); 113 } 114 115 static int 116 drd_rcm_fini(void) 117 { 118 drd_dbg("drd_rcm_fini..."); 119 120 if (rcm_hdl != NULL) 121 rcm_free_handle(rcm_hdl); 122 123 return (0); 124 } 125 126 static int 127 drd_rcm_cpu_config_request(drctl_rsrc_t *rsrcs, int nrsrc) 128 { 129 int idx; 130 131 drd_dbg("drd_rcm_cpu_config_request..."); 132 dump_cpu_rsrc_list(NULL, rsrcs, nrsrc); 133 134 /* 135 * There is no RCM operation to request the addition 136 * of resources. So, by definition, the operation for 137 * all the CPUs is allowed. 138 */ 139 for (idx = 0; idx < nrsrc; idx++) 140 rsrcs[idx].status = DRCTL_STATUS_ALLOW; 141 142 dump_cpu_rsrc_list("returning:", rsrcs, nrsrc); 143 144 return (0); 145 } 146 147 static int 148 drd_rcm_cpu_config_notify(drctl_rsrc_t *rsrcs, int nrsrc) 149 { 150 int rv = 0; 151 152 drd_dbg("drd_rcm_cpu_config_notify..."); 153 dump_cpu_rsrc_list(NULL, rsrcs, nrsrc); 154 155 /* notify RCM about the newly added CPUs */ 156 if (drd_rcm_online_cpu_notify(rsrcs, nrsrc) != 0) { 157 rv = -1; 158 goto done; 159 } 160 161 /* notify RCM about the increased CPU capacity */ 162 if (drd_rcm_add_cpu_notify(rsrcs, nrsrc) != 0) { 163 rv = -1; 164 } 165 166 done: 167 dump_cpu_rsrc_list("returning:", rsrcs, nrsrc); 168 169 return (rv); 170 } 171 172 static int 173 drd_rcm_cpu_unconfig_request(drctl_rsrc_t *rsrcs, int nrsrc) 174 { 175 int rv = 0; 176 int idx; 177 178 drd_dbg("drd_rcm_cpu_unconfig_request..."); 179 dump_cpu_rsrc_list(NULL, rsrcs, nrsrc); 180 181 /* contact RCM to request a decrease in CPU capacity */ 182 if (drd_rcm_del_cpu_request(rsrcs, nrsrc) != 0) { 183 rv = -1; 184 goto done; 185 } 186 187 /* contact RCM to request the removal of CPUs */ 188 if (drd_rcm_offline_cpu_request(rsrcs, nrsrc) != 0) { 189 rv = -1; 190 goto done; 191 } 192 193 done: 194 /* 195 * If any errors occurred, the status field for 196 * a CPU may still be in the INIT state. Set the 197 * status for any such CPU to DENY to ensure it 198 * gets processed properly. 199 */ 200 for (idx = 0; idx < nrsrc; idx++) { 201 if (rsrcs[idx].status == DRCTL_STATUS_INIT) 202 rsrcs[idx].status = DRCTL_STATUS_DENY; 203 } 204 205 dump_cpu_rsrc_list("returning:", rsrcs, nrsrc); 206 207 return (rv); 208 } 209 210 static int 211 drd_rcm_cpu_unconfig_notify(drctl_rsrc_t *rsrcs, int nrsrc) 212 { 213 int rv = 0; 214 215 drd_dbg("drd_rcm_cpu_unconfig_notify..."); 216 dump_cpu_rsrc_list(NULL, rsrcs, nrsrc); 217 218 /* 219 * Notify RCM about the CPUs that were removed. 220 * Failures are ignored so that CPUs that could 221 * not be unconfigured can be processed by RCM. 222 */ 223 (void) drd_rcm_remove_cpu_notify(rsrcs, nrsrc); 224 225 /* 226 * Notify RCM about any CPUs that did not make it 227 * in to the unconfigured state. 228 */ 229 if (drd_rcm_restore_cpu_notify(rsrcs, nrsrc) != 0) { 230 rv = -1; 231 goto done; 232 } 233 234 /* notify RCM about the decreased CPU capacity */ 235 if (drd_rcm_del_cpu_notify(rsrcs, nrsrc) != 0) { 236 rv = -1; 237 } 238 239 done: 240 dump_cpu_rsrc_list("returning:", rsrcs, nrsrc); 241 242 return (rv); 243 } 244 245 static int 246 drd_rcm_online_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc) 247 { 248 char **rlist; 249 int rv = 0; 250 rcm_info_t *rinfo; 251 252 drd_dbg("drd_rcm_online_cpu_notify..."); 253 254 if ((rlist = drd_rcm_cpu_rlist_init(rsrcs, nrsrc, 255 DRCTL_STATUS_CONFIG_SUCCESS)) == NULL) { 256 drd_dbg(" no CPUs were successfully added, nothing to do"); 257 return (0); 258 } 259 260 rcm_notify_online_list(rcm_hdl, rlist, 0, &rinfo); 261 if (rv != RCM_SUCCESS) { 262 drd_info("rcm_notify_online_list failed: %d", rv); 263 rcm_free_info(rinfo); 264 rv = -1; 265 } 266 267 drd_rcm_cpu_rlist_fini(rlist); 268 269 return (rv); 270 } 271 272 static int 273 drd_rcm_add_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc) 274 { 275 cpuid_t *cpus = NULL; 276 int ncpus; 277 int rv = -1; 278 cpuid_t *oldcpus = NULL; 279 cpuid_t *newcpus = NULL; 280 int oldncpus = 0; 281 int newncpus = 0; 282 nvlist_t *nvl = NULL; 283 int idx; 284 rcm_info_t *rinfo; 285 286 drd_dbg("drd_rcm_add_cpu_notify..."); 287 288 if ((rsrcs == NULL) || (nrsrc == 0)) { 289 drd_err("add_cpu_notify: cpu list empty"); 290 goto done; 291 } 292 293 ncpus = nrsrc; 294 cpus = (cpuid_t *)malloc(nrsrc * sizeof (cpuid_t)); 295 296 for (idx = 0; idx < nrsrc; idx++) { 297 drd_dbg(" cpu[%d] = %d", idx, rsrcs[idx].res_cpu_id); 298 cpus[idx] = rsrcs[idx].res_cpu_id; 299 } 300 301 /* allocate an nvlist for the RCM call */ 302 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 303 goto done; 304 305 /* 306 * Added CPU capacity, so newcpus is the current list 307 * of CPUs in the system. 308 */ 309 if (get_sys_cpuids(&newcpus, &newncpus) == -1) 310 goto done; 311 312 /* 313 * Since the operation added CPU capacity, the old CPU 314 * list is the new CPU list with the CPUs involved in 315 * the operation removed. 316 */ 317 oldcpus = (cpuid_t *)calloc(newncpus, sizeof (cpuid_t)); 318 if (oldcpus == NULL) 319 goto done; 320 321 for (idx = 0; idx < newncpus; idx++) { 322 if (!is_cpu_in_list(newcpus[idx], cpus, ncpus)) 323 oldcpus[oldncpus++] = newcpus[idx]; 324 } 325 326 /* dump pre and post lists */ 327 dump_cpu_list("oldcpus: ", oldcpus, oldncpus); 328 dump_cpu_list("newcpus: ", newcpus, newncpus); 329 dump_cpu_list("delta: ", cpus, ncpus); 330 331 /* setup the nvlist for the RCM call */ 332 if (nvlist_add_string(nvl, "state", "capacity") || 333 nvlist_add_int32(nvl, "old_total", oldncpus) || 334 nvlist_add_int32(nvl, "new_total", newncpus) || 335 nvlist_add_int32_array(nvl, "old_cpu_list", oldcpus, oldncpus) || 336 nvlist_add_int32_array(nvl, "new_cpu_list", newcpus, newncpus)) { 337 goto done; 338 } 339 340 rv = rcm_notify_capacity_change(rcm_hdl, RCM_CPU_ALL, 0, nvl, &rinfo); 341 rv = (rv == RCM_SUCCESS) ? 0 : -1; 342 343 done: 344 s_nvfree(nvl); 345 s_free(cpus); 346 s_free(oldcpus); 347 s_free(newcpus); 348 349 return (rv); 350 } 351 352 static int 353 drd_rcm_del_cpu_request(drctl_rsrc_t *rsrcs, int nrsrc) 354 { 355 cpuid_t *cpus = NULL; 356 int ncpus; 357 int rv = -1; 358 cpuid_t *oldcpus = NULL; 359 cpuid_t *newcpus = NULL; 360 int oldncpus = 0; 361 int newncpus = 0; 362 nvlist_t *nvl = NULL; 363 int idx; 364 rcm_info_t *rinfo; 365 366 drd_dbg("drd_rcm_del_cpu_request..."); 367 368 if ((rsrcs == NULL) || (nrsrc == 0)) { 369 drd_err("del_cpu_request: cpu list empty"); 370 goto done; 371 } 372 373 ncpus = nrsrc; 374 cpus = (cpuid_t *)malloc(nrsrc * sizeof (cpuid_t)); 375 376 for (idx = 0; idx < nrsrc; idx++) { 377 cpus[idx] = rsrcs[idx].res_cpu_id; 378 } 379 380 /* allocate an nvlist for the RCM call */ 381 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 382 goto done; 383 } 384 385 /* 386 * Removing CPU capacity, so oldcpus is the current 387 * list of CPUs in the system. 388 */ 389 if (get_sys_cpuids(&oldcpus, &oldncpus) == -1) { 390 goto done; 391 } 392 393 /* 394 * Since this is a request to remove CPU capacity, 395 * the new CPU list is the old CPU list with the CPUs 396 * involved in the operation removed. 397 */ 398 newcpus = (cpuid_t *)calloc(oldncpus, sizeof (cpuid_t)); 399 if (newcpus == NULL) { 400 goto done; 401 } 402 403 for (idx = 0; idx < oldncpus; idx++) { 404 if (!is_cpu_in_list(oldcpus[idx], cpus, ncpus)) 405 newcpus[newncpus++] = oldcpus[idx]; 406 } 407 408 /* dump pre and post lists */ 409 dump_cpu_list("oldcpus: ", oldcpus, oldncpus); 410 dump_cpu_list("newcpus: ", newcpus, newncpus); 411 dump_cpu_list("delta: ", cpus, ncpus); 412 413 /* setup the nvlist for the RCM call */ 414 if (nvlist_add_string(nvl, "state", "capacity") || 415 nvlist_add_int32(nvl, "old_total", oldncpus) || 416 nvlist_add_int32(nvl, "new_total", newncpus) || 417 nvlist_add_int32_array(nvl, "old_cpu_list", oldcpus, oldncpus) || 418 nvlist_add_int32_array(nvl, "new_cpu_list", newcpus, newncpus)) { 419 goto done; 420 } 421 422 rv = rcm_request_capacity_change(rcm_hdl, RCM_CPU_ALL, 0, nvl, &rinfo); 423 if (rv != RCM_SUCCESS) { 424 drd_dbg("RCM call failed: %d", rv); 425 /* 426 * Since the capcity change was blocked, we 427 * mark all CPUs as blocked. It is up to the 428 * user to reframe the query so that it can 429 * succeed. 430 */ 431 for (idx = 0; idx < nrsrc; idx++) { 432 rsrcs[idx].status = DRCTL_STATUS_DENY; 433 } 434 435 /* tack on message to first resource */ 436 rsrcs[0].offset = (uintptr_t)strdup("unable to remove " 437 "specified number of CPUs"); 438 drd_dbg(" unable to remove specified number of CPUs"); 439 goto done; 440 } 441 442 rv = 0; 443 444 done: 445 s_nvfree(nvl); 446 s_free(cpus); 447 s_free(oldcpus); 448 s_free(newcpus); 449 450 return (rv); 451 } 452 453 static int 454 drd_rcm_offline_cpu_request(drctl_rsrc_t *rsrcs, int nrsrc) 455 { 456 char **rlist; 457 drctl_rsrc_t *rsrc; 458 int idx; 459 int state; 460 int rv = 0; 461 rcm_info_t *rinfo = NULL; 462 rcm_info_tuple_t *tuple = NULL; 463 const char *rsrcstr; 464 const char *errstr; 465 466 drd_dbg("drd_rcm_offline_cpu_request..."); 467 468 if ((rlist = drd_rcm_cpu_rlist_init(rsrcs, nrsrc, 469 DRCTL_STATUS_INIT)) == NULL) { 470 drd_err("unable to generate resource list"); 471 return (-1); 472 } 473 474 rv = rcm_request_offline_list(rcm_hdl, rlist, 0, &rinfo); 475 if (rv == RCM_SUCCESS) { 476 drd_dbg("RCM success, rinfo=%p", rinfo); 477 goto done; 478 } 479 480 drd_dbg("RCM call failed (%d):", rv); 481 482 /* 483 * Loop through the result of the operation and add 484 * any error messages to the resource structure. 485 */ 486 while ((tuple = rcm_info_next(rinfo, tuple)) != NULL) { 487 488 /* find the resource of interest */ 489 rsrcstr = rcm_info_rsrc(tuple); 490 rsrc = cpu_rsrcstr_to_rsrc(rsrcstr, rsrcs, nrsrc); 491 492 if (rsrc == NULL) { 493 drd_dbg("unable to find resource for %s", rsrcstr); 494 continue; 495 } 496 497 errstr = rcm_info_error(tuple); 498 499 if (errstr) { 500 drd_dbg(" %s: '%s'", rsrcstr, errstr); 501 rsrc->offset = (uintptr_t)strdup(errstr); 502 } 503 } 504 505 rcm_free_info(rinfo); 506 rv = 0; 507 508 done: 509 /* 510 * Set the state of the resource based on the RCM 511 * state. CPUs in the offline state have the ok to 512 * proceed. All others have been blocked. 513 */ 514 for (idx = 0; rlist[idx] != NULL; idx++) { 515 516 state = 0; 517 rcm_get_rsrcstate(rcm_hdl, rlist[idx], &state); 518 519 /* find the resource of interest */ 520 rsrc = cpu_rsrcstr_to_rsrc(rlist[idx], rsrcs, nrsrc); 521 522 if (rsrc == NULL) { 523 drd_dbg("unable to find resource for %s", rlist[idx]); 524 continue; 525 } 526 527 rsrc->status = ((state == RCM_STATE_OFFLINE) ? 528 DRCTL_STATUS_ALLOW : DRCTL_STATUS_DENY); 529 } 530 531 drd_rcm_cpu_rlist_fini(rlist); 532 533 return (rv); 534 } 535 536 static int 537 drd_rcm_remove_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc) 538 { 539 char **rlist; 540 int rv = 0; 541 rcm_info_t *rinfo; 542 543 drd_dbg("drd_rcm_remove_cpu_notify..."); 544 545 if ((rlist = drd_rcm_cpu_rlist_init(rsrcs, nrsrc, 546 DRCTL_STATUS_CONFIG_SUCCESS)) == NULL) { 547 drd_dbg(" no CPUs in the success state, nothing to do"); 548 return (0); 549 } 550 551 rv = rcm_notify_remove_list(rcm_hdl, rlist, 0, &rinfo); 552 if (rv != RCM_SUCCESS) { 553 drd_info("rcm_notify_remove_list failed: %d", rv); 554 rcm_free_info(rinfo); 555 rv = -1; 556 } 557 558 drd_rcm_cpu_rlist_fini(rlist); 559 560 return (rv); 561 } 562 563 static int 564 drd_rcm_restore_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc) 565 { 566 char **rlist; 567 char **full_rlist; 568 int idx; 569 int ridx; 570 int state; 571 int rv = 0; 572 rcm_info_t *rinfo; 573 574 drd_dbg("drd_rcm_restore_cpu_notify..."); 575 576 if ((full_rlist = drd_rcm_cpu_rlist_init(rsrcs, nrsrc, 577 DRCTL_STATUS_CONFIG_FAILURE)) == NULL) { 578 drd_dbg(" no CPUs in the failed state, nothing to do"); 579 return (0); 580 } 581 582 /* 583 * Since the desired result of this operation is to 584 * restore resources to the online state, filter out 585 * the resources already in the online state before 586 * passing the list to RCM. 587 */ 588 589 /* allocate a zero filled array to ensure NULL terminated list */ 590 rlist = (char **)calloc((nrsrc + 1), sizeof (char *)); 591 if (rlist == NULL) { 592 drd_err("calloc failed: %s", strerror(errno)); 593 rv = -1; 594 goto done; 595 } 596 597 for (idx = 0, ridx = 0; full_rlist[idx] != NULL; idx++) { 598 state = 0; 599 rcm_get_rsrcstate(rcm_hdl, full_rlist[idx], &state); 600 if (state != RCM_STATE_ONLINE) { 601 rlist[ridx] = full_rlist[idx]; 602 ridx++; 603 } 604 } 605 606 /* check if everything got filtered out */ 607 if (ridx == 0) { 608 drd_dbg(" all CPUs already online, nothing to do"); 609 goto done; 610 } 611 612 rv = rcm_notify_online_list(rcm_hdl, rlist, 0, &rinfo); 613 if (rv != RCM_SUCCESS) { 614 drd_info("rcm_notify_online_list failed: %d", rv); 615 rcm_free_info(rinfo); 616 rv = -1; 617 } 618 619 done: 620 drd_rcm_cpu_rlist_fini(full_rlist); 621 s_free(rlist); 622 623 return (rv); 624 } 625 626 static int 627 drd_rcm_del_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc) 628 { 629 cpuid_t *cpus = NULL; 630 int rv = -1; 631 cpuid_t *oldcpus = NULL; 632 cpuid_t *newcpus = NULL; 633 int oldncpus = 0; 634 int newncpus = 0; 635 nvlist_t *nvl = NULL; 636 int idx; 637 int cidx; 638 rcm_info_t *rinfo; 639 640 drd_dbg("drd_rcm_del_cpu_notify..."); 641 642 if ((rsrcs == NULL) || (nrsrc == 0)) { 643 drd_err("del_cpu_notify: cpu list empty"); 644 goto done; 645 } 646 647 cpus = (cpuid_t *)malloc(nrsrc * sizeof (cpuid_t)); 648 649 /* 650 * Filter out the CPUs that could not be unconfigured. 651 */ 652 for (idx = 0, cidx = 0; idx < nrsrc; idx++) { 653 if (rsrcs[idx].status != DRCTL_STATUS_CONFIG_SUCCESS) 654 continue; 655 drd_dbg(" cpu[%d] = %d", idx, rsrcs[idx].res_cpu_id); 656 cpus[cidx] = rsrcs[idx].res_cpu_id; 657 cidx++; 658 } 659 660 drd_dbg(" ncpus = %d", cidx); 661 662 /* nothing to do */ 663 if (cidx == 0) { 664 rv = 0; 665 goto done; 666 } 667 668 /* allocate an nvlist for the RCM call */ 669 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { 670 goto done; 671 } 672 673 /* 674 * Removed CPU capacity, so newcpus is the current list 675 * of CPUs in the system. 676 */ 677 if (get_sys_cpuids(&newcpus, &newncpus) == -1) { 678 goto done; 679 } 680 681 /* 682 * Since the operation removed CPU capacity, the old CPU 683 * list is the new CPU list with the CPUs involved in 684 * the operation added. 685 */ 686 oldcpus = (cpuid_t *)calloc(newncpus + cidx, sizeof (cpuid_t)); 687 if (oldcpus == NULL) { 688 goto done; 689 } 690 691 for (idx = 0; idx < newncpus; idx++) { 692 if (!is_cpu_in_list(newcpus[idx], cpus, cidx)) 693 oldcpus[oldncpus++] = newcpus[idx]; 694 } 695 696 for (idx = 0; idx < cidx; idx++) { 697 oldcpus[oldncpus++] = cpus[idx]; 698 } 699 700 /* dump pre and post lists */ 701 dump_cpu_list("oldcpus: ", oldcpus, oldncpus); 702 dump_cpu_list("newcpus: ", newcpus, newncpus); 703 dump_cpu_list("delta: ", cpus, cidx); 704 705 /* setup the nvlist for the RCM call */ 706 if (nvlist_add_string(nvl, "state", "capacity") || 707 nvlist_add_int32(nvl, "old_total", oldncpus) || 708 nvlist_add_int32(nvl, "new_total", newncpus) || 709 nvlist_add_int32_array(nvl, "old_cpu_list", oldcpus, oldncpus) || 710 nvlist_add_int32_array(nvl, "new_cpu_list", newcpus, newncpus)) { 711 goto done; 712 } 713 714 rv = rcm_notify_capacity_change(rcm_hdl, RCM_CPU_ALL, 0, nvl, &rinfo); 715 rv = (rv == RCM_SUCCESS) ? 0 : -1; 716 717 done: 718 s_nvfree(nvl); 719 s_free(cpus); 720 s_free(oldcpus); 721 s_free(newcpus); 722 723 return (rv); 724 } 725 726 /* 727 * Given a list of resource structures, create a list of CPU 728 * resource strings formatted as expected by RCM. Only resources 729 * that are in the state specified by the status argument are 730 * included in the resulting list. 731 */ 732 static char ** 733 drd_rcm_cpu_rlist_init(drctl_rsrc_t *rsrcs, int nrsrc, int status) 734 { 735 char rbuf[RCM_CPU_MAX_LEN]; 736 char **rlist; 737 int idx; 738 int ridx; 739 740 drd_dbg("drd_rcm_cpu_rlist_init..."); 741 742 if ((rsrcs == NULL) || (nrsrc == 0)) { 743 drd_dbg("cpu list is empty"); 744 return (NULL); 745 } 746 747 /* allocate a zero filled array to ensure NULL terminated list */ 748 rlist = (char **)calloc((nrsrc + 1), sizeof (char *)); 749 if (rlist == NULL) { 750 drd_err("calloc failed: %s", strerror(errno)); 751 return (NULL); 752 } 753 754 for (idx = 0, ridx = 0; idx < nrsrc; idx++) { 755 756 drd_dbg(" checking cpu %d, status=%d, expected status=%d", 757 rsrcs[idx].res_cpu_id, rsrcs[idx].status, status); 758 759 /* 760 * Filter out the CPUs that are not in 761 * the requested state. 762 */ 763 if (rsrcs[idx].status != status) 764 continue; 765 766 /* generate the resource string */ 767 (void) sprintf(rbuf, "%s%d", RCM_CPU, rsrcs[idx].res_cpu_id); 768 769 rlist[ridx] = strdup(rbuf); 770 if (rlist[ridx] == NULL) { 771 drd_err("strdup failed: %s", strerror(errno)); 772 drd_rcm_cpu_rlist_fini(rlist); 773 return (NULL); 774 } 775 776 ridx++; 777 } 778 779 /* cleanup if the list is empty */ 780 if (ridx == 0) { 781 s_free(rlist); 782 } 783 784 drd_dbg("final rlist:"); 785 dump_cpu_rlist(rlist); 786 787 return (rlist); 788 } 789 790 static void 791 drd_rcm_cpu_rlist_fini(char **rlist) 792 { 793 int idx; 794 795 drd_dbg("drd_rcm_cpu_rlist_fini..."); 796 797 dump_cpu_rlist(rlist); 798 799 for (idx = 0; rlist[idx] != NULL; idx++) { 800 s_free(rlist[idx]); 801 } 802 803 s_free(rlist); 804 } 805 806 /* 807 * Convert an RCM CPU resource string into a numerical cpuid. 808 * Assumes the resource string has the form: "SUNW_cpu/cpu<C>" 809 * where "<C>" is the numerical cpuid of interest. 810 */ 811 static cpuid_t 812 cpu_rsrcstr_to_cpuid(const char *rsrc) 813 { 814 char *cpuid_off; 815 cpuid_t cpuid; 816 817 /* 818 * Search for the last occurrance of 'u' in the 819 * expected RCM resource string "SUNW_cpu/cpu<C>". 820 * This will give a pointer to the cpuid portion. 821 */ 822 cpuid_off = strrchr(rsrc, 'u'); 823 cpuid_off++; 824 825 cpuid = atoi(cpuid_off); 826 827 return (cpuid); 828 } 829 830 /* 831 * Given an RCM CPU resource string, return a pointer to the 832 * corresponding resource structure from the given resource list. 833 * NULL is returned if no matching resource structure can be 834 * found. 835 */ 836 static drctl_rsrc_t * 837 cpu_rsrcstr_to_rsrc(const char *rsrcstr, drctl_rsrc_t *rsrcs, int nrsrc) 838 { 839 cpuid_t cpuid; 840 int idx; 841 842 cpuid = cpu_rsrcstr_to_cpuid(rsrcstr); 843 844 for (idx = 0; idx < nrsrc; idx++) { 845 if (rsrcs[idx].res_cpu_id == cpuid) 846 return (&rsrcs[idx]); 847 } 848 849 return (NULL); 850 } 851 852 static int 853 get_sys_cpuids(cpuid_t **cpuids, int *ncpuids) 854 { 855 int ncpu = 0; 856 int maxncpu; 857 kstat_t *ksp; 858 kstat_ctl_t *kc = NULL; 859 cpuid_t *cp; 860 861 drd_dbg("get_sys_cpuids..."); 862 863 if ((maxncpu = sysconf(_SC_NPROCESSORS_MAX)) == -1) 864 return (-1); 865 866 if ((kc = kstat_open()) == NULL) 867 return (-1); 868 869 if ((cp = (cpuid_t *)calloc(maxncpu, sizeof (cpuid_t))) == NULL) { 870 (void) kstat_close(kc); 871 return (-1); 872 } 873 874 for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) { 875 if (strcmp(ksp->ks_module, "cpu_info") == 0) 876 cp[ncpu++] = ksp->ks_instance; 877 } 878 879 dump_cpu_list("syscpus: ", cp, ncpu); 880 881 (void) kstat_close(kc); 882 883 *cpuids = cp; 884 *ncpuids = ncpu; 885 886 return (0); 887 } 888 889 static boolean_t 890 is_cpu_in_list(cpuid_t cpuid, cpuid_t *list, int len) 891 { 892 int idx; 893 894 if (list == NULL) 895 return (B_FALSE); 896 897 for (idx = 0; idx < len; idx++) { 898 if (list[idx] == cpuid) 899 return (B_TRUE); 900 } 901 902 return (B_FALSE); 903 } 904 905 #define CPUIDS_PER_LINE 16 906 #define LINEWIDTH (2 * (CPUIDS_PER_LINE * 4)) 907 908 static void 909 dump_cpu_list(char *prefix, cpuid_t *cpuids, int ncpuids) 910 { 911 char line[LINEWIDTH]; 912 char *curr; 913 int i, j; 914 915 /* return if not debugging */ 916 if (drd_debug == 0) 917 return; 918 919 /* print just the prefix if CPU list is empty */ 920 if (ncpuids == 0) { 921 if (prefix) 922 drd_dbg("%s", prefix); 923 return; 924 } 925 926 for (i = 0; i < ncpuids; i += CPUIDS_PER_LINE) { 927 928 bzero(line, LINEWIDTH); 929 curr = line; 930 931 /* start with the prefix */ 932 (void) sprintf(curr, "%s", (prefix) ? prefix : ""); 933 curr = line + strlen(line); 934 935 /* format the CPUs for this line */ 936 for (j = 0; (j < CPUIDS_PER_LINE) && ((i + j) < ncpuids); j++) { 937 (void) sprintf(curr, "%3d ", cpuids[i + j]); 938 curr = line + strlen(line); 939 } 940 941 drd_dbg("%s", line); 942 } 943 } 944 945 static void 946 dump_cpu_rsrc_list(char *prefix, drctl_rsrc_t *rsrcs, int nrsrc) 947 { 948 int idx; 949 char *errstr; 950 951 /* just return if not debugging */ 952 if (drd_debug == 0) 953 return; 954 955 if (prefix) 956 drd_dbg("%s", prefix); 957 958 for (idx = 0; idx < nrsrc; idx++) { 959 960 /* get a pointer to the error string */ 961 errstr = (char *)(uintptr_t)rsrcs[idx].offset; 962 963 drd_dbg(" cpu[%d]: cpuid=%d, status=%d, errstr='%s'", idx, 964 rsrcs[idx].res_cpu_id, rsrcs[idx].status, 965 (errstr != NULL) ? errstr : ""); 966 } 967 } 968 969 static void 970 dump_cpu_rlist(char **rlist) 971 { 972 int idx; 973 int state; 974 975 static char *rcm_state_str[] = { 976 "UNKNOWN", "ONLINE", "ONLINING", 977 "OFFLINE_FAIL", "OFFLINING", "OFFLINE", 978 "REMOVING", "INVALID_7", "INVALID_8", 979 "INVALID_9", "RESUMING", "SUSPEND_FAIL", 980 "SUSPENDING", "SUSPEND", "REMOVE", 981 "OFFLINE_QUERYING", "OFFLINE_QUERY_FAIL", "OFFLINE_QUERY", 982 "SUSPEND_QUERYING", "SUSPEND_QUERY_FAIL", "SUSPEND_QUERY" 983 }; 984 985 /* just return if not debugging */ 986 if (drd_debug == 0) 987 return; 988 989 if (rlist == NULL) { 990 drd_dbg(" empty rlist"); 991 return; 992 } 993 994 for (idx = 0; rlist[idx] != NULL; idx++) { 995 state = 0; 996 rcm_get_rsrcstate(rcm_hdl, rlist[idx], &state); 997 drd_dbg(" rlist[%d]: rsrc=%s, state=%-2d (%s)", idx, 998 rlist[idx], state, rcm_state_str[state]); 999 } 1000 } 1001 1002 static int 1003 drd_rcm_io_config_request(drctl_rsrc_t *rsrc, int nrsrc) 1004 { 1005 drd_dbg("drd_rcm_io_config_request..."); 1006 1007 if (nrsrc != 1) { 1008 drd_dbg("drd_rcm_cpu_config_request: only 1 resource " 1009 "allowed for I/O requests, passed %d resources\n", nrsrc); 1010 rsrc->status = DRCTL_STATUS_DENY; 1011 1012 return (-1); 1013 } 1014 1015 /* 1016 * There is no RCM operation to request the addition 1017 * of resources. So, by definition, the operation for 1018 * the current resource is allowed. 1019 */ 1020 rsrc->status = DRCTL_STATUS_ALLOW; 1021 1022 return (0); 1023 } 1024 1025 /*ARGSUSED*/ 1026 static int 1027 drd_rcm_io_config_notify(drctl_rsrc_t *rsrcs, int nrsrc) 1028 { 1029 drd_dbg("drd_rcm_io_config_notify..."); 1030 1031 if (nrsrc != 1) { 1032 drd_dbg("drd_rcm_cpu_config_notify: only 1 resource " 1033 "allowed for I/O requests, passed %d resources\n", nrsrc); 1034 1035 return (-1); 1036 } 1037 1038 return (0); 1039 } 1040 1041 1042 static int 1043 drd_rcm_io_unconfig_request(drctl_rsrc_t *rsrc, int nrsrc) 1044 { 1045 int rv; 1046 char *dev = rsrc->res_dev_path; 1047 rcm_info_t *rinfo = NULL; 1048 1049 if (nrsrc != 1) { 1050 drd_dbg("drd_io_unconfig_request: only 1 resource " 1051 "allowed for I/O requests, passed %d resources\n", nrsrc); 1052 rsrc->status = DRCTL_STATUS_DENY; 1053 1054 return (-1); 1055 } 1056 1057 if ((rv = rcm_request_offline(rcm_hdl, dev, 0, &rinfo)) == RCM_SUCCESS) 1058 rsrc->status = DRCTL_STATUS_ALLOW; 1059 else { 1060 rcm_notify_online(rcm_hdl, dev, 0, NULL); 1061 rsrc->status = DRCTL_STATUS_DENY; 1062 rsrc->offset = (uintptr_t)rcm_info_table(rinfo); 1063 1064 } 1065 1066 rcm_free_info(rinfo); 1067 drd_dbg("drd_rcm_io_unconfig_request(%s) = %d", dev, rv); 1068 1069 return (rv); 1070 } 1071 1072 static int 1073 drd_rcm_io_unconfig_notify(drctl_rsrc_t *rsrc, int nrsrc) 1074 { 1075 drd_dbg("drd_rcm_io_unconfig_notify..."); 1076 1077 if (nrsrc != 1) { 1078 drd_dbg("drd_io_cpu_unconfig_notify: only 1 resource " 1079 "allowed for I/O requests, passed %d resources\n", nrsrc); 1080 1081 return (-1); 1082 } 1083 1084 return (rcm_notify_remove(rcm_hdl, rsrc->res_dev_path, 0, NULL)); 1085 } 1086 1087 #define MAX_FORMAT 80 1088 1089 /* 1090 * Convert rcm_info_t data into a printable table. 1091 */ 1092 static char * 1093 rcm_info_table(rcm_info_t *rinfo) 1094 { 1095 int i; 1096 size_t w; 1097 size_t width = 0; 1098 size_t w_rsrc = 0; 1099 size_t w_info = 0; 1100 size_t table_size = 0; 1101 uint_t tuples = 0; 1102 rcm_info_tuple_t *tuple = NULL; 1103 char *rsrc; 1104 char *info; 1105 char *table; 1106 static char format[MAX_FORMAT]; 1107 const char *infostr; 1108 1109 /* Protect against invalid arguments */ 1110 if (rinfo == NULL) 1111 return (NULL); 1112 1113 /* Set localized table header strings */ 1114 rsrc = dgettext(TEXT_DOMAIN, "Resource"); 1115 info = dgettext(TEXT_DOMAIN, "Information"); 1116 1117 /* A first pass, to size up the RCM information */ 1118 while (tuple = rcm_info_next(rinfo, tuple)) { 1119 if ((infostr = rcm_info_info(tuple)) != NULL) { 1120 tuples++; 1121 if ((w = strlen(rcm_info_rsrc(tuple))) > w_rsrc) 1122 w_rsrc = w; 1123 if ((w = strlen(infostr)) > w_info) 1124 w_info = w; 1125 } 1126 } 1127 1128 /* If nothing was sized up above, stop early */ 1129 if (tuples == 0) 1130 return (NULL); 1131 1132 /* Adjust column widths for column headings */ 1133 if ((w = strlen(rsrc)) > w_rsrc) 1134 w_rsrc = w; 1135 else if ((w_rsrc - w) % 2) 1136 w_rsrc++; 1137 if ((w = strlen(info)) > w_info) 1138 w_info = w; 1139 else if ((w_info - w) % 2) 1140 w_info++; 1141 1142 /* 1143 * Compute the total line width of each line, 1144 * accounting for intercolumn spacing. 1145 */ 1146 width = w_info + w_rsrc + 4; 1147 1148 /* Allocate space for the table */ 1149 table_size = (2 + tuples) * (width + 1) + 2; 1150 1151 /* zero fill for the strcat() call below */ 1152 table = calloc(table_size, sizeof (char)); 1153 if (table == NULL) 1154 return (NULL); 1155 1156 /* Place a table header into the string */ 1157 1158 /* The resource header */ 1159 (void) strcat(table, "\n"); 1160 w = strlen(rsrc); 1161 for (i = 0; i < ((w_rsrc - w) / 2); i++) 1162 (void) strcat(table, " "); 1163 (void) strcat(table, rsrc); 1164 for (i = 0; i < ((w_rsrc - w) / 2); i++) 1165 (void) strcat(table, " "); 1166 1167 /* The information header */ 1168 (void) strcat(table, " "); 1169 w = strlen(info); 1170 for (i = 0; i < ((w_info - w) / 2); i++) 1171 (void) strcat(table, " "); 1172 (void) strcat(table, info); 1173 for (i = 0; i < ((w_info - w) / 2); i++) 1174 (void) strcat(table, " "); 1175 /* Underline the headers */ 1176 (void) strcat(table, "\n"); 1177 for (i = 0; i < w_rsrc; i++) 1178 (void) strcat(table, "-"); 1179 (void) strcat(table, " "); 1180 for (i = 0; i < w_info; i++) 1181 (void) strcat(table, "-"); 1182 1183 /* Construct the format string */ 1184 (void) snprintf(format, MAX_FORMAT, "%%-%ds %%-%ds", 1185 (int)w_rsrc, (int)w_info); 1186 1187 /* Add the tuples to the table string */ 1188 tuple = NULL; 1189 while ((tuple = rcm_info_next(rinfo, tuple)) != NULL) { 1190 if ((infostr = rcm_info_info(tuple)) != NULL) { 1191 (void) strcat(table, "\n"); 1192 (void) sprintf(&((table)[strlen(table)]), 1193 format, rcm_info_rsrc(tuple), 1194 infostr); 1195 } 1196 } 1197 drd_dbg("rcm_info_table: %s\n", table); 1198 1199 return (table); 1200 } 1201