]> granicus.if.org Git - spl/commitdiff
Add an almost feature complete implemenation of kstat. I chose
authorbehlendo <behlendo@7e1ea52c-4ff2-0310-8f11-9dd32ca42a1c>
Thu, 8 May 2008 23:21:47 +0000 (23:21 +0000)
committerbehlendo <behlendo@7e1ea52c-4ff2-0310-8f11-9dd32ca42a1c>
Thu, 8 May 2008 23:21:47 +0000 (23:21 +0000)
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

include/sys/kstat.h
include/sys/proc.h
modules/spl/Makefile.in
modules/spl/spl-generic.c
modules/spl/spl-kstat.c [new file with mode: 0644]
modules/spl/spl-proc.c

index 0b79a41c04e6068cf5efb0a8e35ace82431c3f79..1ce084729fa15cb41f7b0609d6e26f172d92ae37 100644 (file)
 extern "C" {
 #endif
 
+#define DEBUG_KSTAT
+
 #include <linux/module.h>
 #include <sys/types.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      /* 30 chars + NULL; must be 16 * n - 1 */
+#define KSTAT_STRLEN            31
 
-#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;
 
-
-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;
-}
-
-static __inline__ void
-kstat_install(kstat_t *ksp)
-{
-       return;
-}
-
-static __inline__ void
-kstat_delete(kstat_t *ksp)
-{
-       return;
-}
+#define KSTAT_NAMED_STR_PTR(knptr) ((knptr)->value.string.addr.ptr)
+#define KSTAT_NAMED_STR_BUFLEN(knptr) ((knptr)->value.string.len)
+
+typedef struct kstat_intr {
+        uint_t intrs[KSTAT_NUM_INTRS];
+} kstat_intr_t;
+
+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;
+
+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
 }
index e77ea5fb29360f7a062df2a1bc52eb647ed191d8..e76e4dbfa623f8c0999e8ce248bb375231fb05fe 100644 (file)
@@ -2,6 +2,23 @@
 #define _SPL_PROC_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);
 void proc_fini(void);
index bd2a5f9f2d75cf48e4a5ee5fab1e6ffd4c64b361..230de9166ac6cecbe7a89907efdbe937268ec7ab 100644 (file)
@@ -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/
index 99497dd512418049aedab6a32effb9cea019db04..0ca0061ab4b5b69abbbd1fab7e84cc8f982bdd09 100644 (file)
@@ -5,6 +5,7 @@
 #include <sys/mutex.h>
 #include <sys/debug.h>
 #include <sys/proc.h>
+#include <sys/kstat.h>
 #include <linux/kmod.h>
 #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 (file)
index 0000000..3f547c6
--- /dev/null
@@ -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;
+}
+
index 1b9787a79263f164ad21e2990865665c6b87ad54..07e18102aefc96bb12dbb24bbf3445f3b13944d8 100644 (file)
@@ -1,14 +1,4 @@
-#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/debug.h>
-#include "config.h"
+#include <sys/proc.h>
 
 #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 */