#include <unistd.h>
#include <strings.h>
#include <errno.h>
#include <kstat.h>
#include <libnvpair.h>
#include <librcm.h>
#include <locale.h>
#include <assert.h>
#include "drd.h"
static int drd_rcm_init(void);
static int drd_rcm_fini(void);
static int drd_rcm_cpu_config_request(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_cpu_config_notify(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_cpu_unconfig_request(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_cpu_unconfig_notify(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_io_config_request(drctl_rsrc_t *rsrc, int nrsrc);
static int drd_rcm_io_config_notify(drctl_rsrc_t *rsrc, int nrsrc);
static int drd_rcm_io_unconfig_request(drctl_rsrc_t *rsrc, int nrsrc);
static int drd_rcm_io_unconfig_notify(drctl_rsrc_t *rsrc, int nrsrc);
static int drd_rcm_mem_config_request(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_mem_config_notify(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_mem_unconfig_request(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_mem_unconfig_notify(drctl_rsrc_t *rsrcs, int nrsrc);
drd_backend_t drd_rcm_backend = {
drd_rcm_init,
drd_rcm_fini,
drd_rcm_cpu_config_request,
drd_rcm_cpu_config_notify,
drd_rcm_cpu_unconfig_request,
drd_rcm_cpu_unconfig_notify,
drd_rcm_io_config_request,
drd_rcm_io_config_notify,
drd_rcm_io_unconfig_request,
drd_rcm_io_unconfig_notify,
drd_rcm_mem_config_request,
drd_rcm_mem_config_notify,
drd_rcm_mem_unconfig_request,
drd_rcm_mem_unconfig_notify
};
typedef int (*rcm_op_t)(rcm_handle_t *, char *, uint_t, nvlist_t *,
rcm_info_t **);
#define RCM_MEM_ALL "SUNW_memory"
#define RCM_CPU_ALL "SUNW_cpu"
#define RCM_CPU RCM_CPU_ALL"/cpu"
#define RCM_CPU_MAX_LEN (32)
static rcm_handle_t *rcm_hdl;
static int drd_rcm_online_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_add_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_del_cpu_request(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_offline_cpu_request(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_remove_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_restore_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc);
static int drd_rcm_del_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc);
static char **drd_rcm_cpu_rlist_init(drctl_rsrc_t *, int nrsrc, int status);
static void drd_rcm_cpu_rlist_fini(char **rlist);
static drctl_rsrc_t *cpu_rsrcstr_to_rsrc(const char *, drctl_rsrc_t *, int);
static int get_sys_cpuids(cpuid_t **cpuids, int *ncpuids);
static boolean_t is_cpu_in_list(cpuid_t cpuid, cpuid_t *list, int len);
static char *rcm_info_table(rcm_info_t *rinfo);
static void dump_cpu_list(char *prefix, cpuid_t *cpuids, int ncpuids);
static void dump_cpu_rsrc_list(char *prefix, drctl_rsrc_t *, int nrsrc);
static void dump_cpu_rlist(char **rlist);
static int
drd_rcm_init(void)
{
int rv;
drd_dbg("drd_rcm_init...");
rv = rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl);
if (rv == RCM_FAILURE) {
drd_err("unable to allocate RCM handle: %s", strerror(errno));
return (-1);
}
return (0);
}
static int
drd_rcm_fini(void)
{
drd_dbg("drd_rcm_fini...");
if (rcm_hdl != NULL)
rcm_free_handle(rcm_hdl);
return (0);
}
static int
drd_rcm_cpu_config_request(drctl_rsrc_t *rsrcs, int nrsrc)
{
int idx;
drd_dbg("drd_rcm_cpu_config_request...");
dump_cpu_rsrc_list(NULL, rsrcs, nrsrc);
for (idx = 0; idx < nrsrc; idx++)
rsrcs[idx].status = DRCTL_STATUS_ALLOW;
dump_cpu_rsrc_list("returning:", rsrcs, nrsrc);
return (0);
}
static int
drd_rcm_cpu_config_notify(drctl_rsrc_t *rsrcs, int nrsrc)
{
int rv = 0;
drd_dbg("drd_rcm_cpu_config_notify...");
dump_cpu_rsrc_list(NULL, rsrcs, nrsrc);
if (drd_rcm_online_cpu_notify(rsrcs, nrsrc) != 0) {
rv = -1;
goto done;
}
if (drd_rcm_add_cpu_notify(rsrcs, nrsrc) != 0) {
rv = -1;
}
done:
dump_cpu_rsrc_list("returning:", rsrcs, nrsrc);
return (rv);
}
static int
drd_rcm_cpu_unconfig_request(drctl_rsrc_t *rsrcs, int nrsrc)
{
int rv = 0;
int idx;
drd_dbg("drd_rcm_cpu_unconfig_request...");
dump_cpu_rsrc_list(NULL, rsrcs, nrsrc);
if (drd_rcm_del_cpu_request(rsrcs, nrsrc) != 0) {
rv = -1;
goto done;
}
if (drd_rcm_offline_cpu_request(rsrcs, nrsrc) != 0) {
rv = -1;
goto done;
}
done:
for (idx = 0; idx < nrsrc; idx++) {
if (rsrcs[idx].status == DRCTL_STATUS_INIT)
rsrcs[idx].status = DRCTL_STATUS_DENY;
}
dump_cpu_rsrc_list("returning:", rsrcs, nrsrc);
return (rv);
}
static int
drd_rcm_cpu_unconfig_notify(drctl_rsrc_t *rsrcs, int nrsrc)
{
int rv = 0;
drd_dbg("drd_rcm_cpu_unconfig_notify...");
dump_cpu_rsrc_list(NULL, rsrcs, nrsrc);
(void) drd_rcm_remove_cpu_notify(rsrcs, nrsrc);
if (drd_rcm_restore_cpu_notify(rsrcs, nrsrc) != 0) {
rv = -1;
goto done;
}
if (drd_rcm_del_cpu_notify(rsrcs, nrsrc) != 0) {
rv = -1;
}
done:
dump_cpu_rsrc_list("returning:", rsrcs, nrsrc);
return (rv);
}
static int
drd_rcm_online_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc)
{
char **rlist;
int rv = 0;
rcm_info_t *rinfo;
drd_dbg("drd_rcm_online_cpu_notify...");
if ((rlist = drd_rcm_cpu_rlist_init(rsrcs, nrsrc,
DRCTL_STATUS_CONFIG_SUCCESS)) == NULL) {
drd_dbg(" no CPUs were successfully added, nothing to do");
return (0);
}
rcm_notify_online_list(rcm_hdl, rlist, 0, &rinfo);
if (rv != RCM_SUCCESS) {
drd_info("rcm_notify_online_list failed: %d", rv);
rcm_free_info(rinfo);
rv = -1;
}
drd_rcm_cpu_rlist_fini(rlist);
return (rv);
}
static int
drd_rcm_add_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc)
{
cpuid_t *cpus = NULL;
int ncpus;
int rv = -1;
cpuid_t *oldcpus = NULL;
cpuid_t *newcpus = NULL;
int oldncpus = 0;
int newncpus = 0;
nvlist_t *nvl = NULL;
int idx;
rcm_info_t *rinfo;
drd_dbg("drd_rcm_add_cpu_notify...");
if ((rsrcs == NULL) || (nrsrc == 0)) {
drd_err("add_cpu_notify: cpu list empty");
goto done;
}
ncpus = nrsrc;
cpus = (cpuid_t *)malloc(nrsrc * sizeof (cpuid_t));
for (idx = 0; idx < nrsrc; idx++) {
drd_dbg(" cpu[%d] = %d", idx, rsrcs[idx].res_cpu_id);
cpus[idx] = rsrcs[idx].res_cpu_id;
}
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
goto done;
if (get_sys_cpuids(&newcpus, &newncpus) == -1)
goto done;
oldcpus = (cpuid_t *)calloc(newncpus, sizeof (cpuid_t));
if (oldcpus == NULL)
goto done;
for (idx = 0; idx < newncpus; idx++) {
if (!is_cpu_in_list(newcpus[idx], cpus, ncpus))
oldcpus[oldncpus++] = newcpus[idx];
}
dump_cpu_list("oldcpus: ", oldcpus, oldncpus);
dump_cpu_list("newcpus: ", newcpus, newncpus);
dump_cpu_list("delta: ", cpus, ncpus);
if (nvlist_add_string(nvl, "state", "capacity") ||
nvlist_add_int32(nvl, "old_total", oldncpus) ||
nvlist_add_int32(nvl, "new_total", newncpus) ||
nvlist_add_int32_array(nvl, "old_cpu_list", oldcpus, oldncpus) ||
nvlist_add_int32_array(nvl, "new_cpu_list", newcpus, newncpus)) {
goto done;
}
rv = rcm_notify_capacity_change(rcm_hdl, RCM_CPU_ALL, 0, nvl, &rinfo);
rv = (rv == RCM_SUCCESS) ? 0 : -1;
done:
s_nvfree(nvl);
s_free(cpus);
s_free(oldcpus);
s_free(newcpus);
return (rv);
}
static int
drd_rcm_del_cpu_request(drctl_rsrc_t *rsrcs, int nrsrc)
{
cpuid_t *cpus = NULL;
int ncpus;
int rv = -1;
cpuid_t *oldcpus = NULL;
cpuid_t *newcpus = NULL;
int oldncpus = 0;
int newncpus = 0;
nvlist_t *nvl = NULL;
int idx;
rcm_info_t *rinfo;
drd_dbg("drd_rcm_del_cpu_request...");
if ((rsrcs == NULL) || (nrsrc == 0)) {
drd_err("del_cpu_request: cpu list empty");
goto done;
}
ncpus = nrsrc;
cpus = (cpuid_t *)malloc(nrsrc * sizeof (cpuid_t));
for (idx = 0; idx < nrsrc; idx++) {
cpus[idx] = rsrcs[idx].res_cpu_id;
}
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
goto done;
}
if (get_sys_cpuids(&oldcpus, &oldncpus) == -1) {
goto done;
}
newcpus = (cpuid_t *)calloc(oldncpus, sizeof (cpuid_t));
if (newcpus == NULL) {
goto done;
}
for (idx = 0; idx < oldncpus; idx++) {
if (!is_cpu_in_list(oldcpus[idx], cpus, ncpus))
newcpus[newncpus++] = oldcpus[idx];
}
dump_cpu_list("oldcpus: ", oldcpus, oldncpus);
dump_cpu_list("newcpus: ", newcpus, newncpus);
dump_cpu_list("delta: ", cpus, ncpus);
if (nvlist_add_string(nvl, "state", "capacity") ||
nvlist_add_int32(nvl, "old_total", oldncpus) ||
nvlist_add_int32(nvl, "new_total", newncpus) ||
nvlist_add_int32_array(nvl, "old_cpu_list", oldcpus, oldncpus) ||
nvlist_add_int32_array(nvl, "new_cpu_list", newcpus, newncpus)) {
goto done;
}
rv = rcm_request_capacity_change(rcm_hdl, RCM_CPU_ALL, 0, nvl, &rinfo);
if (rv != RCM_SUCCESS) {
drd_dbg("RCM call failed: %d", rv);
for (idx = 0; idx < nrsrc; idx++) {
rsrcs[idx].status = DRCTL_STATUS_DENY;
}
rsrcs[0].offset = (uintptr_t)strdup("unable to remove "
"specified number of CPUs");
drd_dbg(" unable to remove specified number of CPUs");
goto done;
}
rv = 0;
done:
s_nvfree(nvl);
s_free(cpus);
s_free(oldcpus);
s_free(newcpus);
return (rv);
}
static int
drd_rcm_offline_cpu_request(drctl_rsrc_t *rsrcs, int nrsrc)
{
char **rlist;
drctl_rsrc_t *rsrc;
int idx;
int state;
int rv = 0;
rcm_info_t *rinfo = NULL;
rcm_info_tuple_t *tuple = NULL;
const char *rsrcstr;
const char *errstr;
drd_dbg("drd_rcm_offline_cpu_request...");
if ((rlist = drd_rcm_cpu_rlist_init(rsrcs, nrsrc,
DRCTL_STATUS_INIT)) == NULL) {
drd_err("unable to generate resource list");
return (-1);
}
rv = rcm_request_offline_list(rcm_hdl, rlist, 0, &rinfo);
if (rv == RCM_SUCCESS) {
drd_dbg("RCM success, rinfo=%p", rinfo);
goto done;
}
drd_dbg("RCM call failed (%d):", rv);
while ((tuple = rcm_info_next(rinfo, tuple)) != NULL) {
rsrcstr = rcm_info_rsrc(tuple);
rsrc = cpu_rsrcstr_to_rsrc(rsrcstr, rsrcs, nrsrc);
if (rsrc == NULL) {
drd_dbg("unable to find resource for %s", rsrcstr);
continue;
}
errstr = rcm_info_error(tuple);
if (errstr) {
drd_dbg(" %s: '%s'", rsrcstr, errstr);
rsrc->offset = (uintptr_t)strdup(errstr);
}
}
rcm_free_info(rinfo);
rv = 0;
done:
for (idx = 0; rlist[idx] != NULL; idx++) {
state = 0;
rcm_get_rsrcstate(rcm_hdl, rlist[idx], &state);
rsrc = cpu_rsrcstr_to_rsrc(rlist[idx], rsrcs, nrsrc);
if (rsrc == NULL) {
drd_dbg("unable to find resource for %s", rlist[idx]);
continue;
}
rsrc->status = ((state == RCM_STATE_OFFLINE) ?
DRCTL_STATUS_ALLOW : DRCTL_STATUS_DENY);
}
drd_rcm_cpu_rlist_fini(rlist);
return (rv);
}
static int
drd_rcm_remove_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc)
{
char **rlist;
int rv = 0;
rcm_info_t *rinfo;
drd_dbg("drd_rcm_remove_cpu_notify...");
if ((rlist = drd_rcm_cpu_rlist_init(rsrcs, nrsrc,
DRCTL_STATUS_CONFIG_SUCCESS)) == NULL) {
drd_dbg(" no CPUs in the success state, nothing to do");
return (0);
}
rv = rcm_notify_remove_list(rcm_hdl, rlist, 0, &rinfo);
if (rv != RCM_SUCCESS) {
drd_info("rcm_notify_remove_list failed: %d", rv);
rcm_free_info(rinfo);
rv = -1;
}
drd_rcm_cpu_rlist_fini(rlist);
return (rv);
}
static int
drd_rcm_restore_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc)
{
char **rlist;
char **full_rlist;
int idx;
int ridx;
int state;
int rv = 0;
rcm_info_t *rinfo;
drd_dbg("drd_rcm_restore_cpu_notify...");
if ((full_rlist = drd_rcm_cpu_rlist_init(rsrcs, nrsrc,
DRCTL_STATUS_CONFIG_FAILURE)) == NULL) {
drd_dbg(" no CPUs in the failed state, nothing to do");
return (0);
}
rlist = (char **)calloc((nrsrc + 1), sizeof (char *));
if (rlist == NULL) {
drd_err("calloc failed: %s", strerror(errno));
rv = -1;
goto done;
}
for (idx = 0, ridx = 0; full_rlist[idx] != NULL; idx++) {
state = 0;
rcm_get_rsrcstate(rcm_hdl, full_rlist[idx], &state);
if (state != RCM_STATE_ONLINE) {
rlist[ridx] = full_rlist[idx];
ridx++;
}
}
if (ridx == 0) {
drd_dbg(" all CPUs already online, nothing to do");
goto done;
}
rv = rcm_notify_online_list(rcm_hdl, rlist, 0, &rinfo);
if (rv != RCM_SUCCESS) {
drd_info("rcm_notify_online_list failed: %d", rv);
rcm_free_info(rinfo);
rv = -1;
}
done:
drd_rcm_cpu_rlist_fini(full_rlist);
s_free(rlist);
return (rv);
}
static int
drd_rcm_del_cpu_notify(drctl_rsrc_t *rsrcs, int nrsrc)
{
cpuid_t *cpus = NULL;
int rv = -1;
cpuid_t *oldcpus = NULL;
cpuid_t *newcpus = NULL;
int oldncpus = 0;
int newncpus = 0;
nvlist_t *nvl = NULL;
int idx;
int cidx;
rcm_info_t *rinfo;
drd_dbg("drd_rcm_del_cpu_notify...");
if ((rsrcs == NULL) || (nrsrc == 0)) {
drd_err("del_cpu_notify: cpu list empty");
goto done;
}
cpus = (cpuid_t *)malloc(nrsrc * sizeof (cpuid_t));
for (idx = 0, cidx = 0; idx < nrsrc; idx++) {
if (rsrcs[idx].status != DRCTL_STATUS_CONFIG_SUCCESS)
continue;
drd_dbg(" cpu[%d] = %d", idx, rsrcs[idx].res_cpu_id);
cpus[cidx] = rsrcs[idx].res_cpu_id;
cidx++;
}
drd_dbg(" ncpus = %d", cidx);
if (cidx == 0) {
rv = 0;
goto done;
}
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
goto done;
}
if (get_sys_cpuids(&newcpus, &newncpus) == -1) {
goto done;
}
oldcpus = (cpuid_t *)calloc(newncpus + cidx, sizeof (cpuid_t));
if (oldcpus == NULL) {
goto done;
}
for (idx = 0; idx < newncpus; idx++) {
if (!is_cpu_in_list(newcpus[idx], cpus, cidx))
oldcpus[oldncpus++] = newcpus[idx];
}
for (idx = 0; idx < cidx; idx++) {
oldcpus[oldncpus++] = cpus[idx];
}
dump_cpu_list("oldcpus: ", oldcpus, oldncpus);
dump_cpu_list("newcpus: ", newcpus, newncpus);
dump_cpu_list("delta: ", cpus, cidx);
if (nvlist_add_string(nvl, "state", "capacity") ||
nvlist_add_int32(nvl, "old_total", oldncpus) ||
nvlist_add_int32(nvl, "new_total", newncpus) ||
nvlist_add_int32_array(nvl, "old_cpu_list", oldcpus, oldncpus) ||
nvlist_add_int32_array(nvl, "new_cpu_list", newcpus, newncpus)) {
goto done;
}
rv = rcm_notify_capacity_change(rcm_hdl, RCM_CPU_ALL, 0, nvl, &rinfo);
rv = (rv == RCM_SUCCESS) ? 0 : -1;
done:
s_nvfree(nvl);
s_free(cpus);
s_free(oldcpus);
s_free(newcpus);
return (rv);
}
static char **
drd_rcm_cpu_rlist_init(drctl_rsrc_t *rsrcs, int nrsrc, int status)
{
char rbuf[RCM_CPU_MAX_LEN];
char **rlist;
int idx;
int ridx;
drd_dbg("drd_rcm_cpu_rlist_init...");
if ((rsrcs == NULL) || (nrsrc == 0)) {
drd_dbg("cpu list is empty");
return (NULL);
}
rlist = (char **)calloc((nrsrc + 1), sizeof (char *));
if (rlist == NULL) {
drd_err("calloc failed: %s", strerror(errno));
return (NULL);
}
for (idx = 0, ridx = 0; idx < nrsrc; idx++) {
drd_dbg(" checking cpu %d, status=%d, expected status=%d",
rsrcs[idx].res_cpu_id, rsrcs[idx].status, status);
if (rsrcs[idx].status != status)
continue;
(void) sprintf(rbuf, "%s%d", RCM_CPU, rsrcs[idx].res_cpu_id);
rlist[ridx] = strdup(rbuf);
if (rlist[ridx] == NULL) {
drd_err("strdup failed: %s", strerror(errno));
drd_rcm_cpu_rlist_fini(rlist);
return (NULL);
}
ridx++;
}
if (ridx == 0) {
s_free(rlist);
}
drd_dbg("final rlist:");
dump_cpu_rlist(rlist);
return (rlist);
}
static void
drd_rcm_cpu_rlist_fini(char **rlist)
{
int idx;
drd_dbg("drd_rcm_cpu_rlist_fini...");
dump_cpu_rlist(rlist);
for (idx = 0; rlist[idx] != NULL; idx++) {
s_free(rlist[idx]);
}
s_free(rlist);
}
static cpuid_t
cpu_rsrcstr_to_cpuid(const char *rsrc)
{
char *cpuid_off;
cpuid_t cpuid;
cpuid_off = strrchr(rsrc, 'u');
cpuid_off++;
cpuid = atoi(cpuid_off);
return (cpuid);
}
static drctl_rsrc_t *
cpu_rsrcstr_to_rsrc(const char *rsrcstr, drctl_rsrc_t *rsrcs, int nrsrc)
{
cpuid_t cpuid;
int idx;
cpuid = cpu_rsrcstr_to_cpuid(rsrcstr);
for (idx = 0; idx < nrsrc; idx++) {
if (rsrcs[idx].res_cpu_id == cpuid)
return (&rsrcs[idx]);
}
return (NULL);
}
static int
get_sys_cpuids(cpuid_t **cpuids, int *ncpuids)
{
int ncpu = 0;
int maxncpu;
kstat_t *ksp;
kstat_ctl_t *kc = NULL;
cpuid_t *cp;
drd_dbg("get_sys_cpuids...");
if ((maxncpu = sysconf(_SC_NPROCESSORS_MAX)) == -1)
return (-1);
if ((kc = kstat_open()) == NULL)
return (-1);
if ((cp = (cpuid_t *)calloc(maxncpu, sizeof (cpuid_t))) == NULL) {
(void) kstat_close(kc);
return (-1);
}
for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
if (strcmp(ksp->ks_module, "cpu_info") == 0)
cp[ncpu++] = ksp->ks_instance;
}
dump_cpu_list("syscpus: ", cp, ncpu);
(void) kstat_close(kc);
*cpuids = cp;
*ncpuids = ncpu;
return (0);
}
static boolean_t
is_cpu_in_list(cpuid_t cpuid, cpuid_t *list, int len)
{
int idx;
if (list == NULL)
return (B_FALSE);
for (idx = 0; idx < len; idx++) {
if (list[idx] == cpuid)
return (B_TRUE);
}
return (B_FALSE);
}
#define CPUIDS_PER_LINE 16
#define LINEWIDTH (2 * (CPUIDS_PER_LINE * 4))
static void
dump_cpu_list(char *prefix, cpuid_t *cpuids, int ncpuids)
{
char line[LINEWIDTH];
char *curr;
int i, j;
if (drd_debug == 0)
return;
if (ncpuids == 0) {
if (prefix)
drd_dbg("%s", prefix);
return;
}
for (i = 0; i < ncpuids; i += CPUIDS_PER_LINE) {
bzero(line, LINEWIDTH);
curr = line;
(void) sprintf(curr, "%s", (prefix) ? prefix : "");
curr = line + strlen(line);
for (j = 0; (j < CPUIDS_PER_LINE) && ((i + j) < ncpuids); j++) {
(void) sprintf(curr, "%3d ", cpuids[i + j]);
curr = line + strlen(line);
}
drd_dbg("%s", line);
}
}
static void
dump_cpu_rsrc_list(char *prefix, drctl_rsrc_t *rsrcs, int nrsrc)
{
int idx;
char *errstr;
if (drd_debug == 0)
return;
if (prefix)
drd_dbg("%s", prefix);
for (idx = 0; idx < nrsrc; idx++) {
errstr = (char *)(uintptr_t)rsrcs[idx].offset;
drd_dbg(" cpu[%d]: cpuid=%d, status=%d, errstr='%s'", idx,
rsrcs[idx].res_cpu_id, rsrcs[idx].status,
(errstr != NULL) ? errstr : "");
}
}
static void
dump_cpu_rlist(char **rlist)
{
int idx;
int state;
static char *rcm_state_str[] = {
"UNKNOWN", "ONLINE", "ONLINING",
"OFFLINE_FAIL", "OFFLINING", "OFFLINE",
"REMOVING", "INVALID_7", "INVALID_8",
"INVALID_9", "RESUMING", "SUSPEND_FAIL",
"SUSPENDING", "SUSPEND", "REMOVE",
"OFFLINE_QUERYING", "OFFLINE_QUERY_FAIL", "OFFLINE_QUERY",
"SUSPEND_QUERYING", "SUSPEND_QUERY_FAIL", "SUSPEND_QUERY"
};
if (drd_debug == 0)
return;
if (rlist == NULL) {
drd_dbg(" empty rlist");
return;
}
for (idx = 0; rlist[idx] != NULL; idx++) {
state = 0;
rcm_get_rsrcstate(rcm_hdl, rlist[idx], &state);
drd_dbg(" rlist[%d]: rsrc=%s, state=%-2d (%s)", idx,
rlist[idx], state, rcm_state_str[state]);
}
}
static int
drd_rcm_io_config_request(drctl_rsrc_t *rsrc, int nrsrc)
{
drd_dbg("drd_rcm_io_config_request...");
if (nrsrc != 1) {
drd_dbg("drd_rcm_cpu_config_request: only 1 resource "
"allowed for I/O requests, passed %d resources\n", nrsrc);
rsrc->status = DRCTL_STATUS_DENY;
return (-1);
}
rsrc->status = DRCTL_STATUS_ALLOW;
return (0);
}
static int
drd_rcm_io_config_notify(drctl_rsrc_t *rsrcs, int nrsrc)
{
drd_dbg("drd_rcm_io_config_notify...");
if (nrsrc != 1) {
drd_dbg("drd_rcm_cpu_config_notify: only 1 resource "
"allowed for I/O requests, passed %d resources\n", nrsrc);
return (-1);
}
return (0);
}
static int
drd_rcm_io_unconfig_request(drctl_rsrc_t *rsrc, int nrsrc)
{
int rv;
char *dev = rsrc->res_dev_path;
rcm_info_t *rinfo = NULL;
if (nrsrc != 1) {
drd_dbg("drd_io_unconfig_request: only 1 resource "
"allowed for I/O requests, passed %d resources\n", nrsrc);
rsrc->status = DRCTL_STATUS_DENY;
return (-1);
}
if ((rv = rcm_request_offline(rcm_hdl, dev, 0, &rinfo)) == RCM_SUCCESS)
rsrc->status = DRCTL_STATUS_ALLOW;
else {
rcm_notify_online(rcm_hdl, dev, 0, NULL);
rsrc->status = DRCTL_STATUS_DENY;
rsrc->offset = (uintptr_t)rcm_info_table(rinfo);
}
rcm_free_info(rinfo);
drd_dbg("drd_rcm_io_unconfig_request(%s) = %d", dev, rv);
return (rv);
}
static int
drd_rcm_io_unconfig_notify(drctl_rsrc_t *rsrc, int nrsrc)
{
drd_dbg("drd_rcm_io_unconfig_notify...");
if (nrsrc != 1) {
drd_dbg("drd_io_cpu_unconfig_notify: only 1 resource "
"allowed for I/O requests, passed %d resources\n", nrsrc);
return (-1);
}
return (rcm_notify_remove(rcm_hdl, rsrc->res_dev_path, 0, NULL));
}
#define MAX_FORMAT 80
static char *
rcm_info_table(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 table_size = 0;
uint_t tuples = 0;
rcm_info_tuple_t *tuple = NULL;
char *rsrc;
char *info;
char *table;
static char format[MAX_FORMAT];
const char *infostr;
if (rinfo == NULL)
return (NULL);
rsrc = dgettext(TEXT_DOMAIN, "Resource");
info = dgettext(TEXT_DOMAIN, "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 (tuples == 0)
return (NULL);
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++;
width = w_info + w_rsrc + 4;
table_size = (2 + tuples) * (width + 1) + 2;
table = calloc(table_size, sizeof (char));
if (table == NULL)
return (NULL);
(void) strcat(table, "\n");
w = strlen(rsrc);
for (i = 0; i < ((w_rsrc - w) / 2); i++)
(void) strcat(table, " ");
(void) strcat(table, rsrc);
for (i = 0; i < ((w_rsrc - w) / 2); i++)
(void) strcat(table, " ");
(void) strcat(table, " ");
w = strlen(info);
for (i = 0; i < ((w_info - w) / 2); i++)
(void) strcat(table, " ");
(void) strcat(table, info);
for (i = 0; i < ((w_info - w) / 2); i++)
(void) strcat(table, " ");
(void) strcat(table, "\n");
for (i = 0; i < w_rsrc; i++)
(void) strcat(table, "-");
(void) strcat(table, " ");
for (i = 0; i < w_info; i++)
(void) strcat(table, "-");
(void) snprintf(format, MAX_FORMAT, "%%-%ds %%-%ds",
(int)w_rsrc, (int)w_info);
tuple = NULL;
while ((tuple = rcm_info_next(rinfo, tuple)) != NULL) {
if ((infostr = rcm_info_info(tuple)) != NULL) {
(void) strcat(table, "\n");
(void) sprintf(&((table)[strlen(table)]),
format, rcm_info_rsrc(tuple),
infostr);
}
}
drd_dbg("rcm_info_table: %s\n", table);
return (table);
}
static void
dump_mem_rsrc_list(char *prefix, drctl_rsrc_t *rsrcs, int nrsrc)
{
int idx;
char *errstr;
if (drd_debug == 0)
return;
if (prefix)
drd_dbg("%s", prefix);
for (idx = 0; idx < nrsrc; idx++) {
errstr = (char *)(uintptr_t)rsrcs[idx].offset;
drd_dbg(" mem[%d]: addr=0x%llx, size=0x%llx"
" status=%d, errstr='%s'", idx,
rsrcs[idx].res_mem_addr, rsrcs[idx].res_mem_size,
rsrcs[idx].status, (errstr != NULL) ? errstr : "");
}
}
static int
drd_rcm_mem_op(rcm_op_t op, uint64_t change)
{
int rv = -1;
int pgsize;
long oldpages;
long newpages;
nvlist_t *nvl = NULL;
rcm_info_t *rinfo;
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
goto done;
if ((pgsize = sysconf(_SC_PAGE_SIZE)) == -1 ||
(newpages = sysconf(_SC_PHYS_PAGES)) == -1)
goto done;
assert(change % pgsize == 0);
if (change > 0) {
oldpages = newpages - (long)(change / pgsize);
} else {
assert(newpages >= change / pgsize);
oldpages = newpages;
newpages = oldpages + (long)(change / pgsize);
}
drd_dbg("oldpages=%lld newpages=%lld delta=%lld",
oldpages, newpages, newpages - oldpages);
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) {
goto done;
}
rv = (*op)(rcm_hdl, RCM_MEM_ALL, 0, nvl, &rinfo);
rv = (rv == RCM_SUCCESS) ? 0 : -1;
done:
s_nvfree(nvl);
return (rv);
}
static int
drd_rcm_mem_config_request(drctl_rsrc_t *rsrcs, int nrsrc)
{
int idx;
drd_dbg("drd_rcm_mem_config_request...");
if ((rsrcs == NULL) || (nrsrc == 0))
return (0);
dump_mem_rsrc_list(NULL, rsrcs, nrsrc);
for (idx = 0; idx < nrsrc; idx++)
rsrcs[idx].status = DRCTL_STATUS_ALLOW;
dump_mem_rsrc_list("returning:", rsrcs, nrsrc);
return (0);
}
static int
drd_rcm_mem_config_notify(drctl_rsrc_t *rsrcs, int nrsrc)
{
int idx;
int rv = -1;
uint64_t change = 0;
drd_dbg("drd_rcm_mem_config_notify...");
if ((rsrcs == NULL) || (nrsrc == 0)) {
drd_err("mem_config_notify: mem list empty");
goto done;
}
dump_mem_rsrc_list(NULL, rsrcs, nrsrc);
for (idx = 0; idx < nrsrc; idx++) {
if (rsrcs[idx].status == DRCTL_STATUS_CONFIG_SUCCESS)
change += rsrcs[idx].res_mem_size;
drd_dbg(" idx=%d addr=0x%llx size=0x%llx",
idx, rsrcs[idx].res_mem_addr, rsrcs[idx].res_mem_size);
}
rv = drd_rcm_mem_op(rcm_notify_capacity_change, change);
done:
return (rv);
}
static int
drd_rcm_mem_unconfig_request(drctl_rsrc_t *rsrcs, int nrsrc)
{
int rv = -1;
int idx;
uint64_t change = 0;
drd_dbg("drd_rcm_del_mem_request...");
if ((rsrcs == NULL) || (nrsrc == 0)) {
drd_err("mem_unconfig_request: mem list empty");
goto done;
}
dump_mem_rsrc_list(NULL, rsrcs, nrsrc);
for (idx = 0; idx < nrsrc; idx++) {
drd_dbg(" idx=%d addr=0x%llx size=0x%llx",
idx, rsrcs[idx].res_mem_addr, rsrcs[idx].res_mem_size);
change += rsrcs[idx].res_mem_size;
}
rv = drd_rcm_mem_op(rcm_request_capacity_change, -change);
if (rv != RCM_SUCCESS) {
drd_dbg("RCM call failed: %d", rv);
for (idx = 0; idx < nrsrc; idx++) {
rsrcs[idx].status = DRCTL_STATUS_DENY;
}
rsrcs[0].offset = (uintptr_t)strdup("unable to remove "
"specified amount of memory");
drd_dbg(" unable to remove specified amount of memory");
} else {
for (idx = 0; idx < nrsrc; idx++)
rsrcs[idx].status = DRCTL_STATUS_ALLOW;
rv = 0;
}
done:
dump_mem_rsrc_list("returning:", rsrcs, nrsrc);
return (rv);
}
static int
drd_rcm_mem_unconfig_notify(drctl_rsrc_t *rsrcs, int nrsrc)
{
int idx;
int rv = -1;
uint64_t change = 0;
drd_dbg("drd_rcm_mem_unconfig_notify...");
if ((rsrcs == NULL) || (nrsrc == 0)) {
drd_err("unconfig_mem_notify: mem list empty");
goto done;
}
dump_mem_rsrc_list(NULL, rsrcs, nrsrc);
for (idx = 0; idx < nrsrc; idx++) {
if (rsrcs[idx].status != DRCTL_STATUS_CONFIG_SUCCESS)
change += rsrcs[idx].res_mem_size;
drd_dbg(" idx=%d addr=0x%llx size=0x%llx",
idx, rsrcs[idx].res_mem_addr, rsrcs[idx].res_mem_size);
}
rv = drd_rcm_mem_op(rcm_notify_capacity_change, change);
done:
return (rv);
}