- adapt to MODULAR.

- some preparations to have more backends.
- add some comments.
This commit is contained in:
yamt 2009-03-10 14:45:02 +00:00
parent 8586a84598
commit e1b625b4bc
3 changed files with 265 additions and 35 deletions

View File

@ -1,7 +1,7 @@
/* $NetBSD: tprof_pmi.c,v 1.4 2009/02/24 06:03:55 yamt Exp $ */
/* $NetBSD: tprof_pmi.c,v 1.5 2009/03/10 14:45:02 yamt Exp $ */
/*-
* Copyright (c)2008 YAMAMOTO Takashi,
* Copyright (c)2008,2009 YAMAMOTO Takashi,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -27,11 +27,12 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tprof_pmi.c,v 1.4 2009/02/24 06:03:55 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: tprof_pmi.c,v 1.5 2009/03/10 14:45:02 yamt Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/cpu.h>
#include <sys/xcall.h>
@ -102,6 +103,7 @@ static uint64_t counter_reset_val;
static uint32_t tprof_pmi_lapic_saved[MAXCPUS];
static nmi_handler_t *tprof_pmi_nmi_handle;
static tprof_backend_cookie_t *tprof_cookie;
static void
tprof_pmi_start_cpu(void *arg1, void *arg2)
@ -178,7 +180,7 @@ tprof_pmi_nmi(const struct trapframe *tf, void *dummy)
}
/* record a sample */
tprof_sample(tf);
tprof_sample(tprof_cookie, tf);
/* reset counter */
wrmsr(msr->msr_counter, counter_reset_val);
@ -192,8 +194,8 @@ tprof_pmi_nmi(const struct trapframe *tf, void *dummy)
return 1;
}
uint64_t
tprof_backend_estimate_freq(void)
static uint64_t
tprof_pmi_estimate_freq(void)
{
uint64_t cpufreq = curcpu()->ci_data.cpu_cc_freq;
uint64_t freq = 10000;
@ -206,8 +208,8 @@ tprof_backend_estimate_freq(void)
return freq;
}
int
tprof_backend_start(void)
static int
tprof_pmi_start(tprof_backend_cookie_t *cookie)
{
struct cpu_info * const ci = curcpu();
uint64_t xc;
@ -224,11 +226,14 @@ tprof_backend_start(void)
xc = xc_broadcast(0, tprof_pmi_start_cpu, NULL, NULL);
xc_wait(xc);
KASSERT(tprof_cookie == NULL);
tprof_cookie = cookie;
return 0;
}
void
tprof_backend_stop(void)
static void
tprof_pmi_stop(tprof_backend_cookie_t *cookie)
{
uint64_t xc;
@ -236,6 +241,33 @@ tprof_backend_stop(void)
xc_wait(xc);
KASSERT(tprof_pmi_nmi_handle != NULL);
KASSERT(tprof_cookie == cookie);
nmi_disestablish(tprof_pmi_nmi_handle);
tprof_pmi_nmi_handle = NULL;
tprof_cookie = NULL;
}
static const tprof_backend_ops_t tprof_pmi_ops = {
.tbo_estimate_freq = tprof_pmi_estimate_freq,
.tbo_start = tprof_pmi_start,
.tbo_stop = tprof_pmi_stop,
};
MODULE(MODULE_CLASS_DRIVER, tprof_pmi, "tprof");
static int
tprof_pmi_modcmd(modcmd_t cmd, void *arg)
{
switch (cmd) {
case MODULE_CMD_INIT:
return tprof_backend_register("tprof_pmi", &tprof_pmi_ops,
TPROF_BACKEND_VERSION);
case MODULE_CMD_FINI:
return tprof_backend_unregister("tprof_pmi");
default:
return ENOTTY;
}
}

View File

