ap_rcm.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <macros.h>
#include <errno.h>
#include <kstat.h>
#include <sys/kmem.h>
#include <dlfcn.h>
#include <libdevinfo.h>
#include <librcm.h>
#include <libintl.h>
#define CFGA_PLUGIN_LIB
#include <config_admin.h>
#include <sys/sbd_ioctl.h>
#include "ap.h"
typedef int32_t cpuid_t;
typedef struct {
int valid;
cfga_stat_t ostate;
int ncap;
union {
long npages;
cpuid_t cpuid[SBD_MAX_CORES_PER_CMP];
} type;
} cap_info_t;
typedef struct {
int firstcm; /* first component to operate on */
int lastcm; /* last component to operate on */
void *lib;
char **rlist;
cap_info_t *capinfo;
int ncpus; /* # of CPUs in cpuids list */
cpuid_t *cpuids; /* List of cpuids */
int capcpus; /* # of CPUs - tracking capacity */
int cappages; /* # of memory pages - tracking capacity */
rcm_handle_t *hd;
rcm_info_t *rinfo;
rcm_info_tuple_t *infot;
int (*alloc_handle)(char *, uint_t, void *, rcm_handle_t **);
void (*free_handle)(rcm_handle_t *);
int (*get_info)(rcm_handle_t *, char *, uint_t, rcm_info_t **);
void (*free_info)(rcm_info_t *);
rcm_info_tuple_t *(*info_next)(rcm_info_t *, rcm_info_tuple_t *);
int (*info_state)(rcm_info_tuple_t *);
pid_t (*info_pid)(rcm_info_tuple_t *);
const char *(*info_error)(rcm_info_tuple_t *);
const char *(*info_info)(rcm_info_tuple_t *);
const char *(*info_rsrc)(rcm_info_tuple_t *);
int (*request_offline_list)(rcm_handle_t *, char **, uint_t,
rcm_info_t **);
int (*notify_online_list)(rcm_handle_t *, char **, uint_t,
rcm_info_t **);
int (*request_suspend)(rcm_handle_t *, char *, uint_t, timespec_t *,
rcm_info_t **);
int (*notify_resume)(rcm_handle_t *, char *, uint_t, rcm_info_t **);
int (*notify_remove_list)(rcm_handle_t *, char **, uint_t,
rcm_info_t **);
int (*request_capacity_change)(rcm_handle_t *, char *, uint_t,
nvlist_t *, rcm_info_t **);
int (*notify_capacity_change)(rcm_handle_t *, char *, uint_t,
nvlist_t *, rcm_info_t **);
} rcmd_t;
static char *
ap_rcm_ops[] = {
"rcm_alloc_handle",
"rcm_free_handle",
"rcm_get_info",
"rcm_free_info",
"rcm_info_next",
"rcm_info_state",
"rcm_info_pid",
"rcm_info_error",
"rcm_info_info",
"rcm_info_rsrc",
"rcm_request_offline_list",
"rcm_notify_online_list",
"rcm_request_suspend",
"rcm_notify_resume",
"rcm_notify_remove_list",
"rcm_request_capacity_change",
"rcm_notify_capacity_change",
NULL
};
#define ALLOC_HANDLE 0
#define FREE_HANDLE 1
#define GET_INFO 2
#define FREE_INFO 3
#define INFO_TUPLE_NEXT 4
#define INFO_TUPLE_STATE 5
#define INFO_TUPLE_ID 6
#define INFO_TUPLE_ERROR 7
#define INFO_TUPLE_INFO 8
#define INFO_TUPLE_RSRC 9
#define REQUEST_OFFLINE 10
#define NOTIFY_ONLINE 11
#define REQUEST_SUSPEND 12
#define NOTIFY_RESUME 13
#define NOTIFY_REMOVE 14
#define REQUEST_CAP_CHANGE 15
#define NOTIFY_CAP_CHANGE 16
/*
* There is no consumer for SUNW_OS. This is defined here
* for generic OS quiescence.
*/
#define OS "SUNW_OS" /* XXX */
/* Max width of an RCM formatted message line */
#define RCM_MAX_FORMAT 80
#ifdef __sparcv9
#define RCMLIB "/usr/lib/sparcv9/librcm.so";
#else
#define RCMLIB "/usr/lib/librcm.so";
#endif
static cfga_err_t
ap_capinfo(apd_t *a, int firstcm, int lastcm, cap_info_t **capinfo)
{
int cm;
int ncm;
void *cap;
int *ncap;
cfga_stat_t *os;
cap_info_t *cinfo, *cp;
DBG("ap_capinfo(%p)\n", (void *)a);
if (capinfo == NULL) {
ap_err(a, ERR_PLUGIN, "null capinfo");
return (CFGA_LIB_ERROR);
}
/*
* Assume there are components with valid capacity
* information and allocate space for them. If there
* are none at the end, free the allocated space.
*/
ncm = lastcm - firstcm + 1;
cinfo = (cap_info_t *)calloc(ncm, sizeof (cap_info_t));
if (cinfo == NULL) {
ap_err(a, ERR_NOMEM);
return (CFGA_LIB_ERROR);
}
*capinfo = NULL;
ncm = 0;
for (cp = cinfo, cm = firstcm; cm <= lastcm; cm++, cp++) {
os = &cp->ostate;
ncap = &cp->ncap;
switch (ap_cm_type(a, cm)) {
case AP_CPU:
case AP_CMP:
cap = (void *)(cp->type.cpuid);
break;
case AP_MEM:
cap = (void *)&(cp->type.npages);
break;
default:
continue;
}
/*
* Remember which components have valid
* capacity information.
*/
if (ap_cm_capacity(a, cm, cap, ncap, os)) {
cp->valid = 1;
ncm++;
}
}
if (ncm == 0)
free(cinfo);
else
*capinfo = cinfo;
return (CFGA_OK);
}
static int
getsyscpuids(int *ncpuids, cpuid_t **cpuids)
{
int ncpu;
int maxncpu;
kstat_t *ksp;
kstat_ctl_t *kc = NULL;
cpuid_t *cp;
DBG("getsyscpuids\n");
if ((maxncpu = sysconf(_SC_NPROCESSORS_MAX)) == -1 ||
(kc = kstat_open()) == NULL ||
(cp = (cpuid_t *)calloc(maxncpu, sizeof (cpuid_t))) == NULL) {
/* if calloc failed, clean up kstats */
if (kc != NULL) {
(void) kstat_close(kc);
}
return (-1);
}
DBG("syscpuids: ");
for (ncpu = 0, ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
if (strcmp(ksp->ks_module, "cpu_info") == 0) {
cp[ncpu++] = ksp->ks_instance;
DBG("%d ", ksp->ks_instance);
}
}
DBG("\n");
(void) kstat_close(kc);
*cpuids = cp;
*ncpuids = ncpu;
return (0);
}
cfga_err_t
ap_rcm_init(apd_t *a)
{
int i;
char *err;
char *rcmlib;
void *sym;
void *lib;
char **op;
rcmd_t *rcm;
cfga_err_t rc;
struct stat buf;
DBG("ap_rcm_init(%p)\n", (void *)a);
/*
* If the initial command is status, or the RCM feature is not
* available, or the RCM interface has already been initialized,
* just return.
*/
if ((a->statonly != 0) || (a->norcm != 0) ||
((rcm = (rcmd_t *)a->rcm) != NULL)) {
return (CFGA_OK);
}
rcmlib = RCMLIB;
rc = CFGA_LIB_ERROR;
DBG("Looking for %s\n", rcmlib);
/*
* If the library is not present, there is nothing more
* to do. The RCM offline/suspend steps become no-ops
* in that case.
*/
if (stat(rcmlib, &buf) == -1) {
if (errno == ENOENT) {
a->norcm++;
ap_msg(a, MSG_NORCM);
return (CFGA_OK);
} else {
ap_err(a, ERR_STAT, rcmlib);
return (rc);
}
}
DBG("%s found\n", rcmlib);
if ((a->rcm = calloc(1, sizeof (rcmd_t))) == NULL) {
ap_err(a, ERR_NOMEM);
return (rc);
}
rcm = (rcmd_t *)a->rcm;
if ((lib = dlopen(rcmlib, RTLD_NOW)) == NULL) {
if ((err = dlerror()) != NULL)
err = strdup(err);
ap_err(a, ERR_LIB_OPEN, rcmlib, err);
if (err != NULL)
free(err);
return (rc);
}
rcm->lib = lib;
for (i = 0, op = ap_rcm_ops; *op != NULL; op++, i++) {
if ((sym = dlsym(lib, *op)) == NULL) {
ap_err(a, ERR_LIB_SYM, rcmlib, *op);
return (rc);
}
switch (i) {
case ALLOC_HANDLE:
rcm->alloc_handle = (int(*)
(char *, uint_t, void *, rcm_handle_t **))sym;
break;
case FREE_HANDLE:
rcm->free_handle = (void (*)(rcm_handle_t *))sym;
break;
case GET_INFO:
rcm->get_info = (int (*)
(rcm_handle_t *, char *, uint_t,
rcm_info_t **))sym;
break;
case FREE_INFO:
rcm->free_info = (void (*)(rcm_info_t *))sym;
break;
case INFO_TUPLE_NEXT:
rcm->info_next = (rcm_info_tuple_t *(*)
(rcm_info_t *, rcm_info_tuple_t *))sym;
break;
case INFO_TUPLE_STATE:
rcm->info_state = (int (*)(rcm_info_tuple_t *))sym;
break;
case INFO_TUPLE_ID:
rcm->info_pid = (pid_t (*)(rcm_info_tuple_t *))sym;
break;
case INFO_TUPLE_ERROR:
rcm->info_error = (const char *(*)
(rcm_info_tuple_t *))sym;
break;
case INFO_TUPLE_INFO:
rcm->info_info = (const char *(*)
(rcm_info_tuple_t *))sym;
break;
case INFO_TUPLE_RSRC:
rcm->info_rsrc = (const char *(*)
(rcm_info_tuple_t *))sym;
break;
case REQUEST_OFFLINE:
rcm->request_offline_list = (int (*)
(rcm_handle_t *, char **, uint_t,
rcm_info_t **))sym;
break;
case NOTIFY_ONLINE:
rcm->notify_online_list = (int (*)
(rcm_handle_t *, char **, uint_t,
rcm_info_t **))sym;
break;
case REQUEST_SUSPEND:
rcm->request_suspend = (int (*)
(rcm_handle_t *, char *, uint_t,
timespec_t *, rcm_info_t **))sym;
break;
case NOTIFY_RESUME:
rcm->notify_resume = (int (*)
(rcm_handle_t *, char *, uint_t,
rcm_info_t **))sym;
break;
case NOTIFY_REMOVE:
rcm->notify_remove_list = (int (*)
(rcm_handle_t *, char **, uint_t,
rcm_info_t **))sym;
break;
case REQUEST_CAP_CHANGE:
rcm->request_capacity_change = (int (*)
(rcm_handle_t *, char *, uint_t,
nvlist_t *, rcm_info_t **))sym;
break;
case NOTIFY_CAP_CHANGE:
rcm->notify_capacity_change = (int (*)
(rcm_handle_t *, char *, uint_t,
nvlist_t *, rcm_info_t **))sym;
break;
default:
break;
}
}
if (rcm->alloc_handle == NULL ||
(*rcm->alloc_handle)(NULL, RCM_NOPID, NULL, &rcm->hd)
!= RCM_SUCCESS) {
ap_err(a, ERR_RCM_HANDLE);
return (CFGA_LIB_ERROR);
}
/*
* Offlining/onlining a board means offlining/onlining
* all components on the board. When operating on a
* single component no component sequence number is
* needed since the default is the current (target)
* component.
*/
if (a->tgt == AP_BOARD) {
rcm->firstcm = 0;
rcm->lastcm = a->ncm - 1;
} else {
rcm->firstcm = CM_DFLT;
rcm->lastcm = CM_DFLT;
}
if (rcm->cpuids == NULL) {
int cm;
int ncpu;
/*
* Allocate space for the cpu capacity change info.
* Not every cpu may be relevant to the capacity
* request, but allocating for the maximum makes
* it easier, and the space is insignifcant.
*/
for (ncpu = 0, cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
ap_target_t type = ap_cm_type(a, cm);
if ((type == AP_CPU) || (type == AP_CMP)) {
ncpu += ap_cm_ncap(a, cm);
}
}
rcm->ncpus = ncpu;
if ((rcm->cpuids = (cpuid_t *)calloc(ncpu, sizeof (cpuid_t)))
== NULL) {
ap_err(a, ERR_NOMEM);
return (CFGA_LIB_ERROR);
}
}
/*
* Remember initial capacity information.
* This information is based on the initial
* state of the ap_id, i.e. before any
* state change change operations were
* executed. We will later get the
* current capacity information in order
* to figure out exactly what has changed
* as the result of the executed command
* sequence.
*/
rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &rcm->capinfo);
rcm->capcpus = sysconf(_SC_NPROCESSORS_CONF);
rcm->cappages = sysconf(_SC_PHYS_PAGES);
return (rc);
}
void
ap_rcm_fini(apd_t *a)
{
rcmd_t *rcm;
char **rp;
DBG("ap_rcm_fini(%p)\n", (void *)a);
if ((rcm = (rcmd_t *)a->rcm) == NULL)
return;
if (rcm->hd)
(*rcm->free_handle)(rcm->hd);
(void) dlclose(rcm->lib);
/*
* Free all the names in the resource list, followed
* by the resource list itself.
*/
if (rcm->rlist)
for (rp = rcm->rlist; *rp; rp++)
s_free(*rp);
s_free(rcm->rlist);
s_free(rcm->cpuids);
s_free(rcm->capinfo);
s_free(a->rcm);
}
static cfga_err_t
ap_rcm_rlist(apd_t *a, int firstcm, int lastcm, char ***rlist, int cmd)
{
int n;
int cm;
int ncap;
char *path;
char *cpuname;
char **rp;
DBG("ap_rcm_rlist(%p)\n", (void *)a);
/*
* Allocate space for the maximum number of components
* that can be affected by this operation.
*/
for (ncap = 0, cm = firstcm; cm <= lastcm; cm++) {
ncap += ap_cm_ncap(a, cm);
}
DBG("ncap=%d\n", ncap);
if ((rp = (char **)calloc(ncap + 1, sizeof (char *))) == NULL) {
ap_err(a, ERR_NOMEM);
return (CFGA_LIB_ERROR);
}
n = 12; /* SUNW_cpu/cpuCCC */
/* <--- 12 ---> */
cpuname = "SUNW_cpu/cpuCCC";
/*
* Set the RCM resource name for each component:
*
* io: <device-path>
* cpu: SUNW_cpu/cpu<cpuid>
*
*/
for (ncap = 0, cm = firstcm; cm <= lastcm; cm++) {
switch (ap_cm_type(a, cm)) {
case AP_CPU:
case AP_CMP: {
int i;
int len;
cap_info_t cap;
cfga_stat_t os;
cpuid_t *cpuid;
int *nc;
cap_info_t *prevcap;
rcmd_t *rcm;
int allow_op;
int capindex;
cpuid = cap.type.cpuid;
nc = &cap.ncap;
/*
* See if the request target is a single
* (default) component
*/
capindex = (cm == CM_DFLT) ? 0 : cm;
/* Get the previous capacity info */
rcm = (rcmd_t *)a->rcm;
prevcap = rcm->capinfo;
if (!ap_cm_capacity(a, cm, cpuid, nc, &os)) {
break;
}
len = (strlen(cpuname) - n) + 1;
/*
* For CMD_RCM_OFFLINE and REMOVE, add the CPU to the
* list if it is currently configured. For
* CMD_RCM_ONLINE, do so only if the state has changed
* to CFGA_STAT_CONFIGURED.
*/
allow_op = 0;
if ((cmd == CMD_RCM_OFFLINE) ||
(cmd == CMD_RCM_REMOVE)) {
if (os == CFGA_STAT_CONFIGURED)
allow_op = 1;
} else {
if ((os == CFGA_STAT_CONFIGURED) &&
((prevcap == NULL) ||
(prevcap[capindex].ostate != os)))
allow_op = 1;
}
if (allow_op) {
for (i = 0; i < *nc; i++) {
if ((path = strdup(cpuname)) == NULL) {
ap_err(a, ERR_NOMEM);
return (CFGA_LIB_ERROR);
}
(void) snprintf(&path[n], len, "%d",
cpuid[i]);
DBG("rp[%d]=%s\n", ncap, path);
rp[ncap++] = path;
}
}
break;
}
case AP_IO:
if ((path = ap_cm_devpath(a, cm)) != NULL) {
DBG("rp[%d]=%s\n", ncap, path);
rp[ncap++] = path;
}
break;
case AP_MEM:
/*
* Nothing to do for AP_MEM since only capacity
* change notifications apply to SUNW_memory
*/
default:
break;
}
}
rp[ncap] = NULL;
if (rlist)
*rlist = rp;
return (CFGA_OK);
}
/*
* Returns 1 if the cpu ID 'cpuid' is in the list of CPU IDs
* 'list' of length 'length'. Returns 0 otherwise.
*/
static int
is_cpu_in_list(cpuid_t cpuid, cpuid_t *list, int length)
{
int i;
DBG("is_cpu_in_list\n");
if (list == NULL)
return (0);
for (i = 0; i < length; i++) {
if (list[i] == cpuid)
return (1);
}
return (0);
}
static int
ap_rcm_cap_cpu(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd, uint_t flags,
rcm_info_t **rinfo, int cmd, int change)
{
int i;
int rv = RCM_FAILURE;
int ncpuids;
int oldncpuids;
int newncpuids;
char buf[32];
const char *fmt;
size_t size;
nvlist_t *nvl = NULL;
cpuid_t *cpuids = NULL;
cpuid_t *oldcpuids = NULL;
cpuid_t *newcpuids = NULL;
DBG("ap_rcm_cap_cpu(%p)\n", (void *)a);
/*
* Get the current number of configured cpus.
*/
if (getsyscpuids(&ncpuids, &cpuids) == -1)
return (rv);
else if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
free(cpuids);
goto done;
}
if (change == 1)
fmt = "(%d cpu)";
else
fmt = "(%d cpus)";
size = sizeof (cpuid_t);
if (cmd == CMD_RCM_CAP_DEL) {
/*
* A delete request. rcm->cpuids represents the
* cpus that will be unconfigured. The current
* set of cpus, before the unconfigure operation,
* are the old CPUs. The new CPUs are those
* that would remain.
*/
oldncpuids = ncpuids;
oldcpuids = cpuids;
/*
* Fill newcpuids with the CPU IDs in the cpuids array,
* but not in rcm->cpuids.
*/
newcpuids = (cpuid_t *)calloc(ncpuids, size);
if (newcpuids == NULL)
goto done;
newncpuids = 0;
for (i = 0; i < ncpuids; i++) {
if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
newcpuids[newncpuids++] = cpuids[i];
}
} else if (cmd == CMD_RCM_CAP_NOTIFY) {
/*
* An unconfigure capacity change notification. This
* notification is sent after a DR unconfigure, whether
* or not the DR was successful. rcm->cpuids represents
* the CPUs that have been unconfigured.
*/
/* New CPU IDs are the CPUs configured right now. */
newncpuids = ncpuids;
newcpuids = cpuids;
/*
* Old CPU IDs are the CPUs configured right now
* in addition to those that have been unconfigured.
* We build the old CPU ID list by concatenating
* cpuids and rcm->cpuids.
*/
oldcpuids = (cpuid_t *)calloc(ncpuids + change, size);
if (oldcpuids == NULL)
goto done;
oldncpuids = 0;
for (i = 0; i < ncpuids; i++) {
if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
oldcpuids[oldncpuids++] = cpuids[i];
}
for (i = 0; i < change; i++)
oldcpuids[oldncpuids++] = rcm->cpuids[i];
} else {
DBG("ap_rcm_cap_cpu: CPU capacity, old = %d, new = %d \n",
rcm->capcpus, ncpuids);
if (rcm->capcpus == ncpuids) {
/* No real change in CPU capacity */
rv = RCM_SUCCESS;
goto done;
}
/*
* An add notification. rcm->cpuids represents the
* cpus that have been configured. The current
* set of cpus, after the configure operation,
* are the new CPU IDs.
*/
newncpuids = ncpuids;
newcpuids = cpuids;
/*
* Fill oldcpuids with the CPU IDs in the cpuids array,
* but not in rcm->cpuids.
*/
oldcpuids = (cpuid_t *)calloc(ncpuids, size);
if (oldcpuids == NULL)
goto done;
oldncpuids = 0;
for (i = 0; i < ncpuids; i++) {
if (!is_cpu_in_list(cpuids[i], rcm->cpuids, change))
oldcpuids[oldncpuids++] = cpuids[i];
}
}
DBG("oldcpuids: ");
for (i = 0; i < oldncpuids; i++)
DBG("%d ", oldcpuids[i]);
DBG("\n");
DBG("change : ");
for (i = 0; i < change; i++)
DBG("%d ", rcm->cpuids[i]);
DBG("\n");
DBG("newcpuids: ");
for (i = 0; i < newncpuids; i++)
DBG("%d ", newcpuids[i]);
DBG("\n");
if (nvlist_add_string(nvl, "state", "capacity") != 0 ||
nvlist_add_int32(nvl, "old_total", oldncpuids) != 0 ||
nvlist_add_int32(nvl, "new_total", newncpuids) != 0 ||
nvlist_add_int32_array(nvl, "old_cpu_list", oldcpuids,
oldncpuids) != 0 ||
nvlist_add_int32_array(nvl, "new_cpu_list", newcpuids,
newncpuids) != 0)
goto done;
snprintf(buf, sizeof (buf), fmt, change);
ap_msg(a, MSG_ISSUE, cmd, buf);
if (cmd == CMD_RCM_CAP_DEL)
rv = (*rcm->request_capacity_change)(hd, "SUNW_cpu",
flags, nvl, rinfo);
else
rv = (*rcm->notify_capacity_change)(hd, "SUNW_cpu",
flags & ~RCM_FORCE, nvl, rinfo);
done:
if (nvl)
nvlist_free(nvl);
s_free(oldcpuids);
s_free(newcpuids);
return (rv);
}
static int
ap_rcm_cap_mem(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd, uint_t flags,
rcm_info_t **rinfo, int cmd, long change)
{
int rv;
int pgsize;
long oldpages;
long newpages;
long currpages;
char buf[32];
nvlist_t *nvl;
DBG("ap_rcm_cap_mem(%p)\n", (void *)a);
/*
* Get the current amount of configured memory.
*/
if ((pgsize = sysconf(_SC_PAGE_SIZE)) == -1 ||
(currpages = sysconf(_SC_PHYS_PAGES)) == -1 ||
nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) > 0)
return (RCM_FAILURE);
/*
* If this is a (delete) request, change represents
* the amount of capacity that will be deleted from the
* system. If this is an (add) notification, change
* represents the amount of capacity that has already
* been added to the system.
*/
if (cmd == CMD_RCM_CAP_DEL) {
oldpages = currpages;
newpages = currpages - change;
} else if (cmd == CMD_RCM_CAP_NOTIFY) {
newpages = currpages;
oldpages = rcm->cappages;
} else {
if (rcm->cappages == currpages) {
/* No real change in memory capacity */
DBG("ap_rcm_cap_mem: no change in capacity.\n");
nvlist_free(nvl);
return (RCM_SUCCESS);
}
oldpages = currpages - change;
newpages = currpages;
}
DBG("ap_rcm_cap_mem: Memory capacity, old = %ld, new = %ld\n",
oldpages, newpages);
if (nvlist_add_string(nvl, "state", "capacity") != 0 ||
nvlist_add_int32(nvl, "page_size", pgsize) != 0 ||
nvlist_add_int32(nvl, "old_pages", oldpages) != 0 ||
nvlist_add_int32(nvl, "new_pages", newpages) != 0) {
nvlist_free(nvl);
return (RCM_FAILURE);
}
(void) snprintf(buf, sizeof (buf), "(%ld pages)", change);
ap_msg(a, MSG_ISSUE, cmd, buf);
if (cmd == CMD_RCM_CAP_DEL)
rv = (*rcm->request_capacity_change)(hd, "SUNW_memory",
flags, nvl, rinfo);
else
rv = (*rcm->notify_capacity_change)(hd, "SUNW_memory",
flags & ~RCM_FORCE, nvl, rinfo);
nvlist_free(nvl);
return (rv);
}
static cfga_err_t
ap_rcm_request_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
int *rv, uint_t flags, rcm_info_t **rinfo)
{
int cm;
int ncpus;
long npages;
cap_info_t *capinfo;
ap_target_t type;
DBG("ap_rcm_request_cap(%p)\n", (void *)a);
if ((capinfo = rcm->capinfo) == NULL) {
ap_err(a, ERR_PLUGIN, "null capinfo");
return (CFGA_LIB_ERROR);
}
ncpus = npages = 0;
for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
int i, j;
/*
* See if the request target is a single
* (default) component
*/
i = (cm == CM_DFLT) ? 0 : cm;
/*
* We are interested only in those components
* in the configured state since they represent
* available capacity.
*/
type = ap_cm_type(a, cm);
if (capinfo[i].valid == 0 ||
capinfo[i].ostate != CFGA_STAT_CONFIGURED)
continue;
else if ((type == AP_CPU) || (type == AP_CMP)) {
for (j = 0; j < capinfo[i].ncap; j++) {
rcm->cpuids[ncpus++] = capinfo[i].type.cpuid[j];
}
} else if (type == AP_MEM)
npages += capinfo[i].type.npages;
}
if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
CMD_RCM_CAP_DEL, ncpus)) != RCM_SUCCESS))
return (CFGA_LIB_ERROR);
if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
CMD_RCM_CAP_DEL, npages)) != RCM_SUCCESS))
return (CFGA_LIB_ERROR);
return (CFGA_OK);
}
static cfga_err_t
ap_rcm_add_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
int *rv, uint_t flags, rcm_info_t **rinfo)
{
int cm;
int ncpus;
long npages;
cap_info_t *capinfo, *prevcapinfo;
cfga_err_t rc;
DBG("ap_rcm_add_cap(%p)\n", (void *)a);
/* Get the new capacity info to figure out what has changed */
if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo))
!= CFGA_OK)
return (rc);
if (capinfo == NULL) {
DBG("no pertinent capacity info\n");
return (CFGA_OK);
}
ncpus = npages = 0;
prevcapinfo = rcm->capinfo;
for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
int i, j;
cfga_stat_t os, prevos;
int prevvalidity;
ap_target_t type;
/*
* See if the request target is a single
* (default) component
*/
i = cm == CM_DFLT ? 0 : cm;
os = capinfo[i].ostate;
if (prevcapinfo == NULL) {
prevos = CFGA_STAT_EMPTY;
prevvalidity = 1;
} else {
prevos = prevcapinfo[i].ostate;
prevvalidity = prevcapinfo[i].valid;
}
type = ap_cm_type(a, cm);
DBG("cm=%d valid=%d type=%d, prevos=%d os=%d\n",
cm, prevvalidity, type, prevos, os);
/*
* We are interested only in those components
* whose states have changed to configured as
* the result of the current cfgadm request.
*/
if (prevvalidity == 0 || os != CFGA_STAT_CONFIGURED) {
capinfo[i].valid = 0;
continue;
} else if (prevos != CFGA_STAT_CONFIGURED) {
/*
* The occupant state is configured, and
* the previous occupant state was not.
*/
if ((type == AP_CPU) || (type == AP_CMP)) {
for (j = 0; j < capinfo[i].ncap; j++) {
rcm->cpuids[ncpus++] =
capinfo[i].type.cpuid[j];
}
} else if (type == AP_MEM)
npages += capinfo[i].type.npages;
}
}
free(capinfo);
if (ncpus && ((*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
CMD_RCM_CAP_ADD, ncpus)) != RCM_SUCCESS))
return (CFGA_LIB_ERROR);
if (npages && ((*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
CMD_RCM_CAP_ADD, npages)) != RCM_SUCCESS))
return (CFGA_LIB_ERROR);
return (CFGA_OK);
}
/*
* ap_rcm_notify_cap:
*
* This routine handles the CMD_RCM_CAP_NOTIFY command. It
* is called after a successful/failed DR unconfigure
* operation. It filters out components that have changed
* and passes this information on to ap_rcm_cap_{cpu,mem}.
*
* ap_rcm_cap_{cpu,mem} will still be called if all the
* components have not changed and at least one {cpu,mem}
* component was originally configured.
*/
static cfga_err_t
ap_rcm_notify_cap(apd_t *a, rcmd_t *rcm, rcm_handle_t *hd,
int *rv, uint_t flags, rcm_info_t **rinfo)
{
cfga_err_t rc;
cap_info_t *capinfo;
cap_info_t *prevcapinfo;
int cm;
long npages = 0;
int ncpus = 0;
int prev_mem = 0; /* # of prev. configured mem components */
int prev_cpus = 0; /* # of prev. configured CPUs */
DBG("ap_rcm_notify_cap(%p)\n", (void *)a);
/* Get the new capacity info to figure out what has changed */
if ((rc = ap_capinfo(a, rcm->firstcm, rcm->lastcm, &capinfo))
!= CFGA_OK)
return (rc);
if (capinfo == NULL) {
DBG("no pertinent capacity info\n");
return (CFGA_OK);
}
/* The original capacity info */
prevcapinfo = rcm->capinfo;
/*
* Cycle through all components that we are operating
* on. Record which components' occupant states have
* changed.
*/
for (cm = rcm->firstcm; cm <= rcm->lastcm; cm++) {
int i;
cfga_stat_t prevos, os;
ap_target_t type;
int prev_conf = 0;
int now_conf = 0;
/*
* See if the request target is a single
* (default) component
*/
i = cm == CM_DFLT ? 0 : cm;
os = capinfo[i].ostate;
if (prevcapinfo == NULL) {
prevos = CFGA_STAT_EMPTY;
} else {
prevos = prevcapinfo[i].ostate;
if (prevcapinfo[i].valid == 0) {
DBG("ap_rcm_notify_cap: skipping component "
"due to prevvalidity == 0\n");
continue;
}
}
type = ap_cm_type(a, cm);
prev_conf = (prevos == CFGA_STAT_CONFIGURED);
now_conf = (os == CFGA_STAT_CONFIGURED);
/*
* Build up rcm->cpuids with the IDs of CPUs that
* have been removed. Record the number of removed
* CPUs and pages.
*/
if (type == AP_CPU || type == AP_CMP) {
if (prev_conf)
prev_cpus++;
if (prev_conf && !now_conf) {
int j;
for (j = 0; j < capinfo[i].ncap; j++) {
rcm->cpuids[ncpus++] =
capinfo[i].type.cpuid[j];
}
}
} else if (type == AP_MEM) {
if (prev_conf)
prev_mem++;
if (prev_conf && !now_conf)
npages += capinfo[i].type.npages;
}
}
free(capinfo);
/*
* If any CPU or memory components were operated on,
* successfully or not, the rcm_notify_capacity_change()
* routine must be called.
*/
if (prev_cpus) {
*rv = ap_rcm_cap_cpu(a, rcm, hd, flags, rinfo,
CMD_RCM_CAP_NOTIFY, ncpus);
if (*rv != RCM_SUCCESS)
return (CFGA_LIB_ERROR);
}
if (prev_mem) {
*rv = ap_rcm_cap_mem(a, rcm, hd, flags, rinfo,
CMD_RCM_CAP_NOTIFY, npages);
if (*rv != RCM_SUCCESS)
return (CFGA_LIB_ERROR);
}
return (CFGA_OK);
}
cfga_err_t
ap_rcm_ctl(apd_t *a, int cmd)
{
int i;
int rv;
int noop;
int ncpus;
int cm;
uint_t flags;
char *rsrc;
char **rlist;
rcmd_t *rcm;
rcm_info_t *rinfo;
rcm_handle_t *hd;
cfga_err_t rc;
cpuid_t *growcpuids;
DBG("ap_rcm_ctl(%p)\n", (void *)a);
if ((rcm = (rcmd_t *)a->rcm) == NULL) {
ap_msg(a, MSG_SKIP, cmd, a->target);
return (CFGA_OK);
}
hd = rcm->hd;
rv = RCM_SUCCESS;
rc = CFGA_OK;
if (ap_getopt(a, OPT_FORCE))
flags = RCM_FORCE;
else
flags = 0;
rinfo = NULL;
rlist = NULL;
rsrc = NULL;
noop = 0;
switch (cmd) {
case CMD_RCM_CAP_DEL:
if (rcm->capinfo == NULL)
noop++;
else
rc = ap_rcm_request_cap(a, rcm, hd, &rv,
flags, &rinfo);
break;
case CMD_RCM_CAP_ADD:
rc = ap_rcm_add_cap(a, rcm, hd, &rv, flags, &rinfo);
break;
case CMD_RCM_CAP_NOTIFY:
rc = ap_rcm_notify_cap(a, rcm, hd, &rv, flags, &rinfo);
break;
case CMD_RCM_ONLINE:
/* Refresh changed component states */
if ((rc = ap_stat(a, 1)) != CFGA_OK) {
noop++;
break;
}
if (a->tgt == AP_BOARD) {
rcm->firstcm = 0;
rcm->lastcm = a->ncm - 1;
/* Check if we need to grow our cpuids list */
for (ncpus = 0, cm = rcm->firstcm; cm <= rcm->lastcm;
cm++) {
ap_target_t type = ap_cm_type(a, cm);
if ((type == AP_CPU) || (type == AP_CMP))
ncpus += ap_cm_ncap(a, cm);
}
if (rcm->ncpus < ncpus) {
if ((growcpuids =
(cpuid_t *)realloc(rcm->cpuids,
(ncpus * sizeof (cpuid_t)))) == NULL) {
ap_err(a, ERR_NOMEM);
return (CFGA_LIB_ERROR);
}
rcm->ncpus = ncpus;
rcm->cpuids = growcpuids;
}
} else {
rcm->firstcm = CM_DFLT;
rcm->lastcm = CM_DFLT;
}
/*FALLTHROUGH*/
case CMD_RCM_OFFLINE:
case CMD_RCM_REMOVE: {
uint_t nrsrc;
if (cmd == CMD_RCM_REMOVE) {
/*
* An unconfigure has just taken place, so
* refresh the changed component states.
*/
if ((rc = ap_stat(a, 1)) != CFGA_OK) {
noop++;
break;
}
}
/* Check if this is an empty board, i.e. no components */
if (a->ncm == 0) {
noop++;
break;
}
if ((rlist = rcm->rlist) == NULL) {
rc = ap_rcm_rlist(a, rcm->firstcm, rcm->lastcm, &rlist,
cmd);
if ((rc == CFGA_OK) && (rlist != NULL) &&
(rlist[0] != NULL)) {
rcm->rlist = rlist;
} else {
/* Do not pass up empty resource list to RCM */
noop++;
break;
}
}
for (nrsrc = 0; rlist[nrsrc] != NULL; nrsrc++)
ap_msg(a, MSG_ISSUE, cmd, rlist[nrsrc]);
if (cmd == CMD_RCM_OFFLINE)
rv = (*rcm->request_offline_list)(hd, rlist, flags,
&rinfo);
else if (cmd == CMD_RCM_ONLINE)
rv = (*rcm->notify_online_list)(hd, rlist,
flags & ~RCM_FORCE, &rinfo);
else
rv = (*rcm->notify_remove_list)(hd, rlist,
flags & ~RCM_FORCE, &rinfo);
break;
}
case CMD_RCM_SUSPEND: {
timespec_t t;
t.tv_sec = (time_t)0;
t.tv_nsec = (long)0;
rsrc = OS;
ap_msg(a, MSG_ISSUE, cmd, rsrc);
rv = (*rcm->request_suspend)(hd, rsrc, flags, &t, &rinfo);
break;
}
case CMD_RCM_RESUME:
rsrc = OS;
ap_msg(a, MSG_ISSUE, cmd, rsrc);
rv = (*rcm->notify_resume)(hd, rsrc, 0, &rinfo);
break;
default:
ap_err(a, ERR_CMD_INVAL, cmd);
return (CFGA_INVAL);
}
if (rv != RCM_SUCCESS) {
rcm->rinfo = rinfo;
rcm->infot = NULL;
ap_err(a, ERR_RCM_CMD, cmd);
(*rcm->free_info)(rinfo);
if (rc == CFGA_OK)
rc = CFGA_LIB_ERROR; /* make sure error is set */
}
if ((rc == CFGA_OK) && (noop == 0)) {
if (rlist)
for (i = 0; rlist[i]; i++)
ap_msg(a, MSG_DONE, cmd, rlist[i]);
else if (rsrc)
ap_msg(a, MSG_DONE, cmd, rsrc);
else
ap_msg(a, MSG_DONE, cmd, a->target);
}
return (rc);
}
/*
* ap_rcm_info
*
* Takes an ap_id and a character pointer, and formats
* the rcm_info_t data in the form of a table to the given character pointer.
* Code duplicated from the scsi plugin.
* Note: This function will go away when a generic librcm callback is
* implemented to format RCM messages for plugins.
*/
int
ap_rcm_info(apd_t *a, char **msg)
{
rcmd_t *rcm;
rcm_info_t *rinfo;
int i;
size_t w;
size_t width = 0;
size_t w_rsrc = 0;
size_t w_info = 0;
size_t msg_size = 0;
uint_t tuples = 0;
rcm_info_tuple_t *tuple = NULL;
char *rsrc;
char *info;
char *newmsg;
static char format[RCM_MAX_FORMAT];
const char *infostr;
DBG("ap_rcm_info(%p)\n", (void *)a);
/* Protect against invalid arguments */
if ((a == NULL) || ((rcm = (rcmd_t *)a->rcm) == NULL) ||
((rinfo = rcm->rinfo) == NULL) || (msg == NULL)) {
return (-1);
}
/* Set localized table header strings */
rsrc = dgettext(TEXT_DOMAIN, "Resource");
info = dgettext(TEXT_DOMAIN, "Information");
/* A first pass, to size up the RCM information */
while (tuple = (*rcm->info_next)(rinfo, tuple)) {
if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
tuples++;
if ((w = strlen((*rcm->info_rsrc)(tuple))) > w_rsrc)
w_rsrc = w;
if ((w = strlen(infostr)) > w_info)
w_info = w;
}
}
/* If nothing was sized up above, stop early */
if (tuples == 0)
return (0);
/* Adjust column widths for column headings */
if ((w = strlen(rsrc)) > w_rsrc)
w_rsrc = w;
else if ((w_rsrc - w) % 2)
w_rsrc++;
if ((w = strlen(info)) > w_info)
w_info = w;
else if ((w_info - w) % 2)
w_info++;
/*
* Compute the total line width of each line,
* accounting for intercolumn spacing.
*/
width = w_info + w_rsrc + 4;
/* Allocate space for the table */
msg_size = (2 + tuples) * (width + 1) + 2;
if (*msg == NULL) {
/* zero fill for the strcat() call below */
*msg = calloc(msg_size, sizeof (char));
if (*msg == NULL)
return (-1);
} else {
newmsg = realloc(*msg, strlen(*msg) + msg_size);
if (newmsg == NULL)
return (-1);
else
*msg = newmsg;
}
/* Place a table header into the string */
/* The resource header */
(void) strcat(*msg, "\n");
w = strlen(rsrc);
for (i = 0; i < ((w_rsrc - w) / 2); i++)
(void) strcat(*msg, " ");
(void) strcat(*msg, rsrc);
for (i = 0; i < ((w_rsrc - w) / 2); i++)
(void) strcat(*msg, " ");
/* The information header */
(void) strcat(*msg, " ");
w = strlen(info);
for (i = 0; i < ((w_info - w) / 2); i++)
(void) strcat(*msg, " ");
(void) strcat(*msg, info);
for (i = 0; i < ((w_info - w) / 2); i++)
(void) strcat(*msg, " ");
/* Underline the headers */
(void) strcat(*msg, "\n");
for (i = 0; i < w_rsrc; i++)
(void) strcat(*msg, "-");
(void) strcat(*msg, " ");
for (i = 0; i < w_info; i++)
(void) strcat(*msg, "-");
/* Construct the format string */
(void) snprintf(format, RCM_MAX_FORMAT, "%%-%ds %%-%ds",
(int)w_rsrc, (int)w_info);
/* Add the tuples to the table string */
tuple = NULL;
while ((tuple = (*rcm->info_next)(rinfo, tuple)) != NULL) {
if ((infostr = (*rcm->info_info)(tuple)) != NULL) {
(void) strcat(*msg, "\n");
(void) sprintf(&((*msg)[strlen(*msg)]), format,
(*rcm->info_rsrc)(tuple), infostr);
}
}
DBG("ap_rcm_info(%p) success\n", (void *)a);
return (0);
}