/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* RCM backend for the DR Daemon
*/
#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"
/*
* RCM Backend Support
*/
static int drd_rcm_init(void);
static int drd_rcm_fini(void);
drd_rcm_init, /* init */
drd_rcm_fini, /* fini */
drd_rcm_cpu_config_request, /* cpu_config_request */
drd_rcm_cpu_config_notify, /* cpu_config_notify */
drd_rcm_cpu_unconfig_request, /* cpu_unconfig_request */
drd_rcm_cpu_unconfig_notify, /* cpu_unconfig_notify */
drd_rcm_io_config_request, /* io_config_request */
drd_rcm_io_config_notify, /* io_config_notify */
drd_rcm_io_unconfig_request, /* io_unconfig_request */
drd_rcm_io_unconfig_notify, /* io_unconfig_notify */
drd_rcm_mem_config_request, /* mem_config_request */
drd_rcm_mem_config_notify, /* mem_config_notify */
drd_rcm_mem_unconfig_request, /* mem_unconfig_request */
drd_rcm_mem_unconfig_notify /* mem_unconfig_notify */
};
rcm_info_t **);
/* global RCM handle used in all RCM operations */
/* functions that call into RCM */
/* utility functions */
static void drd_rcm_cpu_rlist_fini(char **rlist);
/* debugging utility functions */
static void dump_cpu_rlist(char **rlist);
static int
drd_rcm_init(void)
{
int rv;
drd_dbg("drd_rcm_init...");
if (rv == RCM_FAILURE) {
return (-1);
}
return (0);
}
static int
drd_rcm_fini(void)
{
drd_dbg("drd_rcm_fini...");
return (0);
}
static int
{
int idx;
drd_dbg("drd_rcm_cpu_config_request...");
/*
* There is no RCM operation to request the addition
* of resources. So, by definition, the operation for
* all the CPUs is allowed.
*/
return (0);
}
static int
{
int rv = 0;
drd_dbg("drd_rcm_cpu_config_notify...");
/* notify RCM about the newly added CPUs */
rv = -1;
goto done;
}
/* notify RCM about the increased CPU capacity */
rv = -1;
}
done:
return (rv);
}
static int
{
int rv = 0;
int idx;
drd_dbg("drd_rcm_cpu_unconfig_request...");
/* contact RCM to request a decrease in CPU capacity */
rv = -1;
goto done;
}
/* contact RCM to request the removal of CPUs */
rv = -1;
goto done;
}
done:
/*
* If any errors occurred, the status field for
* a CPU may still be in the INIT state. Set the
* status for any such CPU to DENY to ensure it
* gets processed properly.
*/
}
return (rv);
}
static int
{
int rv = 0;
drd_dbg("drd_rcm_cpu_unconfig_notify...");
/*
* Notify RCM about the CPUs that were removed.
* Failures are ignored so that CPUs that could
* not be unconfigured can be processed by RCM.
*/
/*
* Notify RCM about any CPUs that did not make it
* in to the unconfigured state.
*/
rv = -1;
goto done;
}
/* notify RCM about the decreased CPU capacity */
rv = -1;
}
done:
return (rv);
}
static int
{
char **rlist;
int rv = 0;
drd_dbg("drd_rcm_online_cpu_notify...");
DRCTL_STATUS_CONFIG_SUCCESS)) == NULL) {
drd_dbg(" no CPUs were successfully added, nothing to do");
return (0);
}
if (rv != RCM_SUCCESS) {
rv = -1;
}
return (rv);
}
static int
{
int ncpus;
int oldncpus = 0;
int newncpus = 0;
int idx;
drd_dbg("drd_rcm_add_cpu_notify...");
drd_err("add_cpu_notify: cpu list empty");
goto done;
}
}
/* allocate an nvlist for the RCM call */
goto done;
/*
* Added CPU capacity, so newcpus is the current list
* of CPUs in the system.
*/
goto done;
/*
* Since the operation added CPU capacity, the old CPU
* list is the new CPU list with the CPUs involved in
* the operation removed.
*/
goto done;
}
/* dump pre and post lists */
/* setup the nvlist for the RCM call */
goto done;
}
done:
return (rv);
}
static int
{
int ncpus;
int oldncpus = 0;
int newncpus = 0;
int idx;
drd_dbg("drd_rcm_del_cpu_request...");
drd_err("del_cpu_request: cpu list empty");
goto done;
}
}
/* allocate an nvlist for the RCM call */
goto done;
}
/*
* Removing CPU capacity, so oldcpus is the current
* list of CPUs in the system.
*/
goto done;
}
/*
* Since this is a request to remove CPU capacity,
* the new CPU list is the old CPU list with the CPUs
* involved in the operation removed.
*/
goto done;
}
}
/* dump pre and post lists */
/* setup the nvlist for the RCM call */
goto done;
}
if (rv != RCM_SUCCESS) {
/*
* Since the capacity change was blocked, we
* mark all CPUs as blocked. It is up to the
* user to reframe the query so that it can
* succeed.
*/
}
/* tack on message to first resource */
"specified number of CPUs");
drd_dbg(" unable to remove specified number of CPUs");
goto done;
}
rv = 0;
done:
return (rv);
}
static int
{
char **rlist;
int idx;
int state;
int rv = 0;
const char *rsrcstr;
const char *errstr;
drd_dbg("drd_rcm_offline_cpu_request...");
DRCTL_STATUS_INIT)) == NULL) {
drd_err("unable to generate resource list");
return (-1);
}
if (rv == RCM_SUCCESS) {
goto done;
}
/*
* Loop through the result of the operation and add
* any error messages to the resource structure.
*/
/* find the resource of interest */
continue;
}
if (errstr) {
}
}
rv = 0;
done:
/*
* Set the state of the resource based on the RCM
* state. CPUs in the offline state have the ok to
* proceed. All others have been blocked.
*/
state = 0;
/* find the resource of interest */
continue;
}
}
return (rv);
}
static int
{
char **rlist;
int rv = 0;
drd_dbg("drd_rcm_remove_cpu_notify...");
DRCTL_STATUS_CONFIG_SUCCESS)) == NULL) {
drd_dbg(" no CPUs in the success state, nothing to do");
return (0);
}
if (rv != RCM_SUCCESS) {
rv = -1;
}
return (rv);
}
static int
{
char **rlist;
char **full_rlist;
int idx;
int ridx;
int state;
int rv = 0;
drd_dbg("drd_rcm_restore_cpu_notify...");
DRCTL_STATUS_CONFIG_FAILURE)) == NULL) {
drd_dbg(" no CPUs in the failed state, nothing to do");
return (0);
}
/*
* Since the desired result of this operation is to
* restore resources to the online state, filter out
* the resources already in the online state before
* passing the list to RCM.
*/
/* allocate a zero filled array to ensure NULL terminated list */
rv = -1;
goto done;
}
state = 0;
if (state != RCM_STATE_ONLINE) {
ridx++;
}
}
/* check if everything got filtered out */
if (ridx == 0) {
drd_dbg(" all CPUs already online, nothing to do");
goto done;
}
if (rv != RCM_SUCCESS) {
rv = -1;
}
done:
return (rv);
}
static int
{
int oldncpus = 0;
int newncpus = 0;
int idx;
int cidx;
drd_dbg("drd_rcm_del_cpu_notify...");
drd_err("del_cpu_notify: cpu list empty");
goto done;
}
/*
* Filter out the CPUs that could not be unconfigured.
*/
continue;
cidx++;
}
/* nothing to do */
if (cidx == 0) {
rv = 0;
goto done;
}
/* allocate an nvlist for the RCM call */
goto done;
}
/*
* Removed CPU capacity, so newcpus is the current list
* of CPUs in the system.
*/
goto done;
}
/*
* Since the operation removed CPU capacity, the old CPU
* list is the new CPU list with the CPUs involved in
* the operation added.
*/
goto done;
}
}
}
/* dump pre and post lists */
/* setup the nvlist for the RCM call */
goto done;
}
done:
return (rv);
}
/*
* Given a list of resource structures, create a list of CPU
* resource strings formatted as expected by RCM. Only resources
* that are in the state specified by the status argument are
* included in the resulting list.
*/
static char **
{
char **rlist;
int idx;
int ridx;
drd_dbg("drd_rcm_cpu_rlist_init...");
drd_dbg("cpu list is empty");
return (NULL);
}
/* allocate a zero filled array to ensure NULL terminated list */
return (NULL);
}
drd_dbg(" checking cpu %d, status=%d, expected status=%d",
/*
* Filter out the CPUs that are not in
* the requested state.
*/
continue;
/* generate the resource string */
return (NULL);
}
ridx++;
}
/* cleanup if the list is empty */
if (ridx == 0) {
}
drd_dbg("final rlist:");
return (rlist);
}
static void
{
int idx;
drd_dbg("drd_rcm_cpu_rlist_fini...");
}
}
/*
* Convert an RCM CPU resource string into a numerical cpuid.
* where "<C>" is the numerical cpuid of interest.
*/
static cpuid_t
{
char *cpuid_off;
/*
* Search for the last occurrance of 'u' in the
* This will give a pointer to the cpuid portion.
*/
cpuid_off++;
return (cpuid);
}
/*
* Given an RCM CPU resource string, return a pointer to the
* corresponding resource structure from the given resource list.
* NULL is returned if no matching resource structure can be
* found.
*/
static drctl_rsrc_t *
{
int idx;
}
return (NULL);
}
static int
{
int ncpu = 0;
int maxncpu;
drd_dbg("get_sys_cpuids...");
return (-1);
return (-1);
(void) kstat_close(kc);
return (-1);
}
}
(void) kstat_close(kc);
return (0);
}
static boolean_t
{
int idx;
return (B_FALSE);
return (B_TRUE);
}
return (B_FALSE);
}
static void
{
char *curr;
int i, j;
/* return if not debugging */
if (drd_debug == 0)
return;
/* print just the prefix if CPU list is empty */
if (ncpuids == 0) {
if (prefix)
return;
}
for (i = 0; i < ncpuids; i += CPUIDS_PER_LINE) {
/* start with the prefix */
/* format the CPUs for this line */
for (j = 0; (j < CPUIDS_PER_LINE) && ((i + j) < ncpuids); j++) {
}
}
}
static void
{
int idx;
char *errstr;
/* just return if not debugging */
if (drd_debug == 0)
return;
if (prefix)
/* get a pointer to the error string */
}
}
static void
{
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"
};
/* just return if not debugging */
if (drd_debug == 0)
return;
drd_dbg(" empty rlist");
return;
}
state = 0;
}
}
static int
{
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);
return (-1);
}
/*
* There is no RCM operation to request the addition
* of resources. So, by definition, the operation for
* the current resource is allowed.
*/
return (0);
}
/*ARGSUSED*/
static int
{
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
{
int rv;
if (nrsrc != 1) {
drd_dbg("drd_io_unconfig_request: only 1 resource "
"allowed for I/O requests, passed %d resources\n", nrsrc);
return (-1);
}
else {
}
return (rv);
}
static int
{
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);
}
}
/*
* Convert rcm_info_t data into a printable table.
*/
static char *
{
int i;
size_t w;
char *rsrc;
char *info;
char *table;
const char *infostr;
/* Protect against invalid arguments */
return (NULL);
/* Set localized table header strings */
/* A first pass, to size up the RCM information */
tuples++;
w_rsrc = w;
w_info = w;
}
}
/* If nothing was sized up above, stop early */
if (tuples == 0)
return (NULL);
/* Adjust column widths for column headings */
w_rsrc = w;
else if ((w_rsrc - w) % 2)
w_rsrc++;
w_info = w;
else if ((w_info - w) % 2)
w_info++;
/*
* Compute the total line width of each line,
* accounting for intercolumn spacing.
*/
/* Allocate space for the table */
/* zero fill for the strcat() call below */
return (NULL);
/* Place a table header into the string */
/* The resource header */
for (i = 0; i < ((w_rsrc - w) / 2); i++)
for (i = 0; i < ((w_rsrc - w) / 2); i++)
/* The information header */
for (i = 0; i < ((w_info - w) / 2); i++)
for (i = 0; i < ((w_info - w) / 2); i++)
/* Underline the headers */
for (i = 0; i < w_rsrc; i++)
for (i = 0; i < w_info; i++)
/* Construct the format string */
/* Add the tuples to the table string */
infostr);
}
}
return (table);
}
static void
{
int idx;
char *errstr;
/* just return if not debugging */
if (drd_debug == 0)
return;
if (prefix)
/* get a pointer to the error string */
drd_dbg(" mem[%d]: addr=0x%llx, size=0x%llx"
" status=%d, errstr='%s'", idx,
}
}
static int
{
int pgsize;
long oldpages;
long newpages;
/* allocate an nvlist for the RCM call */
goto done;
goto done;
/*
* If this is a notify add, the capacity change
* was positive and the current page count reflects
* the new capacity level.
*
* If this is a request del, the capacity change
* is negative and the current page count will
* reflect the old capacity level.
*/
if (change > 0) {
} else {
}
drd_dbg("oldpages=%lld newpages=%lld delta=%lld",
/* setup the nvlist for the RCM call */
goto done;
}
done:
return (rv);
}
/*
* RCM clients can interpose only on removal of resources.
*/
static int
{
int idx;
drd_dbg("drd_rcm_mem_config_request...");
return (0);
/*
* There is no RCM operation to request the addition
* of resources. So, by definition, the operation for
* all the memory is allowed.
*/
return (0);
}
static int
{
int idx;
drd_dbg("drd_rcm_mem_config_notify...");
drd_err("mem_config_notify: mem list empty");
goto done;
}
drd_dbg(" idx=%d addr=0x%llx size=0x%llx",
}
done:
return (rv);
}
static int
{
int idx;
drd_dbg("drd_rcm_del_mem_request...");
drd_err("mem_unconfig_request: mem list empty");
goto done;
}
drd_dbg(" idx=%d addr=0x%llx size=0x%llx",
}
if (rv != RCM_SUCCESS) {
/*
* Since the capacity change was blocked, we
* mark all mblocks as blocked. It is up to the
* user to reframe the query so that it can
* succeed.
*/
}
/* tack on message to first resource */
"specified amount of memory");
drd_dbg(" unable to remove specified amount of memory");
} else {
rv = 0;
}
done:
return (rv);
}
static int
{
int idx;
drd_dbg("drd_rcm_mem_unconfig_notify...");
drd_err("unconfig_mem_notify: mem list empty");
goto done;
}
/*
* Filter out the memory that was configured.
*
* We need to notify RCM about a memory capacity change
* only if the memory unconfigure request wasn't successful
* because if both the RCM capacity delete request and the
* memory unconfigure succeed, this notify would give a
* memory capacity identical to the delete request.
*/
drd_dbg(" idx=%d addr=0x%llx size=0x%llx",
}
done:
return (rv);
}