Add an almost feature complete implemenation of kstat. I chose

not to support a few flags (we assert if they are used), and I
did not add the libkstat interface and instead exported everything
to proc for easy access.



git-svn-id: https://outreach.scidac.gov/svn/spl/trunk@103 7e1ea52c-4ff2-0310-8f11-9dd32ca42a1c
This commit is contained in:
behlendo 2008-05-08 23:21:47 +00:00
parent d4c540de38
commit 04a479f706
6 changed files with 662 additions and 108 deletions

View File

@ -5,130 +5,151 @@
extern "C" { extern "C" {
#endif #endif
#define DEBUG_KSTAT
#include <linux/module.h> #include <linux/module.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/kmem.h>
#include <sys/proc.h>
/* XXX - The minimum functionality here is stubbed out but nothing works. */ #define KSTAT_STRLEN 31
#define KSTAT_STRLEN 31 /* 30 chars + NULL; must be 16 * n - 1 */ /* For reference valid classes are:
* disk, tape, net, controller, vm, kvm, hat, streams, kstat, misc
#define KSTAT_TYPE_RAW 0 /* can be anything */ */
/* ks_ndata >= 1 */
#define KSTAT_TYPE_NAMED 1 /* name/value pair */
/* ks_ndata >= 1 */
#define KSTAT_TYPE_INTR 2 /* interrupt statistics */
/* ks_ndata == 1 */
#define KSTAT_TYPE_IO 3 /* I/O statistics */
/* ks_ndata == 1 */
#define KSTAT_TYPE_TIMER 4 /* event timer */
/* ks_ndata >= 1 */
#define KSTAT_TYPE_RAW 0 /* can be anything; ks_ndata >= 1 */
#define KSTAT_TYPE_NAMED 1 /* name/value pair; 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_TIMER 4 /* event timer; ks_ndata >= 1 */
#define KSTAT_NUM_TYPES 5 #define KSTAT_NUM_TYPES 5
#define KSTAT_DATA_CHAR 0 #define KSTAT_DATA_CHAR 0
#define KSTAT_DATA_INT32 1 #define KSTAT_DATA_INT32 1
#define KSTAT_DATA_UINT32 2 #define KSTAT_DATA_UINT32 2
#define KSTAT_DATA_INT64 3 #define KSTAT_DATA_INT64 3
#define KSTAT_DATA_UINT64 4 #define KSTAT_DATA_UINT64 4
#define KSTAT_DATA_LONG 5
#define KSTAT_DATA_ULONG 6
#define KSTAT_DATA_STRING 7
#define KSTAT_NUM_DATAS 8
#define KSTAT_INTR_HARD 0
#define KSTAT_INTR_SOFT 1
#define KSTAT_INTR_WATCHDOG 2
#define KSTAT_INTR_SPURIOUS 3
#define KSTAT_INTR_MULTSVC 4
#define KSTAT_NUM_INTRS 5
#define KSTAT_FLAG_VIRTUAL 0x01 #define KSTAT_FLAG_VIRTUAL 0x01
#define KSTAT_FLAG_VAR_SIZE 0x02 #define KSTAT_FLAG_VAR_SIZE 0x02
#define KSTAT_FLAG_WRITABLE 0x04 #define KSTAT_FLAG_WRITABLE 0x04
#define KSTAT_FLAG_PERSISTENT 0x08 #define KSTAT_FLAG_PERSISTENT 0x08
#define KSTAT_FLAG_DORMANT 0x10 #define KSTAT_FLAG_DORMANT 0x10
#define KSTAT_FLAG_INVALID 0x2 #define KSTAT_FLAG_UNSUPPORTED (KSTAT_FLAG_VAR_SIZE | KSTAT_FLAG_WRITABLE | \
KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_DORMANT)
#define KS_MAGIC 0x9d9d9d9d
typedef int kid_t; /* unique kstat id */ typedef int kid_t; /* unique kstat id */
typedef struct kstat_s { typedef struct kstat_s {
/* int ks_magic; /* magic value */
* Fields relevant to both kernel and user
*/
hrtime_t ks_crtime; /* creation time (from gethrtime()) */
struct kstat_s *ks_next; /* kstat chain linkage */
kid_t ks_kid; /* unique kstat ID */ kid_t ks_kid; /* unique kstat ID */
char ks_module[KSTAT_STRLEN]; /* provider module name */ hrtime_t ks_crtime; /* creation time */
uchar_t ks_resv; /* reserved, currently just padding */ hrtime_t ks_snaptime; /* last access time */
int ks_instance; /* provider module's instance */ char ks_module[KSTAT_STRLEN+1]; /* provider module name */
char ks_name[KSTAT_STRLEN]; /* kstat name */ int ks_instance; /* provider module instance */
char ks_name[KSTAT_STRLEN+1]; /* kstat name */
char ks_class[KSTAT_STRLEN+1]; /* kstat class */
uchar_t ks_type; /* kstat data type */ uchar_t ks_type; /* kstat data type */
char ks_class[KSTAT_STRLEN]; /* kstat class */
uchar_t ks_flags; /* kstat flags */ uchar_t ks_flags; /* kstat flags */
void *ks_data; /* kstat type-specific data */ void *ks_data; /* kstat type-specific data */
uint_t ks_ndata; /* # of type-specific data records */ uint_t ks_ndata; /* # of type-specific data records */
size_t ks_data_size; /* total size of kstat data section */ size_t ks_data_size; /* size of kstat data section */
hrtime_t ks_snaptime; /* time of last data shapshot */ struct proc_dir_entry *ks_proc; /* proc linkage */
/* spinlock_t ks_lock; /* kstat data lock */
* Fields relevant to kernel only struct list_head ks_list; /* kstat linkage */
*/
int (*ks_update)(struct kstat *, int); /* dynamic update */
void *ks_private; /* arbitrary provider-private data */
int (*ks_snapshot)(struct kstat *, void *, int);
void *ks_lock; /* protects this kstat's data */
} kstat_t; } kstat_t;
typedef struct kstat_named_s { typedef struct kstat_named_s {
char name[KSTAT_STRLEN]; /* name of counter */ char name[KSTAT_STRLEN]; /* name of counter */
uchar_t data_type; /* data type */ uchar_t data_type; /* data type */
union { union {
char c[16]; /* enough for 128-bit ints */ char c[16]; /* 128-bit int */
int32_t i32; int32_t i32; /* 32-bit signed int */
uint32_t ui32; uint32_t ui32; /* 32-bit unsigned int */
int64_t i64; /* 64-bit signed int */
uint64_t ui64; /* 64-bit unsigned int */
long l; /* native signed long */
ulong_t ul; /* native unsigned long */
struct { struct {
union { union {
char *ptr; /* NULL-term string */ char *ptr; /* NULL-term string */
char __pad[8]; /* 64-bit padding */ char __pad[8]; /* 64-bit padding */
} addr; } addr;
uint32_t len; /* # bytes for strlen + '\0' */ uint32_t len; /* # bytes for strlen + '\0' */
} str; } string;
/* } value;
* The int64_t and uint64_t types are not valid for a maximally conformant
* 32-bit compilation environment (cc -Xc) using compilers prior to the
* introduction of C99 conforming compiler (reference ISO/IEC 9899:1990).
* In these cases, the visibility of i64 and ui64 is only permitted for
* 64-bit compilation environments or 32-bit non-maximally conformant
* C89 or C90 ANSI C compilation environments (cc -Xt and cc -Xa). In the
* C99 ANSI C compilation environment, the long long type is supported.
* The _INT64_TYPE is defined by the implementation (see sys/int_types.h).
*/
int64_t i64;
uint64_t ui64;
long l;
ulong_t ul;
/* These structure members are obsolete */
longlong_t ll;
u_longlong_t ull;
float f;
double d;
} value; /* value of counter */
} kstat_named_t; } kstat_named_t;
#define KSTAT_NAMED_STR_PTR(knptr) ((knptr)->value.string.addr.ptr)
#define KSTAT_NAMED_STR_BUFLEN(knptr) ((knptr)->value.string.len)
static __inline__ kstat_t * typedef struct kstat_intr {
kstat_create(const char *ks_module, int ks_instance, const char *ks_name, uint_t intrs[KSTAT_NUM_INTRS];
const char *ks_class, uchar_t ks_type, uint_t ks_ndata, } kstat_intr_t;
uchar_t ks_flags)
{
return NULL;
}
static __inline__ void typedef struct kstat_io {
kstat_install(kstat_t *ksp) u_longlong_t nread; /* number of bytes read */
{ u_longlong_t nwritten; /* number of bytes written */
return; uint_t reads; /* number of read operations */
} uint_t writes; /* number of write operations */
hrtime_t wtime; /* cumulative wait (pre-service) time */
hrtime_t wlentime; /* cumulative wait length*time product*/
hrtime_t wlastupdate; /* last time wait queue changed */
hrtime_t rtime; /* cumulative run (service) time */
hrtime_t rlentime; /* cumulative run length*time product */
hrtime_t rlastupdate; /* last time run queue changed */
uint_t wcnt; /* count of elements in wait state */
uint_t rcnt; /* count of elements in run state */
} kstat_io_t;
static __inline__ void typedef struct kstat_timer {
kstat_delete(kstat_t *ksp) char name[KSTAT_STRLEN+1]; /* event name */
{ u_longlong_t num_events; /* number of events */
return; hrtime_t elapsed_time; /* cumulative elapsed time */
} hrtime_t min_time; /* shortest event duration */
hrtime_t max_time; /* longest event duration */
hrtime_t start_time; /* previous event start time */
hrtime_t stop_time; /* previous event stop time */
} kstat_timer_t;
int kstat_init(void);
void kstat_fini(void);
#ifdef DEBUG_KSTAT
extern kstat_t *__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,
uchar_t ks_flags);
extern void __kstat_install(kstat_t *ksp);
extern void __kstat_delete(kstat_t *ksp);
#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_delete(k) __kstat_delete(k)
#else
#define kstat_create(m,i,n,c,t,s,f) (NULL)
#define kstat_install(k) ((void)0)
#define kstat_delete(k) ((void)0)
#endif /* DEBUG_KSTAT */
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -2,6 +2,23 @@
#define _SPL_PROC_H #define _SPL_PROC_H
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/kmod.h>
#include <linux/uaccess.h>
#include <linux/ctype.h>
#include <linux/sysctl.h>
#include <linux/seq_file.h>
#include <sys/sysmacros.h>
#include <sys/kmem.h>
#include <sys/mutex.h>
#include <sys/kstat.h>
#include <sys/debug.h>
#ifdef DEBUG_KSTAT
extern struct proc_dir_entry *proc_sys_spl_kstat;
struct proc_dir_entry *proc_dir_entry_find(struct proc_dir_entry *root,
const char *str);
int proc_dir_entries(struct proc_dir_entry *root);
#endif
int proc_init(void); int proc_init(void);
void proc_fini(void); void proc_fini(void);

View File

@ -23,6 +23,7 @@ spl-objs += spl-module.o
spl-objs += spl-generic.o spl-objs += spl-generic.o
spl-objs += spl-atomic.o spl-objs += spl-atomic.o
spl-objs += spl-mutex.o spl-objs += spl-mutex.o
spl-objs += spl-kstat.o
splmodule := spl.ko splmodule := spl.ko
splmoduledir := @kmoduledir@/kernel/lib/ splmoduledir := @kmoduledir@/kernel/lib/

View File

@ -5,6 +5,7 @@
#include <sys/mutex.h> #include <sys/mutex.h>
#include <sys/debug.h> #include <sys/debug.h>
#include <sys/proc.h> #include <sys/proc.h>
#include <sys/kstat.h>
#include <linux/kmod.h> #include <linux/kmod.h>
#include "config.h" #include "config.h"
@ -109,11 +110,16 @@ static int __init spl_init(void)
if ((rc = proc_init())) if ((rc = proc_init()))
GOTO(out4, rc); GOTO(out4, rc);
if ((rc = kstat_init()))
GOTO(out5, rc);
if ((rc = set_hostid())) if ((rc = set_hostid()))
GOTO(out5, rc = -EADDRNOTAVAIL); GOTO(out6, rc = -EADDRNOTAVAIL);
printk("SPL: Loaded Solaris Porting Layer v%s\n", VERSION); printk("SPL: Loaded Solaris Porting Layer v%s\n", VERSION);
RETURN(rc); RETURN(rc);
out6:
kstat_fini();
out5: out5:
proc_fini(); proc_fini();
out4: out4:
@ -135,6 +141,7 @@ static void spl_fini(void)
ENTRY; ENTRY;
printk("SPL: Unloaded Solaris Porting Layer v%s\n", VERSION); printk("SPL: Unloaded Solaris Porting Layer v%s\n", VERSION);
kstat_fini();
proc_fini(); proc_fini();
vn_fini(); vn_fini();
kmem_fini(); kmem_fini();

470
modules/spl/spl-kstat.c Normal file
View File

@ -0,0 +1,470 @@
#include <sys/kstat.h>
#ifdef DEBUG_KSTAT
static spinlock_t kstat_lock;
static struct list_head kstat_list;
static kid_t kstat_id;
static void
kstat_seq_show_headers(struct seq_file *f)
{
kstat_t *ksp = (kstat_t *)f->private;
ASSERT(ksp->ks_magic == KS_MAGIC);
seq_printf(f, "%d %d 0x%02x %d %d %lld %lld\n",
ksp->ks_kid, ksp->ks_type, ksp->ks_flags,
ksp->ks_ndata, (int)ksp->ks_data_size,
ksp->ks_crtime, ksp->ks_snaptime);
switch (ksp->ks_type) {
case KSTAT_TYPE_RAW:
seq_printf(f, "raw data");
break;
case KSTAT_TYPE_NAMED:
seq_printf(f, "%-31s %-4s %s\n",
"name", "type", "data");
break;
case KSTAT_TYPE_INTR:
seq_printf(f, "%-8s %-8s %-8s %-8s %-8s\n",
"hard", "soft", "watchdog",
"spurious", "multsvc");
break;
case KSTAT_TYPE_IO:
seq_printf(f,
"%-8s %-8s %-8s %-8s %-8s %-8s "
"%-8s %-8s %-8s %-8s %-8s %-8s\n",
"nread", "nwritten", "reads", "writes",
"wtime", "wlentime", "wupdate",
"rtime", "rlentime", "rupdate",
"wcnt", "rcnt");
break;
case KSTAT_TYPE_TIMER:
seq_printf(f,
"%-31s %-8s "
"%-8s %-8s %-8s %-8s %-8s\n",
"name", "events", "elapsed",
"min", "max", "start", "stop");
break;
default:
SBUG(); /* Unreachable */
}
}
static int
kstat_seq_show_raw(struct seq_file *f, unsigned char *p, int l)
{
int i, j;
for (i = 0; ; i++) {
seq_printf(f, "%03x:", i);
for (j = 0; j < 16; j++) {
if (i * 16 + j >= l) {
seq_printf(f, "\n");
goto out;
}
seq_printf(f, " %02x", (unsigned char)p[i * 16 + j]);
}
seq_printf(f, "\n");
}
out:
return 0;
}
static int
kstat_seq_show_named(struct seq_file *f, kstat_named_t *knp)
{
seq_printf(f, "%-31s %-4d ", knp->name, knp->data_type);
switch (knp->data_type) {
case KSTAT_DATA_CHAR:
knp->value.c[15] = '\0'; /* NULL terminate */
seq_printf(f, "%-16s", knp->value.c);
break;
/* XXX - We need to be more careful able what tokens are
* used for each arch, for now this is correct for x86_64.
*/
case KSTAT_DATA_INT32:
seq_printf(f, "%d", knp->value.i32);
break;
case KSTAT_DATA_UINT32:
seq_printf(f, "%u", knp->value.ui32);
break;
case KSTAT_DATA_INT64:
seq_printf(f, "%d", (int)knp->value.i64);
break;
case KSTAT_DATA_UINT64:
seq_printf(f, "%u", (unsigned int)knp->value.ui64);
break;
case KSTAT_DATA_LONG:
seq_printf(f, "%ld", knp->value.l);
break;
case KSTAT_DATA_ULONG:
seq_printf(f, "%lu", knp->value.l);
break;
case KSTAT_DATA_STRING:
KSTAT_NAMED_STR_PTR(knp)
[KSTAT_NAMED_STR_BUFLEN(knp)-1] = '\0';
seq_printf(f, "%s", KSTAT_NAMED_STR_PTR(knp));
break;
default:
SBUG(); /* Unreachable */
}
seq_printf(f, "\n");
return 0;
}
static int
kstat_seq_show_intr(struct seq_file *f, kstat_intr_t *kip)
{
seq_printf(f, "%-8u %-8u %-8u %-8u %-8u\n",
kip->intrs[KSTAT_INTR_HARD],
kip->intrs[KSTAT_INTR_SOFT],
kip->intrs[KSTAT_INTR_WATCHDOG],
kip->intrs[KSTAT_INTR_SPURIOUS],
kip->intrs[KSTAT_INTR_MULTSVC]);
return 0;
}
static int
kstat_seq_show_io(struct seq_file *f, kstat_io_t *kip)
{
seq_printf(f,
"%-8llu %-8llu %-8u %-8u %-8lld %-8lld "
"%-8lld %-8lld %-8lld %-8lld %-8u %-8u\n",
kip->nread, kip->nwritten,
kip->reads, kip->writes,
kip->wtime, kip->wlentime, kip->wlastupdate,
kip->rtime, kip->wlentime, kip->rlastupdate,
kip->wcnt, kip->rcnt);
return 0;
}
static int
kstat_seq_show_timer(struct seq_file *f, kstat_timer_t *ktp)
{
seq_printf(f,
"%-31s %-8llu %-8lld %-8lld %-8lld %-8lld %-8lld\n",
ktp->name, ktp->num_events, ktp->elapsed_time,
ktp->min_time, ktp->max_time,
ktp->start_time, ktp->stop_time);
return 0;
}
static int
kstat_seq_show(struct seq_file *f, void *p)
{
kstat_t *ksp = (kstat_t *)f->private;
int rc = 0;
ASSERT(ksp->ks_magic == KS_MAGIC);
switch (ksp->ks_type) {
case KSTAT_TYPE_RAW:
ASSERT(ksp->ks_ndata == 1);
rc = kstat_seq_show_raw(f, ksp->ks_data,
ksp->ks_data_size);
break;
case KSTAT_TYPE_NAMED:
rc = kstat_seq_show_named(f, (kstat_named_t *)p);
break;
case KSTAT_TYPE_INTR:
rc = kstat_seq_show_intr(f, (kstat_intr_t *)p);
break;
case KSTAT_TYPE_IO:
rc = kstat_seq_show_io(f, (kstat_io_t *)p);
break;
case KSTAT_TYPE_TIMER:
rc = kstat_seq_show_timer(f, (kstat_timer_t *)p);
break;
default:
SBUG(); /* Unreachable */
}
return rc;
}
static void *
kstat_seq_data_addr(kstat_t *ksp, loff_t n)
{
void *rc = NULL;
ENTRY;
switch (ksp->ks_type) {
case KSTAT_TYPE_RAW:
rc = ksp->ks_data;
break;
case KSTAT_TYPE_NAMED:
rc = ksp->ks_data + n * sizeof(kstat_named_t);
break;
case KSTAT_TYPE_INTR:
rc = ksp->ks_data + n * sizeof(kstat_intr_t);
break;
case KSTAT_TYPE_IO:
rc = ksp->ks_data + n * sizeof(kstat_io_t);
break;
case KSTAT_TYPE_TIMER:
rc = ksp->ks_data + n * sizeof(kstat_timer_t);
break;
default:
SBUG(); /* Unreachable */
}
RETURN(rc);
}
static void *
kstat_seq_start(struct seq_file *f, loff_t *pos)
{
loff_t n = *pos;
kstat_t *ksp = (kstat_t *)f->private;
ASSERT(ksp->ks_magic == KS_MAGIC);
ENTRY;
spin_lock(&ksp->ks_lock);
ksp->ks_snaptime = gethrtime();
if (!n)
kstat_seq_show_headers(f);
if (n >= ksp->ks_ndata)
RETURN(NULL);
RETURN(kstat_seq_data_addr(ksp, n));
}
static void *
kstat_seq_next(struct seq_file *f, void *p, loff_t *pos)
{
kstat_t *ksp = (kstat_t *)f->private;
ASSERT(ksp->ks_magic == KS_MAGIC);
ENTRY;
++*pos;
if (*pos >= ksp->ks_ndata)
RETURN(NULL);
RETURN(kstat_seq_data_addr(ksp, *pos));
}
static void
kstat_seq_stop(struct seq_file *f, void *v)
{
kstat_t *ksp = (kstat_t *)f->private;
ASSERT(ksp->ks_magic == KS_MAGIC);
spin_unlock(&ksp->ks_lock);
}
static struct seq_operations kstat_seq_ops = {
.show = kstat_seq_show,
.start = kstat_seq_start,
.next = kstat_seq_next,
.stop = kstat_seq_stop,
};
static int
proc_kstat_open(struct inode *inode, struct file *filp)
{
struct seq_file *f;
int rc;
rc = seq_open(filp, &kstat_seq_ops);
if (rc)
return rc;
f = filp->private_data;
f->private = PDE(inode)->data;
return rc;
}
static struct file_operations proc_kstat_operations = {
.open = proc_kstat_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
kstat_t *
__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,
uchar_t ks_flags)
{
kstat_t *ksp;
ASSERT(ks_module);
ASSERT(ks_instance == 0);
ASSERT(ks_name);
ASSERT(!(ks_flags & KSTAT_FLAG_UNSUPPORTED));
if ((ks_type == KSTAT_TYPE_INTR) || (ks_type == KSTAT_TYPE_IO))
ASSERT(ks_ndata == 1);
ksp = kmem_zalloc(sizeof(*ksp), KM_SLEEP);
if (ksp == NULL)
return ksp;
spin_lock(&kstat_lock);
ksp->ks_kid = kstat_id;
kstat_id++;
spin_unlock(&kstat_lock);
ksp->ks_magic = KS_MAGIC;
spin_lock_init(&ksp->ks_lock);
INIT_LIST_HEAD(&ksp->ks_list);
ksp->ks_crtime = gethrtime();
ksp->ks_snaptime = ksp->ks_crtime;
strncpy(ksp->ks_module, ks_module, KSTAT_STRLEN);
ksp->ks_instance = ks_instance;
strncpy(ksp->ks_name, ks_name, KSTAT_STRLEN);
strncpy(ksp->ks_class, ks_class, KSTAT_STRLEN);
ksp->ks_type = ks_type;
ksp->ks_flags = ks_flags;
switch (ksp->ks_type) {
case KSTAT_TYPE_RAW:
ksp->ks_ndata = 1;
ksp->ks_data_size = ks_ndata;
break;
case KSTAT_TYPE_NAMED:
ksp->ks_ndata = ks_ndata;
ksp->ks_data_size = ks_ndata * sizeof(kstat_named_t);
break;
case KSTAT_TYPE_INTR:
ksp->ks_ndata = ks_ndata;
ksp->ks_data_size = ks_ndata * sizeof(kstat_intr_t);
break;
case KSTAT_TYPE_IO:
ksp->ks_ndata = ks_ndata;
ksp->ks_data_size = ks_ndata * sizeof(kstat_io_t);
break;
case KSTAT_TYPE_TIMER:
ksp->ks_ndata = ks_ndata;
ksp->ks_data_size = ks_ndata * sizeof(kstat_timer_t);
break;
default:
SBUG(); /* Unreachable */
}
if (ksp->ks_flags & KSTAT_FLAG_VIRTUAL) {
ksp->ks_data = NULL;
} else {
ksp->ks_data = kmem_alloc(ksp->ks_data_size, KM_SLEEP);
if (ksp->ks_data == NULL) {
kmem_free(ksp, sizeof(*ksp));
ksp = NULL;
}
}
return ksp;
}
EXPORT_SYMBOL(__kstat_create);
void
__kstat_install(kstat_t *ksp)
{
struct proc_dir_entry *de_module, *de_name;
kstat_t *tmp;
int rc = 0;
ENTRY;
spin_lock(&kstat_lock);
/* Item may only be added to the list once */
list_for_each_entry(tmp, &kstat_list, ks_list) {
if (tmp == ksp) {
spin_unlock(&kstat_lock);
GOTO(out, rc = -EEXIST);
}
}
list_add_tail(&ksp->ks_list, &kstat_list);
spin_unlock(&kstat_lock);
de_module = proc_dir_entry_find(proc_sys_spl_kstat, ksp->ks_module);
if (de_module == NULL) {
de_module = proc_mkdir(ksp->ks_module, proc_sys_spl_kstat);
if (de_module == NULL)
GOTO(out, rc = -EUNATCH);
}
de_name = create_proc_entry(ksp->ks_name, 0444, de_module);
if (de_name == NULL)
GOTO(out, rc = -EUNATCH);
spin_lock(&ksp->ks_lock);
ksp->ks_proc = de_name;
de_name->proc_fops = &proc_kstat_operations;
de_name->data = (void *)ksp;
spin_unlock(&ksp->ks_lock);
out:
if (rc) {
spin_lock(&kstat_lock);
list_del_init(&ksp->ks_list);
spin_unlock(&kstat_lock);
}
EXIT;
}
EXPORT_SYMBOL(__kstat_install);
void
__kstat_delete(kstat_t *ksp)
{
struct proc_dir_entry *de_module;
spin_lock(&kstat_lock);
list_del_init(&ksp->ks_list);
spin_unlock(&kstat_lock);
if (ksp->ks_proc) {
de_module = ksp->ks_proc->parent;
remove_proc_entry(ksp->ks_name, de_module);
/* Remove top level module directory if it's empty */
if (proc_dir_entries(de_module) == 0)
remove_proc_entry(de_module->name, de_module->parent);
}
if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL))
kmem_free(ksp->ks_data, ksp->ks_data_size);
kmem_free(ksp, sizeof(*ksp));
return;
}
EXPORT_SYMBOL(__kstat_delete);
#endif /* DEBUG_KSTAT */
int
kstat_init(void)
{
ENTRY;
#ifdef DEBUG_KSTAT
spin_lock_init(&kstat_lock);
INIT_LIST_HEAD(&kstat_list);
kstat_id = 0;
#endif /* DEBUG_KSTAT */
RETURN(0);
}
void
kstat_fini(void)
{
ENTRY;
#ifdef DEBUG_KSTAT
ASSERT(list_empty(&kstat_list));
#endif /* DEBUG_KSTAT */
EXIT;
}

