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