@ -1,7 +1,7 @@
/* $NetBSD: tprof.c,v 1.3 2009/01/20 15:13:54 yamt Exp $ */
/* $NetBSD: tprof.c,v 1.4 2009/03/10 14:45:02 yamt Exp $ */
/*-
* Copyright (c)2008 YAMAMOTO Takashi,
* Copyright (c)2008,2009 YAMAMOTO Takashi,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tprof.c,v 1.3 2009/01/20 15:13:54 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: tprof.c,v 1.4 2009/03/10 14:45:02 yamt Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -37,6 +37,7 @@ __KERNEL_RCSID(0, "$NetBSD: tprof.c,v 1.3 2009/01/20 15:13:54 yamt Exp $");
#include <sys/conf.h>
#include <sys/callout.h>
#include <sys/kmem.h>
#include <sys/module.h>
#include <sys/workqueue.h>
#include <sys/queue.h>
@ -45,6 +46,19 @@ __KERNEL_RCSID(0, "$NetBSD: tprof.c,v 1.3 2009/01/20 15:13:54 yamt Exp $");
#include <machine/db_machdep.h> /* PC_REGS */
/*
* locking order:
* tprof_reader_lock -> tprof_lock
* tprof_startstop_lock -> tprof_lock
*/
/*
* protected by:
* L: tprof_lock
* R: tprof_reader_lock
* S: tprof_startstop_lock
*/
typedef struct {
uintptr_t s_pc; /* program counter */
} tprof_sample_t;
@ -69,30 +83,35 @@ typedef struct {
callout_t c_callout;
} __aligned(CACHE_LINE_SIZE) tprof_cpu_t;
/*
* locking order:
* tprof_reader_lock -> tprof_lock
* tprof_startstop_lock -> tprof_lock
*/
typedef struct tprof_backend {
const char *tb_name;
const tprof_backend_ops_t *tb_ops;
LIST_ENTRY(tprof_backend) tb_list;
int tb_usecount; /* S: */
} tprof_backend_t;
static kmutex_t tprof_lock;
static bool tprof_running;
static u_int tprof_nworker;
static u_int tprof_nworker; /* L: # of running worker LWPs */
static lwp_t *tprof_owner;
static STAILQ_HEAD(, tprof_buf) tprof_list;
static u_int tprof_nbuf_on_list;
static STAILQ_HEAD(, tprof_buf) tprof_list; /* L: global buffer list */
static u_int tprof_nbuf_on_list; /* L: # of buffers on tprof_list */
static struct workqueue *tprof_wq;
static tprof_cpu_t tprof_cpus[MAXCPUS] __aligned(CACHE_LINE_SIZE);
static u_int tprof_samples_per_buf;
static tprof_backend_t *tprof_backend; /* S: */
static LIST_HEAD(, tprof_backend) tprof_backends =
LIST_HEAD_INITIALIZER(tprof_backend); /* S: */
static kmutex_t tprof_reader_lock;
static kcondvar_t tprof_reader_cv;
static off_t tprof_reader_offset;
static kcondvar_t tprof_reader_cv; /* L: */
static off_t tprof_reader_offset; /* R: */
static kmutex_t tprof_startstop_lock;
static kcondvar_t tprof_cv;
static kcondvar_t tprof_cv; /* L: */
static struct tprof_stat tprof_stat;
static struct tprof_stat tprof_stat; /* L: */
static tprof_cpu_t *
tprof_cpu(struct cpu_info *ci)
@ -234,6 +253,7 @@ tprof_start(const struct tprof_param *param)
struct cpu_info *ci;
int error;
uint64_t freq;
tprof_backend_t *tb;
KASSERT(mutex_owned(&tprof_startstop_lock));
if (tprof_running) {
@ -241,7 +261,18 @@ tprof_start(const struct tprof_param *param)
goto done;
}
freq = tprof_backend_estimate_freq();
tb = tprof_backend;
if (tb == NULL) {
error = ENOENT;
goto done;
}
if (tb->tb_usecount > 0) {
error = EBUSY;
goto done;
}
tb->tb_usecount++;
freq = tb->tb_ops->tbo_estimate_freq();
tprof_samples_per_buf = MIN(freq * 2, TPROF_MAX_SAMPLES_PER_BUF);
error = workqueue_create(&tprof_wq, "tprofmv", tprof_worker, NULL,
@ -264,7 +295,7 @@ tprof_start(const struct tprof_param *param)
callout_setfunc(&c->c_callout, tprof_kick, ci);
}
error = tprof_backend_start();
error = tb->tb_ops->tbo_start(NULL);
if (error != 0) {
tprof_stop1();
goto done;
@ -290,13 +321,17 @@ tprof_stop(void)
{
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
tprof_backend_t *tb;
KASSERT(mutex_owned(&tprof_startstop_lock));
if (!tprof_running) {
goto done;
}
tprof_backend_stop();
tb = tprof_backend;
KASSERT(tb->tb_usecount > 0);
tb->tb_ops->tbo_stop(NULL);
tb->tb_usecount--;
mutex_enter(&tprof_lock);
tprof_running = false;
@ -316,6 +351,10 @@ done:
;
}
/*
* tprof_clear: drain unread samples.
*/
static void
tprof_clear(void)
{
@ -341,6 +380,21 @@ tprof_clear(void)
memset(&tprof_stat, 0, sizeof(tprof_stat));
}
static tprof_backend_t *
tprof_backend_lookup(const char *name)
{
tprof_backend_t *tb;
KASSERT(mutex_owned(&tprof_startstop_lock));
LIST_FOREACH(tb, &tprof_backends, tb_list) {
if (!strcmp(tb->tb_name, name)) {
return tb;
}
}
return NULL;
}
/* -------------------- backend interfaces */
/*
@ -351,7 +405,7 @@ tprof_clear(void)
*/
void
tprof_sample(const struct trapframe *tf)
tprof_sample(tprof_backend_cookie_t *cookie, const struct trapframe *tf)
{
tprof_cpu_t * const c = tprof_curcpu();
tprof_buf_t * const buf = c->c_buf;
@ -367,6 +421,81 @@ tprof_sample(const struct trapframe *tf)
buf->b_used = idx + 1;
}
/*
* tprof_backend_register:
*/
int
tprof_backend_register(const char *name, const tprof_backend_ops_t *ops,
int vers)
{
tprof_backend_t *tb;
if (vers != TPROF_BACKEND_VERSION) {
return EINVAL;
}
mutex_enter(&tprof_startstop_lock);
tb = tprof_backend_lookup(name);
if (tb != NULL) {
mutex_exit(&tprof_startstop_lock);
return EEXIST;
}
#if 1 /* XXX for now */
if (!LIST_EMPTY(&tprof_backends)) {
mutex_exit(&tprof_startstop_lock);
return ENOTSUP;
}
#endif
tb = kmem_alloc(sizeof(*tb), KM_SLEEP);
tb->tb_name = name;
tb->tb_ops = ops;
tb->tb_usecount = 0;
LIST_INSERT_HEAD(&tprof_backends, tb, tb_list);
#if 1 /* XXX for now */
if (tprof_backend == NULL) {
tprof_backend = tb;
}
#endif
mutex_exit(&tprof_startstop_lock);
return 0;
}
/*
* tprof_backend_unregister:
*/
int
tprof_backend_unregister(const char *name)
{
tprof_backend_t *tb;
mutex_enter(&tprof_startstop_lock);
tb = tprof_backend_lookup(name);
#if defined(DIAGNOSTIC)
if (tb == NULL) {
mutex_exit(&tprof_startstop_lock);
panic("%s: not found '%s'", __func__, name);
}
#endif /* defined(DIAGNOSTIC) */
if (tb->tb_usecount > 0) {
mutex_exit(&tprof_startstop_lock);
return EBUSY;
}
#if 1 /* XXX for now */
if (tprof_backend == tb) {
tprof_backend = NULL;
}
#endif
LIST_REMOVE(tb, tb_list);
mutex_exit(&tprof_startstop_lock);
kmem_free(tb, sizeof(*tb));
return 0;
}
/* -------------------- cdevsw interfaces */
void tprofattach(int);
@ -524,6 +653,15 @@ const struct cdevsw tprof_cdevsw = {
void
tprofattach(int nunits)
{
/* nothing */
}
MODULE(MODULE_CLASS_DRIVER, tprof, NULL);
static void
tprof_driver_init(void)
{
mutex_init(&tprof_lock, MUTEX_DEFAULT, IPL_NONE);
@ -533,3 +671,55 @@ tprofattach(int nunits)
cv_init(&tprof_reader_cv, "tprofread");
STAILQ_INIT(&tprof_list);
}
static void
tprof_driver_fini(void)
{
mutex_destroy(&tprof_lock);
mutex_destroy(&tprof_reader_lock);
mutex_destroy(&tprof_startstop_lock);
cv_destroy(&tprof_cv);
cv_destroy(&tprof_reader_cv);
}
static int
tprof_modcmd(modcmd_t cmd, void *arg)
{
switch (cmd) {
case MODULE_CMD_INIT:
tprof_driver_init();
#if defined(_MODULE)
{
devmajor_t bmajor = NODEVMAJOR;
devmajor_t cmajor = NODEVMAJOR;
int error;
error = devsw_attach("tprof", NULL, &bmajor,
&tprof_cdevsw, &cmajor);
if (error) {
tprof_driver_fini();
return error;
}
}
#endif /* defined(_MODULE) */
return 0;
case MODULE_CMD_FINI:
#if defined(_MODULE)
{
int error;
error = devsw_detach(NULL, &tprof_cdevsw);
if (error) {
return error;
}
}
#endif /* defined(_MODULE) */
tprof_driver_fini();
return 0;
default:
return ENOTTY;
}
}

View File

@ -1,7 +1,7 @@
/* $NetBSD: tprof.h,v 1.1 2008/01/01 21:28:38 yamt Exp $ */
/* $NetBSD: tprof.h,v 1.2 2009/03/10 14:45:02 yamt Exp $ */
/*-
* Copyright (c)2008 YAMAMOTO Takashi,
* Copyright (c)2008,2009 YAMAMOTO Takashi,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -29,11 +29,19 @@
#ifndef _DEV_TPROF_TPROF_H_
#define _DEV_TPROF_TPROF_H_
uint64_t tprof_backend_estimate_freq(void);
int tprof_backend_start(void);
void tprof_backend_stop(void);
typedef struct tprof_backend_cookie tprof_backend_cookie_t;
typedef struct tprof_backend_ops {
uint64_t (*tbo_estimate_freq)(void);
int (*tbo_start)(tprof_backend_cookie_t *);
void (*tbo_stop)(tprof_backend_cookie_t *);
} tprof_backend_ops_t;
#define TPROF_BACKEND_VERSION 1
int tprof_backend_register(const char *, const tprof_backend_ops_t *, int);
int tprof_backend_unregister(const char *);
struct trapframe;
void tprof_sample(const struct trapframe *);
void tprof_sample(tprof_backend_cookie_t *, const struct trapframe *);
#endif /* _DEV_TPROF_TPROF_H_ */