/*
* 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
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <libdevinfo.h>
#include <errno.h>
#include <libintl.h>
#define CFGA_PLUGIN_LIB
#include <config_admin.h>
#include "ap.h"
#include <sys/obpdefs.h>
#include <sys/processor.h>
#include <sys/stat.h>
#include <sys/sbd_ioctl.h>
#include <sys/int_fmtio.h>
static cfga_err_t
ap_getncm(apd_t *a, sbd_comp_type_t type, int *ncm)
{
sbd_ioctl_arg_t *ctl;
sbd_getncm_cmd_t *cp;
if (a->fd == -1 || a->ctl == NULL)
return (CFGA_LIB_ERROR);
ctl = (sbd_ioctl_arg_t *)a->ctl;
ctl->ic_type = type;
ctl->ic_name[0] = '\0';
ctl->ic_unit = 0;
ctl->i_len = 0;
ctl->i_opts = NULL;
DBG("ioctl(%d SBD_CMD_GETNCM, 0x%p)\n", a->fd, (void *)ctl);
if (ioctl(a->fd, SBD_CMD_GETNCM, ctl) == -1) {
ap_err(a, ERR_CMD_FAIL, CMD_GETNCM);
return (CFGA_ERROR);
}
cp = &ctl->i_cmd.cmd_getncm;
DBG("ncm(%d)=%d\n", type, cp->g_ncm);
if (ncm)
*ncm = cp->g_ncm;
return (CFGA_OK);
}
cfga_err_t
ap_stat(apd_t *a, int all)
{
int fd;
int ncm;
int select;
int stsize;
int oflag;
sbd_stat_cmd_t *sc;
sbd_ioctl_arg_t *ctl;
cfga_err_t rc;
sbd_stat_t *new_stat;
rc = CFGA_LIB_ERROR;
DBG("ap_stat(%s)\n", a->path);
/* Open the file descriptor if not already open */
if (a->fd == -1) {
DBG("open(%s)\n", a->path);
if (a->statonly != 0)
oflag = O_RDONLY;
else
oflag = O_RDWR;
if ((fd = open(a->path, oflag, 0)) == -1) {
ap_err(a, ERR_AP_INVAL);
return (rc);
}
a->fd = fd;
} else {
fd = a->fd;
}
if (a->ctl == NULL && (a->ctl = calloc(1, sizeof (*ctl))) == NULL) {
ap_err(a, ERR_CMD_FAIL, CMD_STATUS);
return (rc);
}
if (a->tgt == AP_BOARD) {
/*
* The status target is the board. If we need to
* return component data (to support the -a option),
* get the number of components on the board.
*/
select = 0;
if (all) {
cfga_err_t r;
r = ap_getncm(a, SBD_COMP_NONE, &ncm);
if (r != CFGA_OK) {
return (r);
}
} else {
ncm = 0;
}
} else {
select = 1;
ncm = 1;
}
DBG("ncm=%d\n", ncm);
a->ncm = ncm;
/*
* The status structure contains space for one component;
* add the space for the other components if necessary.
*/
stsize = sizeof (sbd_stat_t);
if (ncm > 1)
stsize += ((ncm - 1) * sizeof (sbd_dev_stat_t));
if ((new_stat = realloc(a->stat, stsize)) == NULL) {
ap_err(a, ERR_CMD_FAIL, CMD_STATUS);
return (rc);
}
a->stat = new_stat;
ctl = (sbd_ioctl_arg_t *)a->ctl;
ctl->i_len = 0;
ctl->i_opts = NULL;
ctl->ic_type = SBD_COMP_NONE;
if (all)
ctl->i_flags |= SBD_FLAG_ALLCMP;
sc = &ctl->i_cmd.cmd_stat;
sc->s_statp = (caddr_t)a->stat;
sc->s_nbytes = stsize;
if (select) {
/*
* The target is a specific component. Pass its
* name and unit number to the driver. Set its
* type to UNKNOWN since the plugin does not know
* the type of the component specified by the user.
*/
ctl->ic_type = SBD_COMP_UNKNOWN;
ctl->ic_unit = a->cnum;
(void) strcpy(ctl->ic_name, a->cname);
}
DBG("ioctl(%d SBD_CMD_STATUS, sc=0x%p sz=%d flags=%d",
fd, (void *)sc->s_statp, sc->s_nbytes, ctl->i_flags);
if (select)
DBG(" cname=<%s> cnum=%d", a->cname, a->cnum);
DBG(")\n");
if (ioctl(fd, SBD_CMD_STATUS, ctl) == -1) {
ap_err(a, ERR_CMD_FAIL, CMD_STATUS);
rc = CFGA_ERROR;
} else
rc = CFGA_OK;
DBG("ap_stat()=%d\n", rc);
return (rc);
}
/*
* Convert a component to a target type.
*/
static ap_target_t
ap_cm_tgt(sbd_comp_type_t type)
{
ap_target_t c;
switch (type) {
case SBD_COMP_CPU:
c = AP_CPU;
break;
case SBD_COMP_MEM:
c = AP_MEM;
break;
case SBD_COMP_IO:
c = AP_IO;
break;
case SBD_COMP_CMP:
c = AP_CMP;
break;
default:
c = AP_NONE;
break;
}
return (c);
}
cfga_err_t
apd_init(apd_t *a, int all)
{
int i;
char *cn, *dn;
sbd_stat_t *st;
sbd_dev_stat_t *dst;
cfga_err_t rc;
/*
* Ideally, for board operations (other than status) it is not
* necessary to issue the STATUS ioctl. The call however allows a
* final sanity check to ensure that the board number returned
* by the driver matches the plugin's notion of the board number
* as extracted from the ap_id. If this check is not desirable,
* we can change the code to issue the status call only when
* necessary. Note that for component operations, we need to do
* the STATUS in order to figure out the component type and
* validate the command/options accordingly. XXX
*/
if ((rc = ap_stat(a, all)) != CFGA_OK) {
ap_err(a, ERR_AP_INVAL);
return (rc);
}
st = (sbd_stat_t *)a->stat;
/*
* Set the component count to the returned stat count.
*/
if (a->ncm > st->s_nstat) {
DBG("ncm=%d nstat=%d (truncated)\n", a->ncm, st->s_nstat);
a->ncm = st->s_nstat;
}
if (a->tgt == AP_BOARD) {
DBG("tgt=%d\n", a->tgt);
/*
* Initialize the RCM module here so that it can record
* the initial state of the capacity information.
*/
rc = ap_rcm_init(a);
return (rc);
}
a->tgt = AP_NONE;
cn = a->cname;
DBG("cname=<%s> cunit=<%d>\n", a->cname, a->cnum);
for (dst = st->s_stat, i = 0; i < st->s_nstat; i++, dst++) {
DBG("ds_name,ds_unit,ds_type=<%s,%d,%d> ",
dst->ds_name, dst->ds_unit, dst->ds_type);
if (dst->ds_unit != a->cnum)
continue;
/*
* Consider the names matched if they are either
* both absent or the same. It is conceivable that
* a NULL component name be considered valid
* by the driver.
*/
dn = dst->ds_name;
if ((dn == NULL && cn == NULL) ||
(dn != NULL && cn != NULL && strcmp(dn, cn) == 0)) {
a->tgt = ap_cm_tgt(dst->ds_type);
a->cmstat = (void *)dst;
DBG("found ");
break;
}
}
DBG("tgt=%d\n", a->tgt);
if (a->tgt == AP_NONE) {
ap_err(a, ERR_CM_INVAL, a->cid);
return (CFGA_INVAL);
}
/*
* Initialize the RCM module here so that it can record
* the initial state of the capacity information.
*/
rc = ap_rcm_init(a);
return (rc);
}
void
apd_free(apd_t *a)
{
if (a == NULL)
return;
ap_rcm_fini(a);
if (a->fd != -1)
(void) close(a->fd);
s_free(a->options);
s_free(a->path);
s_free(a->drv);
s_free(a->target);
s_free(a->cname);
s_free(a->ctl);
s_free(a->stat);
free(a);
}
apd_t *
apd_alloc(const char *ap_id, cfga_flags_t flags, char **errstring,
struct cfga_msg *msgp, struct cfga_confirm *confp)
{
apd_t *a;
if ((a = calloc(1, sizeof (*a))) == NULL)
return (NULL);
if (errstring != NULL)
*errstring = NULL;
a->fd = -1;
a->errstring = errstring;
a->msgp = msgp;
a->confp = confp;
a->class = "sbd";
if (flags & CFGA_FLAG_LIST_ALL)
ap_setopt(a, OPT_LIST_ALL);
if (flags & CFGA_FLAG_FORCE)
ap_setopt(a, OPT_FORCE);
if (flags & CFGA_FLAG_VERBOSE)
ap_setopt(a, OPT_VERBOSE);
if (ap_id == NULL || ap_parse(a, ap_id) == 0)
return (a);
apd_free(a);
return (NULL);
}
/*
* The type field is defined to be parsable by cfgadm(1M): It
* must not contain white space characters. This function
* converts white space to underscore.
*/
static void
parsable_strncpy(char *op, const char *ip, size_t n)
{
char c;
while (n-- > 0) {
c = *ip++;
if (isspace(c))
c = '_';
*op++ = c;
if (c == '\0')
break;
}
}
void
ap_init(apd_t *a, cfga_list_data_t *ap)
{
sbd_stat_t *st;
st = (sbd_stat_t *)a->stat;
DBG("ap_init bd=%d rs=%d os=%d type=<%s>\n",
a->bnum, st->s_rstate, st->s_ostate, st->s_type);
parsable_strncpy(ap->ap_type, st->s_type, sizeof (ap->ap_type));
ap->ap_r_state = (cfga_stat_t)st->s_rstate;
ap->ap_o_state = (cfga_stat_t)st->s_ostate;
ap->ap_cond = (cfga_cond_t)st->s_cond;
ap->ap_busy = (cfga_busy_t)st->s_busy;
ap->ap_status_time = st->s_time;
ap_info(a, ap->ap_info, AP_BOARD);
}
typedef struct {
int cmd;
int ioc;
} ap_ioc_t;
static ap_ioc_t
ap_iocs[] = {
{CMD_ASSIGN, SBD_CMD_ASSIGN },
{CMD_POWERON, SBD_CMD_POWERON },
{CMD_TEST, SBD_CMD_TEST },
{CMD_CONNECT, SBD_CMD_CONNECT },
{CMD_CONFIGURE, SBD_CMD_CONFIGURE },
{CMD_UNCONFIGURE, SBD_CMD_UNCONFIGURE },
{CMD_DISCONNECT, SBD_CMD_DISCONNECT },
{CMD_POWEROFF, SBD_CMD_POWEROFF },
{CMD_STATUS, SBD_CMD_STATUS },
{CMD_GETNCM, SBD_CMD_GETNCM },
{CMD_UNASSIGN, SBD_CMD_UNASSIGN },
{CMD_PASSTHRU, SBD_CMD_PASSTHRU },
{CMD_NONE, 0 }
};
static int
ap_ioc(int cmd)
{
ap_ioc_t *acp;
DBG("ap_ioc(%d)\n", cmd);
for (acp = ap_iocs; acp->cmd != CMD_NONE; acp++)
if (acp->cmd == cmd)
break;
DBG("ap_ioc(%d)=0x%x\n", cmd, acp->ioc);
return (acp->ioc);
}
cfga_err_t
ap_suspend_query(apd_t *a, int cmd, int *check)
{
int ioc;
sbd_dev_stat_t *dst;
/*
* See if the a quiesce operation is required for
* this command for any of the components. If the
* command does not map to an ioctl, then there is
* nothing to do.
*/
if ((ioc = ap_ioc(cmd)) == 0)
return (CFGA_OK);
else if (a->tgt == AP_BOARD) {
int i;
dst = ((sbd_stat_t *)a->stat)->s_stat;
/*
* See if any component requires a
* OS suspension for this command.
*/
for (i = 0; i < a->ncm; i++, dst++)
if (SBD_CHECK_SUSPEND(ioc, dst->ds_suspend))
(*check)++;
} else {
dst = (sbd_dev_stat_t *)a->cmstat;
if (SBD_CHECK_SUSPEND(ioc, dst->ds_suspend))
(*check)++;
}
return (CFGA_OK);
}
cfga_err_t
ap_platopts_check(apd_t *a, int first, int last)
{
int c;
uint_t platopts;
sbd_stat_t *stat;
ap_opts_t *opts;
opts = &a->opts;
stat = (sbd_stat_t *)a->stat;
platopts = stat->s_platopts;
/*
* If there are no platform options set then there
* is no need to check this operation
*/
if (opts->platform == NULL)
return (CFGA_OK);
/*
* Check if any of the steps in the sequence
* allows for a platform option
*/
for (c = first; c <= last; c++)
/*
* If the platopt is set it means that the platform does not
* support options for this cmd
*/
if (SBD_CHECK_PLATOPTS(ap_ioc(c), platopts) == 0) {
return (CFGA_OK);
}
ap_err(a, ERR_OPT_INVAL, opts->platform);
return (CFGA_INVAL);
}
cfga_err_t
ap_ioctl(apd_t *a, int cmd)
{
int ioc;
sbd_ioctl_arg_t *ctl;
if (a->ctl == NULL && (a->ctl = calloc(1, sizeof (*ctl))) == NULL) {
ap_err(a, ERR_CMD_FAIL, cmd);
return (CFGA_LIB_ERROR);
}
ap_msg(a, MSG_ISSUE, cmd, a->target);
ctl = (sbd_ioctl_arg_t *)a->ctl;
ctl->i_flags = 0;
ctl->i_len = 0;
ctl->i_opts = NULL;
if (ap_getopt(a, OPT_FORCE))
ctl->i_flags |= SBD_FLAG_FORCE;
if (ap_getopt(a, OPT_SUSPEND_OK))
ctl->i_flags |= SBD_FLAG_QUIESCE_OKAY;
if (a->tgt == AP_BOARD)
ctl->ic_type = SBD_COMP_NONE;
else {
ctl->ic_type = SBD_COMP_UNKNOWN;
ctl->ic_unit = a->cnum;
(void) strcpy(ctl->ic_name, a->cname);
}
if (!(ioc = ap_ioc(cmd))) {
ap_err(a, ERR_CMD_FAIL, cmd);
return (CFGA_LIB_ERROR);
}
/*
* If this is a passthru command, pass all of its
* options; otherwise, pass all options after the
* platform keyword.
*/
if (cmd == CMD_PASSTHRU)
ctl->i_opts = a->options;
else {
/*
* Only pass the platform option to the cmds that the platform
* has specified as ok
*/
sbd_stat_t *stat;
stat = (sbd_stat_t *)a->stat;
if (SBD_CHECK_PLATOPTS(ioc, stat->s_platopts) == 0)
ctl->i_opts = a->opts.platform;
}
if (ctl->i_opts != NULL)
ctl->i_len = strlen(ctl->i_opts) + 1;
DBG("i_opts=%s\n", ctl->i_opts ? ctl->i_opts : "NULL");
DBG("i_flags=0x%x\n", ctl->i_flags);
if (ap_getopt(a, OPT_SIM)) {
ap_msg(a, MSG_DONE, cmd, a->target);
return (CFGA_OK);
}
if (ioctl(a->fd, ioc, ctl) == -1) {
ap_err(a, ERR_CMD_FAIL, cmd);
return (CFGA_ERROR);
}
ap_msg(a, MSG_DONE, cmd, a->target);
return (CFGA_OK);
}
/*
* Return the error string corresponding to a given error code.
* String table and error code sets are provided by sbd_etab. This data
* structure is automatically generated at compile time from the error
* code and message text information in sbd_ioctl.h.
*/
static char *
mod_estr(int code)
{
int i;
char *s;
extern sbd_etab_t sbd_etab[];
extern int sbd_etab_len;
s = NULL;
for (i = 0; i < sbd_etab_len; i++) {
sbd_etab_t *eptr = &sbd_etab[i];
if ((code >= eptr->t_base) && (code <= eptr->t_bnd)) {
int index;
char **t_text;
/*
* Found it. Just extract the string
*/
index = code - eptr->t_base;
t_text = eptr->t_text;
s = strdup(t_text[index]);
break;
}
}
if (i == sbd_etab_len) {
char buf[32];
(void) snprintf(buf, sizeof (buf), "error %d", code);
s = strdup(buf);
}
return (s);
}
char *
ap_sys_err(apd_t *a, char **rp)
{
int code;
char *p;
char *rsc;
sbd_ioctl_arg_t *ctl = (sbd_ioctl_arg_t *)a->ctl;
/*
* The driver sets the errno to EIO if it returns
* more detailed error info via e_code. In all
* other cases, use standard error text.
*/
if (ctl == NULL || errno != EIO) {
if ((p = strerror(errno)) != NULL)
p = strdup(p);
return (p);
}
code = ctl->ie_code;
rsc = ctl->ie_rsc;
if (code)
p = mod_estr(code);
else if ((p = strerror(errno)) != NULL)
p = strdup(p);
if (*rsc != '\0' && rp != NULL)
*rp = strdup(rsc);
return (p);
}
/*
* cfgadm -o err=plugin-err,cmd=name,code=ecode -x errtest ap_id.
*/
cfga_err_t
ap_test_err(apd_t *a, const char *options)
{
int err;
int cmd;
ap_opts_t *opts;
sbd_ioctl_arg_t ctl;
opts = &a->opts;
err = opts->err;
cmd = CMD_DISCONNECT;
DBG("ap_test_err(%d %d)\n", opts->code, opts->err);
switch (err) {
case ERR_CMD_INVAL:
ap_err(a, err, ap_cmd_name(cmd));
break;
case ERR_CMD_NOTSUPP:
ap_err(a, err, cmd);
break;
case ERR_CMD_FAIL:
errno = EIO;
ctl.i_err.e_code = opts->code;
*ctl.i_err.e_rsc = '\0';
a->ctl = &ctl;
ap_err(a, err, cmd);
a->ctl = NULL;
break;
case ERR_OPT_INVAL:
ap_err(a, err, options);
break;
case ERR_OPT_NOVAL:
ap_err(a, err, options);
break;
case ERR_AP_INVAL:
ap_err(a, err);
break;
case ERR_CM_INVAL:
ap_err(a, err, a->cid);
break;
case ERR_TRANS_INVAL:
ap_err(a, ERR_TRANS_INVAL, cmd);
break;
}
return (CFGA_LIB_ERROR);
}
static char *
ap_help_topics[] = {
"\nSbd specific commands/options:\n\n",
"\tcfgadm [-o parsable] -l ap_id\n",
"\tcfgadm [-o unassign|nopoweroff] -c disconnect ap_id\n",
"\tcfgadm -t ap_id\n",
"\tcfgadm -x assign ap_id\n",
"\tcfgadm -x unassign ap_id\n",
"\tcfgadm -x poweron ap_id\n",
"\tcfgadm -x poweroff ap_id\n",
NULL
};
/*ARGSUSED*/
cfga_err_t
ap_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
{
int len;
char **p;
char *q;
if (msgp == NULL || msgp->message_routine == NULL)
return (CFGA_OK);
for (p = ap_help_topics; *p != NULL; p++) {
if ((len = strlen(*p)) == 0)
continue;
if ((q = (char *)calloc(len + 1, 1)) == NULL)
continue;
(void) strcpy(q, *p);
(*msgp->message_routine)(msgp->appdata_ptr, q);
free(q);
}
return (CFGA_OK);
}
static char *
ap_dev_type(sbd_dev_stat_t *dst)
{
char *type;
switch (dst->ds_type) {
case SBD_COMP_CPU:
type = "cpu";
break;
case SBD_COMP_MEM:
type = "memory";
break;
case SBD_COMP_IO:
type = "io";
break;
case SBD_COMP_CMP:
type = "cpu";
break;
default:
type = "other";
break;
}
DBG("ap_dev_type(%d)=%s\n", dst->ds_type, type);
return (type);
}
static sbd_dev_stat_t *
ap_cm_stat(apd_t *a, int seq)
{
sbd_stat_t *st;
if (seq == CM_DFLT)
return (a->cmstat);
st = (sbd_stat_t *)a->stat;
return (st->s_stat + seq);
}
char *
ap_cm_devpath(apd_t *a, int seq)
{
int len;
char *path;
char *devpath;
sbd_io_stat_t *dst;
/*
* If no component sequence number is provided
* default to the current target component.
* Assume an io component so that we can get
* the path if the component is indeed of type io.
*/
if (seq == CM_DFLT)
dst = (sbd_io_stat_t *)a->cmstat;
else {
sbd_stat_t *st;
st = (sbd_stat_t *)a->stat;
dst = (sbd_io_stat_t *)st->s_stat + seq;
}
if (dst->is_type != SBD_COMP_IO)
path = NULL;
else
path = dst->is_pathname;
if (str_valid(path)) {
len = strlen(DEVDIR) + strlen(path) + 1;
if ((devpath = calloc(1, len)) == NULL)
return (NULL);
(void) snprintf(devpath, len, "%s%s", DEVDIR, path);
} else
devpath = NULL;
DBG("ap_cm_path(%d)=%s\n", seq, devpath ? devpath : "");
return (devpath);
}
void
ap_cm_id(apd_t *a, int seq, char *id, size_t bufsize)
{
int unit;
char *name;
sbd_dev_stat_t *dst;
dst = ap_cm_stat(a, seq);
unit = dst->ds_unit;
name = dst->ds_name;
/*
* If the component has a unit number,
* add it to the id, otherwise just use
* the component's name.
*/
if (unit == -1)
(void) snprintf(id, bufsize, "%s", name);
else
(void) snprintf(id, bufsize, "%s%d", name, unit);
DBG("ap_cm_id(%d)=%s\n", seq, id);
}
/*
* Convert a component to a target type.
*/
ap_target_t
ap_cm_type(apd_t *a, int seq)
{
ap_target_t c;
sbd_dev_stat_t *dst;
dst = ap_cm_stat(a, seq);
switch (dst->ds_type) {
case SBD_COMP_CPU:
c = AP_CPU;
break;
case SBD_COMP_MEM:
c = AP_MEM;
break;
case SBD_COMP_IO:
c = AP_IO;
break;
case SBD_COMP_CMP:
c = AP_CMP;
break;
default:
c = AP_NONE;
break;
}
return (c);
}
int
ap_cm_ncap(apd_t *a, int seq)
{
sbd_dev_stat_t *dst;
int ncap;
dst = ap_cm_stat(a, seq);
switch (dst->ds_type) {
case SBD_COMP_CPU:
case SBD_COMP_MEM:
case SBD_COMP_IO:
ncap = 1;
break;
case SBD_COMP_CMP:
ncap = ((sbd_cmp_stat_t *)dst)->ps_ncores;
break;
default:
ncap = 0;
break;
}
return (ncap);
}
int
ap_cm_capacity(apd_t *a, int seq, void *cap, int *ncap, cfga_stat_t *ostate)
{
int i;
sbd_dev_stat_t *dst;
cfga_stat_t os;
if (cap == NULL)
return (0);
dst = ap_cm_stat(a, seq);
os = (cfga_stat_t)dst->ds_ostate;
if (os != CFGA_STAT_CONFIGURED && os != CFGA_STAT_UNCONFIGURED)
return (0);
if (ostate)
*ostate = os;
*ncap = 1;
switch (dst->ds_type) {
case SBD_COMP_CPU: {
sbd_cpu_stat_t *cpu = (sbd_cpu_stat_t *)dst;
*((processorid_t *)cap) = cpu->cs_cpuid;
break;
}
case SBD_COMP_MEM: {
sbd_mem_stat_t *mem = (sbd_mem_stat_t *)dst;
*((long *)cap) = mem->ms_totpages;
break;
}
case SBD_COMP_CMP: {
sbd_cmp_stat_t *cmp = (sbd_cmp_stat_t *)dst;
processorid_t *cpuid;
cpuid = (processorid_t *)cap;
for (i = 0; i < cmp->ps_ncores; i++) {
cpuid[i] = cmp->ps_cpuid[i];
}
*ncap = cmp->ps_ncores;
break;
}
default:
return (0);
}
DBG("ap_cm_capacity(%d)=(", seq);
for (i = 0; i < *ncap; i++) {
DBG("%d ", ((int *)cap)[i]);
}
DBG("%d)\n", *ostate);
return (1);
}
void
ap_cm_init(apd_t *a, cfga_list_data_t *ap, int seq)
{
char *type;
sbd_stat_t *st;
sbd_dev_stat_t *dst;
st = (sbd_stat_t *)a->stat;
dst = st->s_stat + seq;
type = ap_dev_type(dst);
a->cmstat = (void *)dst;
DBG("ap_cm_init bd=%d rs=%d os=%d type=<%s> seq=%d\n",
a->bnum, st->s_rstate, dst->ds_ostate, type, seq);
(void) strncpy(ap->ap_type, type, sizeof (ap->ap_type));
ap->ap_r_state = (cfga_stat_t)st->s_rstate;
ap->ap_o_state = (cfga_stat_t)dst->ds_ostate;
ap->ap_cond = (cfga_cond_t)dst->ds_cond;
ap->ap_busy = (cfga_busy_t)dst->ds_busy;
ap->ap_status_time = dst->ds_time;
ap_info(a, ap->ap_info, ap_cm_tgt(dst->ds_type));
}
void
ap_state(apd_t *a, cfga_stat_t *rs, cfga_stat_t *os)
{
sbd_stat_t *st;
sbd_dev_stat_t *dst;
st = (sbd_stat_t *)a->stat;
dst = (sbd_dev_stat_t *)a->cmstat;
if (rs != NULL) {
if (a->tgt == AP_NONE)
*rs = CFGA_STAT_NONE;
else
*rs = (cfga_stat_t)st->s_rstate;
}
if (os != NULL) {
if (a->tgt == AP_NONE)
*os = CFGA_STAT_NONE;
else if (a->tgt == AP_BOARD)
*os = (cfga_stat_t)st->s_ostate;
else
*os = (cfga_stat_t)dst->ds_ostate;
}
}
#define BI_POWERED 0
#define BI_ASSIGNED 1
static const char *
binfo[] = {
"powered-on",
", assigned"
};
static const char *
binfo_parsable[] = {
"powered-on",
" assigned"
};
static void
bd_info(apd_t *a, cfga_info_t info, int parsable)
{
int i;
int nsep;
const char **p;
sbd_stat_t *st;
char *end = &info[sizeof (cfga_info_t)];
DBG("bd_info(%p)\n", (void *)info);
st = (sbd_stat_t *)a->stat;
if (parsable) {
p = binfo_parsable;
nsep = 1;
} else {
p = binfo;
nsep = 2;
}
i = nsep;
if (st->s_power) {
info += snprintf(info, end - info, p[BI_POWERED]);
i = 0;
}
if (st->s_assigned)
info += snprintf(info, end - info, p[BI_ASSIGNED] + i);
}
#define CI_CPUID 0
#define CI_SPEED 1
#define CI_ECACHE 2
static const char *
cpuinfo[] = {
"cpuid %d",
", speed %d MHz",
", ecache %d MBytes"
};
static const char *
cpuinfo_parsable[] = {
"cpuid=%d",
" speed=%d",
" ecache=%d"
};
static void
cpu_info(apd_t *a, cfga_info_t info, int parsable)
{
const char **p;
sbd_cpu_stat_t *dst;
char *end = &info[sizeof (cfga_info_t)];
DBG("cpu_info(%p)\n", (void *)info);
dst = (sbd_cpu_stat_t *)a->cmstat;
if (parsable)
p = cpuinfo_parsable;
else
p = cpuinfo;
info += snprintf(info, end - info, p[CI_CPUID], dst->cs_cpuid);
info += snprintf(info, end - info, p[CI_SPEED], dst->cs_speed);
info += snprintf(info, end - info, p[CI_ECACHE], dst->cs_ecache);
}
#define MI_ADDRESS 0
#define MI_SIZE 1
#define MI_PERMANENT 2
#define MI_UNCONFIGURABLE 3
#define MI_SOURCE 4
#define MI_TARGET 5
#define MI_DELETED 6
#define MI_REMAINING 7
#define MI_INTERLEAVE 8
static const char *
meminfo_nonparsable[] = {
"base address 0x%" PRIx64,
", %lu KBytes total",
", %lu KBytes permanent",
", unconfigurable",
", memory delete requested on %s",
", memory delete in progress on %s",
", %lu KBytes deleted",
", %lu KBytes remaining",
", inter board interleave"
};
static const char *
meminfo_parsable[] = {
"address=0x%" PRIx64,
" size=%lu",
" permanent=%lu",
" unconfigurable",
" source=%s",
" target=%s",
" deleted=%lu",
" remaining=%lu",
" inter-board-interleave"
};
#define _K1 1024
/*
* This function assumes pagesize > 1024 and that
* pagesize is a multiple of 1024.
*/
static ulong_t
pages_to_kbytes(uint_t pgs)
{
long pagesize;
pagesize = sysconf(_SC_PAGESIZE);
return (pgs * (pagesize / _K1));
}
static uint64_t
pages_to_bytes(uint_t pgs)
{
long pagesize;
pagesize = sysconf(_SC_PAGESIZE);
return ((uint64_t)pgs * pagesize);
}
static void
mem_info(apd_t *a, cfga_info_t info, int parsable)
{
const char **p;
sbd_mem_stat_t *dst;
int want_progress;
char *end = &info[sizeof (cfga_info_t)];
DBG("mem_info(%p)\n", (void *)info);
dst = (sbd_mem_stat_t *)a->cmstat;
if (parsable)
p = meminfo_parsable;
else
p = meminfo_nonparsable;
info += snprintf(info, end - info, p[MI_ADDRESS],
pages_to_bytes(dst->ms_basepfn));
info += snprintf(info, end - info, p[MI_SIZE],
pages_to_kbytes(dst->ms_totpages));
if (dst->ms_noreloc_pages)
info += snprintf(info, end - info, p[MI_PERMANENT],
pages_to_kbytes(dst->ms_noreloc_pages));
if (!dst->ms_cage_enabled)
info += snprintf(info, end - info, p[MI_UNCONFIGURABLE]);
if (dst->ms_interleave)
info += snprintf(info, end - info, p[MI_INTERLEAVE]);
/*
* If there is a valid peer physical ap_id specified,
* convert it to a logical id.
*/
want_progress = 0;
if (str_valid(dst->ms_peer_ap_id)) {
char *cm;
char *peer;
char physid[MAXPATHLEN];
char logid[MAXPATHLEN];
(void) snprintf(physid, sizeof (physid), "%s%s",
DEVDIR, dst->ms_peer_ap_id);
/*
* Save the component portion of the physid and
* add it back after converting to logical format.
*/
if ((cm = strstr(physid, "::")) != NULL) {
*cm = '\0';
cm += 2;
}
/* attempt to resolve to symlink */
if (ap_symid(a, physid, logid, sizeof (logid)) == 0)
peer = logid;
else
peer = physid;
if (dst->ms_peer_is_target) {
info += snprintf(info, end - info, p[MI_TARGET], peer);
if (cm)
info += snprintf(info, end - info, "::%s", cm);
want_progress = 1;
} else {
info += snprintf(info, end - info, p[MI_SOURCE], peer);
if (cm)
info += snprintf(info, end - info, "::%s", cm);
}
}
if (want_progress ||
(dst->ms_detpages != 0 && dst->ms_detpages != dst->ms_totpages)) {
info += snprintf(info, end - info, p[MI_DELETED],
pages_to_kbytes(dst->ms_detpages));
info += snprintf(info, end - info, p[MI_REMAINING],
pages_to_kbytes(dst->ms_totpages -
dst->ms_detpages));
}
}
#define II_DEVICE 0
#define II_REFERENCED 1
static const char *
ioinfo[] = {
"device %s",
", referenced"
};
static const char *
ioinfo_parsable[] = {
"device=%s",
" referenced"
};
static void
io_info(apd_t *a, cfga_info_t info, int parsable)
{
const char **p;
sbd_io_stat_t *dst;
char *end = &info[sizeof (cfga_info_t)];
dst = (sbd_io_stat_t *)a->cmstat;
if (parsable)
p = ioinfo_parsable;
else
p = ioinfo;
info += snprintf(info, end - info, p[II_DEVICE], dst->is_pathname);
if (dst->is_referenced)
info += snprintf(info, end - info, p[II_REFERENCED]);
}
#define PI_CPUID 0
#define PI_CPUID_PAIR 1
#define PI_CPUID_CONT 2
#define PI_CPUID_LAST 3
#define PI_SPEED 4
#define PI_ECACHE 5
static const char *
cmpinfo[] = {
"cpuid %d",
" and %d",
", %d",
", and %d",
", speed %d MHz",
", ecache %d MBytes"
};
static const char *
cmpinfo_parsable[] = {
"cpuid=%d",
",%d",
",%d",
",%d",
" speed=%d",
" ecache=%d"
};
static void
cmp_info(apd_t *a, cfga_info_t info, int parsable)
{
int i;
int last;
const char **p;
sbd_cmp_stat_t *dst;
char *end = &info[sizeof (cfga_info_t)];
DBG("cmp_info(%p)\n", (void *)info);
dst = (sbd_cmp_stat_t *)a->cmstat;
if (parsable)
p = cmpinfo_parsable;
else
p = cmpinfo;
/* Print the first cpuid */
info += snprintf(info, end - info, p[PI_CPUID], dst->ps_cpuid[0]);
/*
* Print the middle cpuids, if necessary. Stop before
* the last one, since printing the last cpuid is a
* special case for the non parsable form.
*/
for (i = 1; i < (dst->ps_ncores - 1); i++) {
info += snprintf(info, end - info, p[PI_CPUID_CONT],
dst->ps_cpuid[i]);
}
/* Print the last cpuid, if necessary */
if (dst->ps_ncores > 1) {
last = (dst->ps_ncores == 2) ? PI_CPUID_PAIR : PI_CPUID_LAST;
info += snprintf(info, end - info,
dgettext(TEXT_DOMAIN, p[last]), dst->ps_cpuid[i]);
}
info += snprintf(info, end - info, p[PI_SPEED], dst->ps_speed);
info += snprintf(info, end - info, p[PI_ECACHE], dst->ps_ecache);
}
void
ap_info(apd_t *a, cfga_info_t info, ap_target_t tgt)
{
int parsable = ap_getopt(a, OPT_PARSABLE);
DBG("ap_info(%p, %d)\n", (void *)info, parsable);
switch (tgt) {
case AP_BOARD:
bd_info(a, info, parsable);
break;
case AP_CPU:
cpu_info(a, info, parsable);
break;
case AP_MEM:
mem_info(a, info, parsable);
break;
case AP_IO:
io_info(a, info, parsable);
break;
case AP_CMP:
cmp_info(a, info, parsable);
break;
default:
break;
}
}