The Driver Tracing Interface (DTI) code.

Signed-off-by: Tom Zanussi <[EMAIL PROTECTED]>
Signed-off-by: David Wilder <[EMAIL PROTECTED]>
---
 drivers/base/Kconfig           |   11 
 drivers/base/Makefile          |    1 
 drivers/base/dti.c             |  836 +++++++++++++++++++++++++++++++++++++++++
 drivers/base/dti_merged_view.c |  332 ++++++++++++++++
 include/linux/dti.h            |  293 ++++++++++++++
 5 files changed, 1473 insertions(+)

diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 5d6312e..fbc9c0e 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -49,6 +49,17 @@ config DEBUG_DEVRES
 
          If you are unsure about this, Say N here.
 
+config DTI
+       bool "Driver Tracing Interface (DTI)"
+       select GTSC
+       help
+       Provides  functions to write variable length trace records
+       into a wraparound memory trace buffer. One purpose of
+       this is to inspect the debug traces after a system crash in order to
+       analyze the reason for the failure. The traces are accessable from
+       system dumps via dump analysis tools like crash or lcrash. In live
+       systems the traces can be read via a debugfs interface.
+
 config SYS_HYPERVISOR
        bool
        default n
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index b39ea3f..7caa5f5 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_NUMA)    += node.o
 obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o
 obj-$(CONFIG_SMP)      += topology.o
 obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
