Index: kernel/sched.c
===================================================================
--- kernel/sched.c	(.../tags/starting_base_kernel)	(revision 95)
+++ kernel/sched.c	(.../trunk/kernel)	(revision 95)
@@ -44,6 +44,7 @@
 #include <linux/seq_file.h>
 #include <linux/syscalls.h>
 #include <linux/times.h>
+#include <linux/logdev.h>
 #include <asm/tlb.h>
 
 #include <asm/unistd.h>
@@ -1392,6 +1393,8 @@
 		rq->prev_mm = oldmm;
 	}
 
+	LOGSWITCH(prev,next);
+
 	/* Here we just switch the register state and the stack. */
 	switch_to(prev, next, prev);
 
Index: include/linux/logdev.h
===================================================================
--- include/linux/logdev.h	(.../tags/starting_base_kernel)	(revision 0)
+++ include/linux/logdev.h	(.../trunk/kernel)	(revision 95)
@@ -0,0 +1,148 @@
+/*
+ * Logdevice - A device used to record debuging information in the kernel.
+ *             It uses a large memory ring buffer consisting of individual pages
+ *             to keep down on hogging large sections. A user may then read the device
+ *             to get debugging information out of it.  Or if configured, this can
+ *             be dumped to the network on a system crash.
+ *
+ * Copyright - 2005 - Steven Rostedt, Kihon Technologies, (rostedt at kihontech dot com)
+ */
+#ifndef _LOG_DEV_H
+#define _LOG_DEV_H
+
+#define LOGDEV_CUSTOM 0x1afb
+#define LOGDEV_SWITCH_ID 0x2afc
+#define LOGDEV_PKT_ID 0x42aa
+#define LOGDEV_PRINT 0x4adb
+#define LOGDEV_PRINT_TIME 0x4ade
+
+struct logdev_switch_struct {
+	int id;
+	int size;
+	int cpu;
+	struct timeval tv;
+	short pid_prev;
+	short pid_next;
+	short prev_len;
+	short next_len;
+	char prev_comm[0];
+	char next_comm[0];
+};
+
+struct logdev_pkt {
+	int id;
+	int size;
+	int cpu;
+	struct timeval tv;
+	short protocol;
+	short dir;
+	char packet[0];
+};
+
+struct logdev_print {
+	int id;
+	int size;
+	char str[0];
+};
+
+struct logdev_print_time {
+	int id;
+	int size;
+	struct timeval tv;
+	char str[0];
+};
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
+/*
+ * I also use this with Ingo Molnar's RT kernel.
+ */
+#ifndef CONFIG_PREEMPT_RT
+#undef raw_spinlock_t
+#undef RAW_SPIN_LOCK_UNLOCKED
+#define raw_spinlock_t spinlock_t
+#define RAW_SPIN_LOCK_UNLOCKED SPIN_LOCK_UNLOCKED
+#endif
+
+#ifdef CONFIG_LOGDEV_HOOKS
+extern int (*logdev_print_hook)(const char *fmt, ...);
+extern int (*logdev_print_time_hook)(const char *fmt, ...);
+extern void (*logdev_record_switch_hook)(struct task_struct *a,struct task_struct *b);
+extern int (*logdev_dev_hook)(int id, int size, const void *data);
+extern int (*logdev_write_hook)(const char *data, int size);
+extern int (*logdev_read_hook)(char *buf, int size);
+extern void (*logdev_pkt_hook)(struct sk_buff *skb, int dir);
+extern raw_spinlock_t logdev_add_hook_lock;
+extern void (*logdev_dump_hook)(void);
+extern void (*logdev_dumpnet_hook)(void);
+
+extern atomic_t logdev_level;
+extern atomic_t logdev_switch;
+extern atomic_t logdev_print_enabled;
+
+#define logdev_switch_on() atomic_inc(&logdev_switch)
+#define logdev_switch_off() atomic_dec(&logdev_switch)
+
+/*
+ * Only exists when compiled in.
+ */
+#ifdef CONFIG_LOGDEV
+void logdev_gettimeofday(struct timeval *tv);
+#endif
+
+#ifndef CONFIG_LOGDEV
+/*
+ * dumpnet is expected to be called on a bug, so we don't
+ * need to lock it.
+ */
+#define LOGDUMP() do { if (logdev_dump_hook) logdev_dump_hook(); } while(0)
+#define LOGDUMPNET() do { if (logdev_dumpnet_hook) logdev_dumpnet_hook(); } while(0)
+
+#define LOGDEV(x,y...) do { \
+	spin_lock(&logdev_add_hook_lock); \
+	if (logdev_##x##_hook) \
+		logdev_##x##_hook(y); \
+	spin_unlock(&logdev_add_hook_lock); \
+} while (0)
+#else
+
+void logdev_record_switch(struct task_struct *prev, struct task_struct *next);
+void logdev_pkt(struct sk_buff *skb, int direction);
+int logdev_print(const char *str, ...)
+	__attribute__ ((format (printf, 1, 2)));
+int logdev_print_time(const char *str, ...)
+	__attribute__ ((format (printf, 1, 2)));
+int logdev_record(int id, int size, const void *data);
+int logdev_record_write(const char *data, int size);
+int logdev_record_read(char *data, int size);
+void logdev_dumpnet(void);
+void logdev_dump(void);
+
+#define LOGDUMP() logdev_dump()
+#define LOGDUMPNET() logdev_dumpnet()
+#define LOGDEV(x,y...) logdev_##x(y)
+
+#endif
+#define LOGPRINT(x...) do { if (atomic_read(&logdev_print_enabled)) LOGDEV(print,x); } while(0)
+#define LOGTPRINT(x...) do { if (atomic_read(&logdev_print_enabled)) LOGDEV(print_time,x); } while(0)
+#define LOGSWITCH(prev,next) LOGDEV(record_switch,prev,next)
+#define LOGPKT(skb,direction) LOGDEV(pkt,skb,direction)
+
+#define lprint(x...) do { if (atomic_read(&logdev_switch)) { LOGPRINT(x); } } while(0)
+
+#else /* !LOGDEV_HOOKS */
+#define LOGDUMP() do {} while(0)
+#define LOGDEV(x,y...) do {} while(0)
+#define LOGSWITCH(prev,next) do {} while(0)
+#define LOGPKT(skb,direction) do {} while(0)
+#define logdev_switch_on() do {} while(0)
+#define logdev_switch_off() do {} while(0)
+#endif /* LOGDEV_HOOKS */
+
+#endif
+
+#endif
Index: lib/Kconfig.debug
===================================================================
--- lib/Kconfig.debug	(.../tags/starting_base_kernel)	(revision 95)
+++ lib/Kconfig.debug	(.../trunk/kernel)	(revision 95)
@@ -107,6 +107,50 @@
         If you're truly short on disk space or don't expect to report any
         bugs back to the UML developers, say N, otherwise say Y.
 
+config LOGDEV_HOOKS
+	bool "Enable hooks for logdev device"
+	default n
+	help
+	  If you plan on using the logdev device, you need to include this
+	  into your kernel.  This will add the hooks necessary for the logdev
+	  device to be loaded.
+	
+	
+config LOGDEV
+	tristate "Enable logdev device"
+	depends on LOGDEV_HOOKS
+	default m
+	help
+	  The logdev device stores data into the kernel that can be retrieved
+	  later through a misc device (major 10).  The minor number is 
+	  dynamic and is posted through /proc/logdev_minor. Utilities
+	  to open and read the device can be found at
+	  http://www.kihontech.com/logdev
+	  
+	  This device allows for tracing lots of information in the kernel
+	  when simply printk is too expensive.  When the logdev is initialized,
+	  it allocates a default of 1 meg of memory (in page size units). This
+	  allows for saving data in a ring buffer without the need to allocate.
+
+
+config LOGDEV_PAGES
+	int "Number of pages to allocate for logdev device"
+	depends on LOGDEV
+	default 256
+	help
+	  The Logdev device allocates a number of pages for the sole
+	  purpose of logging data.  This is the number of pages that
+	  the Logdev device should allocate upon loading / initializing.
+
+config LOGDEV_PRINT_ENABLED
+	bool "Default Logdev prints should be enabled on startup"
+	depends on LOGDEV_HOOKS
+	default y
+	help
+	  Enable this if you expect the LOGPRINT macros to be enabled
+	  as soon as the logdev device is loaded. Otherwise you must 
+	  enable it with /proc/logdev_print
+
 if !X86_64
 config FRAME_POINTER
 	bool "Compile the kernel with frame pointers"
