Re: [RFC PATCH 2/6] Driver Tracing Interface (DTI) code

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Please see comments below,

* Tom Zanussi ([email protected]) wrote:
> 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];
> +

Why do you do a supplementary copy of this data? It could be written
directly to the buffers.

> +/*
> + * 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();

sched_clock() is dangerous.. you have to make sure you provide correct
clock errors in the trace parsing.. not exactly something interesting
when you are looking at the "one" case over 10000 that was faulty. See
the i386 sched-clock.c:

 * Scheduler clock - returns current time in nanosec units.
 * All data is local to the CPU.
 * The values are approximately[1] monotonic local to a CPU, but not
 * between CPUs.   There might be also an occasionally random error,
 * but not too bad. Between CPUs the values can be non monotonic.
 *
 * [1] no attempt to stop CPU instruction reordering, which can hit
 * in a 100 instruction window or so.

How do you plan to use these timestamps to reorder events across CPUs ?

> +	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);

As I said, why not put the result of the vsnprintf directly in the
buffer after the relay_reserve ?

> +	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)
> +{

What do you do with the data recorded by the system while you do this
buffer copy? Is the timestamp ordering kept? Why do you copy the static
buffers into other buffers at all anyway ? They could be exported
through relay (if it supported exporting statically mapped buffers).

> +	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);
> +

The relay patches should come before this one in the patchset, since
this code won't compile without relay_kernel_read(), relay_reserve_cpu()
and relay_reset_consumed().

> +	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/
> 

-- 
Mathieu Desnoyers
Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68
-
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/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux