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 #include <ctype.h>
  27 #include <stdio.h>
  28 #include <stdlib.h>
  29 #include <stdarg.h>
  30 #include <string.h>
  31 #include <unistd.h>
  32 #include <macros.h>
  33 #include <errno.h>
  34 #include <kstat.h>
  35 #include <sys/kmem.h>
  36 #include <dlfcn.h>
  37 #include <libdevinfo.h>
  38 #include <librcm.h>
  39 #include <libintl.h>
  40 #define CFGA_PLUGIN_LIB
  41 #include <config_admin.h>
  42 #include <sys/sbd_ioctl.h>
  43 #include "ap.h"
  44 
  45 typedef int32_t cpuid_t;
  46 
  47 typedef struct {
  48         int valid;
  49         cfga_stat_t ostate;
  50         int ncap;
  51         union {
  52                 long npages;
  53                 cpuid_t cpuid[SBD_MAX_CORES_PER_CMP];
  54         } type;
  55 } cap_info_t;
  56 
  57 typedef struct {
  58         int firstcm;            /* first component to operate on */
  59         int lastcm;             /* last component to operate on */
  60         void *lib;
  61         char **rlist;
  62         cap_info_t *capinfo;
  63         int ncpus;              /* # of CPUs in cpuids list */
  64         cpuid_t *cpuids;        /* List of cpuids */
  65         int capcpus;            /* # of CPUs - tracking capacity */
  66         int cappages;           /* # of memory pages - tracking capacity */
  67         rcm_handle_t *hd;
  68         rcm_info_t *rinfo;
  69         rcm_info_tuple_t *infot;
  70         int (*alloc_handle)(char *, uint_t, void *, rcm_handle_t **);
  71         void (*free_handle)(rcm_handle_t *);
  72         int (*get_info)(rcm_handle_t *, char *, uint_t, rcm_info_t **);
  73         void (*free_info)(rcm_info_t *);
  74         rcm_info_tuple_t *(*info_next)(rcm_info_t *, rcm_info_tuple_t *);
  75         int (*info_state)(rcm_info_tuple_t *);
  76         pid_t (*info_pid)(rcm_info_tuple_t *);
  77         const char *(*info_error)(rcm_info_tuple_t *);
  78         const char *(*info_info)(rcm_info_tuple_t *);
  79         const char *(*info_rsrc)(rcm_info_tuple_t *);
  80         int (*request_offline_list)(rcm_handle_t *, char **, uint_t,
  81             rcm_info_t **);
  82         int (*notify_online_list)(rcm_handle_t *, char **, uint_t,
  83             rcm_info_t **);
  84         int (*request_suspend)(rcm_handle_t *, char *, uint_t, timespec_t *,
  85                 rcm_info_t **);
  86         int (*notify_resume)(rcm_handle_t *, char *, uint_t, rcm_info_t **);
  87         int (*notify_remove_list)(rcm_handle_t *, char **, uint_t,
  88             rcm_info_t **);
  89         int (*request_capacity_change)(rcm_handle_t *, char *, uint_t,
  90                 nvlist_t *, rcm_info_t **);
  91         int (*notify_capacity_change)(rcm_handle_t *, char *, uint_t,
  92                 nvlist_t *, rcm_info_t **);
  93 } rcmd_t;
  94 
  95 static char *
  96 ap_rcm_ops[] = {
  97         "rcm_alloc_handle",
  98         "rcm_free_handle",
  99         "rcm_get_info",
 100         "rcm_free_info",
 101         "rcm_info_next",
 102         "rcm_info_state",
 103         "rcm_info_pid",
 104         "rcm_info_error",
 105         "rcm_info_info",
 106         "rcm_info_rsrc",
 107         "rcm_request_offline_list",
 108         "rcm_notify_online_list",
 109         "rcm_request_suspend",
 110         "rcm_notify_resume",
 111         "rcm_notify_remove_list",
 112         "rcm_request_capacity_change",
 113         "rcm_notify_capacity_change",
 114         NULL
 115 };
 116 
 117 #define ALLOC_HANDLE            0
 118 #define FREE_HANDLE             1
 119 #define GET_INFO                2
 120 #define FREE_INFO               3
 121 #define INFO_TUPLE_NEXT         4
 122 #define INFO_TUPLE_STATE        5
 123 #define INFO_TUPLE_ID           6
 124 #define INFO_TUPLE_ERROR        7
 125 #define INFO_TUPLE_INFO         8
 126 #define INFO_TUPLE_RSRC         9
 127 #define REQUEST_OFFLINE         10
 128 #define NOTIFY_ONLINE           11
 129 #define REQUEST_SUSPEND         12
 130 #define NOTIFY_RESUME           13
 131 #define NOTIFY_REMOVE           14
 132 #define REQUEST_CAP_CHANGE      15
 133 #define NOTIFY_CAP_CHANGE       16
 134 
 135 /*
 136  * There is no consumer for SUNW_OS. This is defined here
 137  * for generic OS quiescence.
 138  */
 139 #define OS      "SUNW_OS"       /* XXX */
 140 
 141 /* Max width of an RCM formatted message line */
 142 #define RCM_MAX_FORMAT  80
 143 
 144 #ifdef  __sparcv9
 145 #define RCMLIB  "/lib/sparcv9/librcm.so";
 146 #elif defined(__amd64)
 147 #define RCMLIB  "/lib/amd64/librcm.so";
 148 #else
 149 #define RCMLIB  "/lib/librcm.so";
 150 #endif
 151 
 152 static cfga_err_t
 153 ap_capinfo(apd_t *a, int firstcm, int lastcm, cap_info_t **capinfo)
 154 {
 155         int cm;
 156         int ncm;
 157         void *cap;
 158         int *ncap;
 159         cfga_stat_t *os;
 160         cap_info_t *cinfo, *cp;
 161 
 162         DBG("ap_capinfo(%p)\n", (void *)a);
 163 
 164         if (capinfo == NULL) {
 165                 ap_err(a, ERR_PLUGIN, "null capinfo");
 166                 return (CFGA_LIB_ERROR);
 167         }
 168 
 169         /*
 170          * Assume there are components with valid capacity
 171          * information and allocate space for them.  If there
 172          * are none at the end, free the allocated space.
 173          */
 174         ncm = lastcm - firstcm + 1;
 175 
 176         cinfo = (cap_info_t *)calloc(ncm, sizeof (cap_info_t));
 177         if (cinfo == NULL) {
 178                 ap_err(a, ERR_NOMEM);
 179                 return (CFGA_LIB_ERROR);
 180         }
 181 
 182         *capinfo = NULL;
 183         ncm = 0;
 184         for (cp = cinfo, cm = firstcm; cm <= lastcm; cm++, cp++) {
 185                 os = &cp->ostate;
 186                 ncap = &cp->ncap;
 187 
 188                 switch (ap_cm_type(a, cm)) {
 189                 case AP_CPU:
 190                 case AP_CMP:
 191                         cap = (void *)(cp->type.cpuid);
 192                         break;
 193                 case AP_MEM:
 194                         cap = (void *)&(cp->type.npages);
 195                         break;
 196                 default:
 197                         continue;
 198                 }
 199                 /*
 200                  * Remember which components have valid
 201                  * capacity information.
 202                  */
 203                 if (ap_cm_capacity(a, cm, cap, ncap, os)) {
 204                         cp->valid = 1;
 205                         ncm++;
 206                 }
 207         }
 208 
 209         if (ncm == 0)
 210                 free(cinfo);
 211         else
 212                 *capinfo = cinfo;
 213 
 214         return (CFGA_OK);
 215 }
 216 
 217 static int
 218 getsyscpuids(int *ncpuids, cpuid_t **cpuids)
 219 {
 220         int             ncpu;
 221         int             maxncpu;
 222         kstat_t         *ksp;
 223         kstat_ctl_t     *kc = NULL;
 224         cpuid_t         *cp;
 225 
 226         DBG("getsyscpuids\n");
 227 
 228         if ((maxncpu = sysconf(_SC_NPROCESSORS_MAX)) == -1 ||
 229             (kc = kstat_open()) == NULL ||
 230             (cp = (cpuid_t *)calloc(maxncpu, sizeof (cpuid_t))) == NULL) {
 231                 /* if calloc failed, clean up kstats */
 232                 if (kc != NULL) {
 233                         (void) kstat_close(kc);
 234                 }
 235                 return (-1);
 236         }
 237 
 238         DBG("syscpuids: ");
 239         for (ncpu = 0, ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
 240                 if (strcmp(ksp->ks_module, "cpu_info") == 0) {
 241                         cp[ncpu++] = ksp->ks_instance;
 242                         DBG("%d ", ksp->ks_instance);
 243                 }
 244         }
 245         DBG("\n");
 246 
 247         (void) kstat_close(kc);
 248         *cpuids = cp;
 249         *ncpuids = ncpu;
 250         return (0);
 251 }
 252 
 253 cfga_err_t
 254 ap_rcm_init(apd_t *a)
 255 {
 256         int i;
 257         char *err;
 258         char *rcmlib;
 259         void *sym;
 260         void *lib;
 261         char **op;
 262         rcmd_t *rcm;
 263         cfga_err_t rc;
 264         struct stat buf;
 265 
 266         DBG("ap_rcm_init(%p)\n", (void *)a);
 267 
 268         /*
 269          * If the initial command is status, or the RCM feature is not
 270          * available, or the RCM interface has already been initialized,
 271          * just return.
 272          */
 273 
 274         if ((a->statonly != 0) || (a->norcm != 0) ||
 275             ((rcm = (rcmd_t *)a->rcm) != NULL)) {
 276                 return (CFGA_OK);
 277         }
 278 
 279         rcmlib = RCMLIB;
 280         rc = CFGA_LIB_ERROR;
 281 
 282         DBG("Looking for %s\n", rcmlib);
 283         /*
 284          * If the library is not present, there is nothing more
 285          * to do.  The RCM offline/suspend steps become no-ops
 286          * in that case.
 287          */
 288         if (stat(rcmlib, &buf) == -1) {
 289                 if (errno == ENOENT) {
 290                         a->norcm++;
 291                         ap_msg(a, MSG_NORCM);
 292                         return (CFGA_OK);
 293                 } else {
 294                         ap_err(a, ERR_STAT, rcmlib);
 295                         return (rc);
 296                 }
 297         }
 298         DBG("%s found\n", rcmlib);
 299 
 300         if ((a->rcm = calloc(1, sizeof (rcmd_t))) == NULL) {
 301                 ap_err(a, ERR_NOMEM);
 302                 return (rc);
 303         }
 304 
 305         rcm = (rcmd_t *)a->rcm;
 306 
 307         if ((lib = dlopen(rcmlib, RTLD_NOW)) == NULL) {
 308                 if ((err = dlerror()) != NULL)
 309                         err = strdup(err);
 310                 ap_err(a, ERR_LIB_OPEN, rcmlib, err);
 311                 if (err != NULL)
 312                         free(err);
 313                 return (rc);
 314         }
 315 
 316         rcm->lib = lib;
 317 
 318         for (i = 0, op = ap_rcm_ops; *op != NULL; op++, i++) {
 319                 if ((sym = dlsym(lib, *op)) == NULL) {
 320                         ap_err(a, ERR_LIB_SYM, rcmlib, *op);
 321                         return (rc);
 322                 }
 323                 switch (i) {
 324                 case ALLOC_HANDLE:
 325                         rcm->alloc_handle = (int(*)
 326                             (char *, uint_t, void *, rcm_handle_t **))sym;
 327                         break;
 328                 case FREE_HANDLE:
 329                         rcm->free_handle = (void (*)(rcm_handle_t *))sym;
 330                         break;
 331                 case GET_INFO:
 332                         rcm->get_info = (int (*)
 333                             (rcm_handle_t *, char *, uint_t, rcm_info_t **))sym;
 334                         break;
 335                 case FREE_INFO:
 336                         rcm->free_info = (void (*)(rcm_info_t *))sym;
 337                         break;
 338                 case INFO_TUPLE_NEXT:
 339                         rcm->info_next = (rcm_info_tuple_t *(*)
 340                             (rcm_info_t *, rcm_info_tuple_t *))sym;
 341                         break;
 342                 case INFO_TUPLE_STATE:
 343                         rcm->info_state = (int (*)(rcm_info_tuple_t *))sym;
 344                         break;
 345                 case INFO_TUPLE_ID:
 346                         rcm->info_pid = (pid_t (*)(rcm_info_tuple_t *))sym;
 347                         break;
 348                 case INFO_TUPLE_ERROR:
 349                         rcm->info_error = (const char *(*)
 350                             (rcm_info_tuple_t *))sym;
 351                         break;
 352                 case INFO_TUPLE_INFO:
 353                         rcm->info_info = (const char *(*)
 354                             (rcm_info_tuple_t *))sym;
 355                         break;
 356                 case INFO_TUPLE_RSRC:
 357                         rcm->info_rsrc = (const char *(*)
 358                             (rcm_info_tuple_t *))sym;
 359                         break;
 360                 case REQUEST_OFFLINE:
 361                         rcm->request_offline_list = (int (*)
 362                             (rcm_handle_t *, char **, uint_t,
 363                             rcm_info_t **))sym;
 364                         break;
 365                 case NOTIFY_ONLINE:
 366                         rcm->notify_online_list = (int (*)
 367                             (rcm_handle_t *, char **, uint_t,
 368                             rcm_info_t **))sym;
 369                         break;
 370                 case REQUEST_SUSPEND:
 371                         rcm->request_suspend = (int (*)
 372                             (rcm_handle_t *, char *, uint_t,
 373                             timespec_t *, rcm_info_t **))sym;
 374                         break;
 375                 case NOTIFY_RESUME:
 376                         rcm->notify_resume = (int (*)
 377                             (rcm_handle_t *, char *, uint_t,
 378                             rcm_info_t **))sym;
 379                         break;
 380                 case NOTIFY_REMOVE:
 381                         rcm->notify_remove_list = (int (*)
 382                             (rcm_handle_t *, char **, uint_t,
 383                             rcm_info_t **))sym;
 384                         break;
 385                 case REQUEST_CAP_CHANGE:
 386                         rcm->request_capacity_change = (int (*)
 387                             (rcm_handle_t *, char *, uint_t,
 388                             nvlist_t *, rcm_info_t **))sym;
 389                         break;
 390                 case NOTIFY_CAP_CHANGE:
 391                         rcm->notify_capacity_change = (int (*)
 392                             (rcm_handle_t *, char *, uint_t,
 393                             nvlist_t *, rcm_info_t **))sym;
 394                         break;
 395                 default:
 396                         break;
 397                 }
 398         }
 399 
 400         if (rcm->alloc_handle == NULL ||
 401             (*rcm->alloc_handle)(NULL, RCM_NOPID, NULL, &rcm->hd)
 402             != RCM_SUCCESS) {
 403                 ap_err(a, ERR_RCM_HANDLE);
 404                 return (CFGA_LIB_ERROR);
 405         }
 406 
 407         /*
 408          * Offlining/onlining a board means offlining/onlining
 409          * all components on the board.  When operating on a
 410          * single component no component sequence number is
 411          * needed since the default is the current (target)
 412          * component.
 413          */
 414         if (a->tgt == AP_BOARD) {
 415                 rcm->firstcm = 0;
 416                 rcm->lastcm = a->ncm - 1;
 417         } else {
 418                 rcm->firstcm = CM_DFLT;
 419                 rcm->lastcm = CM_DFLT;
 420         }
 421 
 422         if (rcm->cpuids == NULL) {
 423                 int cm;
 424                 int ncpu;
 425 
 426                 /*
 427                  * Allocate space for the cpu capacity change info.
 428                  * Not every cpu may be relevant to the capacity
 429                  * request, but allocating for the maximum makes
 430                  * it easier, and the space is insignifcant.
 431                  */
 432                 for (ncpu = 0, cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
 433 
 434                         ap_target_t type = ap_cm_type(a, cm);
 435 
 436                         if ((type == AP_CPU) || (type == AP_CMP)) {
 437                                 ncpu += ap_cm_ncap(a, cm);
 438                         }
 439                 }
 440 
 441                 rcm->ncpus = ncpu;
 442                 if ((rcm->cpuids = (cpuid_t *)calloc(ncpu, sizeof (cpuid_t)))
 443                     == NULL) {
 444                         ap_err(a, ERR_NOMEM);
 445                         return (CFGA_LIB_ERROR);
 446                 }
 447         }
 448 
 449         /*
 450          * Remember initial capacity information.
 451          * This information is based on the initial
 452          * state of the ap_id, i.e. before any
 453          * state change change operations were
 454          * executed.  We will later get the
 455          * current capacity information in order
 456          * to figure out exactly what has changed
 457          * as the result of the executed command
 458          * sequence.
 459          */
 460         rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &rcm->capinfo);
 461 
 462         rcm->capcpus = sysconf(_SC_NPROCESSORS_CONF);
 463         rcm->cappages = sysconf(_SC_PHYS_PAGES);
 464 
 465         return (rc);
 466 }
 467 
 468 void
 469 ap_rcm_fini(apd_t *a)
 470 {
 471         rcmd_t *rcm;
 472         char **rp;
 473 
 474         DBG("ap_rcm_fini(%p)\n", (void *)a);
 475 
 476         if ((rcm = (rcmd_t *)a->rcm) == NULL)
 477                 return;
 478 
 479         if (rcm->hd)
 480                 (*rcm->free_handle)(rcm->hd);
 481 
 482         (void) dlclose(rcm->lib);
 483 
 484         /*
 485          * Free all the names in the resource list, followed
 486          * by the resource list itself.
 487          */
 488         if (rcm->rlist)
 489                 for (rp = rcm->rlist; *rp; rp++)
 490                         s_free(*rp);
 491         s_free(rcm->rlist);
 492         s_free(rcm->cpuids);
 493         s_free(rcm->capinfo);
 494         s_free(a->rcm);
 495 }
 496 
 497 static cfga_err_t
 498 ap_rcm_rlist(apd_t *a, int firstcm, int lastcm, char ***rlist, int cmd)
 499 {
 500         int n;
 501         int cm;
 502         int ncap;
 503         char *path;
 504         char *cpuname;
 505         char **rp;
 506 
 507         DBG("ap_rcm_rlist(%p)\n", (void *)a);
 508 
 509         /*
 510          * Allocate space for the maximum number of components
 511          * that can be affected by this operation.
 512          */
 513         for (ncap = 0, cm = firstcm; cm <= lastcm; cm++) {
 514                 ncap += ap_cm_ncap(a, cm);
 515         }
 516 
 517         DBG("ncap=%d\n", ncap);
 518 
 519         if ((rp = (char **)calloc(ncap + 1, sizeof (char *))) == NULL) {
 520                 ap_err(a, ERR_NOMEM);
 521                 return (CFGA_LIB_ERROR);
 522         }
 523 
 524         n = 12; /* SUNW_cpu/cpuCCC */
 525                 /* <--- 12 --->    */
 526         cpuname = "SUNW_cpu/cpuCCC";
 527         /*
 528          * Set the RCM resource name for each component:
 529          *
 530          * io:          <device-path>
 531          * cpu:         SUNW_cpu/cpu<cpuid>
 532          *
 533          */
 534         for (ncap = 0, cm = firstcm; cm <= lastcm; cm++) {
 535                 switch (ap_cm_type(a, cm)) {
 536                 case AP_CPU:
 537                 case AP_CMP: {
 538                         int             i;
 539                         int             len;
 540                         cap_info_t      cap;
 541                         cfga_stat_t     os;
 542                         cpuid_t         *cpuid;
 543                         int             *nc;
 544                         cap_info_t      *prevcap;
 545                         rcmd_t          *rcm;
 546                         int             allow_op;
 547                         int             capindex;
 548 
 549                         cpuid = cap.type.cpuid;
 550                         nc = &cap.ncap;
 551 
 552                         /*
 553                          * See if the request target is a single
 554                          * (default) component
 555                          */
 556                         capindex = (cm == CM_DFLT) ? 0 : cm;
 557 
 558                         /* Get the previous capacity info */
 559                         rcm = (rcmd_t *)a->rcm;
 560                         prevcap = rcm->capinfo;
 561 
 562                         if (!ap_cm_capacity(a, cm, cpuid, nc, &os)) {
 563                                 break;
 564                         }
 565 
 566                         len = (strlen(cpuname) - n) + 1;
 567 
 568                         /*
 569                          * For CMD_RCM_OFFLINE and REMOVE, add the CPU to the
 570                          * list if it is currently configured. For
 571                          * CMD_RCM_ONLINE, do so only if the state has changed
 572                          * to CFGA_STAT_CONFIGURED.
 573                          */
 574                         allow_op = 0;
 575                         if ((cmd == CMD_RCM_OFFLINE) ||
 576                             (cmd == CMD_RCM_REMOVE)) {
 577                                 if (os == CFGA_STAT_CONFIGURED)
 578                                         allow_op = 1;
 579                         } else {
 580                                 if ((os == CFGA_STAT_CONFIGURED) &&
 581                                     ((prevcap == NULL) ||
 582                                     (prevcap[capindex].ostate != os)))
 583                                         allow_op = 1;
 584                         }
 585 
 586                         if (allow_op) {
 587                                 for (i = 0; i < *nc; i++) {
 588                                         if ((path = strdup(cpuname)) == NULL) {
 589                                                 ap_err(a, ERR_NOMEM);
 590                                                 return (CFGA_LIB_ERROR);
 591                                         }
 592                                         (void) snprintf(&path[n], len, "%d",
 593                                             cpuid[i]);
 594 
 595                                         DBG("rp[%d]=%s\n", ncap, path);
 596                                         rp[ncap++] = path;
 597                                 }
 598                         }
 599                         break;
 600                 }
 601                 case AP_IO:
 602                         if ((path = ap_cm_devpath(a, cm)) != NULL) {
 603                                 DBG("rp[%d]=%s\n", ncap, path);
 604                                 rp[ncap++] = path;
 605                         }
 606                         break;
 607                 case AP_MEM:
 608                         /*
 609                          * Nothing to do for AP_MEM since only capacity
 610                          * change notifications apply to SUNW_memory
 611                          */
 612                 default:
 613                         break;
 614                 }
 615         }
 616 
 617         rp[ncap] = NULL;
 618         if (rlist)
 619                 *rlist = rp;
 620         return (CFGA_OK);
 621 }
 622 
 623 /*
 624  * Returns 1 if the cpu ID 'cpuid' is in the list of CPU IDs
 625  * 'list' of length 'length'. Returns 0 otherwise.
 626  */
 627 static int
 628 is_cpu_in_list(cpuid_t cpuid, cpuid_t *list, int length)
 629 {
 630         int i;
 631 
 632         DBG("is_cpu_in_list\n");
 633 
 634         if (list == NULL)
 635                 return (0);
 636 
 637         for (i = 0; i < length; i++) {
 638                 if (list[i] == cpuid)
 639                         return (1);
 640         }
 641         return (0);
 642 }
 643 
 644 static int
 645 ap_rcm_cap_cpu(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd, uint_t flags,
 646         rcm_info_t **rinfo, int cmd, int change)
 647 {
 648         int i;
 649         int rv = RCM_FAILURE;
 650         int ncpuids;
 651         int oldncpuids;
 652         int newncpuids;
 653         char buf[32];
 654         const char *fmt;
 655         size_t size;
 656         nvlist_t *nvl = NULL;
 657         cpuid_t *cpuids = NULL;
 658         cpuid_t *oldcpuids = NULL;
 659         cpuid_t *newcpuids = NULL;
 660 
 661         DBG("ap_rcm_cap_cpu(%p)\n", (void *)a);
 662 
 663         /*
 664          * Get the current number of configured cpus.
 665          */
 666         if (getsyscpuids(&ncpuids, &cpuids) == -1)
 667                 return (rv);
 668         else if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
 669                 free(cpuids);
 670                 goto done;
 671         }
 672 
 673         if (change == 1)
 674                 fmt = "(%d cpu)";
 675         else
 676                 fmt = "(%d cpus)";
 677 
 678         size = sizeof (cpuid_t);
 679 
 680         if (cmd == CMD_RCM_CAP_DEL) {
 681                 /*
 682                  * A delete request. rcm->cpuids represents the
 683                  * cpus that will be unconfigured. The current
 684                  * set of cpus, before the unconfigure operation,
 685                  * are the old CPUs. The new CPUs are those
 686                  * that would remain.
 687                  */
 688                 oldncpuids = ncpuids;
 689                 oldcpuids = cpuids;
 690 
 691                 /*
 692                  * Fill newcpuids with the CPU IDs in the cpuids array,
 693                  * but not in rcm->cpuids.
 694                  */
 695                 newcpuids = (cpuid_t *)calloc(ncpuids, size);
 696                 if (newcpuids == NULL)
 697                         goto done;
 698 
 699                 newncpuids = 0;
 700                 for (i = 0; i < ncpuids; i++) {
 701                         if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
 702                                 newcpuids[newncpuids++] = cpuids[i];
 703                 }
 704         } else if (cmd == CMD_RCM_CAP_NOTIFY) {
 705                 /*
 706                  * An unconfigure capacity change notification. This
 707                  * notification is sent after a DR unconfigure, whether
 708                  * or not the DR was successful. rcm->cpuids represents
 709                  * the CPUs that have been unconfigured.
 710                  */
 711 
 712                 /* New CPU IDs are the CPUs configured right now. */
 713                 newncpuids = ncpuids;
 714                 newcpuids = cpuids;
 715 
 716                 /*
 717                  * Old CPU IDs are the CPUs configured right now
 718                  * in addition to those that have been unconfigured.
 719                  * We build the old CPU ID list by concatenating
 720                  * cpuids and rcm->cpuids.
 721                  */
 722                 oldcpuids = (cpuid_t *)calloc(ncpuids + change, size);
 723                 if (oldcpuids == NULL)
 724                         goto done;
 725 
 726                 oldncpuids = 0;
 727                 for (i = 0; i < ncpuids; i++) {
 728                         if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
 729                                 oldcpuids[oldncpuids++] = cpuids[i];
 730                 }
 731                 for (i = 0; i < change; i++)
 732                         oldcpuids[oldncpuids++] = rcm->cpuids[i];
 733         } else {
 734                 DBG("ap_rcm_cap_cpu: CPU capacity, old = %d, new = %d \n",
 735                     rcm->capcpus, ncpuids);
 736                 if (rcm->capcpus == ncpuids) {
 737                         /* No real change in CPU capacity */
 738                         rv = RCM_SUCCESS;
 739                         goto done;
 740                 }
 741 
 742                 /*
 743                  * An add notification.  rcm->cpuids represents the
 744                  * cpus that have been configured.  The current
 745                  * set of cpus, after the configure operation,
 746                  * are the new CPU IDs.
 747                  */
 748                 newncpuids = ncpuids;
 749                 newcpuids = cpuids;
 750 
 751                 /*
 752                  * Fill oldcpuids with the CPU IDs in the cpuids array,
 753                  * but not in rcm->cpuids.
 754                  */
 755                 oldcpuids = (cpuid_t *)calloc(ncpuids, size);
 756                 if (oldcpuids == NULL)
 757                         goto done;
 758 
 759                 oldncpuids = 0;
 760                 for (i = 0; i < ncpuids; i++) {
 761                         if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
 762                                 oldcpuids[oldncpuids++] = cpuids[i];
 763                 }
 764         }
 765 
 766         DBG("oldcpuids: ");
 767         for (i = 0; i < oldncpuids; i++)
 768                 DBG("%d ", oldcpuids[i]);
 769         DBG("\n");
 770         DBG("change   : ");
 771         for (i = 0; i < change; i++)
 772                 DBG("%d ", rcm->cpuids[i]);
 773         DBG("\n");
 774         DBG("newcpuids: ");
 775         for (i = 0; i < newncpuids; i++)
 776                 DBG("%d ", newcpuids[i]);
 777         DBG("\n");
 778 
 779         if (nvlist_add_string(nvl, "state", "capacity") != 0 ||
 780             nvlist_add_int32(nvl, "old_total", oldncpuids) != 0 ||
 781             nvlist_add_int32(nvl, "new_total", newncpuids) != 0 ||
 782             nvlist_add_int32_array(nvl, "old_cpu_list", oldcpuids,
 783             oldncpuids) != 0 ||
 784             nvlist_add_int32_array(nvl, "new_cpu_list", newcpuids,
 785             newncpuids) != 0)
 786                 goto done;
 787 
 788         (void) snprintf(buf, sizeof (buf), fmt, change);
 789         ap_msg(a, MSG_ISSUE, cmd, buf);
 790 
 791         if (cmd == CMD_RCM_CAP_DEL) {
 792                 rv = (*rcm->request_capacity_change)(hd, "SUNW_cpu",
 793                     flags, nvl, rinfo);
 794         } else {
 795                 rv = (*rcm->notify_capacity_change)(hd, "SUNW_cpu",
 796                     flags & ~RCM_FORCE, nvl, rinfo);
 797         }
 798 
 799 done:
 800         nvlist_free(nvl);
 801         s_free(oldcpuids);
 802         s_free(newcpuids);
 803         return (rv);
 804 }
 805 
 806 static int
 807 ap_rcm_cap_mem(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd, uint_t flags,
 808         rcm_info_t **rinfo, int cmd, long change)
 809 {
 810         int rv;
 811         int pgsize;
 812         long oldpages;
 813         long newpages;
 814         long currpages;
 815         char buf[32];
 816         nvlist_t *nvl;
 817 
 818         DBG("ap_rcm_cap_mem(%p)\n", (void *)a);
 819 
 820         /*
 821          * Get the current amount of configured memory.
 822          */
 823         if ((pgsize = sysconf(_SC_PAGE_SIZE)) == -1 ||
 824             (currpages = sysconf(_SC_PHYS_PAGES)) == -1 ||
 825             nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) > 0)
 826                 return (RCM_FAILURE);
 827 
 828         /*
 829          * If this is a (delete) request, change represents
 830          * the amount of capacity that will be deleted from the
 831          * system.  If this is an (add) notification, change
 832          * represents the amount of capacity that has already
 833          * been added to the system.
 834          */
 835         if (cmd == CMD_RCM_CAP_DEL) {
 836                 oldpages = currpages;
 837                 newpages = currpages - change;
 838         } else if (cmd == CMD_RCM_CAP_NOTIFY) {
 839                 newpages = currpages;
 840                 oldpages = rcm->cappages;
 841         } else {
 842                 if (rcm->cappages == currpages) {
 843                         /* No real change in memory capacity */
 844                         DBG("ap_rcm_cap_mem: no change in capacity.\n");
 845                         nvlist_free(nvl);
 846                         return (RCM_SUCCESS);
 847                 }
 848 
 849                 oldpages = currpages - change;
 850                 newpages = currpages;
 851         }
 852 
 853         DBG("ap_rcm_cap_mem: Memory capacity, old = %ld, new = %ld\n",
 854             oldpages, newpages);
 855 
 856         if (nvlist_add_string(nvl, "state", "capacity") != 0 ||
 857             nvlist_add_int32(nvl, "page_size", pgsize) != 0 ||
 858             nvlist_add_int32(nvl, "old_pages", oldpages) != 0 ||
 859             nvlist_add_int32(nvl, "new_pages", newpages) != 0) {
 860                 nvlist_free(nvl);
 861                 return (RCM_FAILURE);
 862         }
 863 
 864         (void) snprintf(buf, sizeof (buf), "(%ld pages)", change);
 865         ap_msg(a, MSG_ISSUE, cmd, buf);
 866 
 867         if (cmd == CMD_RCM_CAP_DEL) {
 868                 rv = (*rcm->request_capacity_change)(hd, "SUNW_memory",
 869                     flags, nvl, rinfo);
 870         } else {
 871                 rv = (*rcm->notify_capacity_change)(hd, "SUNW_memory",
 872                     flags & ~RCM_FORCE, nvl, rinfo);
 873         }
 874 
 875         nvlist_free(nvl);
 876 
 877         return (rv);
 878 }
 879 
 880 static cfga_err_t
 881 ap_rcm_request_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
 882         int *rv, uint_t flags, rcm_info_t **rinfo)
 883 {
 884         int cm;
 885         int ncpus;
 886         long npages;
 887         cap_info_t *capinfo;
 888         ap_target_t type;
 889 
 890         DBG("ap_rcm_request_cap(%p)\n", (void *)a);
 891 
 892         if ((capinfo = rcm->capinfo) == NULL) {
 893                 ap_err(a, ERR_PLUGIN, "null capinfo");
 894                 return (CFGA_LIB_ERROR);
 895         }
 896 
 897         ncpus = npages = 0;
 898 
 899         for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
 900                 int i, j;
 901 
 902                 /*
 903                  * See if the request target is a single
 904                  * (default) component
 905                  */
 906                 i = (cm == CM_DFLT) ? 0 : cm;
 907 
 908                 /*
 909                  * We are interested only in those components
 910                  * in the configured state since they represent
 911                  * available capacity.
 912                  */
 913                 type = ap_cm_type(a, cm);
 914                 if (capinfo[i].valid == 0 ||
 915                     capinfo[i].ostate != CFGA_STAT_CONFIGURED)
 916                         continue;
 917                 else if ((type == AP_CPU) || (type == AP_CMP)) {
 918                         for (j = 0; j < capinfo[i].ncap; j++) {
 919                                 rcm->cpuids[ncpus++] = capinfo[i].type.cpuid[j];
 920                         }
 921                 } else if (type == AP_MEM)
 922                         npages += capinfo[i].type.npages;
 923         }
 924 
 925         if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
 926             CMD_RCM_CAP_DEL, ncpus)) != RCM_SUCCESS)) {
 927                 return (CFGA_LIB_ERROR);
 928         }
 929         if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
 930             CMD_RCM_CAP_DEL, npages)) != RCM_SUCCESS)) {
 931                 return (CFGA_LIB_ERROR);
 932         }
 933 
 934         return (CFGA_OK);
 935 }
 936 
 937 static cfga_err_t
 938 ap_rcm_add_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
 939         int *rv, uint_t flags, rcm_info_t **rinfo)
 940 {
 941         int cm;
 942         int ncpus;
 943         long npages;
 944         cap_info_t *capinfo, *prevcapinfo;
 945         cfga_err_t rc;
 946 
 947         DBG("ap_rcm_add_cap(%p)\n", (void *)a);
 948 
 949         /* Get the new capacity info to figure out what has changed */
 950         if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
 951             CFGA_OK)
 952                 return (rc);
 953 
 954         if (capinfo == NULL) {
 955                 DBG("no pertinent capacity info\n");
 956                 return (CFGA_OK);
 957         }
 958 
 959         ncpus = npages = 0;
 960         prevcapinfo = rcm->capinfo;
 961 
 962         for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
 963                 int i, j;
 964                 cfga_stat_t os, prevos;
 965                 int prevvalidity;
 966                 ap_target_t type;
 967 
 968                 /*
 969                  * See if the request target is a single
 970                  * (default) component
 971                  */
 972                 i = cm == CM_DFLT ? 0 : cm;
 973 
 974                 os = capinfo[i].ostate;
 975                 if (prevcapinfo == NULL) {
 976                         prevos = CFGA_STAT_EMPTY;
 977                         prevvalidity = 1;
 978                 } else {
 979                         prevos = prevcapinfo[i].ostate;
 980                         prevvalidity = prevcapinfo[i].valid;
 981                 }
 982 
 983                 type = ap_cm_type(a, cm);
 984 
 985                 DBG("cm=%d valid=%d type=%d, prevos=%d os=%d\n",
 986                     cm, prevvalidity, type, prevos, os);
 987 
 988                 /*
 989                  * We are interested only in those components
 990                  * whose states have changed to configured as
 991                  * the result of the current cfgadm request.
 992                  */
 993                 if (prevvalidity == 0 || os != CFGA_STAT_CONFIGURED) {
 994                         capinfo[i].valid = 0;
 995                         continue;
 996                 } else if (prevos != CFGA_STAT_CONFIGURED) {
 997                         /*
 998                          * The occupant state is configured, and
 999                          * the previous occupant state was not.
1000                          */
1001                         if ((type == AP_CPU) || (type == AP_CMP)) {
1002                                 for (j = 0; j < capinfo[i].ncap; j++) {
1003                                         rcm->cpuids[ncpus++] =
1004                                             capinfo[i].type.cpuid[j];
1005                                 }
1006                         } else if (type == AP_MEM)
1007                                 npages += capinfo[i].type.npages;
1008                 }
1009         }
1010         free(capinfo);
1011 
1012         if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1013             CMD_RCM_CAP_ADD, ncpus)) != RCM_SUCCESS)) {
1014                 return (CFGA_LIB_ERROR);
1015         }
1016         if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1017             CMD_RCM_CAP_ADD, npages)) != RCM_SUCCESS)) {
1018                 return (CFGA_LIB_ERROR);
1019         }
1020 
1021         return (CFGA_OK);
1022 }
1023 
1024 /*
1025  * ap_rcm_notify_cap:
1026  *
1027  * This routine handles the CMD_RCM_CAP_NOTIFY command. It
1028  * is called after a successful/failed DR unconfigure
1029  * operation. It filters out components that have changed
1030  * and passes this information on to ap_rcm_cap_{cpu,mem}.
1031  *
1032  * ap_rcm_cap_{cpu,mem} will still be called if all the
1033  * components have not changed and at least one {cpu,mem}
1034  * component was originally configured.
1035  */
1036 static cfga_err_t
1037 ap_rcm_notify_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
1038         int *rv, uint_t flags, rcm_info_t **rinfo)
1039 {
1040         cfga_err_t  rc;
1041         cap_info_t  *capinfo;
1042         cap_info_t  *prevcapinfo;
1043         int         cm;
1044         long        npages      = 0;
1045         int         ncpus       = 0;
1046         int         prev_mem    = 0; /* # of prev. configured mem components */
1047         int         prev_cpus   = 0; /* # of prev. configured CPUs */
1048 
1049         DBG("ap_rcm_notify_cap(%p)\n", (void *)a);
1050 
1051         /* Get the new capacity info to figure out what has changed */
1052         if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
1053             CFGA_OK)
1054                 return (rc);
1055 
1056         if (capinfo == NULL) {
1057                 DBG("no pertinent capacity info\n");
1058                 return (CFGA_OK);
1059         }
1060 
1061         /* The original capacity info */
1062         prevcapinfo = rcm->capinfo;
1063 
1064         /*
1065          * Cycle through all components that we are operating
1066          * on. Record which components' occupant states have
1067          * changed.
1068          */
1069         for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
1070                 int i;
1071                 cfga_stat_t prevos, os;
1072                 ap_target_t type;
1073                 int prev_conf = 0;
1074                 int now_conf  = 0;
1075 
1076                 /*
1077                  * See if the request target is a single
1078                  * (default) component
1079                  */
1080                 i = cm == CM_DFLT ? 0 : cm;
1081 
1082                 os = capinfo[i].ostate;
1083 
1084                 if (prevcapinfo == NULL) {
1085                         prevos = CFGA_STAT_EMPTY;
1086                 } else {
1087                         prevos = prevcapinfo[i].ostate;
1088                         if (prevcapinfo[i].valid == 0) {
1089                                 DBG("ap_rcm_notify_cap: skipping component "
1090                                     "due to prevvalidity == 0\n");
1091                                 continue;
1092                         }
1093                 }
1094 
1095                 type = ap_cm_type(a, cm);
1096 
1097                 prev_conf = (prevos == CFGA_STAT_CONFIGURED);
1098                 now_conf  = (os == CFGA_STAT_CONFIGURED);
1099 
1100                 /*
1101                  * Build up rcm->cpuids with the IDs of CPUs that
1102                  * have been removed. Record the number of removed
1103                  * CPUs and pages.
1104                  */
1105                 if (type == AP_CPU || type == AP_CMP) {
1106                         if (prev_conf)
1107                                 prev_cpus++;
1108                         if (prev_conf && !now_conf) {
1109                                 int j;
1110                                 for (j = 0; j < capinfo[i].ncap; j++) {
1111                                         rcm->cpuids[ncpus++] =
1112                                             capinfo[i].type.cpuid[j];
1113                                 }
1114                         }
1115                 } else if (type == AP_MEM) {
1116                         if (prev_conf)
1117                                 prev_mem++;
1118                         if (prev_conf && !now_conf)
1119                                 npages += capinfo[i].type.npages;
1120                 }
1121         }
1122         free(capinfo);
1123 
1124         /*
1125          * If any CPU or memory components were operated on,
1126          * successfully or not, the rcm_notify_capacity_change()
1127          * routine must be called.
1128          */
1129 
1130         if (prev_cpus) {
1131                 *rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1132                     CMD_RCM_CAP_NOTIFY, ncpus);
1133 
1134                 if (*rv != RCM_SUCCESS)
1135                         return (CFGA_LIB_ERROR);
1136         }
1137 
1138         if (prev_mem) {
1139                 *rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1140                     CMD_RCM_CAP_NOTIFY, npages);
1141 
1142                 if (*rv != RCM_SUCCESS)
1143                         return (CFGA_LIB_ERROR);
1144         }
1145 
1146         return (CFGA_OK);
1147 }
1148 
1149 cfga_err_t
1150 ap_rcm_ctl(apd_t *a, int cmd)
1151 {
1152         int i;
1153         int rv;
1154         int noop;
1155         int ncpus;
1156         int cm;
1157         uint_t flags;
1158         char *rsrc;
1159         char **rlist;
1160         rcmd_t *rcm;
1161         rcm_info_t *rinfo;
1162         rcm_handle_t *hd;
1163         cfga_err_t rc;
1164         cpuid_t *growcpuids;
1165 
1166         DBG("ap_rcm_ctl(%p)\n", (void *)a);
1167 
1168         if ((rcm = (rcmd_t *)a->rcm) == NULL) {
1169                 ap_msg(a, MSG_SKIP, cmd, a->target);
1170                 return (CFGA_OK);
1171         }
1172 
1173         hd = rcm->hd;
1174         rv = RCM_SUCCESS;
1175         rc = CFGA_OK;
1176         if (ap_getopt(a, OPT_FORCE))
1177                 flags = RCM_FORCE;
1178         else
1179                 flags = 0;
1180         rinfo = NULL;
1181         rlist = NULL;
1182         rsrc = NULL;
1183         noop = 0;
1184 
1185         switch (cmd) {
1186         case CMD_RCM_CAP_DEL:
1187                 if (rcm->capinfo == NULL)
1188                         noop++;
1189                 else
1190                         rc = ap_rcm_request_cap(a, rcm, hd, &rv, flags, &rinfo);
1191                 break;
1192         case CMD_RCM_CAP_ADD:
1193                 rc = ap_rcm_add_cap(a, rcm, hd, &rv, flags, &rinfo);
1194                 break;
1195         case CMD_RCM_CAP_NOTIFY:
1196                 rc = ap_rcm_notify_cap(a, rcm, hd, &rv, flags, &rinfo);
1197                 break;
1198         case CMD_RCM_ONLINE:
1199                 /* Refresh changed component states */
1200                 if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1201                         noop++;
1202                         break;
1203                 }
1204 
1205                 if (a->tgt == AP_BOARD) {
1206                         rcm->firstcm = 0;
1207                         rcm->lastcm = a->ncm - 1;
1208 
1209                         /* Check if we need to grow our cpuids list */
1210                         for (ncpus = 0, cm = rcm->firstcm; cm <= rcm->lastcm;
1211                             cm++) {
1212                                 ap_target_t type = ap_cm_type(a, cm);
1213                                 if ((type == AP_CPU) || (type == AP_CMP))
1214                                         ncpus += ap_cm_ncap(a, cm);
1215                         }
1216 
1217                         if (rcm->ncpus < ncpus) {
1218                                 if ((growcpuids =
1219                                     (cpuid_t *)realloc(rcm->cpuids,
1220                                     (ncpus * sizeof (cpuid_t)))) == NULL) {
1221                                         ap_err(a, ERR_NOMEM);
1222                                         return (CFGA_LIB_ERROR);
1223                                 }
1224                                 rcm->ncpus = ncpus;
1225                                 rcm->cpuids = growcpuids;
1226                         }
1227 
1228                 } else {
1229                         rcm->firstcm = CM_DFLT;
1230                         rcm->lastcm = CM_DFLT;
1231                 }
1232 
1233                 /*FALLTHROUGH*/
1234 
1235         case CMD_RCM_OFFLINE:
1236         case CMD_RCM_REMOVE: {
1237                 uint_t nrsrc;
1238 
1239                 if (cmd == CMD_RCM_REMOVE) {
1240                         /*
1241                          * An unconfigure has just taken place, so
1242                          * refresh the changed component states.
1243                          */
1244                         if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1245                                 noop++;
1246                                 break;
1247                         }
1248                 }
1249 
1250                 /* Check if this is an empty board, i.e. no components */
1251                 if (a->ncm == 0) {
1252                         noop++;
1253                         break;
1254                 }
1255 
1256                 if ((rlist = rcm->rlist) == NULL) {
1257                         rc = ap_rcm_rlist(a, rcm->firstcm, rcm->lastcm, &rlist,
1258                             cmd);
1259                         if ((rc == CFGA_OK) && (rlist != NULL) &&
1260                             (rlist[0] != NULL)) {
1261                                 rcm->rlist = rlist;
1262                         } else {
1263                                 /* Do not pass up empty resource list to RCM */
1264                                 noop++;
1265                                 break;
1266                         }
1267                 }
1268                 for (nrsrc = 0; rlist[nrsrc] != NULL; nrsrc++)
1269                         ap_msg(a, MSG_ISSUE, cmd, rlist[nrsrc]);
1270                 if (cmd == CMD_RCM_OFFLINE)
1271                         rv = (*rcm->request_offline_list)(hd, rlist, flags,
1272                             &rinfo);
1273                 else if (cmd == CMD_RCM_ONLINE)
1274                         rv = (*rcm->notify_online_list)(hd, rlist,
1275                             flags & ~RCM_FORCE, &rinfo);
1276                 else
1277                         rv = (*rcm->notify_remove_list)(hd, rlist,
1278                             flags & ~RCM_FORCE, &rinfo);
1279                 break;
1280         }
1281         case CMD_RCM_SUSPEND: {
1282                 timespec_t t;
1283                 t.tv_sec = (time_t)0;
1284                 t.tv_nsec = (long)0;
1285                 rsrc = OS;
1286                 ap_msg(a, MSG_ISSUE, cmd, rsrc);
1287                 rv = (*rcm->request_suspend)(hd, rsrc, flags, &t, &rinfo);
1288                 break;
1289         }
1290         case CMD_RCM_RESUME:
1291                 rsrc = OS;
1292                 ap_msg(a, MSG_ISSUE, cmd, rsrc);
1293                 rv = (*rcm->notify_resume)(hd, rsrc, 0, &rinfo);
1294                 break;
1295         default:
1296                 ap_err(a, ERR_CMD_INVAL, cmd);
1297                 return (CFGA_INVAL);
1298         }
1299 
1300         if (rv != RCM_SUCCESS) {
1301                 rcm->rinfo = rinfo;
1302                 rcm->infot = NULL;
1303                 ap_err(a, ERR_RCM_CMD, cmd);
1304                 (*rcm->free_info)(rinfo);
1305                 if (rc == CFGA_OK)
1306                         rc = CFGA_LIB_ERROR;    /* make sure error is set */
1307         }
1308         if ((rc == CFGA_OK) && (noop == 0)) {
1309                 if (rlist)
1310                         for (i = 0; rlist[i]; i++)
1311                                 ap_msg(a, MSG_DONE, cmd, rlist[i]);
1312                 else if (rsrc)
1313                         ap_msg(a, MSG_DONE, cmd, rsrc);
1314                 else
1315                         ap_msg(a, MSG_DONE, cmd, a->target);
1316         }
1317 
1318         return (rc);
1319 }
1320 
1321 /*
1322  * ap_rcm_info
1323  *
1324  * Takes an ap_id and a character pointer, and formats
1325  * the rcm_info_t data in the form of a table to the given character pointer.
1326  * Code duplicated from the scsi plugin.
1327  * Note: This function will go away when a generic librcm callback is
1328  *      implemented to format RCM messages for plugins.
1329  */
1330 int
1331 ap_rcm_info(apd_t *a, char **msg)
1332 {
1333         rcmd_t *rcm;
1334         rcm_info_t *rinfo;
1335         int i;
1336         size_t w;
1337         size_t width = 0;
1338         size_t w_rsrc = 0;
1339         size_t w_info = 0;
1340         size_t msg_size = 0;
1341         uint_t tuples = 0;
1342         rcm_info_tuple_t *tuple = NULL;
1343         char *rsrc;
1344         char *info;
1345         char *newmsg;
1346         static char format[RCM_MAX_FORMAT];
1347         const char *infostr;
1348 
1349 
1350         DBG("ap_rcm_info(%p)\n", (void *)a);
1351 
1352         /* Protect against invalid arguments */
1353         if ((a == NULL) || ((rcm = (rcmd_t *)a->rcm) == NULL) ||
1354             ((rinfo = rcm->rinfo) == NULL) || (msg == NULL)) {
1355                 return (-1);
1356         }
1357 
1358         /* Set localized table header strings */
1359         rsrc = dgettext(TEXT_DOMAIN, "Resource");
1360         info = dgettext(TEXT_DOMAIN, "Information");
1361 
1362         /* A first pass, to size up the RCM information */
1363         while (tuple = (*rcm->info_next)(rinfo, tuple)) {
1364                 if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1365                         tuples++;
1366                         if ((w = strlen((*rcm->info_rsrc)(tuple))) > w_rsrc)
1367                                 w_rsrc = w;
1368                         if ((w = strlen(infostr)) > w_info)
1369                                 w_info = w;
1370                 }
1371         }
1372 
1373         /* If nothing was sized up above, stop early */
1374         if (tuples == 0)
1375                 return (0);
1376 
1377         /* Adjust column widths for column headings */
1378         if ((w = strlen(rsrc)) > w_rsrc)
1379                 w_rsrc = w;
1380         else if ((w_rsrc - w) % 2)
1381                 w_rsrc++;
1382         if ((w = strlen(info)) > w_info)
1383                 w_info = w;
1384         else if ((w_info - w) % 2)
1385                 w_info++;
1386 
1387         /*
1388          * Compute the total line width of each line,
1389          * accounting for intercolumn spacing.
1390          */
1391         width = w_info + w_rsrc + 4;
1392 
1393         /* Allocate space for the table */
1394         msg_size = (2 + tuples) * (width + 1) + 2;
1395         if (*msg == NULL) {
1396                 /* zero fill for the strcat() call below */
1397                 *msg = calloc(msg_size, sizeof (char));
1398                 if (*msg == NULL)
1399                         return (-1);
1400         } else {
1401                 newmsg = realloc(*msg, strlen(*msg) + msg_size);
1402                 if (newmsg == NULL)
1403                         return (-1);
1404                 else
1405                         *msg = newmsg;
1406         }
1407 
1408         /* Place a table header into the string */
1409 
1410         /* The resource header */
1411         (void) strcat(*msg, "\n");
1412         w = strlen(rsrc);
1413         for (i = 0; i < ((w_rsrc - w) / 2); i++)
1414                 (void) strcat(*msg, " ");
1415         (void) strcat(*msg, rsrc);
1416         for (i = 0; i < ((w_rsrc - w) / 2); i++)
1417                 (void) strcat(*msg, " ");
1418 
1419         /* The information header */
1420         (void) strcat(*msg, "  ");
1421         w = strlen(info);
1422         for (i = 0; i < ((w_info - w) / 2); i++)
1423                 (void) strcat(*msg, " ");
1424         (void) strcat(*msg, info);
1425         for (i = 0; i < ((w_info - w) / 2); i++)
1426                 (void) strcat(*msg, " ");
1427 
1428         /* Underline the headers */
1429         (void) strcat(*msg, "\n");
1430         for (i = 0; i < w_rsrc; i++)
1431                 (void) strcat(*msg, "-");
1432         (void) strcat(*msg, "  ");
1433         for (i = 0; i < w_info; i++)
1434                 (void) strcat(*msg, "-");
1435 
1436         /* Construct the format string */
1437         (void) snprintf(format, RCM_MAX_FORMAT, "%%-%ds  %%-%ds",
1438             (int)w_rsrc, (int)w_info);
1439 
1440         /* Add the tuples to the table string */
1441         tuple = NULL;
1442         while ((tuple = (*rcm->info_next)(rinfo, tuple)) != NULL) {
1443                 if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1444                         (void) strcat(*msg, "\n");
1445                         (void) sprintf(&((*msg)[strlen(*msg)]), format,
1446                             (*rcm->info_rsrc)(tuple), infostr);
1447                 }
1448         }
1449 
1450         DBG("ap_rcm_info(%p) success\n", (void *)a);
1451         return (0);
1452 }