Index: drivers/char/logdev.c
===================================================================
--- drivers/char/logdev.c	(.../tags/starting_base_kernel)	(revision 0)
+++ drivers/char/logdev.c	(.../trunk/kernel)	(revision 95)
@@ -0,0 +1,1191 @@
+/*
+ * logdev.c
+ *
+ * Copyright (C) 2004 Steven Rostedt <steven.rostedt@kihontech.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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; version 2 of the License (not later!)
+ *
+ * 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
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/rwsem.h>
+#include <linux/wait.h>
+#include <linux/proc_fs.h>
+#include <linux/time.h>
+#include <linux/netpoll.h>
+#include <linux/miscdevice.h>
+
+#include <linux/logdev.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+static char *logdev_version = "0.1.0";
+
+#ifdef CONFIG_DEVFS_FS
+static devfs_handle_t logdev_devfs;
+#endif
+
+static int pages = CONFIG_LOGDEV_PAGES;
+
+module_param(pages, int, 0644);
+MODULE_PARM_DESC(pages, " number of pages to allocate for the logdev ring buffer");
+MODULE_LICENSE("GPL");
+
+#define LOGDEV_NAME "logdev"
+#define LOGDEV_PROC_ENTRY "logdev"
+#define LOGDEV_PROC_SW "logdev_switch"
+#define LOGDEV_PROC_LEVEL "logdev_level"
+#define LOGDEV_PROC_PRINT "logdev_print"
+#define LOGDEV_PROC_MINOR "logdev_minor"
+
+/*
+ * doesn't really need to be atomic, but helps that we don't need to
+ * write another proc interface function.
+ */
+static atomic_t logdev_minor = ATOMIC_INIT(0);
+static struct proc_dir_entry *logdev_proc_entry;
+static struct proc_dir_entry *logdev_proc_sw;
+static struct proc_dir_entry *logdev_proc_print;
+static struct proc_dir_entry *logdev_proc_level;
+static struct proc_dir_entry *logdev_proc_minor;
+
+
+/*
+ * The testing markers are used to cut and paste into a userland program for
+ * easy algorithm debugging.
+ */
+
+/********************* Start testing code here ***********************/
+
+struct logdev_entry {
+	unsigned int head;
+	unsigned int tail;
+	char *dat;
+};
+
+static struct logdev_dev {
+	struct logdev_entry *entry;
+	int size;
+	int len;
+	int start;
+	int end;
+	int corrupted;
+	wait_queue_head_t wait;
+	raw_spinlock_t lock;
+} logdev_dev;
+
+static int logdev_copy_to_dev(struct logdev_dev *dev, const void *dat,
+			      int size);
+
+#define logdev_gettimeofday do_gettimeofday
+
+/*
+ * logdev_record_switch is used to track context switches.
+ *
+ * If the logdev_switch is not set, then this doesn't record. Thus allowing
+ * you to just record the context switches that are needed to record.
+ * Just use logdev_switch_on and logdev_switch_off to turn on this function.
+ */
+void logdev_record_switch(struct task_struct *prev, struct task_struct *next)
+{
+	struct logdev_switch_struct rs;
+	struct logdev_dev *dev;
+
+	if (!atomic_read(&logdev_switch))
+		return;
+	
+	
+	dev = &logdev_dev;
+
+	rs.id = LOGDEV_SWITCH_ID;
+	logdev_gettimeofday(&rs.tv);
+	rs.cpu = smp_processor_id();
+	rs.pid_prev = prev->pid;
+	rs.pid_next = next->pid;
+	rs.prev_len = strlen(prev->comm);
+	rs.next_len = strlen(next->comm);
+	rs.size = sizeof(rs) + rs.prev_len + rs.next_len;
+	
+	spin_lock(&dev->lock);
+	logdev_copy_to_dev(dev,&rs,sizeof(rs));
+	logdev_copy_to_dev(dev,prev->comm,rs.prev_len);
+	logdev_copy_to_dev(dev,next->comm,rs.next_len);
+	spin_unlock(&dev->lock);
+
+}
+
+#ifdef __KERNEL__ /* we debug in userland but not this function */
+#include <linux/skbuff.h>
+#include <net/tcp.h>
+/*
+ * This routine may be placed in the network code if you want to see
+ * what packets are traveling through. You may use the direction to
+ * indicate if they are coming or going.
+ *    direction = 0 :   saddr ==> daddr
+ *                1 :   saddr <== daddr
+ */
+void logdev_pkt(struct sk_buff *skb, int direction)
+{
+	struct logdev_pkt rs;
+	struct logdev_dev *dev;
+	struct tcphdr *th;
+	struct iphdr *iph;
+	unsigned long flags;
+
+	dev = &logdev_dev;
+
+	local_irq_save(flags);
+
+	rs.id = LOGDEV_PKT_ID;
+	rs.size = sizeof(rs);
+
+	logdev_gettimeofday(&rs.tv);
+
+	iph = skb->nh.iph;
+
+	rs.protocol = skb->protocol;
+	rs.dir = direction;
+	rs.cpu = smp_processor_id();
+		
+	if ((skb->protocol != __constant_htons(ETH_P_IP)) ||
+	    (skb->pkt_type != PACKET_HOST) ||
+	    (iph->protocol != IPPROTO_TCP)) {
+		/* Just copy the first 20 bytes of the packet */
+		
+		rs.size = sizeof(rs) + 20;
+		
+		spin_lock(&dev->lock);
+		logdev_copy_to_dev(dev,&rs,sizeof(rs));
+		logdev_copy_to_dev(dev,iph,20);
+		spin_unlock(&dev->lock);
+
+	} else {
+		int iplen;
+		int tcplen;
+
+		iplen = iph->ihl<<2;
+		th = (struct tcphdr*)((void*)(skb->nh.iph)+(iplen));
+		tcplen = th->doff<<2;
+		
+		rs.size = sizeof(rs) + iplen + tcplen;
+
+		spin_lock(&dev->lock);
+		logdev_copy_to_dev(dev,&rs,sizeof(rs));
+		logdev_copy_to_dev(dev,iph,iplen+tcplen);
+		spin_unlock(&dev->lock);
+	}
+	
+
+	local_irq_restore(flags);
+}
+#endif /* endif __KERNEL__ */
+
+
+#define ENTRY_SIZE(e) ((((e)->tail - (e)->head)) & (PAGE_SIZE-1))
+#define ENTRY_FREE(e) ((PAGE_SIZE-1) - ENTRY_SIZE(e))
+#define ENTRY_ADD(e,x) ((e) = ((e) + x) & (PAGE_SIZE-1))
+#define ENTRY_INC(e) ENTRY_ADD(e,1)
+#define ENTRY_MAX (PAGE_SIZE-1)
+
+/*
+ * User buffer is used to get data from userland, and this can sleep when
+ * copying.
+ */
+static DECLARE_RWSEM(user_sem);
+static char user_buffer[PAGE_SIZE];
+
+/*
+ * Kernel buffer is used inside the kernel, and MUST NOT SLEEP.  So
+ * we use a raw spinlock to synchronize things.  This lock must be taken first
+ * before the logdev lock can be taken (unless of course you don't need to 
+ * take this lock at all).
+ */
+static raw_spinlock_t kern_buffer_lock = RAW_SPIN_LOCK_UNLOCKED;
+static char kern_buffer[PAGE_SIZE];
+
+/*
+ * logdev_print acts like printk but it writes to the logdev device instead
+ *  of a console.
+ */
+int logdev_print(const char *str, ...)
+{
+	va_list va;
+	int len;
+	struct logdev_print rs;
+	struct logdev_dev *dev;
+	unsigned long flags;
+
+	dev = &logdev_dev;
+
+	spin_lock(&kern_buffer_lock);
+
+	va_start(va,str);
+	len = vsnprintf(kern_buffer,PAGE_SIZE,str,va);
+	va_end(va);
+
+	if (len > PAGE_SIZE) {
+		kern_buffer[PAGE_SIZE-1] = 0;
+		len = PAGE_SIZE;
+	}
+
+	rs.id = LOGDEV_PRINT;
+	rs.size = sizeof(rs) + len;
+	
+	spin_lock_irqsave(&dev->lock,flags);
+	logdev_copy_to_dev(dev, &rs, sizeof(rs));
+	logdev_copy_to_dev(dev, kern_buffer, len);
+	spin_unlock_irqrestore(&dev->lock,flags);
+
+	spin_unlock(&kern_buffer_lock);
+
+	return len;
+}
+
+/*
+ * logdev_print_time is the same as logdev_print but it attaches a timestamp to it.
+ *  saves on doing it yourself.
+ */
+int logdev_print_time(const char *str, ...)
+{
+	va_list va;
+	int len;
+	struct logdev_print_time rs;
+	struct logdev_dev *dev;
+	unsigned long flags;
+
+	dev = &logdev_dev;
+
+	spin_lock(&kern_buffer_lock);
+
+	va_start(va,str);
+	len = vsnprintf(kern_buffer,PAGE_SIZE,str,va);
+	va_end(va);
+
+	if (len > PAGE_SIZE) {
+		kern_buffer[PAGE_SIZE-1] = 0;
+		len = PAGE_SIZE;
+	}
+
+	rs.id = LOGDEV_PRINT_TIME;
+	rs.size = sizeof(rs) + len;
+	logdev_gettimeofday(&rs.tv);
+	
+	spin_lock_irqsave(&dev->lock,flags);
+	logdev_copy_to_dev(dev, &rs, sizeof(rs));
+	logdev_copy_to_dev(dev, kern_buffer, len);
+	spin_unlock_irqrestore(&dev->lock,flags);
+
+	spin_unlock(&kern_buffer_lock);
+
+	return len;
+}
+
+int logdev_copy_from_dev(struct logdev_dev *dev, char *buf, int size)
+{
+	struct logdev_entry *entry = &dev->entry[dev->start];
+	int ret = 0;
+	
+	if (size < 0) {
+		printk("logdev_copy_from_dev: size < 0 ???\n");
+		return -1;
+	}
+	if (size > dev->size)
+		size = dev->size;
+	
+	while (size && dev->size) {
+		int copy = size;
+		int used;
+
+		if (!ENTRY_SIZE(entry)) {
+			dev->start = (dev->start + 1) % dev->len;
+			entry = &dev->entry[dev->start];
+		}
+			
+		if (copy > (used=ENTRY_SIZE(entry)))
+			copy = used;
+		if (entry->head+copy > PAGE_SIZE)
+			copy = PAGE_SIZE - entry->head;
+		memcpy(buf,entry->dat+entry->head,copy);
+		ENTRY_ADD(entry->head,copy);
+		buf += copy;
+		dev->size -= copy;
+		size -= copy;
+		ret += copy;
+
+	}
+
+	return ret;
+}
+
+static void move_start_to_next_entry(struct logdev_dev *dev)
+{
+	struct logdev_entry *entry = &dev->entry[dev->start];
+	int start = dev->start;
+	int size;
+	struct header {
+		int id;
+		int size;
+	} hdr;
+	
+	if (dev->corrupted) {
+		/* Don't trust headers, just skip to the next entry */
+		dev->size -= ENTRY_SIZE(entry);
+		entry->head = entry->tail = 0;
+		dev->start = (dev->start+1) % dev->len;
+		return;
+	}
+
+	while (start == dev->start) {
+		logdev_copy_from_dev(dev,(char*)&hdr,sizeof(hdr));
+		entry = &dev->entry[dev->start];
+		switch (hdr.id) {
+		case LOGDEV_CUSTOM:
+		case LOGDEV_SWITCH_ID:
+		case LOGDEV_PKT_ID:
+		case LOGDEV_PRINT:
+		case LOGDEV_PRINT_TIME:
+			break;
+		default:
+			dev->corrupted = 1;
+		}
+		if (dev->corrupted) {
+			if (start == dev->start)
+				move_start_to_next_entry(dev);
+			return;
+		}
+
+		size = sizeof(hdr);
+		while (size < hdr.size) {
+			int count = hdr.size - size;
+			if (count > ENTRY_SIZE(entry))
+				count = ENTRY_SIZE(entry);
+			dev->size -= count;
+			size += count;
+			ENTRY_ADD(entry->head,count);
+			if (ENTRY_SIZE(entry) == 0) {
+				dev->start = (dev->start + 1) % dev->len;
+				entry->head = entry->tail = 0;
+				entry = &dev->entry[dev->start];
+			}
+		}
+	}
+}
+
+static int logdev_copy_to_dev(struct logdev_dev *dev, const void *dat, 
+			      int size)
+{
+	struct logdev_entry *entry = &dev->entry[dev->end];
+	const char *buf = dat;
+	int ret = 0;
+	
+	while (size) {
+		int copy;
+		int free;
+		if (ENTRY_SIZE(entry) == ENTRY_MAX) {
+			dev->end = (dev->end+1) % dev->len;
+			entry = &dev->entry[dev->end];
+			/* if we wrapped, then clear out this entire
+			 * buffer.
+			 */
+			if (dev->end == dev->start) {
+				move_start_to_next_entry(dev);
+			}
+		}
+		copy = size;
+		if (copy > (free=ENTRY_FREE(entry)))
+			copy = free;
+		if (entry->tail+copy > PAGE_SIZE)
+			copy = PAGE_SIZE - entry->tail;
+		memcpy(entry->dat+entry->tail,buf,copy);
+		ENTRY_ADD(entry->tail,copy);
+		buf += copy;
+		size -= copy;
+		ret += copy;
+		dev->size += copy;
+
+	}
+#if 0
+	/* wake up those waiting for data */
+	if (waitqueue_active(&dev->wait))
+		wake_up_interruptible(&dev->wait);
+#endif
+
+	return ret;
+
+}
+
+
+/*
+ * If you feel like recording your own data, you can use logdev_record.
+ *  just pass your own id, size and data.  The size is the size of 
+ *  the data being passed and not the size actually being written to the device.
+ *  That is already calculated.
+ *
+ * It is recommended to use LOGDEV_CUSTOM as the id so that you don't corrupt
+ *  the ring buffer (corrupt is as strong word since it still works, just not so
+ *  automatic). And you should have you data have some sort of tag to figure out
+ *  what it is later.
+ */
+int logdev_record(int id, int size, const void *data)
+{
+	struct logdev_dev *dev;
+	int ret;
+	int rsize = size + sizeof(rsize) + sizeof(id);
+	unsigned long flags;
+
+	dev = &logdev_dev;
+
+	spin_lock_irqsave(&dev->lock,flags);
+	logdev_copy_to_dev(dev, &id, sizeof(id));
+	logdev_copy_to_dev(dev, &rsize, sizeof(rsize));
+	ret = logdev_copy_to_dev(dev, data, size);
+	spin_unlock_irqrestore(&dev->lock,flags);
+
+	return ret;
+}
+
+/*
+ * If you just want to write into the buffer using your own methods, then this
+ * is perfectly fine. Just pass in your data and the size of the data being 
+ * passed in. You can read it out later with logdev_record_read. But you wont
+ * have the benefits of keeping integrity when the buffer overflows.
+ */
+int logdev_record_write(const char *data, int size)
+{
+	struct logdev_dev *dev;
+	int ret;
+	unsigned long flags;
+
+	dev = &logdev_dev;
+
+	spin_lock_irqsave(&dev->lock,flags);
+	ret = logdev_copy_to_dev(dev, data, size);
+	spin_unlock_irqrestore(&dev->lock,flags);
+
+	return ret;
+}
+
+/*
+ * logdev_record_read reads some data from the logdev device no matter what
+ * it was.
+ */ 
+int logdev_record_read(char *data, int size)
+{
+	struct logdev_dev *dev;
+	int ret;
+	unsigned long flags;
+
+	dev = &logdev_dev;
+
+	spin_lock_irqsave(&dev->lock,flags);
+	ret = logdev_copy_from_dev(dev, data, size);
+	spin_unlock_irqrestore(&dev->lock,flags);
+
+	return ret;
+}
+
+/************************ Stop testing code here ******************************/
+
+ssize_t logdev_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
+{
+	struct logdev_dev *dev = (struct logdev_dev*)filp->private_data;
+	unsigned long flags;
+	
+	if (count > PAGE_SIZE)
+		count = PAGE_SIZE;
+
+	down_write(&user_sem);
+
+	if (copy_from_user(user_buffer,buf,count)) {
+		count = -EFAULT;
+		goto out;
+	}
+
+	spin_lock_irqsave(&dev->lock,flags);
+	count = logdev_copy_to_dev(dev,user_buffer,count);
+	spin_unlock_irqrestore(&dev->lock,flags);
+
+	/* wake up those waiting for data */
+	if (waitqueue_active(&dev->wait))
+		wake_up_interruptible(&dev->wait);
+
+out:	
+	up_write(&user_sem);
+	return count;
+}
+	
+ssize_t logdev_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
+{
+	struct logdev_dev *dev = (struct logdev_dev*)filp->private_data;
+	unsigned long flags;
+	
+	down_write(&user_sem);
+	spin_lock_irqsave(&dev->lock,flags);
+
+	if (!dev->size) {
+
+		/* TBD - FIXME */
+#if 1
+		
+		count = 0;
+		goto out;
+#endif
+
+		if (filp->f_flags & O_NONBLOCK) {
+			count = -EAGAIN;
+			goto out;
+		}
+
+		do {
+			DECLARE_WAITQUEUE(wait,current);
+			current->state = TASK_INTERRUPTIBLE;
+			add_wait_queue(&dev->wait,&wait);
+			spin_unlock_irqrestore(&dev->lock,flags);
+			schedule();
+			spin_lock_irqsave(&dev->lock,flags);
+			remove_wait_queue(&dev->wait,&wait);
+			if (dev->size)
+				break;
+			if (signal_pending(current)) {
+				count = -ERESTARTSYS;
+				goto out;
+			}
+		} while(1);
+	}
+	
+	if (count > PAGE_SIZE)
+		count = PAGE_SIZE;
+
+
+	count = logdev_copy_from_dev(dev,user_buffer,count);
+
+ out:
+	/* We can't be corrupted if we have no data */
+	if (!dev->size)
+		dev->corrupted = 0;
+	spin_unlock_irqrestore(&dev->lock,flags);
+
+	if (count > 0) {
+		/* Well if we fail here, we just lost the data read :-( */
+		if (copy_to_user(buf,user_buffer,count))
+			count = -EFAULT;
+	}
+	up_write(&user_sem);
+
+	return count;
+}
+	
+
+static int logdev_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg)
+{
+	return -ENOTTY;
+}
+
+static int logdev_close(struct inode *inode, struct file *filp)
+{
+#if 0
+	struct logdev_dev *dev = (struct logdev_dev*)filp->private_data;
+#endif
+	return 0;
+}
+
+	
+static int logdev_open (struct inode *inode, struct file *filp)
+{
+	struct logdev_dev *dev;
+
+	dev = &logdev_dev;
+
+	filp->private_data = dev;
+	
+	return 0;
+}
+
+static struct file_operations logdev_fops = {
+	.read		= logdev_read,
+      	.write		= logdev_write,
+	.ioctl		= logdev_ioctl,
+	.open		= logdev_open,
+	.release	= logdev_close,
+	.llseek		= no_llseek,
+};
+
+/*
+ * The following UDP sending is based off of netconsole.
+ */
+static char config[256];
+module_param_string(logdevnet, config, 256, 0);
+MODULE_PARM_DESC(logdevnet, " logdevnet=[src-port]@[src-ip]/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr]\n");
+
+static struct netpoll np = {
+	.name = "logdevnet",
+	.dev_name = "eth0",
+	.local_port = 6665,
+	.remote_port = 6666,
+	.remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+};
+static int configured = 0;
+
+#define MAX_PRINT_CHUNK 1000
+
+static void write_msg(const char *msg, unsigned int len)
+{
+	int frag, left;
+	unsigned long flags;
+
+	if (!np.dev)
+		return;
+
+	local_irq_save(flags);
+#ifdef CONFIG_PREEMPT_RT
+	/*
+	 * A bit hairy. Netconsole uses mutexes (indirectly) and
+	 * thus must have interrupts enabled:
+	 */
+	if (0) local_irq_enable();
+#endif
+
+	for(left = len; left; ) {
+		frag = min(left, MAX_PRINT_CHUNK);
+		netpoll_send_udp(&np, msg, frag);
+		msg += frag;
+		left -= frag;
+	}
+
+	local_irq_restore(flags);
+}
+
+static int option_setup(char *opt)
+{
+	configured = !netpoll_parse_options(&np, opt);
+	return 0;
+}
+
+__setup("logdevnet=", option_setup);
+
+static void __logdev_dump(int net)
+{
+	int r;
+	int i;
+	int count;
+	char *msg;
+	struct logdev_dev *dev;
+	struct header {
+		int id;
+		int size;
+	} hdr;
+	static int started  = 0;
+	int corrupt = 0;
+
+	if (started)
+		return;
+
+	if (!configured)
+		return;
+
+	dev = &logdev_dev;
+	
+	if (dev->corrupted) {
+		msg = ">>>>> Warning: Logdev corrupted <<<<<\n";
+		i = strlen(msg);
+		if (net)
+			write_msg(msg,i);
+		else 
+			printk(msg);
+	}
+
+	while ((r = logdev_copy_from_dev(dev,(char *)&hdr,sizeof(hdr))) == sizeof(hdr)) {
+		switch (hdr.id) {
+		case LOGDEV_PRINT:
+			for (i=r; i < hdr.size; i += r) {
+				count = hdr.size - i;
+				if (count > PAGE_SIZE-1)
+					count = PAGE_SIZE-1;
+				r = logdev_copy_from_dev(dev,user_buffer,count);
+				if (r < 0)
+					break;
+				user_buffer[count] = 0;
+				if (net)
+					write_msg(user_buffer,count);
+				else
+					printk(user_buffer);
+			}
+			break;
+		case  LOGDEV_SWITCH_ID:
+		{
+			struct logdev_switch_struct rs;
+			memcpy(&rs,&hdr,sizeof(hdr));
+
+			msg = ">>>> IN LOGDEV SWITCH <<<<\n";
+			i = strlen(msg);
+			if (net)
+				write_msg(msg,i);
+			else
+				printk(msg);
+			
+			logdev_copy_from_dev(dev,((char*)&rs)+sizeof(hdr),sizeof(rs) - sizeof(hdr));
+			i = snprintf(user_buffer,100,"%d:%06d  ",
+				     (int)rs.tv.tv_sec,(int)rs.tv.tv_usec);
+			logdev_copy_from_dev(dev,user_buffer+i,rs.prev_len);
+			i += rs.prev_len;
+			
+			i += snprintf(user_buffer+i,100,":%d -->> ",rs.pid_prev);
+			logdev_copy_from_dev(dev,user_buffer+i,rs.next_len);
+			i += rs.next_len;
+			i += snprintf(user_buffer+i,100,":%d\n",rs.pid_next);
+
+			if (net)
+				write_msg(user_buffer,i);
+			else
+				printk(user_buffer);
+
+			break;
+		}
+
+		case LOGDEV_CUSTOM:
+		case LOGDEV_PKT_ID:
+		case LOGDEV_PRINT_TIME:
+			switch (hdr.id) {
+			case LOGDEV_CUSTOM:
+				msg = "skipping! LOGDEV_CUSTOM\n";
+				break;
+			case LOGDEV_PKT_ID:
+				msg = "skipping! LOGDEV_PKT_ID\n";
+				break;
+			case LOGDEV_PRINT_TIME:
+				msg = "skipping! LOGDEV_PRINT_TIME\n";
+				break;
+			default:
+				msg = "skipping! ????\n";
+				break;
+			}
+			i = strlen(msg);
+			if (net)
+				write_msg(msg,i);
+			else
+				printk(msg);
+			for (i = r; i < hdr.size; i+= r) {
+				count = hdr.size - i;
+				if (count > PAGE_SIZE)
+					count = PAGE_SIZE;
+				r = logdev_copy_from_dev(dev,user_buffer,count);
+			}
+			break;
+
+		default:
+			if (!corrupt) {
+				corrupt = 1;
+				i = snprintf(user_buffer,100,">>>>> Unknown logdev header, may be "
+					     "corrupted from this point on\n");
+				if (net)
+					write_msg(user_buffer,i);
+				else 
+					printk(user_buffer);
+			}
+			
+			break;
+		}
+	}
+	msg = ">>>>> done <<<<<\n";
+	i = strlen(msg);
+	if (net)
+		write_msg(msg,i);
+	else
+		printk(msg);
+	started = 0;
+}
+
+void logdev_dumpnet(void)
+{
+	__logdev_dump(1);
+}
+
+void logdev_dump(void)
+{
+	__logdev_dump(0);
+}
+
+int __init logdevnet_init(void)
+{
+	if(strlen(config))
+		option_setup(config);
+	 /*
+	  * Don't need to check the return code on this,
+	  * if it fails then we just don't use the network.
+	  */
+	 if (configured) {
+		 if (netpoll_setup(&np)) {
+			 printk("logdev: warning, netpoll failed to setup\n");
+		 }
+	 }
+	 
+	 return 0;
+}
+
+#if 1
+static int atoi(const char *p)
+{
+	int n = 0;
+	while (*p && '0' <= *p && *p <= '9') {
+		n *= 10;
+		n += *p - '0';
+		p++;
+	}
+	return (n);
+}
+
+static int proc_var_read(char *buffer, char **start, off_t offset, int count,
+			 int *eof, void *data)
+{
+	int var;
+	int len;
+
+	var = atomic_read((atomic_t*)data);
+
+	len = sprintf(buffer,"%d\n",var);
+	
+	if (offset >= len) {
+		*start = buffer;
+		*eof = 1;
+		return 0;
+	}
+	*start = buffer + offset;
+	if ((len -= offset) > count)
+		return count;
+	*eof = 1;
+	return len;
+}
+
+static int proc_var_write(struct file * file, const char * buffer,
+			  unsigned long count, void *data)
+{
+	atomic_t *var;
+	int val;
+	char buf[10];
+
+	var = (atomic_t*)data;
+
+	if (count > 9)
+		count = 9;
+
+	if(copy_from_user(&buf, buffer, count))
+		return -EFAULT;
+
+	buf[count] = 0;
+
+	val = atoi(buf);
+
+	atomic_set(var,val);
+
+	file->f_pos += count;
+
+	return count;
+
+}
+#endif
+
+static void *s_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	struct logdev_dev *dev = m->private;
+	int i = (int)*pos;
+
+	(*pos)++;
+
+	if (i >= dev->len)
+		return NULL;
+
+	return &dev->entry[i];
+}
+
+static void *s_start(struct seq_file *m, loff_t *pos)
+	__acquires(logdev_dev.lock)
+{
+	void *p = NULL;
+	loff_t l = 0;
+
+	/*
+	 * A little strong? Perhaps, but we know that this is bad right
+	 * from the start. Anyway this is for debugging purposes only,
+	 * so it's OK, as well as the big latency we get by turning off 
+	 * intrerrupts. But we also never know who will be locking 
+	 * this.
+	 */
+	if (irqs_disabled())
+		BUG();
+
+	local_irq_disable();
+	spin_lock(&logdev_dev.lock);
+	for (p = (void *)1; p && l < *pos; p = s_next(m,p,&l))
+		;
+
+	return p;
+}
+
+static void s_stop(struct seq_file *m, void *p)
+	__releases(logdev_dev.lock)
+{
+	spin_unlock(&logdev_dev.lock);
+	local_irq_enable();
+}
+
+static int s_show(struct seq_file *m, void *v)
+{
+	int i = (int)(v);
+	struct logdev_entry *entry = v;
+	struct logdev_dev *dev;
+
+	dev = &logdev_dev;
+
+	if (i == 1) {
+		seq_printf(m,"Logdev:\n");
+		seq_printf(m,"\tlen:\t%d\n",dev->len);
+		seq_printf(m,"\tsize:\t%d\n",dev->size);
+		seq_printf(m,"\tstart:\t%d\n",dev->start);
+		seq_printf(m,"\tend:\t%d\n",dev->end);
+		seq_printf(m,"\tcorrupted:%d\n",dev->corrupted);
+		seq_printf(m,"\n\tEntries:\n");
+
+	} else {
+		i = (int)((char*)entry - (char*)dev->entry) / sizeof(struct logdev_entry);
+
+		seq_printf(m,"\t  %d:\t%8u : %8u\tsize: %lu\n", i,
+			   entry->head, entry->tail,
+			   ENTRY_SIZE(entry));
+	}
+
+
+	return 0;
+}
+	
+static struct seq_operations logdev_proc_op = {
+	.start = s_start,
+	.next = s_next,
+	.stop = s_stop,
+	.show = s_show,
+};
+
+static int logdev_proc_open (struct inode *inode, struct file *file)
+{
+	int ret;
+
+	ret = seq_open(file, &logdev_proc_op);
+	if (!ret) {
+		struct seq_file *m = file->private_data;
+		m->private = &logdev_dev;
+	}
+	
+	return ret;
+}
+
+
+static struct file_operations logdev_proc_operations = {
+	.open = logdev_proc_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
+
+static void logdev_proc_setup(void)
+{
+	struct proc_dir_entry *entry;
+
+	entry = create_proc_entry(LOGDEV_PROC_ENTRY,
+				  S_IFREG | S_IRUGO, NULL);
+	if (!entry)
+		return;
+
+	logdev_proc_entry = entry;
+	entry->proc_fops = &logdev_proc_operations;
+
+	entry = create_proc_entry(LOGDEV_PROC_SW,
+				  S_IFREG | S_IRUGO | S_IWUGO,
+				  NULL);
+	if (entry) {
+		entry->read_proc = proc_var_read;
+		entry->write_proc = proc_var_write;
+		entry->data = &logdev_switch;
+		logdev_proc_sw = entry;
+	}
+	entry = create_proc_entry(LOGDEV_PROC_LEVEL,
+				  S_IFREG | S_IRUGO | S_IWUGO,
+				  NULL);
+	if (entry) {
+		entry->read_proc = proc_var_read;
+		entry->write_proc = proc_var_write;
+		entry->data = &logdev_level;
+		logdev_proc_level = entry;
+	}
+
+	entry = create_proc_entry(LOGDEV_PROC_PRINT,
+				  S_IFREG | S_IRUGO | S_IWUGO,
+				  NULL);
+	if (entry) {
+		entry->read_proc = proc_var_read;
+		entry->write_proc = proc_var_write;
+		entry->data = &logdev_print_enabled;
+		logdev_proc_print = entry;
+	}
+
+	entry = create_proc_entry(LOGDEV_PROC_MINOR,
+				  S_IFREG | S_IRUGO,
+				  NULL);
+	if (entry) {
+		entry->read_proc = proc_var_read;
+		entry->data = &logdev_minor;
+		logdev_proc_minor = entry;
+	}
+
+}
+
+
+static struct miscdevice logdev_misc_dev =
+{
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "logdev",
+	.fops = &logdev_fops
+};
+
+void __exit logdev_cleanup(void)
+{
+	int i;
+
+#ifdef MODULE
+	spin_lock(&logdev_dev.lock);
+	logdev_record_switch_hook = NULL;
+	logdev_pkt_hook = NULL;
+	logdev_print_hook = NULL;
+	logdev_print_time_hook = NULL;
+	logdev_dev_hook = NULL;
+	logdev_write_hook = NULL;
+	logdev_read_hook = NULL;
+	spin_unlock(&logdev_dev.lock);
+#endif
+
+	if (logdev_proc_entry)
+		remove_proc_entry(LOGDEV_PROC_ENTRY,0);
+	if (logdev_proc_sw)
+		remove_proc_entry(LOGDEV_PROC_SW,0);
+	if (logdev_proc_level)
+		remove_proc_entry(LOGDEV_PROC_LEVEL,0);
+	if (logdev_proc_minor)
+		remove_proc_entry(LOGDEV_PROC_MINOR,0);
+	if (logdev_proc_print)
+		remove_proc_entry(LOGDEV_PROC_PRINT,0);
+
+	if (logdev_dev.entry) {
+		struct logdev_entry *entry;
+		for (i=0, entry=logdev_dev.entry; i<logdev_dev.len; i++,entry++)
+			if (entry->dat) {
+				free_page((unsigned long)entry->dat);
+				entry->dat = NULL;
+			}
+		logdev_dev.len = 0;
+		kfree(logdev_dev.entry);
+		logdev_dev.entry = NULL;
+	}
+
+	misc_deregister(&logdev_misc_dev);
+		
+}
+
+int __init logdev_init(void)
+{
+	int res = 0;
+	int i;
+	struct logdev_entry *entry;
+
+	printk("Logdevice: copyright Steven Rostedt, Kihon Technologies Inc."
+	       " (Version %s)\n",
+	       logdev_version);
+
+	res = misc_register(&logdev_misc_dev);
+	if (res)
+		goto out;
+	atomic_set(&logdev_minor,logdev_misc_dev.minor);
+
+	logdev_dev.len = pages;
+	
+	res = -ENOMEM;
+	logdev_dev.entry = kmalloc(sizeof(struct logdev_entry)*logdev_dev.len,GFP_KERNEL);
+	if (!logdev_dev.entry)
+		goto fail;
+
+	memset(logdev_dev.entry,0,sizeof(*logdev_dev.entry)*logdev_dev.len);
+	
+	for (i=0,entry=logdev_dev.entry; i<logdev_dev.len; i++,entry++) {
+		entry->dat = (void*)__get_free_pages(GFP_KERNEL,0);
+		if (!entry->dat)
+			goto fail;
+	}
+
+	logdev_proc_setup();
+
+	init_waitqueue_head(&logdev_dev.wait);
+	spin_lock_init(&logdev_dev.lock);
+
+	res = 0;
+
+
+#ifdef MODULE
+	/* Make hooks last! */
+	spin_lock(&logdev_dev.lock);
+	logdev_record_switch_hook = logdev_record_switch;
+	logdev_pkt_hook = logdev_pkt;
+	logdev_print_hook = logdev_print;
+	logdev_print_time_hook = logdev_print_time;
+	logdev_dev_hook = logdev_record;
+	logdev_write_hook = logdev_record_write;
+	logdev_read_hook = logdev_record_read;
+	logdev_dumpnet_hook = logdev_dumpnet;
+	logdev_dump_hook = logdev_dump;
+	spin_unlock(&logdev_dev.lock);
+	 
+	logdevnet_init();
+#endif
+
+out:
+	return res;
+
+fail:
+	logdev_cleanup();
+	goto out;
+}
+
+#ifndef MODULE
+EXPORT_SYMBOL(logdev_record_switch);
+EXPORT_SYMBOL(logdev_pkt);
+EXPORT_SYMBOL(logdev_print);
+EXPORT_SYMBOL(logdev_print_time);
+EXPORT_SYMBOL(logdev_record);
+EXPORT_SYMBOL(logdev_record_write);
+EXPORT_SYMBOL(logdev_record_read);
+EXPORT_SYMBOL(logdev_dumpnet);
+EXPORT_SYMBOL(logdev_dump);
+
+late_initcall(logdevnet_init);
+#endif
+
+module_init(logdev_init);
+module_exit(logdev_cleanup);
Index: drivers/char/logdev_hooks.c
===================================================================
--- drivers/char/logdev_hooks.c	(.../tags/starting_base_kernel)	(revision 0)
+++ drivers/char/logdev_hooks.c	(.../trunk/kernel)	(revision 95)
@@ -0,0 +1,60 @@
+#include <linux/time.h>
+#include <linux/logdev.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
+/*
+ * Only need to add this is LOGDEV is defined as a module
+ */
+#ifndef CONFIG_LOGDEV
+int (*logdev_print_hook)(const char *fmt, ...);
+int (*logdev_print_time_hook)(const char *fmt, ...);
+void (*logdev_record_switch_hook)(struct task_struct *a,struct task_struct *b);
+int (*logdev_dev_hook)(int id, int size, const void *data);
+int (*logdev_write_hook)(const char *data, int size);
+int (*logdev_read_hook)(char *buf, int size);
+void (*logdev_pkt_hook)(struct sk_buff *skb, int dir);
+void (*logdev_dump_hook)(void);
+void (*logdev_dumpnet_hook)(void);
+
+EXPORT_SYMBOL(logdev_print_hook);
+EXPORT_SYMBOL(logdev_print_time_hook);
+EXPORT_SYMBOL(logdev_record_switch_hook);
+EXPORT_SYMBOL(logdev_dev_hook);
+EXPORT_SYMBOL(logdev_write_hook);
+EXPORT_SYMBOL(logdev_read_hook);
+EXPORT_SYMBOL(logdev_pkt_hook);
+EXPORT_SYMBOL(logdev_dumpnet_hook);
+EXPORT_SYMBOL(logdev_dump_hook);
+EXPORT_SYMBOL(logdev_add_hook_lock);
+
+void logdev_add_hook(void **hook, void *func)
+{
+	spin_lock(&logdev_add_hook_lock);
+	*hook = func;
+	spin_unlock(&logdev_add_hook_lock);
+}
+
+void logdev_remove_hook(void **hook)
+{
+	spin_lock(&logdev_add_hook_lock);
+	*hook = NULL;
+	spin_unlock(&logdev_add_hook_lock);
+}
+#endif
+
+atomic_t logdev_level = ATOMIC_INIT(0);
+atomic_t logdev_switch = ATOMIC_INIT(0);
+#ifdef CONFIG_LOGDEV_PRINT_ENABLED
+atomic_t logdev_print_enabled = ATOMIC_INIT(1);
+#else
+atomic_t logdev_print_enabled = ATOMIC_INIT(0);
+#endif
+
+raw_spinlock_t logdev_add_hook_lock = RAW_SPIN_LOCK_UNLOCKED;
+
+EXPORT_SYMBOL(logdev_switch);
+EXPORT_SYMBOL(logdev_level);
+EXPORT_SYMBOL(logdev_print_enabled);
Index: drivers/char/Makefile
===================================================================
--- drivers/char/Makefile	(.../tags/starting_base_kernel)	(revision 95)
+++ drivers/char/Makefile	(.../trunk/kernel)	(revision 95)
@@ -89,6 +89,9 @@
 obj-$(CONFIG_PCMCIA) += pcmcia/
 obj-$(CONFIG_IPMI_HANDLER) += ipmi/
 
+obj-$(CONFIG_LOGDEV_HOOKS) += logdev_hooks.o
+obj-$(CONFIG_LOGDEV) += logdev.o
+
 obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
 
 # Files generated that shall be removed upon make clean
