/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#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 <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 struct {
int valid;
int ncap;
union {
long npages;
} type;
} cap_info_t;
typedef struct {
void *lib;
char **rlist;
rcm_info_t **);
rcm_info_t **);
rcm_info_t **);
rcm_info_t **);
nvlist_t *, rcm_info_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",
};
#define ALLOC_HANDLE 0
/*
* There is no consumer for SUNW_OS. This is defined here
* for generic OS quiescence.
*/
/* Max width of an RCM formatted message line */
#ifdef __sparcv9
#else
#endif
static cfga_err_t
{
int cm;
int ncm;
void *cap;
int *ncap;
DBG("ap_capinfo(%p)\n", (void *)a);
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.
*/
return (CFGA_LIB_ERROR);
}
ncm = 0;
switch (ap_cm_type(a, cm)) {
case AP_CPU:
case AP_CMP:
break;
case AP_MEM:
break;
default:
continue;
}
/*
* Remember which components have valid
* capacity information.
*/
ncm++;
}
}
if (ncm == 0)
else
return (CFGA_OK);
}
static int
{
int ncpu;
int maxncpu;
DBG("getsyscpuids\n");
/* if calloc failed, clean up kstats */
(void) kstat_close(kc);
}
return (-1);
}
DBG("syscpuids: ");
}
}
DBG("\n");
(void) kstat_close(kc);
return (0);
}
{
int i;
char *err;
char *rcmlib;
void *sym;
void *lib;
char **op;
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.
*/
return (CFGA_OK);
}
rc = CFGA_LIB_ERROR;
/*
* If the library is not present, there is nothing more
* in that case.
*/
a->norcm++;
return (CFGA_OK);
} else {
return (rc);
}
}
return (rc);
}
return (rc);
}
return (rc);
}
switch (i) {
case ALLOC_HANDLE:
rcm->alloc_handle = (int(*)
break;
case FREE_HANDLE:
break;
case GET_INFO:
break;
case FREE_INFO:
break;
case INFO_TUPLE_NEXT:
break;
case INFO_TUPLE_STATE:
break;
case INFO_TUPLE_ID:
break;
case INFO_TUPLE_ERROR:
rcm->info_error = (const char *(*)
(rcm_info_tuple_t *))sym;
break;
case INFO_TUPLE_INFO:
(rcm_info_tuple_t *))sym;
break;
case INFO_TUPLE_RSRC:
(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,
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,
break;
case NOTIFY_CAP_CHANGE:
rcm->notify_capacity_change = (int (*)
(rcm_handle_t *, char *, uint_t,
break;
default:
break;
}
}
!= RCM_SUCCESS) {
ap_err(a, ERR_RCM_HANDLE);
return (CFGA_LIB_ERROR);
}
/*
* 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.
*/
} else {
}
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.
*/
}
}
== NULL) {
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.
*/
return (rc);
}
void
{
char **rp;
DBG("ap_rcm_fini(%p)\n", (void *)a);
return;
/*
* Free all the names in the resource list, followed
* by the resource list itself.
*/
}
static cfga_err_t
{
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.
*/
}
return (CFGA_LIB_ERROR);
}
/* <--- 12 ---> */
/*
* Set the RCM resource name for each component:
*
* io: <device-path>
*
*/
switch (ap_cm_type(a, cm)) {
case AP_CPU:
case AP_CMP: {
int i;
int len;
int *nc;
int allow_op;
int capindex;
/*
* See if the request target is a single
* (default) component
*/
/* Get the previous capacity info */
break;
}
/*
* 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) &&
allow_op = 1;
}
if (allow_op) {
for (i = 0; i < *nc; i++) {
return (CFGA_LIB_ERROR);
}
cpuid[i]);
}
}
break;
}
case AP_IO:
}
break;
case AP_MEM:
/*
* Nothing to do for AP_MEM since only capacity
* change notifications apply to SUNW_memory
*/
default:
break;
}
}
if (rlist)
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
{
int i;
DBG("is_cpu_in_list\n");
return (0);
for (i = 0; i < length; i++) {
return (1);
}
return (0);
}
static int
{
int i;
int ncpuids;
int oldncpuids;
int newncpuids;
const char *fmt;
DBG("ap_rcm_cap_cpu(%p)\n", (void *)a);
/*
* Get the current number of configured cpus.
*/
return (rv);
goto done;
}
if (change == 1)
fmt = "(%d cpu)";
else
fmt = "(%d cpus)";
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.
*/
/*
* Fill newcpuids with the CPU IDs in the cpuids array,
* but not in rcm->cpuids.
*/
goto done;
newncpuids = 0;
for (i = 0; i < ncpuids; 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. */
/*
* 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.
*/
goto done;
oldncpuids = 0;
for (i = 0; i < ncpuids; i++) {
}
for (i = 0; i < change; i++)
} else {
DBG("ap_rcm_cap_cpu: CPU capacity, old = %d, new = %d \n",
/* 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.
*/
/*
* Fill oldcpuids with the CPU IDs in the cpuids array,
* but not in rcm->cpuids.
*/
goto done;
oldncpuids = 0;
for (i = 0; i < ncpuids; i++) {
}
}
DBG("oldcpuids: ");
for (i = 0; i < oldncpuids; i++)
DBG("\n");
DBG("change : ");
for (i = 0; i < change; i++)
DBG("\n");
DBG("newcpuids: ");
for (i = 0; i < newncpuids; i++)
DBG("\n");
oldncpuids) != 0 ||
newncpuids) != 0)
goto done;
if (cmd == CMD_RCM_CAP_DEL) {
} else {
}
done:
if (nvl)
return (rv);
}
static int
{
int rv;
int pgsize;
long oldpages;
long newpages;
long currpages;
DBG("ap_rcm_cap_mem(%p)\n", (void *)a);
/*
* Get the current amount of configured memory.
*/
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) {
} else if (cmd == CMD_RCM_CAP_NOTIFY) {
} else {
/* No real change in memory capacity */
DBG("ap_rcm_cap_mem: no change in capacity.\n");
return (RCM_SUCCESS);
}
}
DBG("ap_rcm_cap_mem: Memory capacity, old = %ld, new = %ld\n",
return (RCM_FAILURE);
}
if (cmd == CMD_RCM_CAP_DEL) {
} else {
}
return (rv);
}
static cfga_err_t
{
int cm;
int ncpus;
long npages;
DBG("ap_rcm_request_cap(%p)\n", (void *)a);
return (CFGA_LIB_ERROR);
}
int i, j;
/*
* See if the request target is a single
* (default) component
*/
/*
* We are interested only in those components
* in the configured state since they represent
* available capacity.
*/
continue;
}
}
return (CFGA_LIB_ERROR);
}
return (CFGA_LIB_ERROR);
}
return (CFGA_OK);
}
static cfga_err_t
{
int cm;
int ncpus;
long npages;
DBG("ap_rcm_add_cap(%p)\n", (void *)a);
/* Get the new capacity info to figure out what has changed */
return (rc);
DBG("no pertinent capacity info\n");
return (CFGA_OK);
}
int i, j;
int prevvalidity;
/*
* See if the request target is a single
* (default) component
*/
if (prevcapinfo == NULL) {
prevvalidity = 1;
} else {
}
DBG("cm=%d valid=%d type=%d, prevos=%d os=%d\n",
/*
* We are interested only in those components
* whose states have changed to configured as
* the result of the current cfgadm request.
*/
continue;
} else if (prevos != CFGA_STAT_CONFIGURED) {
/*
* The occupant state is configured, and
* the previous occupant state was not.
*/
}
}
}
return (CFGA_LIB_ERROR);
}
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
{
int cm;
long npages = 0;
int ncpus = 0;
DBG("ap_rcm_notify_cap(%p)\n", (void *)a);
/* Get the new capacity info to figure out what has changed */
return (rc);
DBG("no pertinent capacity info\n");
return (CFGA_OK);
}
/* The original capacity info */
/*
* Cycle through all components that we are operating
* on. Record which components' occupant states have
* changed.
*/
int i;
int prev_conf = 0;
int now_conf = 0;
/*
* See if the request target is a single
* (default) component
*/
if (prevcapinfo == NULL) {
} else {
if (prevcapinfo[i].valid == 0) {
DBG("ap_rcm_notify_cap: skipping component "
"due to prevvalidity == 0\n");
continue;
}
}
/*
* Build up rcm->cpuids with the IDs of CPUs that
* have been removed. Record the number of removed
* CPUs and pages.
*/
if (prev_conf)
prev_cpus++;
int j;
}
}
if (prev_conf)
prev_mem++;
}
}
/*
* If any CPU or memory components were operated on,
* successfully or not, the rcm_notify_capacity_change()
* routine must be called.
*/
if (prev_cpus) {
if (*rv != RCM_SUCCESS)
return (CFGA_LIB_ERROR);
}
if (prev_mem) {
if (*rv != RCM_SUCCESS)
return (CFGA_LIB_ERROR);
}
return (CFGA_OK);
}
{
int i;
int rv;
int noop;
int ncpus;
int cm;
char *rsrc;
char **rlist;
DBG("ap_rcm_ctl(%p)\n", (void *)a);
return (CFGA_OK);
}
rv = RCM_SUCCESS;
else
flags = 0;
noop = 0;
switch (cmd) {
case CMD_RCM_CAP_DEL:
noop++;
else
break;
case CMD_RCM_CAP_ADD:
break;
case CMD_RCM_CAP_NOTIFY:
break;
case CMD_RCM_ONLINE:
/* Refresh changed component states */
noop++;
break;
}
/* Check if we need to grow our cpuids list */
cm++) {
}
if ((growcpuids =
return (CFGA_LIB_ERROR);
}
}
} else {
}
/*FALLTHROUGH*/
case CMD_RCM_OFFLINE:
case CMD_RCM_REMOVE: {
if (cmd == CMD_RCM_REMOVE) {
/*
* An unconfigure has just taken place, so
* refresh the changed component states.
*/
noop++;
break;
}
}
/* Check if this is an empty board, i.e. no components */
if (a->ncm == 0) {
noop++;
break;
}
cmd);
} else {
/* Do not pass up empty resource list to RCM */
noop++;
break;
}
}
if (cmd == CMD_RCM_OFFLINE)
&rinfo);
else if (cmd == CMD_RCM_ONLINE)
else
break;
}
case CMD_RCM_SUSPEND: {
timespec_t t;
t.tv_nsec = (long)0;
break;
}
case CMD_RCM_RESUME:
break;
default:
return (CFGA_INVAL);
}
if (rv != RCM_SUCCESS) {
}
if (rlist)
for (i = 0; rlist[i]; i++)
else if (rsrc)
else
}
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
{
int i;
size_t w;
char *rsrc;
char *info;
char *newmsg;
const char *infostr;
DBG("ap_rcm_info(%p)\n", (void *)a);
/* Protect against invalid arguments */
return (-1);
}
/* 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 (0);
/* 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 (-1);
} else {
return (-1);
else
}
/* 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 */
}
}
DBG("ap_rcm_info(%p) success\n", (void *)a);
return (0);
}