#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/kinfo.h>
#include <sys/queue.h>
#include <sys/sysctl.h>
#include <sys/kthread.h>
#include <machine/cpu.h>
#include <sys/lock.h>
#include <sys/spinlock.h>
#include <sys/kcollect.h>
#include <sys/malloc.h>
#include <sys/thread2.h>
#include <sys/spinlock2.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/vm_kern.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_map.h>
#include <vm/vm_pager.h>
#include <vm/vm_extern.h>
#include <machine/stdarg.h>
#include <machine/smp.h>
#include <machine/clock.h>
static uint32_t kcollect_samples = -1;
TUNABLE_INT("kern.collect_samples", &kcollect_samples);
SYSCTL_UINT(_kern, OID_AUTO, collect_samples, CTLFLAG_RD,
&kcollect_samples, 0, "number of 10-second samples");
static uint64_t kcollect_index;
static const char *kcollect_slots[KCOLLECT_ENTRIES];
static kcallback_t kcollect_callback[KCOLLECT_ENTRIES];
static kcollect_t kcollect_scale;
static kcollect_t *kcollect_ary;
static struct lock kcollect_lock = LOCK_INITIALIZER("kcolk", 0, 0);
MALLOC_DEFINE(M_KCOLLECT, "kcollect", "kcollect array");
int
kcollect_register(int which, const char *id, kcallback_t func, uint64_t scale)
{
int n;
lockmgr(&kcollect_lock, LK_EXCLUSIVE);
if (which >= 0) {
n = which;
} else {
for (n = KCOLLECT_DYNAMIC_START; n < KCOLLECT_ENTRIES; ++n) {
if (kcollect_slots[n] == NULL)
break;
}
}
if (n < 0 || n >= KCOLLECT_ENTRIES) {
n = -1;
} else {
kcollect_slots[n] = id;
kcollect_callback[n] = func;
kcollect_scale.data[n] = scale;
}
lockmgr(&kcollect_lock, LK_RELEASE);
return n;
}
void
kcollect_unregister(int n)
{
lockmgr(&kcollect_lock, LK_EXCLUSIVE);
if (n >= 0 && n < KCOLLECT_ENTRIES) {
kcollect_slots[n] = NULL;
kcollect_callback[n] = NULL;
}
lockmgr(&kcollect_lock, LK_RELEASE);
}
void
kcollect_setvalue(int n, uint64_t value)
{
uint32_t i;
if (n >= 0 && n < KCOLLECT_ENTRIES) {
i = kcollect_index % kcollect_samples;
kcollect_ary[i].data[n] = value;
}
}
void
kcollect_setscale(int n, uint64_t value)
{
if (n >= 0 && n < KCOLLECT_ENTRIES) {
kcollect_scale.data[n] = value;
}
}
static
void
kcollect_thread(void *dummy)
{
uint32_t i;
int n;
for (;;) {
lockmgr(&kcollect_lock, LK_EXCLUSIVE);
i = kcollect_index % kcollect_samples;
bzero(&kcollect_ary[i], sizeof(kcollect_ary[i]));
crit_enter();
kcollect_ary[i].ticks = ticks;
getmicrotime(&kcollect_ary[i].realtime);
crit_exit();
for (n = 0; n < KCOLLECT_ENTRIES; ++n) {
if (kcollect_callback[n]) {
kcollect_ary[i].data[n] =
kcollect_callback[n](n);
}
}
cpu_sfence();
++kcollect_index;
lockmgr(&kcollect_lock, LK_RELEASE);
tsleep(&dummy, 0, "sleep", hz * KCOLLECT_INTERVAL);
}
}
static int
sysctl_kcollect_data(SYSCTL_HANDLER_ARGS)
{
int error;
uint32_t i;
uint32_t start;
uint64_t count;
kcollect_t scale;
kcollect_t id;
if (kcollect_samples == (uint32_t)-1 ||
kcollect_samples == 0) {
return EINVAL;
}
error = 0;
count = kcollect_index;
start = count % kcollect_samples;
if (count >= kcollect_samples)
count = kcollect_samples - 1;
if (req->oldptr == NULL) {
error = SYSCTL_OUT(req, 0, sizeof(kcollect_t) * (count + 2));
return error;
}
if (kcollect_ary == NULL)
return ENOTSUP;
lockmgr(&kcollect_lock, LK_EXCLUSIVE);
scale = kcollect_scale;
scale.ticks = ticks;
scale.hz = hz;
bzero(&id, sizeof(id));
for (i = 0; i < KCOLLECT_ENTRIES; ++i) {
if (kcollect_slots[i]) {
char *ptr = (char *)&id.data[i];
size_t len = strlen(kcollect_slots[i]);
if (len > sizeof(id.data[0]))
len = sizeof(id.data[0]);
bcopy(kcollect_slots[i], ptr, len);
}
}
lockmgr(&kcollect_lock, LK_RELEASE);
error = SYSCTL_OUT(req, &scale, sizeof(scale));
if (error == 0)
error = SYSCTL_OUT(req, &id, sizeof(id));
i = start;
while (count) {
if (req->oldlen - req->oldidx < sizeof(kcollect_t))
break;
if (i == 0)
i = kcollect_samples - 1;
else
--i;
error = SYSCTL_OUT(req, &kcollect_ary[i], sizeof(kcollect_t));
if (error)
break;
--count;
}
return error;
}
SYSCTL_PROC(_kern, OID_AUTO, collect_data,
CTLFLAG_RD | CTLTYPE_STRUCT, 0, 0,
sysctl_kcollect_data, "S,kcollect", "Dump collected statistics");
static
void
kcollect_thread_init(void)
{
thread_t td = NULL;
if ((int)kcollect_samples < 0) {
if (kmem_lim_size() < 1024)
kcollect_samples = 1024;
else
kcollect_samples = 8192;
}
if (kcollect_samples) {
kcollect_ary = kmalloc(kcollect_samples * sizeof(kcollect_t),
M_KCOLLECT, M_WAITOK | M_ZERO);
lwkt_create(kcollect_thread, NULL, &td, NULL, 0, 0, "kcollect");
}
}
SYSINIT(kcol, SI_SUB_HELPER_THREADS, SI_ORDER_ANY, kcollect_thread_init, 0);