creates the qib_trace.c file.

Signed-off-by: Ralph Campbell <ralph.campb...@qlogic.com>
---

 drivers/infiniband/hw/qib/qib_trace.c | 1346 +++++++++++++++++++++++++++++++++
 1 files changed, 1346 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/hw/qib/qib_trace.c

diff --git a/drivers/infiniband/hw/qib/qib_trace.c 
b/drivers/infiniband/hw/qib/qib_trace.c
new file mode 100644
index 0000000..9df230a
--- /dev/null
+++ b/drivers/infiniband/hw/qib/qib_trace.c
@@ -0,0 +1,1346 @@
+/*
+ * Copyright (c) 2008, 2009 QLogic Corporation. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/hardirq.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/vmalloc.h>
+#include <linux/uaccess.h>
+#include <linux/notifier.h>
+
+#include "qib.h"
+
+#define QIB_TRACE_FILE         "ipath_trace"
+#define QIB_TRACE_MAXCACHENAME 32
+
+enum qib_trace_cmd_type {
+       IPATH_TRACE_CLEAR = 0,
+       IPATH_TRACE_RESTART,
+       IPATH_TRACE_DUMP,
+       IPATH_TRACE_DUMP_AND_CLEAR,
+       IPATH_TRACE_GETSIZE,
+       IPATH_TRACE_INJECT
+};
+
+enum qib_evt_type {
+       QIB_EVT_NONE = 0,
+       QIB_EVT_U8,
+       QIB_EVT_U16,
+       QIB_EVT_U32,
+       QIB_EVT_U64,
+       QIB_EVT_STR,
+       QIB_EVT_BLOB,
+       QIB_EVT_EMPTY
+};
+
+enum qib_evt_blobtype {
+       QIB_EVT_BLOB_QP = 0,
+       QIB_EVT_BLOB_CQ,
+       QIB_EVT_BLOB_SRQ
+};
+
+struct qib_trace_cmd {
+       enum qib_trace_cmd_type type;
+
+       union {
+               /* event cursor where we should restart from */
+               unsigned int cursor;
+
+               /* user address for storing the size of the trace buffer */
+               struct {
+                       unsigned int __user *bufsize;
+                       unsigned int __user *blobsize;
+               };
+
+               struct {
+                       void __user *buf;
+                       unsigned int size;
+               };
+       };
+};
+
+struct qib_evt_val {
+       enum qib_evt_type type;
+       size_t len;
+
+       union {
+               u8      d8;
+               u16     d16;
+               u32     d32;
+               u64     d64;
+               const void *blob;
+       };
+};
+
+/*
+ * Event holder
+ */
+struct qib_evt {
+       u64 tsc;
+       u32 dbgmask;
+       u16 len;
+       u16 cpu_type;
+       u64 id;
+       u64 data[0];
+};
+
+struct qib_evt_container {
+       atomic_t refcnt;
+
+       /* needs to be at the end of the struct as blob data is inlined here */
+       struct qib_evt evt;
+};
+
+static inline void qib_evt_settsc(struct qib_evt *evt, u64 tsc)
+{
+       evt->tsc = tsc;
+}
+
+static inline void qib_evt_setdbgmask(struct qib_evt *evt, u32 dbgmask)
+{
+       evt->dbgmask = dbgmask;
+}
+
+static inline void qib_evt_setlen(struct qib_evt *evt, u16 len)
+{
+       evt->len = len;
+}
+
+static inline void qib_evt_setcpu(struct qib_evt *evt, int cpu)
+{
+       evt->cpu_type = (evt->cpu_type & 0xff) | ((cpu & 0xff) << 8);
+}
+
+static inline void qib_evt_setblobtype(struct qib_evt *evt,
+                                      enum qib_evt_blobtype bt)
+{
+       evt->cpu_type = (evt->cpu_type & 0xff07) | ((bt & 0x1f) << 3);
+}
+
+static inline void qib_evt_settype(struct qib_evt *evt, enum qib_evt_type type)
+{
+       evt->cpu_type = (evt->cpu_type & 0xfff8) | (type & 0x7);
+}
+
+static inline void qib_evt_setid(struct qib_evt *evt, u64 id)
+{
+       evt->id = id;
+}
+
+static inline u64 qib_evt_gettsc(struct qib_evt *evt)
+{
+       return evt->tsc;
+}
+
+static inline u32 qib_evt_getdbgmask(struct qib_evt *evt)
+{
+       return evt->dbgmask;
+}
+
+static inline u16 qib_evt_getlen(struct qib_evt *evt)
+{
+       return evt->len;
+}
+
+static inline size_t qib_evt_getsize(struct qib_evt *evt)
+{
+       return sizeof(struct qib_evt) + evt->len;
+}
+
+static inline int qib_evt_getcpu(struct qib_evt *evt)
+{
+       return evt->cpu_type >> 8;
+}
+
+static inline enum qib_evt_blobtype qib_evt_getblobtype(struct qib_evt *evt)
+{
+       return evt->cpu_type >> 3 & 0x1f;
+}
+
+static inline enum qib_evt_type qib_evt_gettype(struct qib_evt *evt)
+{
+       return evt->cpu_type & 0x7;
+}
+
+static inline u64 qib_evt_getid(struct qib_evt *evt)
+{
+       return evt->id;
+}
+
+/*
+ * Event Ring Buffer
+ */
+struct qib_evt_buf {
+       const char *name;
+
+       /* ring buffer is stored here */
+       struct qib_evt_container **array;
+
+       u64 evt_id;
+
+       /* index of the next available spot in the ring buffer */
+       unsigned int cursor;
+
+       /*
+        * spinlock protects all concurrent activity for the
+        * above fields
+        */
+       spinlock_t lock;
+
+       struct kmem_cache *evt_cache;
+       char *cache_name;
+};
+
+/*
+ * File descriptor specific tracking
+ */
+struct qib_evt_file {
+       /* private cursor -- points to the next event to be read */
+       unsigned int cursor;
+
+       /*
+        * Bit that indicates whether there's any pending data to be read
+        */
+#define EVT_FLAG_DATA_TO_READ   (1 << 0)
+
+       int flags;
+
+       /* statistic -- number of events returned for each read(2) */
+       unsigned int nevents;
+};
+
+struct evt_trace_device {
+       struct cdev *cdev;
+       struct device *device;
+};
+
+static int qib_trace_set_bufsize(const char *val, struct kernel_param *kp);
+static int qib_trace_set_maxblobsize(const char *val, struct kernel_param *kp);
+static int qib_trace_open(struct inode *inode, struct file *filp);
+static int qib_trace_close(struct inode *inode, struct file *filp);
+static ssize_t qib_trace_read(struct file *file, char __user *ubuf,
+                             size_t nbytes, loff_t *ppos);
+static ssize_t qib_trace_write(struct file *file, const char __user *ubuf,
+                              size_t nbytes, loff_t *ppos);
+
+/*
+ * One global ring buffer for the driver.
+ */
+struct qib_evt_buf *qib_trace_buf;
+
+static const struct file_operations qib_trace_fops = {
+       .owner = THIS_MODULE,
+       .open = qib_trace_open,
+       .release = qib_trace_close,
+       .read = qib_trace_read,
+       .write = qib_trace_write
+};
+
+static struct evt_trace_device evt_dev;
+
+/*
+ * Sleeping queue when no events are available
+ */
+static DECLARE_WAIT_QUEUE_HEAD(read_wait);
+
+static unsigned int qib_trace_bufsize = 512;
+module_param_call(trace_bufsize, qib_trace_set_bufsize, param_get_uint,
+                 &qib_trace_bufsize, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(trace_bufsize,
+                "size of the event trace ring buffer (default=512 events)");
+
+static unsigned int maxblobsize = 256;
+module_param_call(trace_maxblobsize, qib_trace_set_maxblobsize, param_get_uint,
+                 &maxblobsize, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(trace_maxblobsize,
+                "max size of each blob (default=256 bytes)");
+
+static unsigned int qib_trace_howmany;
+module_param_named(trace_howmany, qib_trace_howmany, uint, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(trace_howmany, "number of events returned by read(2) "
+                "(default=0, 0 means unlimited)");
+
+static unsigned int qib_trace_debug;
+module_param_named(trace_debug, qib_trace_debug, uint, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(trace_debug, "enable debugging (default=0)");
+
+#define EPRINTK(fmt, ...)                                              \
+       printk(KERN_ERR QIB_DRV_NAME ": " fmt, ##__VA_ARGS__)
+
+#define DPRINTK(fmt, ...)                                              \
+       do {                                                            \
+               if (unlikely(qib_trace_debug != 0))                     \
+                       printk(KERN_INFO "%s: " fmt, __func__, ##__VA_ARGS__); \
+       } while (0)
+
+static const char *qib_evt_str(enum qib_evt_type type)
+{
+       switch (type) {
+       case QIB_EVT_U8:
+               return "u8";
+       case QIB_EVT_U16:
+               return "u16";
+       case QIB_EVT_U32:
+               return "u32";
+       case QIB_EVT_U64:
+               return "u64";
+       case QIB_EVT_STR:
+               return "str";
+       case QIB_EVT_BLOB:
+               return "blob";
+       default:
+               return "unknown";
+       }
+}
+
+static void qib_evt_printk(struct qib_evt *evt)
+{
+       unsigned int i;
+
+       switch (qib_evt_gettype(evt)) {
+       case QIB_EVT_U8:
+               printk(KERN_EMERG "0x%01x\n", *((u8 *) evt->data));
+               break;
+       case QIB_EVT_U16:
+               printk(KERN_EMERG "0x%02x\n", *((u16 *) evt->data));
+               break;
+       case QIB_EVT_U32:
+               printk(KERN_EMERG "0x%04x\n", *((u32 *) evt->data));
+               break;
+       case QIB_EVT_U64:
+               printk(KERN_EMERG "0x%08llx\n",
+                       (unsigned long long) *((u64 *) evt->data));
+               break;
+       case QIB_EVT_STR:
+               printk(KERN_EMERG "%s", (char *) evt->data);
+               break;
+       case QIB_EVT_BLOB: {
+               u8 *c = (u8 *) evt->data;
+
+               for (i = 0; i < evt->len; i++)
+                       printk(KERN_EMERG "0x%02x\n", *c);
+               printk(KERN_EMERG "\n");
+               break;
+       }
+       default:
+               break;
+       }
+}
+
+/*
+ * Allocate an event container
+ */
+static struct qib_evt_container *qib_evt_alloc(struct qib_evt_buf *buf)
+{
+       struct qib_evt_container *c;
+
+       c = kmem_cache_alloc(buf->evt_cache, GFP_ATOMIC);
+       if (c) {
+               atomic_set(&c->refcnt, 1);
+               qib_evt_settype(&c->evt, QIB_EVT_NONE);
+               qib_evt_setlen(&c->evt, 0);
+       }
+
+       return c;
+}
+
+/*
+ * Get a reference to an event
+ */
+static inline void qib_evt_get(struct qib_evt_container *c)
+{
+       atomic_inc(&c->refcnt);
+}
+
+/*
+ * Release a reference on an event
+ */
+static inline void qib_evt_put(struct qib_evt_buf *buf,
+                              struct qib_evt_container *c, int shutdown)
+{
+       /* no-op if there's no container */
+       if (!c)
+               return;
+
+       if (atomic_dec_and_test(&c->refcnt))
+               kmem_cache_free(buf->evt_cache, c);
+       else if (shutdown) {
+               static int cnt;
+
+               EPRINTK("PML leaked %s at shutdown %d refcnt=%ld\n",
+                       qib_evt_str(qib_evt_gettype(&c->evt)), cnt++,
+                       (long) atomic_read(&c->refcnt));
+       }
+}
+
+static int qib_evt_buf_init(struct qib_evt_buf *buf, const char *name,
+                           unsigned int size, unsigned int blobsize)
+{
+       unsigned int i;
+
+       buf->array = vmalloc(size * sizeof(struct qib_evt_container *));
+       if (!buf->array) {
+               EPRINTK("failed to allocate ring buffer name=%s\n", name);
+               goto bail;
+       }
+
+       for (i = 0; i < size; i++)
+               buf->array[i] = NULL;
+
+       buf->evt_id = 0;
+       buf->cursor = 0;
+       buf->name = name;
+
+       buf->cache_name = kmalloc(QIB_TRACE_MAXCACHENAME + 1, GFP_KERNEL);
+       if (!buf->cache_name) {
+               EPRINTK("failed to allocate cache for buffer name=%s\n", name);
+               goto bail_array;
+       }
+       snprintf(buf->cache_name, QIB_TRACE_MAXCACHENAME + 1, "%s-%u",
+                name, blobsize);
+
+       buf->evt_cache = kmem_cache_create(buf->cache_name,
+               sizeof(struct qib_evt_container) + maxblobsize, 0, 0, NULL);
+       if (!buf->evt_cache) {
+               EPRINTK("failed to create trace blob cache name=%s\n", name);
+               goto bail_name;
+       }
+
+       spin_lock_init(&buf->lock);
+       return 0;
+
+bail_name:
+       kfree(buf->cache_name);
+bail_array:
+       vfree(buf->array);
+bail:
+       return -ENOMEM;
+}
+
+/*
+ * Even though we should never be called from interrupt level, we don't want
+ * to block while doing this, so always use ATOMIC.
+ */
+static struct qib_evt_buf *
+qib_evt_buf_create(const char *name, size_t size, size_t blobsize)
+{
+       struct qib_evt_buf *buf;
+       int ret;
+
+       buf = kmalloc(sizeof(struct qib_evt_buf), GFP_ATOMIC);
+       if (!buf) {
+               EPRINTK("failed to allocate memory\n");
+               goto bail;
+       }
+
+       ret = qib_evt_buf_init(buf, name, size, blobsize);
+       if (ret) {
+               kfree(buf);
+               buf = NULL;
+       }
+
+bail:
+       return buf;
+}
+
+static void qib_evt_buf_destroy(struct qib_evt_buf *buf)
+{
+       unsigned int i;
+
+       if (buf) {
+               for (i = 0; i < qib_trace_bufsize; i++) {
+                       struct qib_evt_container *c = buf->array[i];
+
+                       qib_evt_put(buf, c, 1);
+               }
+
+               kmem_cache_destroy(buf->evt_cache);
+               kfree(buf->cache_name);
+               vfree(buf->array);
+               kfree(buf);
+               buf = NULL;
+       }
+}
+
+/*
+ * Reset the ring buffer to its initial state
+ *
+ * Assumes the caller is holding the spinlock
+ */
+static void qib_evt_buf_reset(struct qib_evt_buf *buf)
+{
+       unsigned int i;
+
+       for (i = 0; i < qib_trace_bufsize; i++) {
+               struct qib_evt_container *c = buf->array[i];
+
+               qib_evt_put(buf, c, 0);
+               buf->array[i] = NULL;
+       }
+
+       buf->cursor = 0;
+}
+
+/*
+ * Reset the file pointer to its default state
+ */
+static void qib_evt_file_reset(struct qib_evt_file *f)
+{
+       f->cursor = 0;
+       f->nevents = 0;
+       f->flags &= ~EVT_FLAG_DATA_TO_READ;
+}
+
+static int qib_trace_set_bufsize(const char *val, struct kernel_param *kp)
+{
+       struct qib_evt_buf *buf = qib_trace_buf;
+       unsigned int i, newsize;
+       struct qib_evt_container **new = NULL;
+       struct qib_evt_container **old;
+       unsigned long flags;
+       int ret = 0;
+
+       newsize = simple_strtoul(val, NULL, 0);
+       if (newsize == 0)
+               EPRINTK("set bufsize to 0, trace disabled\n");
+
+       if (newsize == qib_trace_bufsize) {
+               DPRINTK("bufsize unchanged (old=%u, new=%u)\n",
+                       qib_trace_bufsize, newsize);
+               goto bail;
+       }
+
+       /*
+        * Skip the buffer allocation if the facility
+        * hasn't been initialized yet.
+        */
+       if (!buf) {
+               DPRINTK("buf creation deferred (old=%u new=%u)\n",
+                       qib_trace_bufsize, newsize);
+               qib_trace_bufsize = newsize;
+               goto bail;
+       }
+
+       if (newsize) {
+               new = vmalloc(newsize * sizeof(struct qib_evt_container *));
+               if (!new) {
+                       EPRINTK("failed to allocate new ring buffer\n");
+                       ret = -ENOMEM;
+                       goto bail;
+               }
+       }
+
+       spin_lock_irqsave(&buf->lock, flags);
+       if (newsize > qib_trace_bufsize) {
+               for (i = 0; i < qib_trace_bufsize; i++)
+                       new[i] = buf->array[i];
+               for (; i < newsize; i++)
+                       new[i] = NULL;
+       } else {
+               /* newsize < qib_trace_bufsize */
+               for (i = 0; i < newsize; i++)
+                       new[i] = buf->array[i];
+               for (; i < qib_trace_bufsize; i++) {
+                       struct qib_evt_container *c = buf->array[i];
+
+                       qib_evt_put(buf, c, 0);
+               }
+       }
+
+       DPRINTK("set bufsize (old=%u, new=%u) cursor=%u\n",
+               qib_trace_bufsize, newsize, buf->cursor);
+       old = buf->array;
+       buf->array = new;
+       qib_trace_bufsize = newsize;
+       spin_unlock_irqrestore(&buf->lock, flags);
+       vfree(old);
+bail:
+       return ret;
+}
+
+static int qib_trace_set_maxblobsize(const char *val, struct kernel_param *kp)
+{
+       struct qib_evt_buf *buf = qib_trace_buf;
+       char *cache_name = NULL;
+       unsigned int i, newsize;
+       struct kmem_cache *new = NULL;
+       unsigned long flags;
+       int ret = 0;
+
+       newsize = simple_strtoul(val, NULL, 0);
+       if (newsize == 0) {
+               EPRINTK("set maxblobsize to 0, invalid\n");
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       if (newsize == maxblobsize) {
+               DPRINTK("maxblobsize unchanged (old=%u, new=%u)\n",
+                       maxblobsize, newsize);
+               goto bail;
+       }
+
+       /*
+        * Skip the buffer allocation if the facility
+        * hasn't been initialized yet.
+        */
+       if (!buf) {
+               DPRINTK("blob cache creation deferred (old=%u new=%u)\n",
+                       maxblobsize, newsize);
+               maxblobsize = newsize;
+               goto bail;
+       }
+
+       if (newsize) {
+               cache_name = kmalloc(QIB_TRACE_MAXCACHENAME + 1, GFP_KERNEL);
+               if (!cache_name) {
+                       EPRINTK("failed to allocate cache_name\n");
+                       ret = -ENOMEM;
+                       goto bail;
+               }
+
+               snprintf(cache_name, QIB_TRACE_MAXCACHENAME + 1, "%s-%u",
+                        buf->name, newsize);
+               new = kmem_cache_create(cache_name,
+                       sizeof(struct qib_evt_container) + newsize, 0, 0, NULL);
+               if (!new) {
+                       EPRINTK("failed to create blob cache name=%s\n",
+                               cache_name);
+                       ret = -ENOMEM;
+                       goto bail;
+               }
+       }
+
+       spin_lock_irqsave(&buf->lock, flags);
+       if (newsize > maxblobsize) {
+               for (i = 0; i < qib_trace_bufsize; i++) {
+                       struct qib_evt_container *old = buf->array[i];
+                       struct qib_evt_container *c;
+
+                       if (!old)
+                               continue;
+
+                       c = kmem_cache_alloc(new, GFP_ATOMIC);
+                       if (!c) {
+                               EPRINTK("failed to allocate evt container "
+                                       "from cache name=%s\n", buf->name);
+                               ret = -ENOMEM;
+                               goto bail_unlock;
+                       }
+
+                       *c = *old;
+                       atomic_set(&c->refcnt, 1);
+                       memcpy(c->evt.data, old->evt.data,
+                              qib_evt_getlen(&old->evt));
+                       buf->array[i] = c;
+                       qib_evt_put(buf, old, 0);
+               }
+       } else { /* newsize < maxblobsize */
+               for (i = 0; i < qib_trace_bufsize; i++) {
+                       struct qib_evt_container *old = buf->array[i];
+                       struct qib_evt_container *c;
+                       unsigned int actual;
+
+                       if (!old)
+                               continue;
+
+                       c = kmem_cache_alloc(new, GFP_ATOMIC);
+                       if (!c) {
+                               EPRINTK("failed to allocate evt container "
+                                       "from cache name=%s\n", buf->name);
+                               ret = -ENOMEM;
+                               goto bail_unlock;
+                       }
+
+                       *c = *old;
+                       atomic_set(&c->refcnt, 1);
+                       actual = min(qib_evt_getlen(&old->evt), (u16) newsize);
+                       memcpy(c->evt.data, old->evt.data, actual);
+                       qib_evt_setlen(&c->evt, actual);
+                       buf->array[i] = c;
+                       qib_evt_put(buf, old, 0);
+               }
+       }
+
+       DPRINTK("set maxblobsize (old=%u, new=%u) cursor=%u\n",
+               maxblobsize, newsize, buf->cursor);
+
+       /* only update the ring buffer if everything went well */
+       kmem_cache_destroy(buf->evt_cache);
+       buf->evt_cache = new;
+
+       kfree(buf->cache_name);
+       buf->cache_name = cache_name;
+       maxblobsize = newsize;
+       spin_unlock_irqrestore(&buf->lock, flags);
+
+       return ret;
+
+bail_unlock:
+       spin_unlock_irqrestore(&buf->lock, flags);
+bail:
+       kfree(cache_name);
+       return ret;
+}
+
+static void qib_evt_buf_restart(struct qib_evt_buf *buf,
+                               struct qib_evt_file *file, unsigned int cursor)
+{
+       DPRINTK("restarting from cursor %u (buf_cursor=%u)\n",
+               cursor, buf->cursor);
+}
+
+static int qib_trace_put(struct qib_evt_buf *buf, int cpu, u64 tsc,
+                        u32 dbgmask, struct qib_evt_val *val)
+{
+       struct qib_evt_container *new, *old;
+       unsigned long flags;
+       int ret = 0;
+
+       new = qib_evt_alloc(buf);
+       if (!new) {
+               EPRINTK("failed to allocate trace event\n");
+               goto bail;
+       }
+
+       qib_evt_setcpu(&new->evt, cpu);
+       qib_evt_settsc(&new->evt, tsc);
+       qib_evt_setdbgmask(&new->evt, dbgmask);
+       qib_evt_settype(&new->evt, val->type);
+       qib_evt_setlen(&new->evt, val->len);
+
+       switch (val->type) {
+       case QIB_EVT_EMPTY:
+               /* do nothing */
+               break;
+       case QIB_EVT_U8:
+               *((u8 *) new->evt.data) = val->d8;
+               break;
+       case QIB_EVT_U16:
+               *((u16 *) new->evt.data) = val->d16;
+               break;
+       case QIB_EVT_U32:
+               *((u32 *) new->evt.data) = val->d32;
+               break;
+       case QIB_EVT_U64:
+               *((u64 *) new->evt.data) = val->d64;
+               break;
+       case QIB_EVT_STR:
+       case QIB_EVT_BLOB:
+               if (val->len > maxblobsize) {
+                       EPRINTK("truncation [%s] len=%lu maxblobsize=%u\n",
+                               qib_evt_str(val->type), val->len, maxblobsize);
+                       val->len = maxblobsize;
+               }
+               memcpy(new->evt.data, val->blob, val->len);
+               break;
+       default:
+               EPRINTK("unsupported event type\n");
+               ret = -EINVAL;
+               break;
+       }
+
+       if (!ret) {
+               spin_lock_irqsave(&buf->lock, flags);
+               DPRINTK("using cursor %u (0..%d) evt_id=%llu\n", buf->cursor,
+                       qib_trace_bufsize - 1,
+                       (unsigned long long) buf->evt_id);
+
+               qib_evt_setid(&new->evt, buf->evt_id);
+               buf->evt_id++;
+
+               old = buf->array[buf->cursor];
+               buf->array[buf->cursor] = new;
+               /* means we successfully copied a datatype */
+               if (buf->cursor + 1 >= qib_trace_bufsize)
+                       buf->cursor = 0;
+               else
+                       buf->cursor++;
+
+               /* notify the observers */
+               wake_up_interruptible(&read_wait);
+               spin_unlock_irqrestore(&buf->lock, flags);
+               qib_evt_put(buf, old, 0);
+       } else
+               qib_evt_put(buf, new, 0);
+
+bail:
+       return ret;
+}
+
+void qib_trace_put8(struct qib_evt_buf *buf, int cpu, u64 tsc,
+                   u32 dbgmask, u8 val)
+{
+       struct qib_evt_val eval;
+
+       eval.type = QIB_EVT_U8;
+       eval.d8 = val;
+       eval.len = sizeof(u8);
+       qib_trace_put(buf, cpu, tsc, dbgmask, &eval);
+}
+
+void qib_trace_put16(struct qib_evt_buf *buf, int cpu, u64 tsc,
+                    u32 dbgmask, u16 val)
+{
+       struct qib_evt_val eval;
+
+       eval.type = QIB_EVT_U16;
+       eval.d16 = val;
+       eval.len = sizeof(u16);
+       qib_trace_put(buf, cpu, tsc, dbgmask, &eval);
+}
+
+void qib_trace_put32(struct qib_evt_buf *buf, int cpu, u64 tsc,
+                    u32 dbgmask, u32 val)
+{
+       struct qib_evt_val eval;
+
+       eval.type = QIB_EVT_U32;
+       eval.d32 = val;
+       eval.len = sizeof(u32);
+       qib_trace_put(buf, cpu, tsc, dbgmask, &eval);
+}
+
+void qib_trace_put64(struct qib_evt_buf *buf, int cpu, u64 tsc,
+                    u32 dbgmask, u64 val)
+{
+       struct qib_evt_val eval;
+
+       eval.type = QIB_EVT_U64;
+       eval.d64 = val;
+       eval.len = sizeof(u64);
+       qib_trace_put(buf, cpu, tsc, dbgmask, &eval);
+}
+
+void qib_trace_putblob(struct qib_evt_buf *buf, int cpu, u64 tsc,
+                      u32 dbgmask, void *blob, u16 len)
+{
+       struct qib_evt_val eval;
+
+       eval.type = QIB_EVT_BLOB;
+       eval.blob = blob;
+       eval.len = len;
+       qib_trace_put(buf, cpu, tsc, dbgmask, &eval);
+}
+
+void qib_trace_putstr(struct qib_evt_buf *buf, int cpu, u64 tsc,
+                     u32 dbgmask, const char *str)
+{
+       struct qib_evt_val eval;
+
+       eval.type = QIB_EVT_STR;
+       eval.blob = str;
+       eval.len = strlen(str) + 1;
+       qib_trace_put(buf, cpu, tsc, dbgmask, &eval);
+}
+
+void qib_trace_vputstr(struct qib_evt_buf *buf, int cpu, u64 tsc,
+                      u32 dbgmask, const char *fmt, ...)
+{
+       struct qib_evt_container *new, *old;
+       unsigned long flags;
+       va_list ap;
+       int n;
+
+       new = qib_evt_alloc(buf);
+       if (!new) {
+               EPRINTK("failed to allocate trace event\n");
+               return;
+       }
+
+       va_start(ap, fmt);
+       n = vscnprintf((void *)new->evt.data, maxblobsize, fmt, ap);
+       va_end(ap);
+
+       qib_evt_settsc(&new->evt, tsc);
+       qib_evt_setdbgmask(&new->evt, dbgmask);
+       qib_evt_setcpu(&new->evt, cpu);
+       qib_evt_settype(&new->evt, QIB_EVT_STR);
+       qib_evt_setlen(&new->evt, n + 1);
+
+       spin_lock_irqsave(&buf->lock, flags);
+
+       DPRINTK("using cursor %u (0..%d) evt_id=%llu\n", buf->cursor,
+               qib_trace_bufsize - 1, (unsigned long long) buf->evt_id);
+       qib_evt_setid(&new->evt, buf->evt_id);
+       buf->evt_id++;
+
+       old = buf->array[buf->cursor];
+       buf->array[buf->cursor] = new;
+
+       if (buf->cursor + 1 >= qib_trace_bufsize)
+               buf->cursor = 0;
+       else
+               buf->cursor++;
+
+       /* notify the observers */
+       wake_up_interruptible(&read_wait);
+       spin_unlock_irqrestore(&buf->lock, flags);
+       qib_evt_put(buf, old, 0);
+}
+
+static void qib_trace_putempty(struct qib_evt_buf *buf, int cpu, u64 tsc,
+                              u32 dbgmask)
+{
+       struct qib_evt_val eval;
+
+       eval.type = QIB_EVT_EMPTY;
+       eval.len = 0;
+       qib_trace_put(buf, cpu, tsc, dbgmask, &eval);
+}
+
+static int qib_trace_open(struct inode *inode, struct file *filp)
+{
+       int ret = 0;
+       const int minor = iminor(inode);
+       struct qib_evt_file *f;
+
+       if (minor != QIB_TRACE_MINOR)
+               return -ENODEV;
+
+       f = kzalloc(sizeof(struct qib_evt_file), GFP_KERNEL);
+       if (!f) {
+               EPRINTK("failed to allocate memory\n");
+               ret = -ENOMEM;
+               goto bail;
+       }
+
+       /* take a snapshot of where the cursor is */
+       f->cursor = qib_trace_buf->cursor;
+
+       filp->private_data = f;
+       DPRINTK("opened %s successfully pid=%u\n",
+               QIB_TRACE_FILE, current->tgid);
+bail:
+       return ret;
+}
+
+static int qib_trace_close(struct inode *inode, struct file *filp)
+{
+       struct qib_evt_file *f = filp->private_data;
+
+       kfree(f);
+       DPRINTK("closed %s successfully pid=%u\n",
+               QIB_TRACE_FILE, current->tgid);
+       return 0;
+}
+
+static ssize_t qib_trace_read(struct file *file, char __user *ubuf,
+                             size_t nbytes, loff_t *ppos)
+{
+       struct qib_evt_file *f = (struct qib_evt_file *) file->private_data;
+       struct qib_evt_container *c;
+       struct qib_evt sentinel = {0};
+       ssize_t ret = 0;
+       u8 __user *to;
+       size_t actual, left;
+       size_t howmany = qib_trace_howmany;
+       int err;
+
+       if (nbytes == 0) {
+               DPRINTK("Tried to read 0 bytes pid=%u\n", current->tgid);
+               goto bail;
+       }
+
+       if (f->cursor == qib_trace_buf->cursor) {
+               if (!(f->flags & EVT_FLAG_DATA_TO_READ) &&
+                   file->f_flags & O_NONBLOCK) {
+                       DPRINTK("no data to read, would block pid=%u\n",
+                               current->tgid);
+                       ret = -EAGAIN;
+                       goto bail;
+               }
+
+               if (f->flags & EVT_FLAG_DATA_TO_READ) {
+                       DPRINTK("returned %u events for pid=%u\n",
+                               f->nevents, current->tgid);
+                       f->flags &= ~EVT_FLAG_DATA_TO_READ;
+                       f->nevents = 0;
+                       goto bail;
+               }
+
+               DPRINTK("going to sleep pid=%u\n", current->tgid);
+               ret = wait_event_interruptible(read_wait,
+                               f->cursor != qib_trace_buf->cursor);
+               if (ret) {
+                       if (ret == -ERESTARTSYS)
+                               ret = -EINTR;
+                       DPRINTK("failed to wake up pid=%u ret=%ld\n",
+                               current->tgid, (long) ret);
+                       return ret;
+               }
+
+               DPRINTK("woken up pid=%u f->cursor=%u buf_cursor=%u\n",
+                       current->tgid, f->cursor, qib_trace_buf->cursor);
+       }
+
+       to = ubuf;
+       left = nbytes;
+       spin_lock_irq(&qib_trace_buf->lock);
+       do {
+               /* keep a local copy of an event */
+               c = qib_trace_buf->array[f->cursor];
+               if (!c) {
+                       /*
+                        * Someone has cleared the buffer,
+                        * so start reading again from 0.
+                        */
+                       DPRINTK("trace buffer was cleared f->cursor=%u "
+                               "buf_cursor=%u pid=%u\n",
+                               f->cursor, qib_trace_buf->cursor,
+                               current->tgid);
+                       f->cursor = 0;
+                       continue;
+               }
+               if (left < qib_evt_getsize(&c->evt)) {
+                       /* the user buffer is full, give up */
+                       EPRINTK("not enough space in user buffer pid=%u\n",
+                               current->tgid);
+                       break;
+               }
+               qib_evt_get(c);
+               if (f->cursor + 1 >= qib_trace_bufsize)
+                       f->cursor = 0;
+               else
+                       f->cursor++;
+               actual = qib_evt_getsize(&c->evt);
+               spin_unlock_irq(&qib_trace_buf->lock);
+
+               DPRINTK("about to copy event type=%s len=%lu sizeof=%lu "
+                       "refcnt=%ld total_left=%lu pid=%u\n",
+                       qib_evt_str(qib_evt_gettype(&c->evt)),
+                       (unsigned long) sizeof(struct qib_evt) +
+                               qib_evt_getlen(&c->evt),
+                       (unsigned long) sizeof(struct qib_evt),
+                       (long) atomic_read(&c->refcnt),
+                       (unsigned long) left, current->tgid);
+
+               err = copy_to_user(to, &c->evt, actual);
+               /* done with the event so release it */
+               qib_evt_put(qib_trace_buf, c, 0);
+
+               if (err) {
+                       EPRINTK("failed to copy_out event pid=%u\n",
+                               current->tgid);
+                       ret = -EFAULT;
+                       goto bail;
+               }
+
+               to += actual;
+               left -= actual;
+               ret += actual;
+
+               f->nevents++;
+               spin_lock_irq(&qib_trace_buf->lock);
+
+               if (howmany) {
+                       if (--howmany == 0)
+                               break;
+               }
+       } while (f->cursor != qib_trace_buf->cursor);
+       spin_unlock_irq(&qib_trace_buf->lock);
+
+       if (copy_to_user(to, &sentinel, sizeof(sentinel))) {
+               EPRINTK("failed to copy_out sentinel pid=%u\n", current->tgid);
+               ret = -EFAULT;
+               goto bail;
+       }
+
+       file_accessed(file);
+       f->flags |= EVT_FLAG_DATA_TO_READ;
+       DPRINTK("copied successfully %ld bytes nevents=%u pid=%u\n",
+               ret, f->nevents, current->tgid);
+bail:
+       return ret;
+}
+
+static ssize_t qib_trace_write(struct file *file, const char __user *ubuf,
+                              size_t nbytes, loff_t *ppos)
+{
+       struct qib_evt_file *f = (struct qib_evt_file *) file->private_data;
+       struct qib_trace_cmd cmd;
+       ssize_t ret = 0;
+       struct qib_evt sentinel = {0};
+
+       if (nbytes == 0) {
+               DPRINTK("Tried to write 0 bytes pid=%u\n", current->tgid);
+               goto bail;
+       }
+
+       if (copy_from_user(&cmd, ubuf, sizeof(cmd))) {
+               EPRINTK("failed to copy trace command pid=%u\n", current->tgid);
+               ret = -EFAULT;
+               goto bail;
+       }
+
+       ret = sizeof(cmd);
+       switch (cmd.type) {
+       case IPATH_TRACE_CLEAR:
+               DPRINTK("got trace_clear pid=%u\n", current->tgid);
+
+               spin_lock_irq(&qib_trace_buf->lock);
+               qib_evt_buf_reset(qib_trace_buf);
+               spin_unlock_irq(&qib_trace_buf->lock);
+
+               qib_evt_file_reset(f);
+               DPRINTK("buffer cleared successfully pid=%u\n", current->tgid);
+               break;
+
+       case IPATH_TRACE_RESTART:
+               DPRINTK("got trace_restart pid=%u\n", current->tgid);
+               spin_lock_irq(&qib_trace_buf->lock);
+               if (cmd.cursor >= qib_trace_bufsize) {
+                       spin_unlock_irq(&qib_trace_buf->lock);
+                       DPRINTK("found invalid cursor %u (0..%u)\n",
+                               cmd.cursor, qib_trace_bufsize - 1);
+                       ret = -EINVAL;
+                       goto bail;
+               }
+
+               qib_evt_buf_restart(qib_trace_buf, f, cmd.cursor);
+               spin_unlock_irq(&qib_trace_buf->lock);
+               DPRINTK("cursor restarted successfully pid=%u\n",
+                       current->tgid);
+               break;
+
+       case IPATH_TRACE_DUMP:
+       case IPATH_TRACE_DUMP_AND_CLEAR: {
+               int i;
+               void __user *to = cmd.buf;
+               unsigned long left = (unsigned long) cmd.size;
+
+               if (cmd.type == IPATH_TRACE_DUMP)
+                       DPRINTK("got trace_dump len %lu pid=%u\n",
+                               left, current->tgid);
+               else
+                       DPRINTK("got trace_dump_and_clear len %lu pid=%u\n",
+                               left, current->tgid);
+
+               if (left < sizeof(sentinel)) {
+                       ret = -EINVAL;
+                       goto bail;
+               }
+               left -= sizeof(sentinel);
+
+               spin_lock_irq(&qib_trace_buf->lock);
+               for (i = 0; i < qib_trace_bufsize; i++) {
+                       struct qib_evt_container *c = qib_trace_buf->array[i];
+                       unsigned long actual;
+                       int err;
+
+                       if (!c)
+                               continue;
+
+                       actual = qib_evt_getsize(&c->evt);
+                       if (left < actual)
+                               break;
+
+                       qib_evt_get(c);
+                       spin_unlock_irq(&qib_trace_buf->lock);
+
+                       DPRINTK("about to copy event type=%s len=%lu "
+                               "sizeof=%lu refcnt=%ld total_left=%lu\n",
+                               qib_evt_str(qib_evt_gettype(&c->evt)),
+                               (unsigned long) sizeof(struct qib_evt) +
+                                       qib_evt_getlen(&c->evt),
+                               (unsigned long) sizeof(struct qib_evt),
+                               (long) atomic_read(&c->refcnt),
+                               (unsigned long) left);
+
+                       err = copy_to_user(to, &c->evt, actual);
+                       qib_evt_put(qib_trace_buf, c, 0);
+
+                       if (err) {
+                               EPRINTK("failed to copy_out event "
+                                       "pid=%u\n", current->tgid);
+                               ret = -EFAULT;
+                               goto bail;
+                       }
+                       to += actual;
+                       left -= actual;
+                       spin_lock_irq(&qib_trace_buf->lock);
+               }
+               if (cmd.type == IPATH_TRACE_DUMP_AND_CLEAR)
+                       qib_evt_buf_reset(qib_trace_buf);
+               spin_unlock_irq(&qib_trace_buf->lock);
+
+               if (copy_to_user(to, &sentinel, sizeof(sentinel))) {
+                       EPRINTK("failed to copy_out sentinel pid=%u\n",
+                               current->tgid);
+                       ret = -EFAULT;
+                       goto bail;
+               }
+
+               if (cmd.type == IPATH_TRACE_DUMP_AND_CLEAR)
+                       qib_evt_file_reset(f);
+               break;
+       }
+
+       case IPATH_TRACE_GETSIZE:
+               DPRINTK("got trace_getsize pid=%u\n", current->tgid);
+
+               if (copy_to_user(cmd.bufsize, &qib_trace_bufsize,
+                                sizeof(qib_trace_bufsize))) {
+                       EPRINTK("failed to copy_out bufsize pid=%u\n",
+                               current->tgid);
+                       ret = -EFAULT;
+                       goto bail;
+               }
+
+               if (copy_to_user(cmd.blobsize, &maxblobsize,
+                                sizeof(maxblobsize))) {
+                       EPRINTK("failed to copy_out blobsize pid=%u\n",
+                               current->tgid);
+                       ret = -EFAULT;
+                       goto bail;
+               }
+
+               DPRINTK("trace_getsize copied successfully pid=%u\n",
+                       current->tgid);
+               break;
+
+       case IPATH_TRACE_INJECT: {
+               int cpu = smp_processor_id();
+               cycles_t now = get_cycles();
+
+               DPRINTK("got trace_inject pid=%u\n", current->tgid);
+               qib_trace_putempty(qib_trace_buf, cpu, now, 0);
+               break;
+       }
+
+       default:
+               EPRINTK("Unsupported cmd type (%d) pid=%u\n",
+                       cmd.type, current->tgid);
+               ret = -ENOSYS;
+               break;
+       }
+
+bail:
+       return ret;
+}
+
+#ifndef ATOMIC_NOTIFIER_INIT
+/*
+ * Some backports don't have this, so define here for now.
+ * We may not want to keep this for upstream.  If we do, we'll
+ * do this via the backports at that time.
+ */
+#define atomic_notifier_chain_register notifier_chain_register
+#define atomic_notifier_chain_unregister notifier_chain_unregister
+#endif
+
+/*
+ * qibtrace_panic_event() is called by the panic handler.
+ * We want to dump our trace buffer on panic, so we can
+ * get useful info when the panic is caused by us.
+ * No newline in printk, normally part of trace buffer.
+ */
+static int qibtrace_panic_event(struct notifier_block *this,
+                               unsigned long event, void *ptr)
+{
+       int i;
+
+       /* bust_spinlocks(1); */
+       printk(KERN_EMERG "Dumping qib trace buffer from panic\n");
+       for (i = 0; i < qib_trace_bufsize; i++) {
+               struct qib_evt_container *c = qib_trace_buf->array[i];
+
+               if (c)
+                       qib_evt_printk(&c->evt);
+       }
+       printk(KERN_EMERG "Done dumping qib trace buffer\n");
+       /* bust_spinlocks(0); */
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block qibtrace_panic_block = {
+       .notifier_call  = qibtrace_panic_event,
+       .priority       = 50, /* seems to be about middle */
+};
+
+/*
+ * Called when the qib module is loaded
+ */
+int __init qib_trace_init(void)
+{
+       int ret = 0;
+
+       if (!qib_trace_bufsize) {
+               EPRINTK("tracing disabled, size is 0\n");
+               goto bail;
+       }
+
+       qib_trace_buf = qib_evt_buf_create("qib_trace", qib_trace_bufsize,
+                                          maxblobsize);
+       if (!qib_trace_buf) {
+               EPRINTK("failed to create event trace ring buffer, "
+                       "tracing disabled\n");
+               qib_trace_bufsize = 0;
+               goto bail;
+       }
+
+       ret = qib_cdev_init(QIB_TRACE_MINOR, QIB_TRACE_FILE, &qib_trace_fops,
+                           &evt_dev.cdev, &evt_dev.device);
+       if (ret)
+               goto bail_buf;
+
+       /* Register a call for panic conditions. */
+       atomic_notifier_chain_register(&panic_notifier_list,
+                                      &qibtrace_panic_block);
+
+       DPRINTK("event trace init successful\n");
+       goto bail;
+
+bail_buf:
+       qib_evt_buf_destroy(qib_trace_buf);
+bail:
+       return ret;
+}
+
+/*
+ * Called when the qib module is unloaded
+ */
+void __exit qib_trace_fini(void)
+{
+       if (qib_trace_buf) {
+               qib_cdev_cleanup(&evt_dev.cdev, &evt_dev.device);
+               atomic_notifier_chain_unregister(&panic_notifier_list,
+                                                &qibtrace_panic_block);
+               qib_evt_buf_destroy(qib_trace_buf);
+       }
+       DPRINTK("event trace fini successful\n");
+}

--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to