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         if (nvl)
 801                 nvlist_free(nvl);
 802         s_free(oldcpuids);
 803         s_free(newcpuids);
 804         return (rv);
 805 }
 806 
 807 static int
 808 ap_rcm_cap_mem(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd, uint_t flags,
 809         rcm_info_t **rinfo, int cmd, long change)
 810 {
 811         int rv;
 812         int pgsize;
 813         long oldpages;
 814         long newpages;
 815         long currpages;
 816         char buf[32];
 817         nvlist_t *nvl;
 818 
 819         DBG("ap_rcm_cap_mem(%p)\n", (void *)a);
 820 
 821         /*
 822          * Get the current amount of configured memory.
 823          */
 824         if ((pgsize = sysconf(_SC_PAGE_SIZE)) == -1 ||
 825             (currpages = sysconf(_SC_PHYS_PAGES)) == -1 ||
 826             nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) > 0)
 827                 return (RCM_FAILURE);
 828 
 829         /*
 830          * If this is a (delete) request, change represents
 831          * the amount of capacity that will be deleted from the
 832          * system.  If this is an (add) notification, change
 833          * represents the amount of capacity that has already
 834          * been added to the system.
 835          */
 836         if (cmd == CMD_RCM_CAP_DEL) {
 837                 oldpages = currpages;
 838                 newpages = currpages - change;
 839         } else if (cmd == CMD_RCM_CAP_NOTIFY) {
 840                 newpages = currpages;
 841                 oldpages = rcm->cappages;
 842         } else {
 843                 if (rcm->cappages == currpages) {
 844                         /* No real change in memory capacity */
 845                         DBG("ap_rcm_cap_mem: no change in capacity.\n");
 846                         nvlist_free(nvl);
 847                         return (RCM_SUCCESS);
 848                 }
 849 
 850                 oldpages = currpages - change;
 851                 newpages = currpages;
 852         }
 853 
 854         DBG("ap_rcm_cap_mem: Memory capacity, old = %ld, new = %ld\n",
 855             oldpages, newpages);
 856 
 857         if (nvlist_add_string(nvl, "state", "capacity") != 0 ||
 858             nvlist_add_int32(nvl, "page_size", pgsize) != 0 ||
 859             nvlist_add_int32(nvl, "old_pages", oldpages) != 0 ||
 860             nvlist_add_int32(nvl, "new_pages", newpages) != 0) {
 861                 nvlist_free(nvl);
 862                 return (RCM_FAILURE);
 863         }
 864 
 865         (void) snprintf(buf, sizeof (buf), "(%ld pages)", change);
 866         ap_msg(a, MSG_ISSUE, cmd, buf);
 867 
 868         if (cmd == CMD_RCM_CAP_DEL) {
 869                 rv = (*rcm->request_capacity_change)(hd, "SUNW_memory",
 870                     flags, nvl, rinfo);
 871         } else {
 872                 rv = (*rcm->notify_capacity_change)(hd, "SUNW_memory",
 873                     flags & ~RCM_FORCE, nvl, rinfo);
 874         }
 875 
 876         nvlist_free(nvl);
 877 
 878         return (rv);
 879 }
 880 
 881 static cfga_err_t
 882 ap_rcm_request_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
 883         int *rv, uint_t flags, rcm_info_t **rinfo)
 884 {
 885         int cm;
 886         int ncpus;
 887         long npages;
 888         cap_info_t *capinfo;
 889         ap_target_t type;
 890 
 891         DBG("ap_rcm_request_cap(%p)\n", (void *)a);
 892 
 893         if ((capinfo = rcm->capinfo) == NULL) {
 894                 ap_err(a, ERR_PLUGIN, "null capinfo");
 895                 return (CFGA_LIB_ERROR);
 896         }
 897 
 898         ncpus = npages = 0;
 899 
 900         for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
 901                 int i, j;
 902 
 903                 /*
 904                  * See if the request target is a single
 905                  * (default) component
 906                  */
 907                 i = (cm == CM_DFLT) ? 0 : cm;
 908 
 909                 /*
 910                  * We are interested only in those components
 911                  * in the configured state since they represent
 912                  * available capacity.
 913                  */
 914                 type = ap_cm_type(a, cm);
 915                 if (capinfo[i].valid == 0 ||
 916                     capinfo[i].ostate != CFGA_STAT_CONFIGURED)
 917                         continue;
 918                 else if ((type == AP_CPU) || (type == AP_CMP)) {
 919                         for (j = 0; j < capinfo[i].ncap; j++) {
 920                                 rcm->cpuids[ncpus++] = capinfo[i].type.cpuid[j];
 921                         }
 922                 } else if (type == AP_MEM)
 923                         npages += capinfo[i].type.npages;
 924         }
 925 
 926         if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
 927             CMD_RCM_CAP_DEL, ncpus)) != RCM_SUCCESS)) {
 928                 return (CFGA_LIB_ERROR);
 929         }
 930         if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
 931             CMD_RCM_CAP_DEL, npages)) != RCM_SUCCESS)) {
 932                 return (CFGA_LIB_ERROR);
 933         }
 934 
 935         return (CFGA_OK);
 936 }
 937 
 938 static cfga_err_t
 939 ap_rcm_add_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
 940         int *rv, uint_t flags, rcm_info_t **rinfo)
 941 {
 942         int cm;
 943         int ncpus;
 944         long npages;
 945         cap_info_t *capinfo, *prevcapinfo;
 946         cfga_err_t rc;
 947 
 948         DBG("ap_rcm_add_cap(%p)\n", (void *)a);
 949 
 950         /* Get the new capacity info to figure out what has changed */
 951         if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
 952             CFGA_OK)
 953                 return (rc);
 954 
 955         if (capinfo == NULL) {
 956                 DBG("no pertinent capacity info\n");
 957                 return (CFGA_OK);
 958         }
 959 
 960         ncpus = npages = 0;
 961         prevcapinfo = rcm->capinfo;
 962 
 963         for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
 964                 int i, j;
 965                 cfga_stat_t os, prevos;
 966                 int prevvalidity;
 967                 ap_target_t type;
 968 
 969                 /*
 970                  * See if the request target is a single
 971                  * (default) component
 972                  */
 973                 i = cm == CM_DFLT ? 0 : cm;
 974 
 975                 os = capinfo[i].ostate;
 976                 if (prevcapinfo == NULL) {
 977                         prevos = CFGA_STAT_EMPTY;
 978                         prevvalidity = 1;
 979                 } else {
 980                         prevos = prevcapinfo[i].ostate;
 981                         prevvalidity = prevcapinfo[i].valid;
 982                 }
 983 
 984                 type = ap_cm_type(a, cm);
 985 
 986                 DBG("cm=%d valid=%d type=%d, prevos=%d os=%d\n",
 987                     cm, prevvalidity, type, prevos, os);
 988 
 989                 /*
 990                  * We are interested only in those components
 991                  * whose states have changed to configured as
 992                  * the result of the current cfgadm request.
 993                  */
 994                 if (prevvalidity == 0 || os != CFGA_STAT_CONFIGURED) {
 995                         capinfo[i].valid = 0;
 996                         continue;
 997                 } else if (prevos != CFGA_STAT_CONFIGURED) {
 998                         /*
 999                          * The occupant state is configured, and
1000                          * the previous occupant state was not.
1001                          */
1002                         if ((type == AP_CPU) || (type == AP_CMP)) {
1003                                 for (j = 0; j < capinfo[i].ncap; j++) {
1004                                         rcm->cpuids[ncpus++] =
1005                                             capinfo[i].type.cpuid[j];
1006                                 }
1007                         } else if (type == AP_MEM)
1008                                 npages += capinfo[i].type.npages;
1009                 }
1010         }
1011         free(capinfo);
1012 
1013         if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1014             CMD_RCM_CAP_ADD, ncpus)) != RCM_SUCCESS)) {
1015                 return (CFGA_LIB_ERROR);
1016         }
1017         if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1018             CMD_RCM_CAP_ADD, npages)) != RCM_SUCCESS)) {
1019                 return (CFGA_LIB_ERROR);
1020         }
1021 
1022         return (CFGA_OK);
1023 }
1024 
1025 /*
1026  * ap_rcm_notify_cap:
1027  *
1028  * This routine handles the CMD_RCM_CAP_NOTIFY command. It
1029  * is called after a successful/failed DR unconfigure
1030  * operation. It filters out components that have changed
1031  * and passes this information on to ap_rcm_cap_{cpu,mem}.
1032  *
1033  * ap_rcm_cap_{cpu,mem} will still be called if all the
1034  * components have not changed and at least one {cpu,mem}
1035  * component was originally configured.
1036  */
1037 static cfga_err_t
1038 ap_rcm_notify_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
1039         int *rv, uint_t flags, rcm_info_t **rinfo)
1040 {
1041         cfga_err_t  rc;
1042         cap_info_t  *capinfo;
1043         cap_info_t  *prevcapinfo;
1044         int         cm;
1045         long        npages      = 0;
1046         int         ncpus       = 0;
1047         int         prev_mem    = 0; /* # of prev. configured mem components */
1048         int         prev_cpus   = 0; /* # of prev. configured CPUs */
1049 
1050         DBG("ap_rcm_notify_cap(%p)\n", (void *)a);
1051 
1052         /* Get the new capacity info to figure out what has changed */
1053         if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo)) !=
1054             CFGA_OK)
1055                 return (rc);
1056 
1057         if (capinfo == NULL) {
1058                 DBG("no pertinent capacity info\n");
1059                 return (CFGA_OK);
1060         }
1061 
1062         /* The original capacity info */
1063         prevcapinfo = rcm->capinfo;
1064 
1065         /*
1066          * Cycle through all components that we are operating
1067          * on. Record which components' occupant states have
1068          * changed.
1069          */
1070         for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
1071                 int i;
1072                 cfga_stat_t prevos, os;
1073                 ap_target_t type;
1074                 int prev_conf = 0;
1075                 int now_conf  = 0;
1076 
1077                 /*
1078                  * See if the request target is a single
1079                  * (default) component
1080                  */
1081                 i = cm == CM_DFLT ? 0 : cm;
1082 
1083                 os = capinfo[i].ostate;
1084 
1085                 if (prevcapinfo == NULL) {
1086                         prevos = CFGA_STAT_EMPTY;
1087                 } else {
1088                         prevos = prevcapinfo[i].ostate;
1089                         if (prevcapinfo[i].valid == 0) {
1090                                 DBG("ap_rcm_notify_cap: skipping component "
1091                                     "due to prevvalidity == 0\n");
1092                                 continue;
1093                         }
1094                 }
1095 
1096                 type = ap_cm_type(a, cm);
1097 
1098                 prev_conf = (prevos == CFGA_STAT_CONFIGURED);
1099                 now_conf  = (os == CFGA_STAT_CONFIGURED);
1100 
1101                 /*
1102                  * Build up rcm->cpuids with the IDs of CPUs that
1103                  * have been removed. Record the number of removed
1104                  * CPUs and pages.
1105                  */
1106                 if (type == AP_CPU || type == AP_CMP) {
1107                         if (prev_conf)
1108                                 prev_cpus++;
1109                         if (prev_conf && !now_conf) {
1110                                 int j;
1111                                 for (j = 0; j < capinfo[i].ncap; j++) {
1112                                         rcm->cpuids[ncpus++] =
1113                                             capinfo[i].type.cpuid[j];
1114                                 }
1115                         }
1116                 } else if (type == AP_MEM) {
1117                         if (prev_conf)
1118                                 prev_mem++;
1119                         if (prev_conf && !now_conf)
1120                                 npages += capinfo[i].type.npages;
1121                 }
1122         }
1123         free(capinfo);
1124 
1125         /*
1126          * If any CPU or memory components were operated on,
1127          * successfully or not, the rcm_notify_capacity_change()
1128          * routine must be called.
1129          */
1130 
1131         if (prev_cpus) {
1132                 *rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
1133                     CMD_RCM_CAP_NOTIFY, ncpus);
1134 
1135                 if (*rv != RCM_SUCCESS)
1136                         return (CFGA_LIB_ERROR);
1137         }
1138 
1139         if (prev_mem) {
1140                 *rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
1141                     CMD_RCM_CAP_NOTIFY, npages);
1142 
1143                 if (*rv != RCM_SUCCESS)
1144                         return (CFGA_LIB_ERROR);
1145         }
1146 
1147         return (CFGA_OK);
1148 }
1149 
1150 cfga_err_t
1151 ap_rcm_ctl(apd_t *a, int cmd)
1152 {
1153         int i;
1154         int rv;
1155         int noop;
1156         int ncpus;
1157         int cm;
1158         uint_t flags;
1159         char *rsrc;
1160         char **rlist;
1161         rcmd_t *rcm;
1162         rcm_info_t *rinfo;
1163         rcm_handle_t *hd;
1164         cfga_err_t rc;
1165         cpuid_t *growcpuids;
1166 
1167         DBG("ap_rcm_ctl(%p)\n", (void *)a);
1168 
1169         if ((rcm = (rcmd_t *)a->rcm) == NULL) {
1170                 ap_msg(a, MSG_SKIP, cmd, a->target);
1171                 return (CFGA_OK);
1172         }
1173 
1174         hd = rcm->hd;
1175         rv = RCM_SUCCESS;
1176         rc = CFGA_OK;
1177         if (ap_getopt(a, OPT_FORCE))
1178                 flags = RCM_FORCE;
1179         else
1180                 flags = 0;
1181         rinfo = NULL;
1182         rlist = NULL;
1183         rsrc = NULL;
1184         noop = 0;
1185 
1186         switch (cmd) {
1187         case CMD_RCM_CAP_DEL:
1188                 if (rcm->capinfo == NULL)
1189                         noop++;
1190                 else
1191                         rc = ap_rcm_request_cap(a, rcm, hd, &rv, flags, &rinfo);
1192                 break;
1193         case CMD_RCM_CAP_ADD:
1194                 rc = ap_rcm_add_cap(a, rcm, hd, &rv, flags, &rinfo);
1195                 break;
1196         case CMD_RCM_CAP_NOTIFY:
1197                 rc = ap_rcm_notify_cap(a, rcm, hd, &rv, flags, &rinfo);
1198                 break;
1199         case CMD_RCM_ONLINE:
1200                 /* Refresh changed component states */
1201                 if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1202                         noop++;
1203                         break;
1204                 }
1205 
1206                 if (a->tgt == AP_BOARD) {
1207                         rcm->firstcm = 0;
1208                         rcm->lastcm = a->ncm - 1;
1209 
1210                         /* Check if we need to grow our cpuids list */
1211                         for (ncpus = 0, cm = rcm->firstcm; cm <= rcm->lastcm;
1212                             cm++) {
1213                                 ap_target_t type = ap_cm_type(a, cm);
1214                                 if ((type == AP_CPU) || (type == AP_CMP))
1215                                         ncpus += ap_cm_ncap(a, cm);
1216                         }
1217 
1218                         if (rcm->ncpus < ncpus) {
1219                                 if ((growcpuids =
1220                                     (cpuid_t *)realloc(rcm->cpuids,
1221                                     (ncpus * sizeof (cpuid_t)))) == NULL) {
1222                                         ap_err(a, ERR_NOMEM);
1223                                         return (CFGA_LIB_ERROR);
1224                                 }
1225                                 rcm->ncpus = ncpus;
1226                                 rcm->cpuids = growcpuids;
1227                         }
1228 
1229                 } else {
1230                         rcm->firstcm = CM_DFLT;
1231                         rcm->lastcm = CM_DFLT;
1232                 }
1233 
1234                 /*FALLTHROUGH*/
1235 
1236         case CMD_RCM_OFFLINE:
1237         case CMD_RCM_REMOVE: {
1238                 uint_t nrsrc;
1239 
1240                 if (cmd == CMD_RCM_REMOVE) {
1241                         /*
1242                          * An unconfigure has just taken place, so
1243                          * refresh the changed component states.
1244                          */
1245                         if ((rc = ap_stat(a, 1)) != CFGA_OK) {
1246                                 noop++;
1247                                 break;
1248                         }
1249                 }
1250 
1251                 /* Check if this is an empty board, i.e. no components */
1252                 if (a->ncm == 0) {
1253                         noop++;
1254                         break;
1255                 }
1256 
1257                 if ((rlist = rcm->rlist) == NULL) {
1258                         rc = ap_rcm_rlist(a, rcm->firstcm, rcm->lastcm, &rlist,
1259                             cmd);
1260                         if ((rc == CFGA_OK) && (rlist != NULL) &&
1261                             (rlist[0] != NULL)) {
1262                                 rcm->rlist = rlist;
1263                         } else {
1264                                 /* Do not pass up empty resource list to RCM */
1265                                 noop++;
1266                                 break;
1267                         }
1268                 }
1269                 for (nrsrc = 0; rlist[nrsrc] != NULL; nrsrc++)
1270                         ap_msg(a, MSG_ISSUE, cmd, rlist[nrsrc]);
1271                 if (cmd == CMD_RCM_OFFLINE)
1272                         rv = (*rcm->request_offline_list)(hd, rlist, flags,
1273                             &rinfo);
1274                 else if (cmd == CMD_RCM_ONLINE)
1275                         rv = (*rcm->notify_online_list)(hd, rlist,
1276                             flags & ~RCM_FORCE, &rinfo);
1277                 else
1278                         rv = (*rcm->notify_remove_list)(hd, rlist,
1279                             flags & ~RCM_FORCE, &rinfo);
1280                 break;
1281         }
1282         case CMD_RCM_SUSPEND: {
1283                 timespec_t t;
1284                 t.tv_sec = (time_t)0;
1285                 t.tv_nsec = (long)0;
1286                 rsrc = OS;
1287                 ap_msg(a, MSG_ISSUE, cmd, rsrc);
1288                 rv = (*rcm->request_suspend)(hd, rsrc, flags, &t, &rinfo);
1289                 break;
1290         }
1291         case CMD_RCM_RESUME:
1292                 rsrc = OS;
1293                 ap_msg(a, MSG_ISSUE, cmd, rsrc);
1294                 rv = (*rcm->notify_resume)(hd, rsrc, 0, &rinfo);
1295                 break;
1296         default:
1297                 ap_err(a, ERR_CMD_INVAL, cmd);
1298                 return (CFGA_INVAL);
1299         }
1300 
1301         if (rv != RCM_SUCCESS) {
1302                 rcm->rinfo = rinfo;
1303                 rcm->infot = NULL;
1304                 ap_err(a, ERR_RCM_CMD, cmd);
1305                 (*rcm->free_info)(rinfo);
1306                 if (rc == CFGA_OK)
1307                         rc = CFGA_LIB_ERROR;    /* make sure error is set */
1308         }
1309         if ((rc == CFGA_OK) && (noop == 0)) {
1310                 if (rlist)
1311                         for (i = 0; rlist[i]; i++)
1312                                 ap_msg(a, MSG_DONE, cmd, rlist[i]);
1313                 else if (rsrc)
1314                         ap_msg(a, MSG_DONE, cmd, rsrc);
1315                 else
1316                         ap_msg(a, MSG_DONE, cmd, a->target);
1317         }
1318 
1319         return (rc);
1320 }
1321 
1322 /*
1323  * ap_rcm_info
1324  *
1325  * Takes an ap_id and a character pointer, and formats
1326  * the rcm_info_t data in the form of a table to the given character pointer.
1327  * Code duplicated from the scsi plugin.
1328  * Note: This function will go away when a generic librcm callback is
1329  *      implemented to format RCM messages for plugins.
1330  */
1331 int
1332 ap_rcm_info(apd_t *a, char **msg)
1333 {
1334         rcmd_t *rcm;
1335         rcm_info_t *rinfo;
1336         int i;
1337         size_t w;
1338         size_t width = 0;
1339         size_t w_rsrc = 0;
1340         size_t w_info = 0;
1341         size_t msg_size = 0;
1342         uint_t tuples = 0;
1343         rcm_info_tuple_t *tuple = NULL;
1344         char *rsrc;
1345         char *info;
1346         char *newmsg;
1347         static char format[RCM_MAX_FORMAT];
1348         const char *infostr;
1349 
1350 
1351         DBG("ap_rcm_info(%p)\n", (void *)a);
1352 
1353         /* Protect against invalid arguments */
1354         if ((a == NULL) || ((rcm = (rcmd_t *)a->rcm) == NULL) ||
1355             ((rinfo = rcm->rinfo) == NULL) || (msg == NULL)) {
1356                 return (-1);
1357         }
1358 
1359         /* Set localized table header strings */
1360         rsrc = dgettext(TEXT_DOMAIN, "Resource");
1361         info = dgettext(TEXT_DOMAIN, "Information");
1362 
1363         /* A first pass, to size up the RCM information */
1364         while (tuple = (*rcm->info_next)(rinfo, tuple)) {
1365                 if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1366                         tuples++;
1367                         if ((w = strlen((*rcm->info_rsrc)(tuple))) > w_rsrc)
1368                                 w_rsrc = w;
1369                         if ((w = strlen(infostr)) > w_info)
1370                                 w_info = w;
1371                 }
1372         }
1373 
1374         /* If nothing was sized up above, stop early */
1375         if (tuples == 0)
1376                 return (0);
1377 
1378         /* Adjust column widths for column headings */
1379         if ((w = strlen(rsrc)) > w_rsrc)
1380                 w_rsrc = w;
1381         else if ((w_rsrc - w) % 2)
1382                 w_rsrc++;
1383         if ((w = strlen(info)) > w_info)
1384                 w_info = w;
1385         else if ((w_info - w) % 2)
1386                 w_info++;
1387 
1388         /*
1389          * Compute the total line width of each line,
1390          * accounting for intercolumn spacing.
1391          */
1392         width = w_info + w_rsrc + 4;
1393 
1394         /* Allocate space for the table */
1395         msg_size = (2 + tuples) * (width + 1) + 2;
1396         if (*msg == NULL) {
1397                 /* zero fill for the strcat() call below */
1398                 *msg = calloc(msg_size, sizeof (char));
1399                 if (*msg == NULL)
1400                         return (-1);
1401         } else {
1402                 newmsg = realloc(*msg, strlen(*msg) + msg_size);
1403                 if (newmsg == NULL)
1404                         return (-1);
1405                 else
1406                         *msg = newmsg;
1407         }
1408 
1409         /* Place a table header into the string */
1410 
1411         /* The resource header */
1412         (void) strcat(*msg, "\n");
1413         w = strlen(rsrc);
1414         for (i = 0; i < ((w_rsrc - w) / 2); i++)
1415                 (void) strcat(*msg, " ");
1416         (void) strcat(*msg, rsrc);
1417         for (i = 0; i < ((w_rsrc - w) / 2); i++)
1418                 (void) strcat(*msg, " ");
1419 
1420         /* The information header */
1421         (void) strcat(*msg, "  ");
1422         w = strlen(info);
1423         for (i = 0; i < ((w_info - w) / 2); i++)
1424                 (void) strcat(*msg, " ");
1425         (void) strcat(*msg, info);
1426         for (i = 0; i < ((w_info - w) / 2); i++)
1427                 (void) strcat(*msg, " ");
1428 
1429         /* Underline the headers */
1430         (void) strcat(*msg, "\n");
1431         for (i = 0; i < w_rsrc; i++)
1432                 (void) strcat(*msg, "-");
1433         (void) strcat(*msg, "  ");
1434         for (i = 0; i < w_info; i++)
1435                 (void) strcat(*msg, "-");
1436 
1437         /* Construct the format string */
1438         (void) snprintf(format, RCM_MAX_FORMAT, "%%-%ds  %%-%ds",
1439             (int)w_rsrc, (int)w_info);
1440 
1441         /* Add the tuples to the table string */
1442         tuple = NULL;
1443         while ((tuple = (*rcm->info_next)(rinfo, tuple)) != NULL) {
1444                 if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
1445                         (void) strcat(*msg, "\n");
1446                         (void) sprintf(&((*msg)[strlen(*msg)]), format,
1447                             (*rcm->info_rsrc)(tuple), infostr);
1448                 }
1449         }
1450 
1451         DBG("ap_rcm_info(%p) success\n", (void *)a);
1452         return (0);
1453 }