diff --git a/include/sys/kstat.h b/include/sys/kstat.h index 0b79a41c04..1ce084729f 100644 --- a/include/sys/kstat.h +++ b/include/sys/kstat.h @@ -5,130 +5,151 @@ extern "C" { #endif +#define DEBUG_KSTAT + #include #include #include +#include +#include -/* 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 */ - -#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 */ +/* 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 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_DATA_CHAR 0 #define KSTAT_DATA_INT32 1 #define KSTAT_DATA_UINT32 2 #define KSTAT_DATA_INT64 3 #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_VAR_SIZE 0x02 +#define KSTAT_FLAG_WRITABLE 0x04 +#define KSTAT_FLAG_PERSISTENT 0x08 +#define KSTAT_FLAG_DORMANT 0x10 +#define KSTAT_FLAG_UNSUPPORTED (KSTAT_FLAG_VAR_SIZE | KSTAT_FLAG_WRITABLE | \ + KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_DORMANT) -#define KSTAT_FLAG_VIRTUAL 0x01 -#define KSTAT_FLAG_VAR_SIZE 0x02 -#define KSTAT_FLAG_WRITABLE 0x04 -#define KSTAT_FLAG_PERSISTENT 0x08 -#define KSTAT_FLAG_DORMANT 0x10 -#define KSTAT_FLAG_INVALID 0x2 +#define KS_MAGIC 0x9d9d9d9d - -typedef int kid_t; /* unique kstat id */ +typedef int kid_t; /* unique kstat id */ typedef struct kstat_s { - /* - * 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 */ - char ks_module[KSTAT_STRLEN]; /* provider module name */ - uchar_t ks_resv; /* reserved, currently just padding */ - int ks_instance; /* provider module's instance */ - char ks_name[KSTAT_STRLEN]; /* kstat name */ - uchar_t ks_type; /* kstat data type */ - char ks_class[KSTAT_STRLEN]; /* kstat class */ - uchar_t ks_flags; /* kstat flags */ - void *ks_data; /* kstat type-specific data */ - uint_t ks_ndata; /* # of type-specific data records */ - size_t ks_data_size; /* total size of kstat data section */ - hrtime_t ks_snaptime; /* time of last data shapshot */ - /* - * Fields relevant to kernel only - */ - 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 */ + int ks_magic; /* magic value */ + kid_t ks_kid; /* unique kstat ID */ + hrtime_t ks_crtime; /* creation time */ + hrtime_t ks_snaptime; /* last access time */ + char ks_module[KSTAT_STRLEN+1]; /* provider module 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_flags; /* kstat flags */ + void *ks_data; /* kstat type-specific data */ + uint_t ks_ndata; /* # of type-specific data records */ + size_t ks_data_size; /* size of kstat data section */ + struct proc_dir_entry *ks_proc; /* proc linkage */ + spinlock_t ks_lock; /* kstat data lock */ + struct list_head ks_list; /* kstat linkage */ } kstat_t; typedef struct kstat_named_s { - char name[KSTAT_STRLEN]; /* name of counter */ - uchar_t data_type; /* data type */ + char name[KSTAT_STRLEN]; /* name of counter */ + uchar_t data_type; /* data type */ union { - char c[16]; /* enough for 128-bit ints */ - int32_t i32; - uint32_t ui32; + char c[16]; /* 128-bit int */ + int32_t i32; /* 32-bit signed int */ + 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 { union { - char *ptr; /* NULL-term string */ - char __pad[8]; /* 64-bit padding */ + char *ptr; /* NULL-term string */ + char __pad[8]; /* 64-bit padding */ } addr; - uint32_t len; /* # bytes for strlen + '\0' */ - } str; -/* - * 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 */ + uint32_t len; /* # bytes for strlen + '\0' */ + } string; + } value; } 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 * -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) -{ - return NULL; -} +typedef struct kstat_intr { + uint_t intrs[KSTAT_NUM_INTRS]; +} kstat_intr_t; -static __inline__ void -kstat_install(kstat_t *ksp) -{ - return; -} +typedef struct kstat_io { + 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 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 -kstat_delete(kstat_t *ksp) -{ - return; -} +typedef struct kstat_timer { + char name[KSTAT_STRLEN+1]; /* event name */ + u_longlong_t num_events; /* number of events */ + 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 } diff --git a/include/sys/proc.h b/include/sys/proc.h index e77ea5fb29..e76e4dbfa6 100644 --- a/include/sys/proc.h +++ b/include/sys/proc.h @@ -2,6 +2,23 @@ #define _SPL_PROC_H #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); void proc_fini(void); diff --git a/modules/spl/Makefile.in b/modules/spl/Makefile.in index bd2a5f9f2d..230de9166a 100644 --- a/modules/spl/Makefile.in +++ b/modules/spl/Makefile.in @@ -23,6 +23,7 @@ spl-objs += spl-module.o spl-objs += spl-generic.o spl-objs += spl-atomic.o spl-objs += spl-mutex.o +spl-objs += spl-kstat.o splmodule := spl.ko splmoduledir := @kmoduledir@/kernel/lib/ diff --git a/modules/spl/spl-generic.c b/modules/spl/spl-generic.c index 99497dd512..0ca0061ab4 100644 --- a/modules/spl/spl-generic.c +++ b/modules/spl/spl-generic.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "config.h" @@ -109,11 +110,16 @@ static int __init spl_init(void) if ((rc = proc_init())) GOTO(out4, rc); + if ((rc = kstat_init())) + GOTO(out5, rc); + if ((rc = set_hostid())) - GOTO(out5, rc = -EADDRNOTAVAIL); + GOTO(out6, rc = -EADDRNOTAVAIL); printk("SPL: Loaded Solaris Porting Layer v%s\n", VERSION); RETURN(rc); +out6: + kstat_fini(); out5: proc_fini(); out4: @@ -135,6 +141,7 @@ static void spl_fini(void) ENTRY; printk("SPL: Unloaded Solaris Porting Layer v%s\n", VERSION); + kstat_fini(); proc_fini(); vn_fini(); kmem_fini(); diff --git a/modules/spl/spl-kstat.c b/modules/spl/spl-kstat.c new file mode 100644 index 0000000000..3f547c6016 --- /dev/null +++ b/modules/spl/spl-kstat.c @@ -0,0 +1,470 @@ +#include + +#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; +} + diff --git a/modules/spl/spl-proc.c b/modules/spl/spl-proc.c index 1b9787a792..07e18102ae 100644 --- a/modules/spl/spl-proc.c +++ b/modules/spl/spl-proc.c @@ -1,14 +1,4 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "config.h" +#include #ifdef 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_stats = NULL; #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 #define CTL_SPL 0x87 #define CTL_SPL_DEBUG 0x88 #define CTL_SPL_MUTEX 0x89 #define CTL_SPL_KMEM 0x90 +#define CTL_SPL_KSTAT 0x91 enum { CTL_VERSION = 1, /* Version */ @@ -665,7 +662,13 @@ static struct ctl_table spl_kmem_table[] = { }, {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[] = { /* NB No .strategy entries have been provided since @@ -716,6 +719,14 @@ static struct ctl_table spl_table[] = { .mode = 0555, .child = spl_kmem_table, }, +#endif +#ifdef DEBUG_KSTAT + { + .ctl_name = CTL_SPL_KSTAT, + .procname = "kstat", + .mode = 0555, + .child = spl_kstat_table, + }, #endif { 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); } -static struct proc_dir_entry * +struct proc_dir_entry * proc_dir_entry_find(struct proc_dir_entry *root, const char *str) { struct proc_dir_entry *de; @@ -751,6 +762,18 @@ proc_dir_entry_find(struct proc_dir_entry *root, const char *str) 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 proc_init(void) { @@ -782,7 +805,22 @@ proc_init(void) proc_sys_spl_mutex_stats->proc_fops = &proc_mutex_operations; #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); +out2: + remove_proc_entry("stats_per", proc_sys_spl_mutex); out: unregister_sysctl_table(spl_header); #endif /* CONFIG_SYSCTL */