]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Add callbacks for displaying KSTAT_TYPE_RAW kstats
authorPrakash Surya <surya1@llnl.gov>
Thu, 12 Sep 2013 23:14:51 +0000 (16:14 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Wed, 16 Oct 2013 21:48:35 +0000 (14:48 -0700)
The current implementation for displaying kstats of type KSTAT_TYPE_RAW
is rather crude. This patch attempts to enhance this handling by
allowing a kstat user to register formatting callbacks which can
optionally be used.

The callbacks allow the user to implement functions for interpreting
their data and transposing it into a character buffer. This buffer,
containing a string representation of the raw data, is then be displayed
through the current /proc textual interface.

Additionally the kstats are made writable because it's now possible
to provide a useful handler via the existing ks_update() interface.

Signed-off-by: Prakash Surya <surya1@llnl.gov>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Issue #296

include/sys/kstat.h
module/spl/spl-kstat.c

index da3c5899db0fedac26df5fddea06fc7fd0cf9718..75b384714c2ca458c4ef0d417fe120817ab16833 100644 (file)
@@ -33,6 +33,7 @@
 #include <sys/mutex.h>
 
 #define KSTAT_STRLEN            31
+#define KSTAT_RAW_MAX          (128*1024)
 
 /* For reference valid classes are:
  * disk, tape, net, controller, vm, kvm, hat, streams, kstat, misc
@@ -79,6 +80,7 @@
 #define KSTAT_WRITE             1
 
 struct kstat_s;
+typedef struct kstat_s kstat_t;
 
 typedef int kid_t;                                  /* unique kstat id */
 typedef int kstat_update_t(struct kstat_s *, int);  /* dynamic update cb */
@@ -90,7 +92,13 @@ typedef struct kstat_module {
        struct proc_dir_entry *ksm_proc;            /* proc entry */
 } 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 */
         kid_t            ks_kid;                    /* unique kstat ID */
         hrtime_t         ks_crtime;                 /* creation time */
@@ -110,7 +118,10 @@ typedef struct kstat_s {
         kmutex_t         ks_lock;                   /* kstat data lock */
         struct list_head ks_list;                   /* kstat 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 {
         char             name[KSTAT_STRLEN];        /* name of counter */
@@ -188,6 +199,10 @@ typedef struct kstat_txg {
 int spl_kstat_init(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,
                             const char *ks_name, const char *ks_class,
                             uchar_t ks_type, uint_t ks_ndata,
@@ -195,6 +210,7 @@ extern kstat_t *__kstat_create(const char *ks_module, int ks_instance,
 extern void __kstat_install(kstat_t *ksp);
 extern void __kstat_delete(kstat_t *ksp);
 
+#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_install(k)               __kstat_install(k)
 #define kstat_delete(k)                        __kstat_delete(k)
index 4e900c066a41eb076cee71a5e8dd9fb3d09990c6..57aabe2d0290c039b92f32c7fd2452b35f6f3067 100644 (file)
@@ -41,10 +41,25 @@ static kmutex_t kstat_module_lock;
 static struct list_head kstat_module_list;
 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;
+}
+
+static int
 kstat_seq_show_headers(struct seq_file *f)
 {
         kstat_t *ksp = (kstat_t *)f->private;
+       int rc = 0;
+
         ASSERT(ksp->ks_magic == KS_MAGIC);
 
         seq_printf(f, "%d %d 0x%02x %d %d %lld %lld\n",
@@ -54,7 +69,17 @@ kstat_seq_show_headers(struct seq_file *f)
 
        switch (ksp->ks_type) {
                 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;
                 case KSTAT_TYPE_NAMED:
                         seq_printf(f, "%-31s %-4s %s\n",
@@ -92,6 +117,8 @@ kstat_seq_show_headers(struct seq_file *f)
                 default:
                         PANIC("Undefined kstat type %d\n", ksp->ks_type);
         }
+
+       return -rc;
 }
 
 static int
@@ -232,9 +259,19 @@ kstat_seq_show(struct seq_file *f, void *p)
 
        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);
+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);
+                                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);
@@ -255,13 +292,17 @@ kstat_seq_show(struct seq_file *f, void *p)
                         PANIC("Undefined kstat type %d\n", ksp->ks_type);
         }
 
-        return rc;
+        return -rc;
 }
 
 int
 kstat_default_update(kstat_t *ksp, int rw)
 {
        ASSERT(ksp != NULL);
+
+       if (rw == KSTAT_WRITE)
+               return (EACCES);
+
        return 0;
 }
 
@@ -273,7 +314,10 @@ kstat_seq_data_addr(kstat_t *ksp, loff_t n)
 
        switch (ksp->ks_type) {
                 case KSTAT_TYPE_RAW:
-                       rc = ksp->ks_data;
+                        if (ksp->ks_raw_ops.addr)
+                                rc = ksp->ks_raw_ops.addr(ksp, n);
+                        else
+                                rc = ksp->ks_data;
                         break;
                 case KSTAT_TYPE_NAMED:
                         rc = ksp->ks_data + n * sizeof(kstat_named_t);
@@ -307,13 +351,18 @@ kstat_seq_start(struct seq_file *f, loff_t *pos)
 
         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 */
         (void) ksp->ks_update(ksp, KSTAT_READ);
 
        ksp->ks_snaptime = gethrtime();
 
-        if (!n)
-                kstat_seq_show_headers(f);
+        if (!n && kstat_seq_show_headers(f))
+               SRETURN(NULL);
 
         if (n >= ksp->ks_ndata)
                 SRETURN(NULL);
@@ -341,6 +390,9 @@ kstat_seq_stop(struct seq_file *f, void *v)
         kstat_t *ksp = (kstat_t *)f->private;
         ASSERT(ksp->ks_magic == KS_MAGIC);
 
+       if (ksp->ks_type == KSTAT_TYPE_RAW)
+               vmem_free(ksp->ks_raw_buf, ksp->ks_raw_bufsize);
+
         mutex_exit(&ksp->ks_lock);
 }
 
@@ -408,13 +460,47 @@ proc_kstat_open(struct inode *inode, struct file *filp)
         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 = {
-        .open           = proc_kstat_open,
-        .read           = seq_read,
-        .llseek         = seq_lseek,
-        .release        = seq_release,
+       .open           = proc_kstat_open,
+       .write          = proc_kstat_write,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .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_create(const char *ks_module, int ks_instance, const char *ks_name,
              const char *ks_class, uchar_t ks_type, uint_t ks_ndata,
@@ -453,6 +539,11 @@ __kstat_create(const char *ks_module, int ks_instance, const char *ks_name,
        ksp->ks_flags = ks_flags;
        ksp->ks_update = kstat_default_update;
        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) {
                 case KSTAT_TYPE_RAW:
@@ -526,7 +617,7 @@ __kstat_install(kstat_t *ksp)
 
        mutex_enter(&ksp->ks_lock);
        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);
        if (ksp->ks_proc == NULL) {
                list_del_init(&ksp->ks_list);