+obj-$(CONFIG_DTI) += dti.o dti_merged_view.o
 
 ifeq ($(CONFIG_DEBUG_DRIVER),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/base/dti.c b/drivers/base/dti.c
new file mode 100644
index 0000000..2feec11
--- /dev/null
+++ b/drivers/base/dti.c
@@ -0,0 +1,836 @@
+/*
+ *    Linux Driver Tracing Interface.
+ *
+ *    Copyright (C) IBM Corp. 2007
+ *    Author(s): Tom Zanussi <[EMAIL PROTECTED]>
+ *               Dave Wilder <[EMAIL PROTECTED]>
+ *               Michael Holzheu <[EMAIL PROTECTED]>
+ */
+
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/relay.h>
+#include <linux/module.h>
+#include <linux/hardirq.h>
+#include <linux/dti.h>
+
+extern int dti_create_merged_views(struct dti_info *dti);
+extern void dti_remove_merged_views(struct dti_info *dti);
+struct file_operations level_fops;
+
+static inline int nr_sub(int size)
+{
+       if (size < 4)
+               return 0;
+       
+       if (size >= 8 * PAGE_SIZE)
+               return 8;
+       else
+               return 4;
+}
+
+static inline int sub_size(int size)
+{
+       if (size < 4)
+               return 0;
+       
+       if (size >= 8 * PAGE_SIZE)
+               return size / 8;
+       else
+               return size / 4;
+}
+
+/*
+ * For dti_printk, maximum size of klog formatting buffer beyond which
+ * truncation will occur
+ */
+#define DTI_PRINTF_TMPBUF_SIZE (1024)
+
+/* per-cpu dti_printf formatting temporary buffer */
+static char dti_printf_tmpbuf[NR_CPUS][DTI_PRINTF_TMPBUF_SIZE];
+
+/*
+ * Low-level registration functions
+ */
+
+static struct dti_info *__dti_register_level(const char *name, int level,
+                                            int sub_size, int nr_sub,
+                                            struct dti_handle *handle)
+{
+       struct dti_info *dti;
+
+       dti = kzalloc(sizeof(*dti), GFP_KERNEL);
+       if(!dti)
+               return NULL;
+
+       dti->trace = trace_setup("dti", name, sub_size, nr_sub,
+                                TRACE_FLIGHT_CHANNEL | TRACE_DISABLE_STATE);
+       if (!dti->trace)
+               goto setup_failed;
+       
+       dti->handle = handle;
+       dti->level = level;
+       dti->level_ctrl = debugfs_create_file("level", 0,
+                                             dti->trace->dir, dti,
+                                             &level_fops);
+       if (!dti->level_ctrl) {
+               printk("Couldn't create level control file\n");
+               goto level_failed;
+       }
+
+       strncpy(dti->name, name, NAME_MAX);
+
+       return dti;
+
+level_failed:
+       trace_cleanup(dti->trace);
+setup_failed:
+       kfree(dti);
+
+       return NULL;
+}
+
+/**
+ * dti_register_level: create trace dir and level ctrl file
+ *
+ * Internal - exported for setup macros.
+ */
+struct dti_info *dti_register_level(const char *name, int level,
+                                   struct dti_handle *handle)
+{
+       return __dti_register_level(name, level, sub_size(handle->size),
+                                   nr_sub(handle->size), handle);
+}
+EXPORT_SYMBOL_GPL(dti_register_level);
+
+static void dti_unregister_level(struct dti_info *dti)
+{
+       debugfs_remove(dti->level_ctrl);
+       trace_cleanup(dti->trace);
+       kfree(dti);
+}
+
+/**
+ * dti_register_channel: create channel part of new trace
+ */
+static int dti_register_channel(struct dti_info *dti)
+{
+       int rc = 0;
+       
+       rc = trace_start(dti->trace);
+       if (rc)
+               return rc;
+
+       if (dti_create_merged_views(dti)) {
+               rc = -ENOMEM;
+               goto failed_view;
+       }
+
+       return rc;
+
+failed_view:
+       trace_cleanup(dti->trace);
+       return rc;
+}
+
+static void dti_unregister_channel(struct dti_info *dti)
+{
+       dti_remove_merged_views(dti);
+       trace_cleanup_channel(dti->trace);
+}
+
+/**
+ * __dti_register: create new trace, explicitly specifying subbuffer sizes
+ * @name: name of trace
+ * @sub_size: size of subbuffer
+ * @nr_sub: number of subbuffers
+ * @init_level: initial log level e.g. DTI_LEVEL_DEFAULT, DTI_LEVEL_OFF, etc
+ *
+ * returns trace handle or NULL, if register failed.
+ *
+ * NOTE: use dti_register() if you don't care about sub-buffer details.
+ */
+struct dti_info *__dti_register(const char *name, int sub_size, int nr_sub,
+                               int init_level)
+{
+       struct dti_info *dti;
+
+       dti = __dti_register_level(name, init_level, sub_size, nr_sub, NULL);
+       if (!dti)
+               return NULL;
+
+       if (dti_register_channel(dti)) {
+               dti_unregister_level(dti);
+               return NULL;
+       }
+       
+       return dti;
+}
+EXPORT_SYMBOL_GPL(__dti_register);
+
+/**
+ * dti_register: main registration function, creates new trace
+ * @name: name of trace
+ * @size: total size of buffer
+ * @init_level: initial log level e.g. DTI_LEVEL_DEFAULT, DTI_LEVEL_OFF, etc
+ *
+ * returns trace handle or NULL, if register failed.
+ */
+struct dti_info *dti_register(const char *name, int size, int init_level)
+{
+       int subsize = sub_size(size);
+       int nrsub = nr_sub(size);
+
+       if (subsize == 0 || nrsub == 0)
+               return NULL;
+
+       return __dti_register(name, subsize, nrsub, init_level);
+}
+EXPORT_SYMBOL_GPL(dti_register);
+
+/**
+ * dti_unregister: unregistration function, destroys trace
+ * @trace: trace handle
+ */
+void dti_unregister(struct dti_info *dti)
+{
+       if (!dti)
+               return;
+       
+       dti_unregister_channel(dti);
+       dti_unregister_level(dti);
+}
+EXPORT_SYMBOL_GPL(dti_unregister);
+
+/**
+ *     dti_register_work - register a channel asynchronously
+ *     @work: work struct that contains the the dti handle
+ *
+ *     This is the work function used to register a dti channel
+ *     asynchronously.
+ *
+ *     Internal - exported for setup macros.
+ */
+void dti_register_work(struct work_struct *work)
+{
+       struct dti_handle *handle =
+               container_of(work, struct dti_handle, work.work);
+
+       dti_register_channel(handle->info);
+}
+EXPORT_SYMBOL_GPL(dti_register_work);
+
+static void dti_register_async(struct dti_handle *handle)
+{
+       if (!handle->registered) {
+               handle->registered = 1;
+               schedule_delayed_work(&handle->work, 1);
+       }
+}
+
+/*
+ * Create channel now if possible, otherwise defer.  Return 0 only if the
+ * channel was successfully created and ready for use after this call.
+ */
+static int complete_channel(struct dti_handle *handle)
+{
+       unsigned long flags;
+       
+       spin_lock_irqsave(&handle->lock, flags);
+       if (handle->registered) {
+               spin_unlock_irqrestore(&handle->lock, flags);
+               return -EAGAIN;
+       }
+               
+       if (in_atomic() || irqs_disabled()) {
+               dti_register_async(handle);
+               spin_unlock_irqrestore(&handle->lock, flags);
+               return -EAGAIN;
+       }
+
+       handle->registered = 1;
+       spin_unlock_irqrestore(&handle->lock, flags);
+       
+       return dti_register_channel(handle->info);
+}
+
+/**
+ *     dti_initbuf_reserve - reserve slot in static channel buffer
+ */
+static inline void *dti_initbuf_reserve(struct dti_handle *handle,
+                                       size_t length)
+{
+       void *reserved;
+       unsigned int subbuf_size = handle->initbuf_size >> 1;
+
+       /* can't log events > initbuf_size / 2 */
+       if (length > subbuf_size)
+               return NULL;
+       
+       if (unlikely(handle->initbuf_offset + length > handle->initbuf_size)) {
+               handle->initbuf_pad[1] =
+                       handle->initbuf_size - handle->initbuf_offset;
+               handle->initbuf_wrapped = 1;
+               handle->initbuf_offset = 0;
+       }
+       
+       if (unlikely(handle->initbuf_offset < subbuf_size &&
+                    handle->initbuf_offset + length >= subbuf_size)) {
+               handle->initbuf_pad[0] = subbuf_size - handle->initbuf_offset;
+               handle->initbuf_offset = subbuf_size;
+       }
+
+       reserved = handle->initbuf + handle->initbuf_offset;
+       handle->initbuf_offset += length;
+
+       return reserved;
+}
+
+/**
+ * __dti_reserve: reserve space in trace buffer, low-level (nonhandle) version
+ * @trace: trace handle
+ * @prio: priority of event (the lower, the higher the priority)
+ * @len: length to reserve
+ *
+ * returns pointer to event payload, if event is reserved. Otherwise NULL.
+ *
+ * NOTE: this is the main reserve function for non-handle event logging.
+ * dti_reserve, the handle version, uses it.
+ */
+void *__dti_reserve(struct dti_info *dti, int prio, size_t len)
+{
+       struct dti_event *event;
+       int event_len;
+
+       if (!dti)
+               return NULL;
+
+       if (prio > dti->level)
+               return NULL;
+
+       event_len = len + sizeof(*event);
+
+       event = relay_reserve(dti->trace->rchan, event_len);
+       if (!event)
+               return NULL;
+
+       event->time = sched_clock();
+       event->len = event_len;
+
+       return (void *)event + sizeof(*event);
+}
+EXPORT_SYMBOL_GPL(__dti_reserve);
+
+/**
+ * dti_reserve_early_handle: reserve in early trace buffer, handle version
+ */
+static void *dti_reserve_early_handle(struct dti_handle *handle, int prio,
+                                     size_t len)
+{
+       struct dti_early_event *event;
+       int event_len;
+
+       if (!handle)
+               return NULL;
+
+       event_len = len + sizeof(*event);
+
+       event = dti_initbuf_reserve(handle, event_len);
+       if (!event)
+               return NULL;
+
+       event->cpu = smp_processor_id();
+       event->event.time = sched_clock();
+       event->event.len = len + sizeof(struct dti_event);
+
+       return (void *)event + sizeof(*event);
+}
+
+/**
+ * dti_reserve_early_fn: early reserve dispatch function
+ *
+ * Internal - exported for setup macros.
+ */
+void *dti_reserve_early_fn(struct dti_handle *handle,
+                          int prio, size_t len)
+{
+       void *rc = NULL;
+
+       if (handle->initbuf && prio <= handle->initlevel)
+               rc = dti_reserve_early_handle(handle, prio, len);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(dti_reserve_early_fn);
+
+/* dti_reserve_normal_fn: normal reserve dispatch function
+ *
+ * Internal - exported for setup macros.
+ */
+void *dti_reserve_normal_fn(struct dti_handle *handle,
+                           int prio, size_t len)
+{
+       void *rc = NULL;
+
+       if (handle->info->trace->rchan)
+               rc = __dti_reserve(handle->info, prio, len);
+       else if (prio <= handle->info->level) {
+               if (complete_channel(handle) == 0)
+                       rc = __dti_reserve(handle->info, prio, len);
+       }
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(dti_reserve_normal_fn);
+
+/**
+ * dti_reserve_handle: reserve space in tracing buffer, handle version
+ * @handle: trace handle
+ * @prio: priority of event (the lower, the higher the priority)
+ * @len: length to reserve
+ *
+ * returns pointer to event payload, if event is reserved. Otherwise NULL.
+ *
+ * NOTE: dti.h defines dti_reserve() to use this on the static handles
+ * defined using DEFINE_DTI_HANDLE, so normally you'd use dti_reserve()
+ * instead of using this directly.
+ */
+void *dti_reserve_handle(struct dti_handle *handle, int prio, size_t len)
+{
+       return handle->reserve(handle, prio, len);
+}
+EXPORT_SYMBOL_GPL(dti_reserve_handle);
+
+static int vprintk_normal(struct dti_info *dti, int prio, const char* fmt,
+                         va_list args)
+{
+       struct dti_event *event;
+       void *buf;
+       int len, event_len, rc = -1;
+       unsigned long flags;
+
+       if (!dti)
+               return -1;
+
+       if (prio > dti->level)
+               return -1;
+
+       local_irq_save(flags);
+       buf = dti_printf_tmpbuf[smp_processor_id()];
+       len = vsnprintf(buf, DTI_PRINTF_TMPBUF_SIZE, fmt, args);
+       event_len = len + sizeof(*event);
+       event = relay_reserve(dti->trace->rchan, event_len);
+       if (event) {
+               event->time = sched_clock();
+               event->len = event_len;
+               memcpy(event->data, buf, len);
+               rc = 0;
+       }
+       local_irq_restore(flags);
+
+       return rc;
+}
+
+/**
+ * __dti_printk: write formatted string to trace, low-level (nonhandle) version
+ * @trace: trace struct pointer
+ * @fmt: format string
+ * @...: parameters
+ *
+ * returns 0, if event is written. Otherwise -1.
+ *
+ * NOTE: this is the main printk function for non-handle printing.
+ * dti_printk, the handle version, uses it.
+ */
+int __dti_printk(struct dti_info *dti, int prio, const char* fmt, ...)
+{
+       va_list args;
+       int rc;
+
+       va_start(args, fmt);
+       rc = vprintk_normal(dti, prio, fmt, args);
+       va_end(args);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(__dti_printk);
+
+static int vprintk_early(struct dti_handle *handle, int prio, const char* fmt,
+                        va_list args)
+{
+       struct dti_early_event *event;
+       void *buf;
+       int len, event_len, rc = -1;
+       unsigned long flags;
+
+       if (!handle)
+               return -1;
+
+       local_irq_save(flags);
+       buf = dti_printf_tmpbuf[smp_processor_id()];
+       len = vsnprintf(buf, DTI_PRINTF_TMPBUF_SIZE, fmt, args);
+       event_len = len + sizeof(*event);
+       event = dti_initbuf_reserve(handle, event_len);
+       if (event) {
+               event->cpu = smp_processor_id();
+               event->event.time = sched_clock();
+               event->event.len = len + sizeof(struct dti_event);
+               memcpy(event->event.data, buf, len);
+               rc = 0;
+       }
+       local_irq_restore(flags);
+
+       return rc;
+}
+
+/**
+ * dti_printk_early: write formatted string to trace, early version
+ */
+static int dti_printk_early(struct dti_handle *handle, int prio,
+                           const char* fmt, ...)
+{
+       va_list args;
+       int rc;
+
+       va_start(args, fmt);
+       rc = vprintk_early(handle, prio, fmt, args);
+       va_end(args);
+
+       return rc;
+}
+
+/**
+ * dti_printk_early_fn: early printk dispatch function
+ *
+ * Internal - exported for setup macros.
+ */
+int dti_printk_early_fn(struct dti_handle *handle,
+                       int prio, const char* fmt, va_list args)
+{
+       int rc = -1;
+
+       if (handle->initbuf) {
+               if (prio <= handle->initlevel)
+                       rc = dti_printk_early(handle, prio, fmt, args);
+       }
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(dti_printk_early_fn);
+
+/**
+ * dti_printk_normal_fn: normal printk dispatch function
+ *
+ * Internal - exported for setup macros.
+ */
+int dti_printk_normal_fn(struct dti_handle *handle,
+                        int prio, const char* fmt, va_list args)
+{
+       int rc = -1;
+
+       if (handle->info->trace->rchan)
+               rc = vprintk_normal(handle->info, prio, fmt, args);
+       else if (prio <= handle->info->level) {
+               if (complete_channel(handle) == 0)
+                       rc = vprintk_normal(handle->info, prio, fmt, args);
+       }
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(dti_printk_normal_fn);
+
+/**
+ * dti_printk_handle: write formatted string to trace, handle version
+ * @handle: trace handle
+ * @fmt: format string
+ * @...: parameters
+ *
+ * returns 0, if event is written. Otherwise -1.
+ *
+ * NOTE: dti.h defines dti_printk() to use this on the static handles
+ * defined using DEFINE_DTI_HANDLE, so normally you'd use dti_printk()
+ * instead of using this directly.
+ */
+int dti_printk_handle(struct dti_handle *handle, int prio,
+                     const char* fmt, ...)
+{
+       va_list args;
+       int rc = -1;
+
+       va_start(args, fmt);
+       rc = handle->printk(handle, prio, fmt, args);
+       va_end(args);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(dti_printk_handle);
+
+/**
+ * __dti_event: write buffer to trace, low-level (nonhandle) version
+ * @trace: trace handle
+ * @prio: priority of event (the lower, the higher the priority)
+ * @buf: buffer to write
+ * @len: length of buffer
+ *
+ * returns 0, if event is written. Otherwise -1.
+ *
+ * NOTE: this is the main event function for non-handle event logging.
+ * dti_event, the handle version, uses it.
+ */
+int __dti_event(struct dti_info *dti,
+               int prio, const void* buf, size_t len)
+{
+       struct dti_event *event;
+       int event_len, rc = -1;
+       unsigned long flags;
+
+       if (!dti)
+               return -1;
+
+       if (prio > dti->level)
+               return -1;
+
+       event_len = len + sizeof(*event);
+
+       local_irq_save(flags);
+       event = relay_reserve(dti->trace->rchan, event_len);
+       if (event) {
+               event->time = sched_clock();
+               event->len = event_len;
+               memcpy(event->data, buf, len);
+               rc = 0;
+       }
+       local_irq_restore(flags);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(__dti_event);
+
+/**
+ * dti_event_early: write buffer to trace, early version
+ */
+static int dti_event_early(struct dti_handle *handle,
+                          int prio, const void* buf, size_t len)
+{
+       struct dti_early_event *event;
+       int event_len, rc = -1;
+       unsigned long flags;
+
+       if (!handle)
+               return -1;
+
+       event_len = len + sizeof(*event);
+
+       local_irq_save(flags);
+       event = dti_initbuf_reserve(handle, event_len);
+       if (event) {
+               event->cpu = smp_processor_id();
+               event->event.time = sched_clock();
+               event->event.len = len + sizeof(struct dti_event);
+               memcpy(event->event.data, buf, len);
+               rc = 0;
+       }
+       local_irq_restore(flags);
+
+       return rc;
+}
+
+/**
+ * dti_event_early_fn: early dti_event dispatch function
+ *
+ * Internal - exported for setup macros.
+ */
+int dti_event_early_fn(struct dti_handle *handle,
+                      int prio, const void* buf, size_t len)
+{
+       int rc = -1;
+
+       if (handle->initbuf && prio <= handle->initlevel)
+               rc = dti_event_early(handle, prio, buf, len);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(dti_event_early_fn);
+
+/**
+ * dti_event_normal_fn: normal dti_event dispatch function
+ *
+ * Internal - exported for setup macros.
+ */
+int dti_event_normal_fn(struct dti_handle *handle,
+                       int prio, const void* buf, size_t len)
+{
+       int rc = -1;
+
+       if (handle->info->trace->rchan)
+               rc = __dti_event(handle->info, prio, buf, len);
+       else if (prio <= handle->info->level) {
+               if (complete_channel(handle) == 0)
+                       rc = __dti_event(handle->info, prio, buf, len);
+       }
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(dti_event_normal_fn);
+
+/**
+ * dti_event_handle: write buffer to trace, handle version
+ * @handle: trace handle
+ * @prio: priority of event (the lower, the higher the priority)
+ * @buf: buffer to write
+ * @len: length of buffer
+ *
+ * returns 0, if event is written. Otherwise -1.
+ *
+ * NOTE: dti.h defines dti_event() to use this on the static handles
+ * defined using DEFINE_DTI_HANDLE, so normally you'd use dti_event()
+ * instead of using this directly.
+ */
+int dti_event_handle(struct dti_handle *handle,
+                    int prio, const void* buf, size_t len)
+{
+       return handle->event(handle, prio, buf, len);
+}
+EXPORT_SYMBOL_GPL(dti_event_handle);
+
+/**
+ *     dti_relog_initbuf - re-log static initbuf into real relay channel
+ *     @work: work struct that contains the the dti handle
+ *
+ *     The global initbuf may contain events from multiple cpus.  These
+ *     events must be put into their respective cpu buffers once the
+ *     per-cpu channel is available.
+ *
+ *     Internal - exported for setup macros.
+ */
+int dti_relog_initbuf(struct dti_handle *handle)
+{
+       void *initbuf, *reserved;
+       struct dti_early_event *event;
+       int rc = 0;
+       unsigned int left, i;
+       unsigned int n_subbufs = 2;
+       unsigned int subbuf_size = handle->initbuf_size >> 1;
+       unsigned int relog_idx = 0;
+       unsigned int relog_n = n_subbufs;
+       unsigned int sizediff = (sizeof(*event) - sizeof(struct dti_event));
+
+       rc = dti_register_channel(handle->info);
+       if (rc)
+               return rc;
+
+       if (handle->initbuf_wrapped && handle->initbuf_offset <= subbuf_size)
+               relog_idx = 1;
+
+       if (!handle->initbuf_wrapped && handle->initbuf_offset < subbuf_size)
+               relog_n = 1;
+
+       for (i = 0; i < relog_n; i++) {
+               initbuf = handle->initbuf + relog_idx * subbuf_size;
+               if (i == 0 && relog_n == 2)
+                       left = subbuf_size - handle->initbuf_pad[relog_idx];
+               else
+                       left = handle->initbuf_offset % subbuf_size;
+               
+               while (left) {
+                       event = initbuf;
+                       reserved = relay_reserve_cpu(handle->info->trace->rchan,
+                                                    event->event.len,
+                                                    event->cpu);
+                       if (reserved)
+                               memcpy(reserved, &event->event,
+                                      event->event.len);
+                       left -= event->event.len + sizediff;
+                       initbuf += event->event.len + sizediff;
+               }
+               if (++relog_idx == n_subbufs)
+                       relog_idx = 0;
+       }
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(dti_relog_initbuf);
+
+/*
+ * control files
+ */
+static int level_open(struct inode *inode, struct file *filp)
+{
+       filp->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t level_read(struct file *filp, char __user *buffer,
+                         size_t count, loff_t *ppos)
+{
+       struct dti_info *dti = filp->private_data;
+       char buf[32];
+
+       sprintf(buf, "%d\n", dti->level);
+       return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
+}
+
+static ssize_t level_write(struct file *filp, const char __user *buffer,
+                          size_t count, loff_t *ppos)
+{
+       char buf[16] = { '\0' };
+       char *tmp;
+       struct dti_info *dti = filp->private_data;
+       int new_level;
+
+       if (count > sizeof(buf) - 1)
+               return -EINVAL;
+       if (copy_from_user(buf, buffer, count))
+               return -EFAULT;
+       buf[count] = '\0';
+       if (strcmp(buf, DTI_LEVEL_OFF_STR) == 0) {
+               dti->level = DTI_LEVEL_OFF;
+               return count;
+       }
+       if (strcmp(buf, DTI_LEVEL_DESTROY_STR) == 0)
+               new_level = DTI_LEVEL_DESTROY;
+       else {
+               new_level = simple_strtol(buf, &tmp, 10);
+               if (tmp == buf)
+                       return -EINVAL;
+               if ((new_level > DTI_LEVEL_MAX) ||
+                   (new_level == DTI_LEVEL_DESTROY && !dti->handle) ||
+                   (new_level < DTI_LEVEL_DESTROY))
+                       return -EINVAL;
+       }
+       
+       if (new_level == DTI_LEVEL_DESTROY) {
+               dti_unregister_channel(dti);
+               dti->level = DTI_LEVEL_OFF;
+       }
+               
+       dti->level = new_level;
+
+       return count;
+}
+
+struct file_operations level_fops = {
+       .owner = THIS_MODULE,
+       .open =  level_open,
+       .read =  level_read,
+       .write = level_write
+};
+
+/**
+ * dti_set_level: set trace level
+ * @trace: trace handle
+ */
+void dti_set_level(struct dti_info *dti, int new_level)
+{
+       if ((new_level > DTI_LEVEL_MAX) || (new_level < DTI_LEVEL_OFF))
+               return;
+
+       dti->level = new_level;
+}
+EXPORT_SYMBOL_GPL(dti_set_level);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tom Zanussi <[EMAIL PROTECTED]>,"
+             "Dave Wilder <[EMAIL PROTECTED]>,"
+             "Michael Holzheu <[EMAIL PROTECTED]>");
+MODULE_DESCRIPTION("Linux Driver Tracing Interface");
diff --git a/drivers/base/dti_merged_view.c b/drivers/base/dti_merged_view.c
new file mode 100644
index 0000000..b3fee0f
--- /dev/null
+++ b/drivers/base/dti_merged_view.c
@@ -0,0 +1,332 @@
+/*
+ * Provides the user with a merged view of DTI's per-cpu buffers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) David Wilder, IBM Corporation, 2007
+ *
+ * 2007-April   Created by David Wilder <[EMAIL PROTECTED]>.
+ *             Sorting code adapted from dti-user (libdti.c)
+ *             created by Tom Zanussi <[EMAIL PROTECTED]>
+ */
+
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/relay.h>
+#include <linux/module.h>
+#include <linux/dti.h>
+
+struct dti_merged_view_info {
+       void *next_event; /* NULL if at end of buffer */
+       struct timeval last_read;
+       int cpu;
+       unsigned long long bytes_left;
+       void *buf;
+        loff_t pos;
+};
+
+struct dti_merged_view {
+       void *current_header; /* header currently being read */
+       ssize_t header_bytes_left;
+       char header[80];
+       void *current_event; /* record currently being read */
+       ssize_t event_bytes_left;
+       struct rchan *chan;
+       int show_timestamps;
+       struct dti_merged_view_info info[NR_CPUS]; /* per-cpu buffer info */
+} __attribute__ ((packed));
+
+struct file_operations dti_merged_view_fops;
+struct file_operations dti_merged_ts_view_fops;
+
+/**
+ * dti_remove_merged_views: remove merged views
+ * @trace: trace handle
+ */
+void dti_remove_merged_views(struct dti_info *trace)
+{
+       if (trace->merged_view)
+               debugfs_remove(trace->merged_view);
+       trace->merged_view = NULL;
+
+       if (trace->merged_ts_view)
+               debugfs_remove(trace->merged_ts_view);
+       trace->merged_ts_view = NULL;
+}
+
+/**
+ * dti_create_merged_views:
+ * Creates merged view files in the trace's parent.
+ *
+ * @trace: trace handle to create view of
+ *
+ * returns 0 on sucess.
+ */
+int dti_create_merged_views(struct dti_info *dti)
+{
+       struct dentry *parent = dti->trace->dir;
+
+       dti->merged_view = debugfs_create_file("merged", 0, parent, dti,
+                                              &dti_merged_view_fops);
+
+       if (dti->merged_view == NULL ||
+           dti->merged_view == (struct dentry *)-ENODEV) 
+               goto cleanup;
+
+       dti->merged_ts_view = debugfs_create_file("merged-ts",
+                                                 0, parent, dti,
+                                                 &dti_merged_ts_view_fops);
+
+       if (dti->merged_ts_view == NULL ||
+           dti->merged_ts_view == (struct dentry *)-ENODEV) 
+               goto cleanup;
+
+       return 0;
+cleanup:
+       dti_remove_merged_views(dti);
+
+       return -ENOMEM;
+}
+
+static int dti_merged_view_open(struct inode *inode, struct file *filp)
+{
+       struct dti_merged_view *view;
+       struct dti_info *dti = inode->i_private;
+       struct rchan *chan = dti->trace->rchan;
+       int i;
+       int trace_level;
+
+       view = kzalloc(sizeof(struct dti_merged_view), GFP_KERNEL);
+       if (!view )
+               return -ENOMEM;
+       filp->private_data = view;
+
+       /* Tracing must be shut off when copying the buffers */
+       trace_level = dti->level;
+       dti_set_level(dti, DTI_LEVEL_OFF);
+       relay_reset_consumed(chan);
+       view->chan = chan;
+
+       for_each_online_cpu(i){
+               view->info[i].buf = (void *)__get_free_page(GFP_KERNEL);
+               if (!view->info[i].buf )
+                       goto free_buf;
+               view->info[i].cpu = i;
+       }
+
+       dti_set_level(dti, trace_level);
+
+       return 0;
+free_buf:
+       for_each_possible_cpu(i)
+               if (view->info[i].buf)
+                       free_page((unsigned long)view->info[i].buf);
+       kfree(view);
+
+       return -ENOMEM;
+}
+
+static int dti_merged_ts_view_open(struct inode *inode, struct file *filp)
+{
+       struct dti_merged_view *view;
+       int ret;
+
+       ret = dti_merged_view_open(inode, filp);
+       if (!ret) {
+               view = filp->private_data;
+               view->show_timestamps = 1;
+       }
+
+       return ret;
+}
+
+static int dti_merged_view_close(struct inode *inode, struct file *filp)
+{
+       int i;
+       struct dti_merged_view *view = filp->private_data;
+       
+       for_each_possible_cpu(i)
+                if (view->info[i].buf)
+                       free_page((unsigned long)view->info[i].buf);
+        kfree(view);
+
+       return 0;
+}
+
+static int compare_recs(const void *rec1, const void *rec2)
+{
+        const struct dti_event *event1 = rec1;
+        const struct dti_event *event2 = rec2;
+
+        if (event1->time < event2->time)
+                return -1;
+        else if (event1->time > event2->time)
+                return 1;
+
+        return 0;
+}
+
+static inline int header_bytes_left(struct dti_merged_view *view)
+{
+       if (view->show_timestamps)
+               return view->header_bytes_left;
+       
+       return 0;
+}
+
+static inline void build_header(struct dti_merged_view *view, unsigned int cpu,
+                               struct dti_event *event)
+{
+       unsigned long nanosec_rem;
+
+       /* build header */
+       nanosec_rem = do_div(event->time, 1000000000);
+       view->header_bytes_left = sprintf(view->header,
+                                         "[%5lu.%06lu][%d]",
+                                         (unsigned long)event->time,
+                                         nanosec_rem/1000,
+                                         cpu);
+
+       view->current_header = view->header;
+}
+
+static int events_left(int cpu, struct dti_merged_view *view)
+{
+       size_t bytes_read;
+       int bytes_left = view->info[cpu].bytes_left;
+       struct dti_event *event = view->info[cpu].next_event;
+
+       if (bytes_left && bytes_left < sizeof(struct dti_event)) {
+               unsigned int offset = PAGE_SIZE - bytes_left;
+               void *header = view->info[cpu].buf + offset;
+               memcpy(view->info[cpu].buf, header, bytes_left);
+       } else if (bytes_left && bytes_left < event->len)
+               memcpy(view->info[cpu].buf, (void *)event, bytes_left);
+       else if (bytes_left)
+               return 1;
+
+       bytes_read = relay_kernel_read(view->chan->buf[cpu],
+                                      view->info[cpu].buf + bytes_left,
+                                      PAGE_SIZE - bytes_left,
+                                      &view->info[cpu].pos);
+
+       view->info[cpu].bytes_left += bytes_read;
+
+       if (view->info[cpu].bytes_left) {
+               view->info[cpu].next_event = view->info[cpu].buf;
+               return 1;
+       }
+
+       return 0;
+}
+
+static void *next_smallest(int *smallest_cpu, struct dti_merged_view *view )
+{
+        int i;
+        void *next, *smallest = NULL;
+
+       for_each_possible_cpu(i) {
+               if (!events_left(i, view))
+                       continue;
+
+                next = view->info[i].next_event;
+                if (next) {
+                        if (!smallest) {
+                                smallest = next;
+                                *smallest_cpu = i;
+                                continue;
+                        }
+                        if (compare_recs(next, smallest) < 0) {
+                                smallest = next;
+                                *smallest_cpu = i;
+                                continue;
+                        }
+                }
+        }
+
+        return smallest;
+}
+
+static ssize_t dti_merged_view_read(struct file *filp, char __user *buffer,
+                                   size_t count, loff_t *ppos)
+{
+       struct dti_event *event;
+       loff_t pos = *ppos;
+       int smallest_cpu=0;
+       struct dti_merged_view *view = filp->private_data;
+
+       if (pos < 0)
+               return -EINVAL;
+
+       if (!header_bytes_left(view) && !view->event_bytes_left)  {
+               event = (struct dti_event *)next_smallest(&smallest_cpu, view);
+               if (!event)
+                       return 0;
+               
+               view->current_event = event->data;
+               view->event_bytes_left = event->len - sizeof(*event);
+               view->info[smallest_cpu].next_event += event->len;
+               view->info[smallest_cpu].bytes_left -= event->len;
+
+               if (view->show_timestamps)
+                       build_header(view, smallest_cpu, event);
+       }
+
+       if ((header_bytes_left(view) < 0) || view->event_bytes_left < 0)
+               return -EINVAL;
+
+       if (header_bytes_left(view)) { 
+               if (count > view->header_bytes_left)
+                       count = view->header_bytes_left;
+
+               if (copy_to_user(buffer, view->current_header, count))
+                       return -EFAULT;
+
+               view->header_bytes_left -= count;
+               view->current_header += count;
+
+               return count;
+       }
+
+       if (view->event_bytes_left) {
+               if (count > view->event_bytes_left)
+                       count = view->event_bytes_left;
+
+               if (copy_to_user(buffer, view->current_event, count)) 
+                       return -EFAULT;
+
+               view->event_bytes_left -= count;
+               view->current_event += count;
+
+               return count;
+       }
+
+       return 0; /* not reached */
+}
+
+struct file_operations dti_merged_view_fops = {
+       .owner = THIS_MODULE,
+       .open =  dti_merged_view_open,
+       .read =  dti_merged_view_read,
+       .release = dti_merged_view_close
+};
+
+struct file_operations dti_merged_ts_view_fops = {
+       .owner = THIS_MODULE,
+       .open =  dti_merged_ts_view_open,
+       .read =  dti_merged_view_read,
+       .release = dti_merged_view_close
+};
+
diff --git a/include/linux/dti.h b/include/linux/dti.h
new file mode 100644
index 0000000..3206e6a
--- /dev/null
+++ b/include/linux/dti.h
@@ -0,0 +1,293 @@
+/*
+ *    Driver Tracing Interface.
+ *
+ *    Copyright (C) IBM Corp. 2007
+ */
+
+#ifndef _LINUX_DTI_H
+#define _LINUX_DTI_H
+
+#include <linux/gtsc.h>
+
+/*
+ * DTI 'log levels'
+ */
+
+#define DTI_LEVEL_MAX          7
+#define DTI_LEVEL_DEFAULT      3
+#define DTI_LEVEL_OFF_STR      "off"
+#define DTI_LEVEL_OFF          -1
+#define DTI_LEVEL_DESTROY_STR  "destroy"
+#define DTI_LEVEL_DESTROY      -2
+
+/* These match printk loglevels */
+#define DTI_LEVEL_EMERG                0
+#define DTI_LEVEL_ALERT                1
+#define DTI_LEVEL_CRIT         2
+#define DTI_LEVEL_ERR          3
+#define DTI_LEVEL_WARNING      4
+#define DTI_LEVEL_NOTICE       5
+#define DTI_LEVEL_INFO         6
+#define DTI_LEVEL_DEBUG                7
+
+extern struct dentry *dti_trace_root;
+struct dti_handle;
+
+/*
+ * raw channel struct
+ */
+struct dti_info {
+       char name[NAME_MAX + 1];
+       struct trace_info *trace;
+       int    level;
+       atomic_t dropped;
+       struct dentry *level_ctrl;
+       struct dentry *merged_view;
+       struct dentry *merged_ts_view;
+       struct dti_handle *handle;
+};
+
+/*
+ * autoregistering channel handle
+ */
+struct dti_handle {
+       char *name;
+       int size;
+       int initlevel;
+       struct dti_info *info;
+       spinlock_t lock;
+       int registered;
+       struct delayed_work work;
+        void *initbuf;
+       unsigned int initbuf_size;
+        unsigned int initbuf_offset;
+       unsigned int initbuf_wrapped;
+       unsigned int initbuf_pad[2];
+
+       int (*printk) (struct dti_handle *handle,
+                      int prio,
+                      const char *fmt,
+                      va_list args);
+       int (*event) (struct dti_handle *handle,
+                     int prio,
+                     const void* buf,
+                     size_t len);
+       void *(*reserve) (struct dti_handle *handle,
+                         int prio,
+                         size_t len);
+};
+
+/*
+ * DTI data header
+ */
+struct dti_event {
+       __u16 len;
+       __u64 time;
+       char data[0];
+}  __attribute__ ((packed));
+
+struct dti_early_event {
+       __u32 cpu;
+       struct dti_event event;
+}  __attribute__ ((packed));
+
+/*
+ * DTI handle-based API
+ */
+#ifdef CONFIG_DTI
+#define DEFINE_DTI_KERNEL_HANDLE(_handle, _name, _size, _initlevel, _initbuf, 
_initbuf_size) \
+       struct dti_handle _handle = {                                   \
+               .name = _name,                                          \
+               .printk = dti_printk_early_fn,                          \
+               .event = dti_event_early_fn,                            \
+               .reserve = dti_reserve_early_fn,                        \
+               .size = _size,                                          \
+               .initlevel = _initlevel,                                \
+               .lock = SPIN_LOCK_UNLOCKED,                             \
+               .work = __DELAYED_WORK_INITIALIZER(_handle.work,        \
+                       dti_register_work),                             \
+                .initbuf = _initbuf,                                   \
+                .initbuf_size = _initbuf_size,                         \
+                .initbuf_offset = 0,                                   \
+                .initbuf_pad = {0},                                    \
+                .initbuf_wrapped = 0,                                  \
+               .info = NULL,                                           \
+       };                                                              \
+       static int _handle ## _dti_init(void)                           \
+       {                                                               \
+               INIT_DTI_HANDLE(_handle);                               \
+               return 0;                                               \
+       }                                                               \
+       postcore_initcall(_handle ## _dti_init)                         \
+
+#define DEFINE_DTI_MODULE_HANDLE(_handle, _name, _size, _initlevel)    \
+       struct dti_handle _handle = {                                   \
+               .name = _name,                                          \
+               .printk = dti_printk_normal_fn,                         \
+               .event = dti_event_normal_fn,                           \
+               .reserve = dti_reserve_normal_fn,                       \
+               .size = _size,                                          \
+               .initlevel = _initlevel,                                \
+               .lock = SPIN_LOCK_UNLOCKED,                             \
+               .work = __DELAYED_WORK_INITIALIZER(_handle.work,        \
+                       dti_register_work),                             \
+               .info = NULL,                                           \
+       }
+
+#define INIT_DTI_HANDLE(_handle)                                       \
+       do {                                                            \
+               if(_handle.info)                                        \
+                       break;                                          \
+               _handle.info = dti_register_level(_handle.name, \
+                                       _handle.initlevel, &_handle);   \
+               if (!_handle.info)                                      \
+                       return -ENOMEM;                                 \
+               _handle.printk = dti_printk_normal_fn;                  \
+               _handle.event = dti_event_normal_fn;                    \
+               _handle.reserve = dti_reserve_normal_fn;                \
+               if (_handle.initbuf)                                    \
+                       dti_relog_initbuf(&_handle);                    \
+       } while (0)
+
+#define CLEANUP_DTI_HANDLE(_handle)                                    \
+       do {                                                            \
+               dti_unregister(_handle.info);                           \
+       } while (0)
+
+#ifdef MODULE
+#define DEFINE_DTI_HANDLE(_handle, _name, _size, _initlevel)           \
+       DEFINE_DTI_MODULE_HANDLE(_handle, _name, _size, _initlevel)
+#define DEFINE_DTI_EARLY_HANDLE(_handle, _name, _size, _initlevel,     \
+                               _initbuf, _initbuf_size)                \
+       DEFINE_DTI_KERNEL_HANDLE(_handle, _name, _size, _initlevel, NULL, 0)
+#else
+#define DEFINE_DTI_HANDLE(_handle, _name, _size, _initlevel)           \
+       DEFINE_DTI_KERNEL_HANDLE(_handle, _name, _size, _initlevel, NULL, 0)
+#define DEFINE_DTI_EARLY_HANDLE(_handle, _name, _size, _initlevel,     \
+                               _initbuf, _initbuf_size)                \
+       DEFINE_DTI_KERNEL_HANDLE(_handle, _name, _size, _initlevel,     \
+                                _initbuf, _initbuf_size)
+#endif /* MODULE */
+#else
+#define DEFINE_DTI_KERNEL_HANDLE(_handle, _name, _size, _initlevel,    \
+                                _initbuf, _initbuf_size)
+#define DEFINE_DTI_MODULE_HANDLE(_handle, _name, _size, _initlevel)
+#define INIT_DTI_HANDLE(_handle)
+#define CLEANUP_DTI_HANDLE(_handle)
+#define DEFINE_DTI_HANDLE(_handle, _name, _size, _initlevel)
+#define DEFINE_DTI_EARLY_HANDLE(_handle, _name, _size, _initlevel,     \
+                               _initbuf, _initbuf_size)
+#endif /* CONFIG_DTI */
+
+/*
+ * handle-based logging functions
+ */
+
+#ifdef CONFIG_DTI
+#define dti_printk(_handle, _prio, _fmt, _args...)     \
+       dti_printk_handle(&(_handle), _prio, _fmt, ## _args)
+#define dti_event(_handle, _prio, _buf, _len)  \
+       dti_event_handle(&(_handle),    _prio, _buf, _len)
+#define dti_reserve(_handle, _prio, _len)      \
+       dti_reserve_handle(&(_handle), _prio, _len)
+#else
+#define dti_printk(_handle, _prio, _fmt, ...)
+#define dti_event(_handle, _prio, _buf, _len)
+#define dti_reserve(_handle, _prio, _len)
+#endif
+
+/**
+ * dti_assert: stop tracing if assertion fails, handle version
+ * @_handle: trace handle
+ * @_expr: expression to test
+ *
+ * If _expr evaluates false, tracing is stopped for _handle
+ */
+#ifdef CONFIG_DTI
+#define dti_assert(_handle, _expr)                                     \
+       do {                                                            \
+               if (!(_expr) && _handle.info)                           \
+                       dti_set_level(_handle.info, DTI_LEVEL_OFF);     \
+       } while (0)
+#else
+#define dti_assert(_handle, _expr)
+#endif
+
+/*
+ * DTI low-level API
+ */
+
+#ifdef CONFIG_DTI
+struct dti_info *dti_register(const char *name, int size, int init_level);
+struct dti_info *__dti_register(const char *name, int size, int nr_sub,
+                               int init_level);
+void dti_unregister(struct dti_info *trace);
+int __dti_printk(struct dti_info *trace, int prio, const char* fmt, ...);
+int __dti_event(struct dti_info *trace, int prio, const void* buf, size_t len);
+void *__dti_reserve(struct dti_info *trace, int prio, size_t len);
+void dti_set_level(struct dti_info *trace, int new_level);
+#else
+static inline struct dti_info *dti_register(const char *name, int size,
+                                           int init_level)
+{
+       return NULL;
+}
+static inline struct dti_info *__dti_register(const char *name, int size,
+                                             int nr_sub, int init_level)
+{
+       return NULL;
+}
+static inline void dti_unregister(struct dti_info *trace) {}
+static inline int __dti_printk(struct dti_info *trace, int prio,
+                              const char* fmt, ...) { return 0; }
+static inline int __dti_event(struct dti_info *trace, int prio,
+                             const void* buf, size_t len) { return 0; }
+static inline void *__dti_reserve(struct dti_info *trace, int prio, size_t len)
+{
+       return NULL;
+}
+static inline void dti_set_level(struct dti_info *trace, int new_level) {}
+#endif
+
+/**
+ * __dti_assert_raw: stop tracing if assertion fails, low-level version
+ * @_info: trace info struct
+ * @_expr: expression to test
+ *
+ * If _expr evaluates false, tracing is stopped for _info
+ */
+#ifdef CONFIG_DTI
+#define __dti_assert(_info, _expr)                                     \
+       do {                                                            \
+               if (!(_expr) && _info)                                  \
+                       dti_set_level(_info, DTI_LEVEL_OFF);            \
+       } while (0)
+#else
+#define __dti_assert(_info, _expr)
+#endif
+
+/*
+ * non-API declarations
+ */
+
+struct dti_info *dti_register_level(const char *name, int level,
+                                   struct dti_handle *handle);
+void dti_register_work(struct work_struct *work);
+int dti_printk_handle(struct dti_handle *handle,int prio,
+                     const char* fmt, ...);
+int dti_printk_early_fn(struct dti_handle *handle, int prio, const char* fmt,
+                       va_list args);
+int dti_printk_normal_fn(struct dti_handle *handle, int prio, const char* fmt,
+                        va_list args);
+int dti_event_handle(struct dti_handle *handle, int prio, const void* buf,
+                    size_t len);
+int dti_event_early_fn(struct dti_handle *handle, int prio, const void* buf,
+                      size_t len);
+int dti_event_normal_fn(struct dti_handle *handle, int prio, const void* buf,
+                       size_t len);
+void *dti_reserve_handle(struct dti_handle *handle, int prio, size_t len);
+void *dti_reserve_early_fn(struct dti_handle *handle, int prio, size_t len);
+void *dti_reserve_normal_fn(struct dti_handle *handle, int prio, size_t len);
+int dti_relog_initbuf(struct dti_handle *handle);
+
+#endif /* _LINUX_DTI_H */



-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to