Merge branch 'kstat'

This branch updates the existing kstat infrastructure to be
more flexible.  In particular, it extends the KSTAT_TYPE_RAW
type so it may be used to generate more dynamic kstats without
the need for additional custom types.

Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
This commit is contained in:
Brian Behlendorf 2013-10-25 13:50:07 -07:00
commit 0f4b9a5806
3 changed files with 209 additions and 87 deletions

View File

@ -33,6 +33,7 @@
#include <sys/mutex.h> #include <sys/mutex.h>
#define KSTAT_STRLEN 31 #define KSTAT_STRLEN 31
#define KSTAT_RAW_MAX (128*1024)
/* For reference valid classes are: /* For reference valid classes are:
* disk, tape, net, controller, vm, kvm, hat, streams, kstat, misc * disk, tape, net, controller, vm, kvm, hat, streams, kstat, misc
@ -43,8 +44,7 @@
#define KSTAT_TYPE_INTR 2 /* interrupt stats; ks_ndata == 1 */ #define KSTAT_TYPE_INTR 2 /* interrupt stats; ks_ndata == 1 */
#define KSTAT_TYPE_IO 3 /* I/O stats; ks_ndata == 1 */ #define KSTAT_TYPE_IO 3 /* I/O stats; ks_ndata == 1 */
#define KSTAT_TYPE_TIMER 4 /* event timer; ks_ndata >= 1 */ #define KSTAT_TYPE_TIMER 4 /* event timer; ks_ndata >= 1 */
#define KSTAT_TYPE_TXG 5 /* txg sync; ks_ndata >= 1 */ #define KSTAT_NUM_TYPES 5
#define KSTAT_NUM_TYPES 6
#define KSTAT_DATA_CHAR 0 #define KSTAT_DATA_CHAR 0
#define KSTAT_DATA_INT32 1 #define KSTAT_DATA_INT32 1
@ -79,6 +79,7 @@
#define KSTAT_WRITE 1 #define KSTAT_WRITE 1
struct kstat_s; struct kstat_s;
typedef struct kstat_s kstat_t;
typedef int kid_t; /* unique kstat id */ typedef int kid_t; /* unique kstat id */
typedef int kstat_update_t(struct kstat_s *, int); /* dynamic update cb */ typedef int kstat_update_t(struct kstat_s *, int); /* dynamic update cb */
@ -90,7 +91,13 @@ typedef struct kstat_module {
struct proc_dir_entry *ksm_proc; /* proc entry */ struct proc_dir_entry *ksm_proc; /* proc entry */
} kstat_module_t; } kstat_module_t;
typedef struct kstat_s { typedef struct kstat_raw_ops {
int (*headers)(char *buf, size_t size);
int (*data)(char *buf, size_t size, void *data);
void *(*addr)(kstat_t *ksp, loff_t index);
} kstat_raw_ops_t;
struct kstat_s {
int ks_magic; /* magic value */ int ks_magic; /* magic value */
kid_t ks_kid; /* unique kstat ID */ kid_t ks_kid; /* unique kstat ID */
hrtime_t ks_crtime; /* creation time */ hrtime_t ks_crtime; /* creation time */
@ -107,10 +114,14 @@ typedef struct kstat_s {
struct proc_dir_entry *ks_proc; /* proc linkage */ struct proc_dir_entry *ks_proc; /* proc linkage */
kstat_update_t *ks_update; /* dynamic updates */ kstat_update_t *ks_update; /* dynamic updates */
void *ks_private; /* private data */ void *ks_private; /* private data */
kmutex_t ks_lock; /* kstat data lock */ kmutex_t ks_private_lock; /* kstat private data lock */
kmutex_t *ks_lock; /* kstat data lock */
struct list_head ks_list; /* kstat linkage */ struct list_head ks_list; /* kstat linkage */
kstat_module_t *ks_owner; /* kstat module linkage */ kstat_module_t *ks_owner; /* kstat module linkage */
} kstat_t; kstat_raw_ops_t ks_raw_ops; /* ops table for raw type */
char *ks_raw_buf; /* buf used for raw ops */
size_t ks_raw_bufsize; /* size of raw ops buffer */
};
typedef struct kstat_named_s { typedef struct kstat_named_s {
char name[KSTAT_STRLEN]; /* name of counter */ char name[KSTAT_STRLEN]; /* name of counter */
@ -165,36 +176,25 @@ typedef struct kstat_timer {
hrtime_t stop_time; /* previous event stop time */ hrtime_t stop_time; /* previous event stop time */
} kstat_timer_t; } kstat_timer_t;
typedef enum kstat_txg_state {
TXG_STATE_OPEN = 1,
TXG_STATE_QUIESCING = 2,
TXG_STATE_SYNCING = 3,
TXG_STATE_COMMITTED = 4,
} kstat_txg_state_t;
typedef struct kstat_txg {
u_longlong_t txg; /* txg id */
kstat_txg_state_t state; /* txg state */
hrtime_t birth; /* birth time stamp */
u_longlong_t nread; /* number of bytes read */
u_longlong_t nwritten; /* number of bytes written */
uint_t reads; /* number of read operations */
uint_t writes; /* number of write operations */
hrtime_t open_time; /* open time */
hrtime_t quiesce_time;/* quiesce time */
hrtime_t sync_time; /* sync time */
} kstat_txg_t;
int spl_kstat_init(void); int spl_kstat_init(void);
void spl_kstat_fini(void); void spl_kstat_fini(void);
extern void __kstat_set_raw_ops(kstat_t *ksp,
int (*headers)(char *buf, size_t size),
int (*data)(char *buf, size_t size, void *data),
void* (*addr)(kstat_t *ksp, loff_t index));
extern kstat_t *__kstat_create(const char *ks_module, int ks_instance, extern kstat_t *__kstat_create(const char *ks_module, int ks_instance,
const char *ks_name, const char *ks_class, const char *ks_name, const char *ks_class,
uchar_t ks_type, uint_t ks_ndata, uchar_t ks_type, uint_t ks_ndata,
uchar_t ks_flags); uchar_t ks_flags);
extern void __kstat_install(kstat_t *ksp); extern void __kstat_install(kstat_t *ksp);
extern void __kstat_delete(kstat_t *ksp); extern void __kstat_delete(kstat_t *ksp);
extern void kstat_waitq_enter(kstat_io_t *);
extern void kstat_waitq_exit(kstat_io_t *);
extern void kstat_runq_enter(kstat_io_t *);
extern void kstat_runq_exit(kstat_io_t *);
#define kstat_set_raw_ops(k,h,d,a) __kstat_set_raw_ops(k,h,d,a)
#define kstat_create(m,i,n,c,t,s,f) __kstat_create(m,i,n,c,t,s,f) #define kstat_create(m,i,n,c,t,s,f) __kstat_create(m,i,n,c,t,s,f)
#define kstat_install(k) __kstat_install(k) #define kstat_install(k) __kstat_install(k)
#define kstat_delete(k) __kstat_delete(k) #define kstat_delete(k) __kstat_delete(k)

View File

@ -51,6 +51,8 @@ typedef void (*thread_func_t)(void *);
#define thread_exit() __thread_exit() #define thread_exit() __thread_exit()
#define thread_join(t) VERIFY(0) #define thread_join(t) VERIFY(0)
#define curthread current #define curthread current
#define getcomm() current->comm
#define getpid() current->pid
extern kthread_t *__thread_create(caddr_t stk, size_t stksize, extern kthread_t *__thread_create(caddr_t stk, size_t stksize,
thread_func_t func, const char *name, thread_func_t func, const char *name,

View File

@ -41,10 +41,91 @@ static kmutex_t kstat_module_lock;
static struct list_head kstat_module_list; static struct list_head kstat_module_list;
static kid_t kstat_id; static kid_t kstat_id;
static void static int
kstat_resize_raw(kstat_t *ksp)
{
if (ksp->ks_raw_bufsize == KSTAT_RAW_MAX)
return ENOMEM;
vmem_free(ksp->ks_raw_buf, ksp->ks_raw_bufsize);
ksp->ks_raw_bufsize = MIN(ksp->ks_raw_bufsize * 2, KSTAT_RAW_MAX);
ksp->ks_raw_buf = vmem_alloc(ksp->ks_raw_bufsize, KM_SLEEP);
return 0;
}
void
kstat_waitq_enter(kstat_io_t *kiop)
{
hrtime_t new, delta;
ulong_t wcnt;
new = gethrtime();
delta = new - kiop->wlastupdate;
kiop->wlastupdate = new;
wcnt = kiop->wcnt++;
if (wcnt != 0) {
kiop->wlentime += delta * wcnt;
kiop->wtime += delta;
}
}
EXPORT_SYMBOL(kstat_waitq_enter);
void
kstat_waitq_exit(kstat_io_t *kiop)
{
hrtime_t new, delta;
ulong_t wcnt;
new = gethrtime();
delta = new - kiop->wlastupdate;
kiop->wlastupdate = new;
wcnt = kiop->wcnt--;
ASSERT((int)wcnt > 0);
kiop->wlentime += delta * wcnt;
kiop->wtime += delta;
}
EXPORT_SYMBOL(kstat_waitq_exit);
void
kstat_runq_enter(kstat_io_t *kiop)
{
hrtime_t new, delta;
ulong_t rcnt;
new = gethrtime();
delta = new - kiop->rlastupdate;
kiop->rlastupdate = new;
rcnt = kiop->rcnt++;
if (rcnt != 0) {
kiop->rlentime += delta * rcnt;
kiop->rtime += delta;
}
}
EXPORT_SYMBOL(kstat_runq_enter);
void
kstat_runq_exit(kstat_io_t *kiop)
{
hrtime_t new, delta;
ulong_t rcnt;
new = gethrtime();
delta = new - kiop->rlastupdate;
kiop->rlastupdate = new;
rcnt = kiop->rcnt--;
ASSERT((int)rcnt > 0);
kiop->rlentime += delta * rcnt;
kiop->rtime += delta;
}
EXPORT_SYMBOL(kstat_runq_exit);
static int
kstat_seq_show_headers(struct seq_file *f) kstat_seq_show_headers(struct seq_file *f)
{ {
kstat_t *ksp = (kstat_t *)f->private; kstat_t *ksp = (kstat_t *)f->private;
int rc = 0;
ASSERT(ksp->ks_magic == KS_MAGIC); ASSERT(ksp->ks_magic == KS_MAGIC);
seq_printf(f, "%d %d 0x%02x %d %d %lld %lld\n", seq_printf(f, "%d %d 0x%02x %d %d %lld %lld\n",
@ -54,7 +135,17 @@ kstat_seq_show_headers(struct seq_file *f)
switch (ksp->ks_type) { switch (ksp->ks_type) {
case KSTAT_TYPE_RAW: case KSTAT_TYPE_RAW:
seq_printf(f, "raw data"); restart:
if (ksp->ks_raw_ops.headers) {
rc = ksp->ks_raw_ops.headers(
ksp->ks_raw_buf, ksp->ks_raw_bufsize);
if (rc == ENOMEM && !kstat_resize_raw(ksp))
goto restart;
if (!rc)
seq_puts(f, ksp->ks_raw_buf);
} else {
seq_printf(f, "raw data\n");
}
break; break;
case KSTAT_TYPE_NAMED: case KSTAT_TYPE_NAMED:
seq_printf(f, "%-31s %-4s %s\n", seq_printf(f, "%-31s %-4s %s\n",
@ -81,17 +172,11 @@ kstat_seq_show_headers(struct seq_file *f)
"name", "events", "elapsed", "name", "events", "elapsed",
"min", "max", "start", "stop"); "min", "max", "start", "stop");
break; break;
case KSTAT_TYPE_TXG:
seq_printf(f,
"%-8s %-5s %-13s %-12s %-12s %-8s %-8s "
"%-12s %-12s %-12s\n",
"txg", "state", "birth",
"nread", "nwritten", "reads", "writes",
"otime", "qtime", "stime");
break;
default: default:
PANIC("Undefined kstat type %d\n", ksp->ks_type); PANIC("Undefined kstat type %d\n", ksp->ks_type);
} }
return -rc;
} }
static int static int
@ -201,27 +286,6 @@ kstat_seq_show_timer(struct seq_file *f, kstat_timer_t *ktp)
return 0; return 0;
} }
static int
kstat_seq_show_txg(struct seq_file *f, kstat_txg_t *ktp)
{
char state;
switch (ktp->state) {
case TXG_STATE_OPEN: state = 'O'; break;
case TXG_STATE_QUIESCING: state = 'Q'; break;
case TXG_STATE_SYNCING: state = 'S'; break;
case TXG_STATE_COMMITTED: state = 'C'; break;
default: state = '?'; break;
}
seq_printf(f,
"%-8llu %-5c %-13llu %-12llu %-12llu %-8u %-8u "
"%12lld %12lld %12lld\n", ktp->txg, state, ktp->birth,
ktp->nread, ktp->nwritten, ktp->reads, ktp->writes,
ktp->open_time, ktp->quiesce_time, ktp->sync_time);
return 0;
}
static int static int
kstat_seq_show(struct seq_file *f, void *p) kstat_seq_show(struct seq_file *f, void *p)
{ {
@ -232,9 +296,19 @@ kstat_seq_show(struct seq_file *f, void *p)
switch (ksp->ks_type) { switch (ksp->ks_type) {
case KSTAT_TYPE_RAW: case KSTAT_TYPE_RAW:
restart:
if (ksp->ks_raw_ops.data) {
rc = ksp->ks_raw_ops.data(
ksp->ks_raw_buf, ksp->ks_raw_bufsize, p);
if (rc == ENOMEM && !kstat_resize_raw(ksp))
goto restart;
if (!rc)
seq_puts(f, ksp->ks_raw_buf);
} else {
ASSERT(ksp->ks_ndata == 1); ASSERT(ksp->ks_ndata == 1);
rc = kstat_seq_show_raw(f, ksp->ks_data, rc = kstat_seq_show_raw(f, ksp->ks_data,
ksp->ks_data_size); ksp->ks_data_size);
}
break; break;
case KSTAT_TYPE_NAMED: case KSTAT_TYPE_NAMED:
rc = kstat_seq_show_named(f, (kstat_named_t *)p); rc = kstat_seq_show_named(f, (kstat_named_t *)p);
@ -248,20 +322,21 @@ kstat_seq_show(struct seq_file *f, void *p)
case KSTAT_TYPE_TIMER: case KSTAT_TYPE_TIMER:
rc = kstat_seq_show_timer(f, (kstat_timer_t *)p); rc = kstat_seq_show_timer(f, (kstat_timer_t *)p);
break; break;
case KSTAT_TYPE_TXG:
rc = kstat_seq_show_txg(f, (kstat_txg_t *)p);
break;
default: default:
PANIC("Undefined kstat type %d\n", ksp->ks_type); PANIC("Undefined kstat type %d\n", ksp->ks_type);
} }
return rc; return -rc;
} }
int int
kstat_default_update(kstat_t *ksp, int rw) kstat_default_update(kstat_t *ksp, int rw)
{ {
ASSERT(ksp != NULL); ASSERT(ksp != NULL);
if (rw == KSTAT_WRITE)
return (EACCES);
return 0; return 0;
} }
@ -273,6 +348,9 @@ kstat_seq_data_addr(kstat_t *ksp, loff_t n)
switch (ksp->ks_type) { switch (ksp->ks_type) {
case KSTAT_TYPE_RAW: case KSTAT_TYPE_RAW:
if (ksp->ks_raw_ops.addr)
rc = ksp->ks_raw_ops.addr(ksp, n);
else
rc = ksp->ks_data; rc = ksp->ks_data;
break; break;
case KSTAT_TYPE_NAMED: case KSTAT_TYPE_NAMED:
@ -287,9 +365,6 @@ kstat_seq_data_addr(kstat_t *ksp, loff_t n)
case KSTAT_TYPE_TIMER: case KSTAT_TYPE_TIMER:
rc = ksp->ks_data + n * sizeof(kstat_timer_t); rc = ksp->ks_data + n * sizeof(kstat_timer_t);
break; break;
case KSTAT_TYPE_TXG:
rc = ksp->ks_data + n * sizeof(kstat_txg_t);
break;
default: default:
PANIC("Undefined kstat type %d\n", ksp->ks_type); PANIC("Undefined kstat type %d\n", ksp->ks_type);
} }
@ -305,15 +380,20 @@ kstat_seq_start(struct seq_file *f, loff_t *pos)
ASSERT(ksp->ks_magic == KS_MAGIC); ASSERT(ksp->ks_magic == KS_MAGIC);
SENTRY; SENTRY;
mutex_enter(&ksp->ks_lock); mutex_enter(ksp->ks_lock);
if (ksp->ks_type == KSTAT_TYPE_RAW) {
ksp->ks_raw_bufsize = PAGE_SIZE;
ksp->ks_raw_buf = vmem_alloc(ksp->ks_raw_bufsize, KM_SLEEP);
}
/* Dynamically update kstat, on error existing kstats are used */ /* Dynamically update kstat, on error existing kstats are used */
(void) ksp->ks_update(ksp, KSTAT_READ); (void) ksp->ks_update(ksp, KSTAT_READ);
ksp->ks_snaptime = gethrtime(); ksp->ks_snaptime = gethrtime();
if (!n) if (!n && kstat_seq_show_headers(f))
kstat_seq_show_headers(f); SRETURN(NULL);
if (n >= ksp->ks_ndata) if (n >= ksp->ks_ndata)
SRETURN(NULL); SRETURN(NULL);
@ -341,7 +421,10 @@ kstat_seq_stop(struct seq_file *f, void *v)
kstat_t *ksp = (kstat_t *)f->private; kstat_t *ksp = (kstat_t *)f->private;
ASSERT(ksp->ks_magic == KS_MAGIC); ASSERT(ksp->ks_magic == KS_MAGIC);
mutex_exit(&ksp->ks_lock); if (ksp->ks_type == KSTAT_TYPE_RAW)
vmem_free(ksp->ks_raw_buf, ksp->ks_raw_bufsize);
mutex_exit(ksp->ks_lock);
} }
static struct seq_operations kstat_seq_ops = { static struct seq_operations kstat_seq_ops = {
@ -408,13 +491,47 @@ proc_kstat_open(struct inode *inode, struct file *filp)
return rc; return rc;
} }
static ssize_t
proc_kstat_write(struct file *filp, const char __user *buf,
size_t len, loff_t *ppos)
{
struct seq_file *f = filp->private_data;
kstat_t *ksp = f->private;
int rc;
ASSERT(ksp->ks_magic == KS_MAGIC);
mutex_enter(ksp->ks_lock);
rc = ksp->ks_update(ksp, KSTAT_WRITE);
mutex_exit(ksp->ks_lock);
if (rc)
return (-rc);
*ppos += len;
return (len);
}
static struct file_operations proc_kstat_operations = { static struct file_operations proc_kstat_operations = {
.open = proc_kstat_open, .open = proc_kstat_open,
.write = proc_kstat_write,
.read = seq_read, .read = seq_read,
.llseek = seq_lseek, .llseek = seq_lseek,
.release = seq_release, .release = seq_release,
}; };
void
__kstat_set_raw_ops(kstat_t *ksp,
int (*headers)(char *buf, size_t size),
int (*data)(char *buf, size_t size, void *data),
void *(*addr)(kstat_t *ksp, loff_t index))
{
ksp->ks_raw_ops.headers = headers;
ksp->ks_raw_ops.data = data;
ksp->ks_raw_ops.addr = addr;
}
EXPORT_SYMBOL(__kstat_set_raw_ops);
kstat_t * kstat_t *
__kstat_create(const char *ks_module, int ks_instance, const char *ks_name, __kstat_create(const char *ks_module, int ks_instance, const char *ks_name,
const char *ks_class, uchar_t ks_type, uint_t ks_ndata, const char *ks_class, uchar_t ks_type, uint_t ks_ndata,
@ -440,7 +557,8 @@ __kstat_create(const char *ks_module, int ks_instance, const char *ks_name,
mutex_exit(&kstat_module_lock); mutex_exit(&kstat_module_lock);
ksp->ks_magic = KS_MAGIC; ksp->ks_magic = KS_MAGIC;
mutex_init(&ksp->ks_lock, NULL, MUTEX_DEFAULT, NULL); mutex_init(&ksp->ks_private_lock, NULL, MUTEX_DEFAULT, NULL);
ksp->ks_lock = &ksp->ks_private_lock;
INIT_LIST_HEAD(&ksp->ks_list); INIT_LIST_HEAD(&ksp->ks_list);
ksp->ks_crtime = gethrtime(); ksp->ks_crtime = gethrtime();
@ -453,6 +571,11 @@ __kstat_create(const char *ks_module, int ks_instance, const char *ks_name,
ksp->ks_flags = ks_flags; ksp->ks_flags = ks_flags;
ksp->ks_update = kstat_default_update; ksp->ks_update = kstat_default_update;
ksp->ks_private = NULL; ksp->ks_private = NULL;
ksp->ks_raw_ops.headers = NULL;
ksp->ks_raw_ops.data = NULL;
ksp->ks_raw_ops.addr = NULL;
ksp->ks_raw_buf = NULL;
ksp->ks_raw_bufsize = 0;
switch (ksp->ks_type) { switch (ksp->ks_type) {
case KSTAT_TYPE_RAW: case KSTAT_TYPE_RAW:
@ -475,10 +598,6 @@ __kstat_create(const char *ks_module, int ks_instance, const char *ks_name,
ksp->ks_ndata = ks_ndata; ksp->ks_ndata = ks_ndata;
ksp->ks_data_size = ks_ndata * sizeof(kstat_timer_t); ksp->ks_data_size = ks_ndata * sizeof(kstat_timer_t);
break; break;
case KSTAT_TYPE_TXG:
ksp->ks_ndata = ks_ndata;
ksp->ks_data_size = ks_ndata * sizeof(kstat_timer_t);
break;
default: default:
PANIC("Undefined kstat type %d\n", ksp->ks_type); PANIC("Undefined kstat type %d\n", ksp->ks_type);
} }
@ -486,7 +605,7 @@ __kstat_create(const char *ks_module, int ks_instance, const char *ks_name,
if (ksp->ks_flags & KSTAT_FLAG_VIRTUAL) { if (ksp->ks_flags & KSTAT_FLAG_VIRTUAL) {
ksp->ks_data = NULL; ksp->ks_data = NULL;
} else { } else {
ksp->ks_data = kmem_alloc(ksp->ks_data_size, KM_SLEEP); ksp->ks_data = kmem_zalloc(ksp->ks_data_size, KM_SLEEP);
if (ksp->ks_data == NULL) { if (ksp->ks_data == NULL) {
kmem_free(ksp, sizeof(*ksp)); kmem_free(ksp, sizeof(*ksp));
ksp = NULL; ksp = NULL;
@ -524,16 +643,16 @@ __kstat_install(kstat_t *ksp)
list_add_tail(&ksp->ks_list, &module->ksm_kstat_list); list_add_tail(&ksp->ks_list, &module->ksm_kstat_list);
mutex_enter(&ksp->ks_lock); mutex_enter(ksp->ks_lock);
ksp->ks_owner = module; ksp->ks_owner = module;
ksp->ks_proc = proc_create_data(ksp->ks_name, 0444, ksp->ks_proc = proc_create_data(ksp->ks_name, 0644,
module->ksm_proc, &proc_kstat_operations, (void *)ksp); module->ksm_proc, &proc_kstat_operations, (void *)ksp);
if (ksp->ks_proc == NULL) { if (ksp->ks_proc == NULL) {
list_del_init(&ksp->ks_list); list_del_init(&ksp->ks_list);
if (list_empty(&module->ksm_kstat_list)) if (list_empty(&module->ksm_kstat_list))
kstat_delete_module(module); kstat_delete_module(module);
} }
mutex_exit(&ksp->ks_lock); mutex_exit(ksp->ks_lock);
out: out:
mutex_exit(&kstat_module_lock); mutex_exit(&kstat_module_lock);
} }
@ -559,7 +678,8 @@ __kstat_delete(kstat_t *ksp)
if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL)) if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL))
kmem_free(ksp->ks_data, ksp->ks_data_size); kmem_free(ksp->ks_data, ksp->ks_data_size);
mutex_destroy(&ksp->ks_lock); ksp->ks_lock = NULL;
mutex_destroy(&ksp->ks_private_lock);
kmem_free(ksp, sizeof(*ksp)); kmem_free(ksp, sizeof(*ksp));
return; return;