#include <locale.h>
#include <string.h>
#include <assert.h>
#include <kstat.h>
#include <pool.h>
#include "utils.h"
#include <sys/pset.h>
#include "poolstat.h"
#include "poolstat_utils.h"
#define SD_UPDATED 1
#define INVALID_SET -2
typedef struct sdata {
kstat_t sd_oks;
kstat_t sd_nks;
void *sd_udata;
uint_t sd_state;
struct sdata *sd_next;
} sdata_t;
typedef struct {
psetid_t opset;
psetid_t npset;
} pset_ud_t;
#define SD_OPSET(p) (((pset_ud_t *)(p)->sd_udata)->opset)
#define SD_NPSET(p) (((pset_ud_t *)(p)->sd_udata)->npset)
static kstat_ctl_t *ks_ctl;
static sdata_t *cpu_list;
static sdata_t *update_sdata_list(sdata_t *, kstat_ctl_t *, char *, int,
char *, int *);
static void update_cpu_list(sdata_t *list);
static void update_pset_stats(statistic_bag_t *, sdata_t *);
void
sa_kstat_init(void *unused)
{
if ((ks_ctl = kstat_open()) == NULL)
die(gettext(ERR_KSTAT_OPEN), get_errstr());
}
void
sa_kstat_update(statistic_bag_t *sbag, int flags)
{
if (flags & SA_REFRESH) {
int ks_error = 0;
if (kstat_chain_update(ks_ctl) == -1)
die(gettext(ERR_KSTAT_DATA), get_errstr());
cpu_list = update_sdata_list(cpu_list, ks_ctl, "cpu",
-1, "sys", &ks_error);
if (ks_error)
die(gettext(ERR_KSTAT_DATA), get_errstr());
update_cpu_list(cpu_list);
}
if (strcmp(sbag->sb_type, PSET_TYPE_NAME) == 0) {
update_pset_stats(sbag, cpu_list);
} else if (strcmp(sbag->sb_type, POOL_TYPE_NAME) == 0) {
return;
} else {
die(gettext(ERR_UNSUPP_STYPE), sbag->sb_type);
}
}
static void *
safe_kstat_data_lookup(kstat_t *ksp, char *name)
{
void *dp;
if ((dp = kstat_data_lookup(ksp, name)) == NULL)
die(gettext(ERR_KSTAT_DLOOKUP),
ksp->ks_name, name, get_errstr());
return (dp);
}
static uint64_t
delta(kstat_t *new_ksp, kstat_t *old_ksp, char *name, int oz)
{
kstat_named_t *new_ksn;
kstat_named_t *old_ksn;
new_ksn = (kstat_named_t *)safe_kstat_data_lookup(new_ksp, name);
if (old_ksp == NULL || old_ksp->ks_data == NULL)
return ((oz == 1) ? new_ksn->value.ui64 : 0);
old_ksn = (kstat_named_t *)safe_kstat_data_lookup(old_ksp, name);
return (new_ksn->value.ui64 - old_ksn->value.ui64);
}
static void
kstat_clone(kstat_t *src, kstat_t *dst, int fr)
{
if (fr)
FREE(dst->ks_data);
*dst = *src;
if (src->ks_data != NULL) {
dst->ks_data = ZALLOC(src->ks_data_size);
(void) memcpy(dst->ks_data, src->ks_data, src->ks_data_size);
} else {
dst->ks_data = NULL;
dst->ks_data_size = 0;
}
}
static void
kstat_erase(kstat_t *src)
{
FREE(src->ks_data);
(void) memset(src, 0, sizeof (*src));
}
static sdata_t *
sdata_new(kstat_t *ksp)
{
sdata_t *sdp;
NEW0(sdp);
kstat_clone(ksp, &sdp->sd_nks, 0);
return (sdp);
}
static void
sdata_free(sdata_t *sdp)
{
FREE(sdp->sd_oks.ks_data);
FREE(sdp->sd_nks.ks_data);
FREE(sdp->sd_udata);
FREE(sdp);
}
static sdata_t *
update_sdata_list(sdata_t *list, kstat_ctl_t *kc, char *module,
int instance, char *name, int *errp)
{
kstat_t *ksp;
sdata_t *sdp, *sdpp;
for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
if (strcmp(ksp->ks_module, module) == 0 &&
(name == NULL || strcmp(ksp->ks_name, name) == 0) &&
(instance == -1 || ksp->ks_instance == instance)) {
if (kstat_read(kc, ksp, NULL) == -1) {
*errp = -1;
return (list);
}
for (sdpp = sdp = list; sdp; sdp = sdp->sd_next) {
if (ksp->ks_instance ==
sdp->sd_nks.ks_instance) {
if (ksp->ks_kid == sdp->sd_nks.ks_kid) {
kstat_clone(&sdp->sd_nks,
&sdp->sd_oks, 1);
} else {
kstat_erase(&sdp->sd_oks);
}
kstat_clone(ksp, &sdp->sd_nks, 1);
sdp->sd_state |= SD_UPDATED;
break;
}
sdpp = sdp;
}
if (!sdp) {
if (!list) {
list = sdata_new(ksp);
list->sd_state |= SD_UPDATED;
} else {
sdpp->sd_next = sdata_new(ksp);
sdpp->sd_next->sd_state |= SD_UPDATED;
}
}
}
}
sdp = list;
sdpp = NULL;
while (sdp != NULL) {
if (sdp->sd_state & SD_UPDATED) {
sdp->sd_state &= ~SD_UPDATED;
sdpp = sdp;
sdp = sdp->sd_next;
} else {
sdata_t *tmp;
if (sdpp == NULL)
list = sdp->sd_next;
else
sdpp->sd_next = sdp->sd_next;
tmp = sdp->sd_next;
sdata_free(sdp);
sdp = tmp;
}
}
*errp = 0;
return (list);
}
static void
update_cpu_list(sdata_t *list)
{
sdata_t *sdp;
for (sdp = list; sdp; sdp = sdp->sd_next) {
if (sdp->sd_udata == NULL) {
sdp->sd_udata = ZALLOC(sizeof (pset_ud_t));
SD_NPSET(sdp) = INVALID_SET;
}
SD_OPSET(sdp) = SD_NPSET(sdp);
if (pset_assign(PS_QUERY, sdp->sd_nks.ks_instance,
&(SD_NPSET(sdp))) == -1)
SD_NPSET(sdp) = INVALID_SET;
}
}
static void
update_pset_stats(statistic_bag_t *sbag, sdata_t *list)
{
sdata_t *sdp;
pset_statistic_bag_t *bag = (pset_statistic_bag_t *)sbag->bag;
uint64_t allticks, ust, kst, ist, wst;
ust = kst = ist = wst = 0;
for (sdp = list; sdp; sdp = sdp->sd_next) {
if ((SD_OPSET(sdp) == SD_NPSET(sdp)) &&
(SD_NPSET(sdp) == bag->pset_sb_sysid)) {
ust += delta(&sdp->sd_nks, &sdp->sd_oks,
"cpu_ticks_user", 0);
kst += delta(&sdp->sd_nks, &sdp->sd_oks,
"cpu_ticks_kernel", 0);
ist += delta(&sdp->sd_nks, &sdp->sd_oks,
"cpu_ticks_idle", 0);
wst += delta(&sdp->sd_nks, &sdp->sd_oks,
"cpu_ticks_wait", 0);
}
}
if ((allticks = ust + kst + wst + ist) != 0) {
bag->pset_sb_used =
(double)(ust + kst) / allticks * bag->pset_sb_size;
} else {
bag->pset_sb_used = 0.0;
}
}