View File

@ -1,14 +1,4 @@
#include <linux/proc_fs.h> #include <sys/proc.h>
#include <linux/kmod.h>
#include <linux/uaccess.h>
#include <linux/ctype.h>
#include <linux/sysctl.h>
#include <linux/seq_file.h>
#include <sys/sysmacros.h>
#include <sys/kmem.h>
#include <sys/mutex.h>
#include <sys/debug.h>
#include "config.h"
#ifdef DEBUG_SUBSYSTEM #ifdef DEBUG_SUBSYSTEM
#undef DEBUG_SUBSYSTEM #undef DEBUG_SUBSYSTEM
@ -29,12 +19,19 @@ static struct proc_dir_entry *proc_sys_spl = NULL;
static struct proc_dir_entry *proc_sys_spl_mutex = NULL; static struct proc_dir_entry *proc_sys_spl_mutex = NULL;
static struct proc_dir_entry *proc_sys_spl_mutex_stats = NULL; static struct proc_dir_entry *proc_sys_spl_mutex_stats = NULL;
#endif #endif
#ifdef DEBUG_KMEM
static struct proc_dir_entry *proc_sys_spl_kmem = NULL;
#endif
#ifdef DEBUG_KSTAT
struct proc_dir_entry *proc_sys_spl_kstat = NULL;
#endif
#endif #endif
#define CTL_SPL 0x87 #define CTL_SPL 0x87
#define CTL_SPL_DEBUG 0x88 #define CTL_SPL_DEBUG 0x88
#define CTL_SPL_MUTEX 0x89 #define CTL_SPL_MUTEX 0x89
#define CTL_SPL_KMEM 0x90 #define CTL_SPL_KMEM 0x90
#define CTL_SPL_KSTAT 0x91
enum { enum {
CTL_VERSION = 1, /* Version */ CTL_VERSION = 1, /* Version */
@ -665,7 +662,13 @@ static struct ctl_table spl_kmem_table[] = {
}, },
{0}, {0},
}; };
#endif /* DEBUG_MUTEX */ #endif /* DEBUG_KMEM */
#ifdef DEBUG_KSTAT
static struct ctl_table spl_kstat_table[] = {
{0},
};
#endif /* DEBUG_KSTAT */
static struct ctl_table spl_table[] = { static struct ctl_table spl_table[] = {
/* NB No .strategy entries have been provided since /* NB No .strategy entries have been provided since
@ -716,6 +719,14 @@ static struct ctl_table spl_table[] = {
.mode = 0555, .mode = 0555,
.child = spl_kmem_table, .child = spl_kmem_table,
}, },
#endif
#ifdef DEBUG_KSTAT
{
.ctl_name = CTL_SPL_KSTAT,
.procname = "kstat",
.mode = 0555,
.child = spl_kstat_table,
},
#endif #endif
{ 0 }, { 0 },
}; };
@ -739,7 +750,7 @@ proc_dir_entry_match(int len, const char *name, struct proc_dir_entry *de)
return !memcmp(name, de->name, len); return !memcmp(name, de->name, len);
} }
static struct proc_dir_entry * struct proc_dir_entry *
proc_dir_entry_find(struct proc_dir_entry *root, const char *str) proc_dir_entry_find(struct proc_dir_entry *root, const char *str)
{ {
struct proc_dir_entry *de; struct proc_dir_entry *de;
@ -751,6 +762,18 @@ proc_dir_entry_find(struct proc_dir_entry *root, const char *str)
return NULL; return NULL;
} }
int
proc_dir_entries(struct proc_dir_entry *root)
{
struct proc_dir_entry *de;
int i = 0;
for (de = root->subdir; de; de = de->next)
i++;
return i;
}
int int
proc_init(void) proc_init(void)
{ {
@ -782,7 +805,22 @@ proc_init(void)
proc_sys_spl_mutex_stats->proc_fops = &proc_mutex_operations; proc_sys_spl_mutex_stats->proc_fops = &proc_mutex_operations;
#endif /* DEBUG_MUTEX */ #endif /* DEBUG_MUTEX */
#ifdef DEBUG_KMEM
proc_sys_spl_kmem = proc_dir_entry_find(proc_sys_spl, "kmem");
if (proc_sys_spl_kmem == NULL)
GOTO(out2, rc = -EUNATCH);
#endif /* DEBUG_KMEM */
#ifdef DEBUG_KSTAT
proc_sys_spl_kstat = proc_dir_entry_find(proc_sys_spl, "kstat");
if (proc_sys_spl_kstat == NULL)
GOTO(out2, rc = -EUNATCH);
#endif /* DEBUG_KSTAT */
RETURN(rc); RETURN(rc);
out2:
remove_proc_entry("stats_per", proc_sys_spl_mutex);
out: out:
unregister_sysctl_table(spl_header); unregister_sysctl_table(spl_header);
#endif /* CONFIG_SYSCTL */ #endif /* CONFIG_SYSCTL */