Index: kernel/sched.c
===================================================================
--- kernel/sched.c	(.../tags/starting_base_kernel_2.6.12)	(revision 300)
+++ kernel/sched.c	(.../trunk/kernel)	(revision 300)
@@ -49,6 +49,8 @@
 #include <linux/acct.h>
 #include <asm/tlb.h>
 
+#include <linux/logdev.h>
+
 #include <asm/unistd.h>
 
 /*
@@ -1372,6 +1374,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_2.6.12)	(revision 0)
+++ include/linux/logdev.h	(.../trunk/kernel)	(revision 300)
@@ -0,0 +1,207 @@
+/*
+ * 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 0x4adc
+#define LOGDEV_PRINT_TIME_FUNC 0x4add
+
+struct logdev_switch_struct {
+	unsigned long high;
+	unsigned long low;
+	short pid_prev;
+	short pid_next;
+	short prev_len;
+	short next_len;
+	char prev_comm[0];
+	char next_comm[0];
+};
+
+struct logdev_pkt {
+	unsigned long high;
+	unsigned long low;
+	short protocol;
+	short dir;
+	char packet[0];
+};
+
+struct logdev_print {
+	char str[0];
+};
+
+struct logdev_print_time {
+	unsigned long high;
+	unsigned long low;
+	char str[0];
+};
+
+struct logdev_print_time_func {
+	const char *file;
+	int line;
+	/* need to be after line, since we use this with print_time. */
+	unsigned long high;
+	unsigned long low;
+	char str[0];
+};
+
+struct logdev_custom {
+	int id;
+	char data[0];
+};
+
+struct logdev_header {
+	int id;
+	int size;
+	int cpu;
+};
+
+struct logdev_item {
+	struct logdev_header hdr;
+	union {
+		struct logdev_switch_struct sw;
+		struct logdev_pkt pkt;
+		struct logdev_print print;
+		struct logdev_print_time print_time;
+		struct logdev_print_time_func print_time_func;
+		struct logdev_custom custom;
+		char data[0];
+	} u;
+};
+
+#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
+#undef raw_local_irq_save
+#undef raw_local_irq_restore
+#undef raw_local_irq_enable
+#undef raw_local_irq_disable
+#undef raw_irqs_disabled
+#define raw_spinlock_t spinlock_t
+#define RAW_SPIN_LOCK_UNLOCKED SPIN_LOCK_UNLOCKED
+#define spin_lock_rt do {} while(0)
+#define raw_local_irq_save local_irq_save
+#define raw_local_irq_restore local_irq_restore
+#define raw_local_irq_enable local_irq_enable
+#define raw_local_irq_disable local_irq_disable
+#define raw_irqs_disabled irqs_disabled
+#endif
+
+#ifdef CONFIG_LOGDEV_HOOKS
+extern int (*logdev_print_hook)(const char *fmt, ...);
+extern int (*logdev_vprint_hook)(const char *fmt, va_list va);
+extern int (*logdev_print_time_hook)(const char *fmt, ...);
+extern int (*logdev_print_time_func_hook)(const char *fmt, ...);
+extern void (*logdev_record_switch_hook)(struct task_struct *a,struct task_struct *b);
+extern int (*logdev_record_hook)(int id, int size, const void *data);
+extern int (*logdev_write_hook)(const char *data, int size);
+extern int (*logdev_read_hook)(void *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() \
+	do { \
+		if (atomic_read(&logdev_switch)) \
+			atomic_dec(&logdev_switch); \
+	} while(0);
+void logdev_write_msg(const char *msg, unsigned int len, int net);
+
+typedef void (*logdev_callback_func)(struct logdev_header *hdr, 
+				     struct logdev_custom *custom,
+				     int net,
+				     void *rec);
+
+/*
+ * 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_vprint(const char *str, va_list va);
+int logdev_print_time(const char *str, ...)
+	__attribute__ ((format (printf, 1, 2)));
+int logdev_print_time_func(const char *file, int line, const char *str, ...)
+	__attribute__ ((format (printf, 3, 4)));
+int logdev_record(int id, int size, const void *data);
+int logdev_record_write(const char *data, int size);
+int logdev_record_read(void *data, int size);
+void logdev_dumpnet(void);
+void logdev_dump(void);
+int logdev_register_callback(int custom_id, logdev_callback_func func);
+int logdev_unregister_callback(int custom_id);
+
+#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 LOGTFPRINT(x...) do { if (atomic_read(&logdev_print_enabled)) LOGDEV(print_time_func,__FUNCTION__,__LINE__,x); } while(0)
+#define LOGSWITCH(prev,next) LOGDEV(record_switch,prev,next)
+#define LOGPKT(skb,direction) LOGDEV(pkt,skb,direction)
+void logdev_time(void);
+
+#define lprint(x...) do { if (atomic_read(&logdev_switch)) { LOGTPRINT(x); } } while(0)
+#define lfprint(x...) do { if (atomic_read(&logdev_switch)) { LOGTFPRINT(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)
+#define logdev_time() do {} while(0)
+#endif /* LOGDEV_HOOKS */
+
+#endif
+
+#endif
Index: lib/Kconfig.debug
===================================================================
--- lib/Kconfig.debug	(.../tags/starting_base_kernel_2.6.12)	(revision 300)
+++ lib/Kconfig.debug	(.../trunk/kernel)	(revision 300)
@@ -159,3 +159,64 @@
 	  If you don't debug the kernel, you can say N, but we may not be able
 	  to solve problems without frame pointers.
 
+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_MULTI_CPUS
+	bool "Logdev Multiple CPU buffers"
+	depends on LOGDEV && SMP
+	help
+	  The Logdev device records to a memory buffer allocate once
+	  on startup.  With this option set, a separate buffer will be
+	  used for each online CPU.  This allows for no blocking while
+	  needing to write to the buffer, but it adds more space since
+	  the amount of pages allocated with the above LOGDEV_PAGES will
+	  be allocated for each CPU.  Also, if you are debugging race 
+	  conditions, then you don't want this option, since one buffer
+	  is better to see the interleaving actions between the CPUS.
+	  But if you don't need to see the interleaving of actions 
+	  between CPUS, than it is probably good to set this.
+
+	  If you don't need interleaving changes between CPU writes, say Y.
+
+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
+
Index: arch/i386/kernel/traps.c
===================================================================
--- arch/i386/kernel/traps.c	(.../tags/starting_base_kernel_2.6.12)	(revision 300)
+++ arch/i386/kernel/traps.c	(.../trunk/kernel)	(revision 300)
@@ -27,6 +27,7 @@
 #include <linux/ptrace.h>
 #include <linux/utsname.h>
 #include <linux/kprobes.h>
+#include <linux/logdev.h>
 
 #ifdef CONFIG_EISA
 #include <linux/ioport.h>
@@ -565,6 +566,7 @@
 	printk(" on CPU%d, eip %08lx, registers:\n",
 		smp_processor_id(), regs->eip);
 	show_registers(regs);
+	LOGDUMP();
 	printk("console shuts up ...\n");
 	console_silent();
 	spin_unlock(&nmi_print_lock);
Index: Makefile
===================================================================
--- Makefile	(.../tags/starting_base_kernel_2.6.12)	(revision 300)
+++ Makefile	(.../trunk/kernel)	(revision 300)
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 6
 SUBLEVEL = 12
-EXTRAVERSION =
+EXTRAVERSION = -logdev
 NAME=Woozy Numbat
 
 # *DOCUMENTATION*
Index: drivers/char/Makefile
===================================================================
--- drivers/char/Makefile	(.../tags/starting_base_kernel_2.6.12)	(revision 300)
+++ drivers/char/Makefile	(.../trunk/kernel)	(revision 300)
@@ -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
 obj-$(CONFIG_TCG_TPM) += tpm/
 # Files generated that shall be removed upon make clean
Index: drivers/char/logdev.c
===================================================================
--- drivers/char/logdev.c	(.../tags/starting_base_kernel_2.6.12)	(revision 0)
+++ drivers/char/logdev.c	(.../trunk/kernel)	(revision 300)
@@ -0,0 +1,2006 @@
+/*
+ * 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/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/skbuff.h>
+#include <linux/kthread.h>
+#include <linux/syscalls.h>
+#include <linux/cpumask.h>
+#include <linux/notifier.h>
+#include <asm/kdebug.h>
+
+#include <linux/logdev.h>
+
+#include <linux/delay.h>
+
+#include <net/tcp.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+static char *logdev_version = "0.3.0";
+
+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_PROC_DIR "logdev"
+#define LOGDEV_PROC_SW "switch"
+#define LOGDEV_PROC_LEVEL "level"
+#define LOGDEV_PROC_PRINT "print"
+#define LOGDEV_PROC_MINOR "minor"
+#define LOGDEV_PROC_ENTRY "entry"
+
+struct proc_dir_entry *logdev_proc_dir;
+
+/*
+ * The testing markers are used to cut and paste into a userland program for
+ * easy algorithm debugging.
+ */
+
+/********************* Start testing code here ***********************/
+
+static LIST_HEAD(logdev_callbacks);
+static raw_spinlock_t logdev_callbacks_lock = RAW_SPIN_LOCK_UNLOCKED;
+
+struct logdev_callback {
+	struct list_head list;
+	int id;
+	logdev_callback_func func;
+};
+
+struct logdev_entry {
+	unsigned int head;
+	unsigned int tail;
+	char *dat;
+};
+
+struct logdev_dev {
+	struct logdev_entry *entry;
+	int init;
+	int size;
+	int len;
+	int start;
+	int end;
+	int corrupted;
+	atomic_t minor;
+	struct proc_dir_entry *dir;
+	wait_queue_head_t wait;
+	raw_spinlock_t lock;
+};
+
+#ifdef CONFIG_LOGDEV_MULTI_CPUS
+#  define LOGDEV_CPUS NR_CPUS
+#  define get_idx(cpu) cpu
+#  define get_logdev(cpu) &logdev_dev[cpu]
+#  define kern_spin_lock() do {} while(0)
+#  define kern_spin_unlock() do {} while(0)
+#  define kern_spin_lock_save(flags) raw_local_irq_save(flags)
+#  define kern_spin_unlock_restore(flags) raw_local_irq_restore(flags)
+#else
+
+#  define LOGDEV_CPUS 1
+#  define get_idx(cpu) ({ (void)cpu; 0; })
+#  define get_logdev(cpu) ({ (void)cpu; &logdev_dev[0]; })
+/*
+ * With a single buffer, we need to have locks.
+ */
+#  define kern_spin_lock() spin_lock(&kern_buffer_lock)
+#  define kern_spin_unlock() spin_unlock(&kern_buffer_lock)
+#  define kern_spin_lock_save(flags) spin_lock_irqsave(&kern_buffer_lock,flags)
+#  define kern_spin_unlock_restore(flags) spin_unlock_irqrestore(&kern_buffer_lock,flags)
+   static raw_spinlock_t kern_buffer_lock = RAW_SPIN_LOCK_UNLOCKED;
+#endif
+
+static DECLARE_MUTEX(user_sem);
+/*
+ * doesn't really need to be atomic, but helps that we don't need to
+ * write another proc interface function.
+ */
+static struct proc_dir_entry *logdev_proc_entry[LOGDEV_CPUS];
+static struct proc_dir_entry *logdev_proc_minor[LOGDEV_CPUS];
+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 logdev_dev logdev_dev[LOGDEV_CPUS];
+
+static int logdev_copy_to_dev(struct logdev_dev *dev, const void *dat,
+			      int size);
+
+void logdev_switch_turnoff(void)
+{
+	atomic_set(&logdev_switch,0);
+}
+
+/*
+ * 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.
+ *
+ * This is called from schedule, and interrupts should already be turned off.
+ */
+void logdev_record_switch(struct task_struct *prev, struct task_struct *next)
+{
+	struct logdev_dev *dev;
+	struct logdev_header hdr;
+	struct logdev_switch_struct rs;
+	unsigned long long t;
+	int cpu = smp_processor_id();
+
+	if (!atomic_read(&logdev_switch))
+		return;
+	
+	dev = get_logdev(cpu);
+	if (!dev->init)
+		return;
+
+	WARN_ON(!irqs_disabled());
+
+	hdr.id = LOGDEV_SWITCH_ID;
+	hdr.cpu = smp_processor_id();
+
+	t = sched_clock();
+	rs.high = (unsigned long)(t>>BITS_PER_LONG);
+	rs.low = (unsigned long)(t);
+
+	rs.pid_prev = prev->pid;
+	rs.pid_next = next->pid;
+	rs.prev_len = strlen(prev->comm);
+	rs.next_len = strlen(next->comm);
+	
+	hdr.size = sizeof(hdr) + sizeof(rs) + rs.prev_len + rs.next_len;
+
+	WARN_ON(!irqs_disabled());
+
+	spin_lock(&dev->lock);
+	logdev_copy_to_dev(dev,&hdr,sizeof(hdr));
+	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_dev *dev;
+	struct logdev_header hdr;
+	struct logdev_pkt rs;
+	struct tcphdr *th;
+	struct iphdr *iph;
+	unsigned long long t;
+	unsigned long flags;
+	int cpu;
+
+	t = sched_clock();
+	
+	raw_local_irq_save(flags);
+
+	cpu = smp_processor_id();
+
+	dev = get_logdev(cpu);
+	if (!dev->init)
+		goto out;
+
+	hdr.id = LOGDEV_PKT_ID;
+	hdr.size = sizeof(hdr) + sizeof(rs);
+	hdr.cpu = smp_processor_id();
+
+	rs.high = (unsigned long)(t>>BITS_PER_LONG);
+	rs.low = (unsigned long)(t);
+
+	iph = skb->nh.iph;
+
+	rs.protocol = skb->protocol;
+	rs.dir = direction;
+		
+	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 */
+		
+		hdr.size += 20;
+		
+		spin_lock(&dev->lock);
+		logdev_copy_to_dev(dev,&hdr,sizeof(hdr));
+		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;
+		
+		hdr.size += iplen + tcplen;
+
+		spin_lock(&dev->lock);
+		logdev_copy_to_dev(dev,&hdr,sizeof(hdr));
+		logdev_copy_to_dev(dev,&rs,sizeof(rs));
+		logdev_copy_to_dev(dev,iph,iplen+tcplen);
+		spin_unlock(&dev->lock);
+	}
+	
+ out:
+	raw_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 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 char kern_buffer[LOGDEV_CPUS][PAGE_SIZE];
+
+/*
+ * logdev_print acts like printk but it writes to the logdev device instead
+ *  of a console.
+ */
+int logdev_vprint(const char *str, va_list va)
+{
+	struct logdev_dev *dev;
+	int len=0;
+	struct logdev_header hdr;
+	struct logdev_print rs;
+	unsigned long flags;
+	int cpu;
+	int idx;
+
+	kern_spin_lock_save(flags);
+
+	cpu = smp_processor_id();
+	idx = get_idx(cpu);
+
+	dev = get_logdev(cpu);
+	if (!dev->init)
+		goto out;
+
+	len = vsnprintf(kern_buffer[idx],PAGE_SIZE,str,va);
+
+	if (len > PAGE_SIZE) {
+		kern_buffer[idx][PAGE_SIZE-1] = 0;
+		len = PAGE_SIZE;
+	}
+
+	hdr.id = LOGDEV_PRINT;
+	hdr.size = sizeof(hdr) + sizeof(rs) + len;
+	
+	hdr.cpu = smp_processor_id();
+
+	spin_lock(&dev->lock);
+	logdev_copy_to_dev(dev, &hdr, sizeof(hdr));
+	if (sizeof(rs))
+		logdev_copy_to_dev(dev, &rs, sizeof(rs));
+	logdev_copy_to_dev(dev, kern_buffer[idx], len);
+	spin_unlock(&dev->lock);
+
+ out:
+	kern_spin_unlock_restore(flags);
+
+	return len;
+}
+
+int logdev_print(const char *str, ...)
+{
+	va_list va;
+	struct logdev_dev *dev;
+	int len=0;
+	struct logdev_header hdr;
+	struct logdev_print rs;
+	unsigned long flags;
+	int cpu;
+	int idx;
+
+	kern_spin_lock_save(flags);
+
+	cpu = smp_processor_id();
+	idx = get_idx(cpu);
+
+	dev = get_logdev(cpu);
+	if (!dev->init)
+		goto out;
+
+	va_start(va,str);
+	len = vsnprintf(kern_buffer[idx],PAGE_SIZE,str,va);
+	va_end(va);
+
+	if (len > PAGE_SIZE) {
+		kern_buffer[idx][PAGE_SIZE-1] = 0;
+		len = PAGE_SIZE;
+	}
+
+	hdr.id = LOGDEV_PRINT;
+	hdr.size = sizeof(hdr) + sizeof(rs) + len;
+	
+	hdr.cpu = smp_processor_id();
+
+	spin_lock(&dev->lock);
+	logdev_copy_to_dev(dev, &hdr, sizeof(hdr));
+	if (sizeof(rs))
+		logdev_copy_to_dev(dev, &rs, sizeof(rs));
+	logdev_copy_to_dev(dev, kern_buffer[idx], len);
+	spin_unlock(&dev->lock);
+
+ out:
+	kern_spin_unlock_restore(flags);
+
+	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, ...)
+{
+	struct logdev_dev *dev;
+	va_list va;
+	int len = 0;
+	struct logdev_header hdr;
+	struct logdev_print_time rs;
+	unsigned long long t;
+	unsigned long flags;
+	int cpu;
+	int idx;
+
+	t = sched_clock();
+
+	hdr.id = LOGDEV_PRINT_TIME;
+
+	kern_spin_lock_save(flags);
+
+	cpu = smp_processor_id();
+	idx = get_idx(cpu);
+
+	dev = get_logdev(cpu);
+	if (!dev->init)
+		goto out;
+
+	rs.high = (unsigned long)(t>>BITS_PER_LONG);
+	rs.low = (unsigned long)t;
+
+	va_start(va,str);
+	len = vsnprintf(kern_buffer[idx],PAGE_SIZE,str,va);
+	va_end(va);
+
+	if (len > PAGE_SIZE) {
+		kern_buffer[idx][PAGE_SIZE-1] = 0;
+		len = PAGE_SIZE;
+	}
+
+	hdr.size = sizeof(hdr) + sizeof(rs) + len;
+
+	hdr.cpu = cpu;
+
+	spin_lock(&dev->lock);
+	logdev_copy_to_dev(dev, &hdr, sizeof(hdr));
+	logdev_copy_to_dev(dev, &rs, sizeof(rs));
+	logdev_copy_to_dev(dev, kern_buffer[idx], len);
+	spin_unlock(&dev->lock);
+
+ out:
+	kern_spin_unlock_restore(flags);
+
+	return len;
+}
+
+/*
+ * logdev_print_time_func quickly stores the time, function and line number.
+ * this is really only good for live runs since the function is just a pointer,
+ * so a user land process would need to have the System.map available.
+ */
+int logdev_print_time_func(const char *file, int line, const char *str, ...)
+{
+	struct logdev_dev *dev;
+	va_list va;
+	int len = 0;
+	struct logdev_header hdr;
+	struct logdev_print_time_func rs;
+	unsigned long long t;
+	unsigned long flags;
+	int cpu;
+	int idx;
+
+	t = sched_clock();
+
+	hdr.id = LOGDEV_PRINT_TIME_FUNC;
+
+	kern_spin_lock_save(flags);
+
+	cpu = smp_processor_id();
+	idx = get_idx(cpu);
+
+	dev = get_logdev(cpu);
+	if (!dev->init)
+		goto out;
+
+	rs.high = (unsigned long)(t>>BITS_PER_LONG);
+	rs.low = (unsigned long)t;
+
+	rs.file = file;
+	rs.line = line;
+
+	va_start(va,str);
+	len = vsnprintf(kern_buffer[idx],PAGE_SIZE,str,va);
+	va_end(va);
+
+	if (len > PAGE_SIZE) {
+		kern_buffer[idx][PAGE_SIZE-1] = 0;
+		len = PAGE_SIZE;
+	}
+
+	hdr.size = sizeof(hdr) + sizeof(rs) + len;
+
+	hdr.cpu = cpu;
+
+	spin_lock(&dev->lock);
+	logdev_copy_to_dev(dev, &hdr, sizeof(hdr));
+	logdev_copy_to_dev(dev, &rs, sizeof(rs));
+	logdev_copy_to_dev(dev, kern_buffer[idx], len);
+	spin_unlock(&dev->lock);
+
+ out:
+	kern_spin_unlock_restore(flags);
+
+	return len;
+}
+
+int logdev_copy_from_dev(struct logdev_dev *dev, void *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:
+		case LOGDEV_PRINT_TIME_FUNC:
+			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.
+ *
+ * This record will be added as LOGDEV_CUSTOM and the given id will be the custom id.
+ */
+int logdev_record(int id, int size, const void *data)
+{
+	struct logdev_dev *dev;
+	struct logdev_header hdr;
+	struct logdev_custom rs;
+	unsigned long flags;
+	int cpu;
+	int ret = 0;
+
+	hdr.id = LOGDEV_CUSTOM;
+	hdr.size = sizeof(hdr) + sizeof(rs) + size;
+
+	rs.id = id;
+
+	raw_local_irq_save(flags);
+
+	cpu = smp_processor_id();
+
+	dev = get_logdev(cpu);
+	if (!dev->init) {
+		local_irq_restore(flags);
+		return 0;
+	}
+	spin_lock(&dev->lock);
+	hdr.cpu = smp_processor_id();
+	logdev_copy_to_dev(dev, &hdr, sizeof(hdr));
+	logdev_copy_to_dev(dev, &rs, sizeof(rs));
+	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;
+	int cpu;
+
+	raw_local_irq_save(flags);
+
+	cpu = smp_processor_id();
+	dev = get_logdev(cpu);
+	if (!dev->init) {
+		raw_local_irq_restore(flags);
+		return 0;
+	}
+
+	spin_lock(&dev->lock);
+	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(void *data, int size)
+{
+	struct logdev_dev *dev;
+	int ret;
+	unsigned long flags;
+	int cpu;
+
+	raw_local_irq_save(flags);
+
+	cpu = smp_processor_id();
+	dev = get_logdev(cpu);
+	if (!dev->init) {
+		raw_local_irq_restore(flags);
+		return 0;
+	}
+
+	spin_lock(&dev->lock);
+	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)
+{
+	int cpu = (int)filp->private_data;
+	struct logdev_dev *dev;
+	unsigned long flags;
+	
+	if (count > PAGE_SIZE)
+		count = PAGE_SIZE;
+
+	if (down_interruptible(&user_sem))
+		return -EINTR;
+
+	dev = get_logdev(cpu);
+	if (!dev->init)
+		goto out;
+
+	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(&user_sem);
+	return count;
+}
+	
+ssize_t logdev_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
+{
+	int cpu = (int)filp->private_data;
+	struct logdev_dev *dev;
+	unsigned long flags;
+
+	if (down_interruptible(&user_sem))
+		return -EINTR;
+
+
+	dev = get_logdev(cpu);
+
+	if (!dev->init)
+		goto out_up;
+
+	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;
+	}
+ out_up:
+	up(&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
+	int cpu = (int)filp->private_data;
+#endif
+	return 0;
+}
+
+	
+static int logdev_open (struct inode *inode, struct file *filp)
+{
+	int minor = iminor(inode);
+	int i;
+
+	for (i=0; i < LOGDEV_CPUS; i++) {
+		if (logdev_dev[i].init && atomic_read(&logdev_dev[i].minor) == minor)
+			break;
+	}
+	if (i == LOGDEV_CPUS)
+		return -ENODEV;
+
+	filp->private_data = (void*)i;
+	
+	return 0;
+}
+
+/**
+ *	logdev_poll - poll file op for logdev
+ *	@filp: the file
+ *	@wait: poll table
+ *
+ *	Poll implemention.
+ */
+static unsigned int
+logdev_poll(struct file *filp, poll_table *wait)
+{
+	struct logdev_dev *dev = (struct logdev_dev*)filp->private_data;
+	unsigned int mask = 0;
+
+	(void)dev;
+#if 0
+	if (filp->f_mode & FMODE_READ) {
+		poll_wait(filp, &app->read_wait, wait);
+		if (!empty_channel(dev))
+			mask |= POLLIN | POLLRDNORM;
+	}
+#endif
+	
+	return mask;
+}
+
+static struct file_operations logdev_fops = {
+	.read		= logdev_read,
+      	.write		= logdev_write,
+	.ioctl		= logdev_ioctl,
+	.open		= logdev_open,
+	.release	= logdev_close,
+	.poll		= logdev_poll,
+	.llseek		= no_llseek,
+};
+
+/*
+ * The following is to register call back functions to print out
+ * a custom record.
+ */
+int logdev_register_callback(int custom_id, logdev_callback_func func)
+{
+	struct list_head *p;
+	struct logdev_callback *cb;
+	unsigned long flags;
+	int ret = 0;
+
+	cb = kmalloc(sizeof(*cb),GFP_KERNEL);
+	if (!cb) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	
+	spin_lock_irqsave(&logdev_callbacks_lock,flags);
+	list_for_each(p,&logdev_callbacks) {
+		struct logdev_callback *c = list_entry(p, struct logdev_callback, list);
+		if (c->id == custom_id) {
+			spin_unlock_irqrestore(&logdev_callbacks_lock,flags);
+			kfree(cb);
+			ret = -EBUSY;
+			goto out;
+		}
+	}
+
+	cb->id = custom_id;
+	cb->func = func;
+	list_add(&cb->list,&logdev_callbacks);
+	spin_unlock_irqrestore(&logdev_callbacks_lock,flags);
+
+ out:
+	return ret;	
+}
+
+int logdev_unregister_callback(int custom_id)
+{
+	struct list_head *p;
+	struct logdev_callback *cb;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&logdev_callbacks_lock,flags);
+	list_for_each(p,&logdev_callbacks) {
+		cb = list_entry(p, struct logdev_callback, list);
+		if (cb->id == custom_id)
+			break;
+	}
+	if (p == &logdev_callbacks) {
+		ret = -ENODEV;
+		spin_unlock_irqrestore(&logdev_callbacks_lock,flags);
+		goto out;
+	}
+
+	list_del(&cb->list);
+	spin_unlock_irqrestore(&logdev_callbacks_lock,flags);
+
+	kfree(cb);
+
+ out:
+	return ret;	
+}
+
+/*
+ * The following UDP sending is based off of netconsole.
+ */
+static int configured = 0;
+
+#ifdef CONFIG_NETPOLL
+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 option_setup(char *opt)
+{
+	configured = !netpoll_parse_options(&np, opt);
+
+	return 0;
+}
+
+__setup("logdevnet=", option_setup);
+#endif
+
+#define MAX_PRINT_CHUNK 1000
+
+#ifdef CONFIG_NETPOLL
+void logdev_write_msg(const char *msg, unsigned int len, int net)
+{
+	int frag, left;
+
+	if (!net) {
+		printk("%s",msg);
+		return;
+	}
+
+	if (!np.dev)
+		return;
+
+	for(left = len; left; ) {
+		frag = min(left, MAX_PRINT_CHUNK);
+		netpoll_send_udp(&np, msg, frag);
+		msg += frag;
+		left -= frag;
+	}
+
+}
+#else /* !CONFIG_NETPOLL */
+void logdev_write_msg(const char *msg, unsigned int len, int net)
+{
+	printk("%s",msg);
+}
+#endif /* CONFIG_NETPOLL */
+
+static void __logdev_dump(int net)
+{
+	int r;
+	int i;
+	int count;
+	int save_switch;
+	char *msg;
+	struct logdev_dev *dev;
+	struct logdev_header hdr;
+	static int started  = 0;
+	int newline = 1;
+	int do_lock = 1;
+	extern int in_logdump;
+	unsigned long long t;
+	unsigned long secs;
+	unsigned long nanosec_rem;
+	const char *file = NULL;
+	int line = 0;
+	int cpu;
+
+	if (started)
+		return;
+
+	if (oops_in_progress)
+		do_lock = 0;
+	in_logdump++;
+
+	/*
+	 * This is for debugging, so we don't want to reintroduce more output.
+	 */
+	save_switch = atomic_read(&logdev_switch);
+	atomic_set(&logdev_switch,0);
+#if 0
+	atomic_set(&logdev_print_enabled,0);
+#endif
+	
+	for (cpu = 0; cpu < LOGDEV_CPUS; cpu++) {
+		unsigned long flags;
+		int corrupt = 0;
+		
+		dev = &logdev_dev[cpu];
+		if (!dev->init)
+			continue;
+		
+		/* This is a long time to hold a lock, but we are dumping
+		 * debug so it's ok. This is not a normal code path. */
+		spin_lock_irqsave(&dev->lock,flags);
+
+		if (do_lock)
+			kern_spin_lock();
+
+		i = sprintf(kern_buffer[cpu],"****** Starting Logdev for CPU %d ********\n",cpu);
+		logdev_write_msg(kern_buffer[cpu],i,net);
+
+		if (do_lock)
+			kern_spin_unlock();
+
+		if (dev->corrupted) {
+			msg = ">>>>> Warning: Logdev corrupted <<<<<\n";
+			i = strlen(msg);
+			logdev_write_msg(msg,i,net);
+			corrupt = 1;
+		}
+
+		while ((r = logdev_copy_from_dev(dev,&hdr,sizeof(hdr))) == sizeof(hdr)) {
+			switch (hdr.id) {
+
+			case LOGDEV_PRINT_TIME_FUNC:
+			{
+				struct logdev_print_time_func rs;
+				int cap = sizeof(rs) - sizeof(struct logdev_print_time);
+
+//				printk("hdr.size=%d r=%d sizeof cap=%d sizeof rs=%d\n",
+//				       hdr.size,r,cap,sizeof(rs));
+				logdev_copy_from_dev(dev,&rs,cap);
+				file = rs.file;
+				line = rs.line;
+
+				r += cap;
+				/* fall through */
+			}
+			case LOGDEV_PRINT_TIME:
+			{
+				struct logdev_print_time rs;
+
+				// printk("sizeof(rs)=%d\n",sizeof(rs));
+				logdev_copy_from_dev(dev,&rs,sizeof(rs));
+
+				if (newline) {
+					if (do_lock)
+						kern_spin_lock();
+					t = ((unsigned long long)rs.high << BITS_PER_LONG) | 
+						rs.low;
+					nanosec_rem = do_div(t, 1000000000)/1000;
+					secs = (unsigned long)t;
+					
+					i = snprintf(kern_buffer[cpu],100,"[%5lu.%06lu] ",
+						     secs,nanosec_rem);
+
+					logdev_write_msg(kern_buffer[cpu],i,net);
+					if (do_lock)
+						kern_spin_unlock();
+				}
+
+				r += sizeof(rs);
+
+				/* fall through */
+			}
+						    
+			case LOGDEV_PRINT:
+				if (do_lock)
+					kern_spin_lock();
+				if (newline) {
+					i = sprintf(kern_buffer[cpu],"cpu:%d ",hdr.cpu);
+					logdev_write_msg(kern_buffer[cpu],i,net);
+
+					if (hdr.id == LOGDEV_PRINT_TIME_FUNC) {
+						i = sprintf(kern_buffer[cpu],"%s:%d ",file, line);
+						logdev_write_msg(kern_buffer[cpu],i,net);
+					}
+				}
+				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,kern_buffer[cpu],count);
+					if (r < 0)
+						break;
+					kern_buffer[cpu][count] = 0;
+					logdev_write_msg(kern_buffer[cpu],count,net);
+					newline = (count) && (kern_buffer[cpu][count - 1] != '\n') ? 0 : 1;
+				}
+				if (do_lock)
+					kern_spin_unlock();
+				break;
+			case  LOGDEV_SWITCH_ID:
+			{
+				struct logdev_switch_struct rs;
+				
+				if (do_lock)
+					kern_spin_lock();
+				
+				i = sprintf(kern_buffer[cpu],">>>> IN LOGDEV SWITCH <<<< cpu:%d \n",hdr.cpu);
+				logdev_write_msg(kern_buffer[cpu],i,net);
+				
+				logdev_copy_from_dev(dev,&rs,sizeof(rs));
+				
+				t = ((unsigned long long)rs.high << BITS_PER_LONG) | 
+					(unsigned long long)rs.low;
+				nanosec_rem = do_div(t, 1000000000)/1000;
+				secs = (unsigned long)t;
+				
+				i = snprintf(kern_buffer[cpu],100,"CPU=%d [%5lu.%06lu] ",
+					     hdr.cpu,
+					     secs,nanosec_rem);
+				logdev_copy_from_dev(dev,&kern_buffer[cpu][i],rs.prev_len);
+				i += rs.prev_len;
+				
+				i += snprintf(&kern_buffer[cpu][i],100,":%d -->> ",rs.pid_prev);
+				logdev_copy_from_dev(dev,&kern_buffer[cpu][i],rs.next_len);
+				i += rs.next_len;
+				i += snprintf(&kern_buffer[cpu][i],100,":%d\n",rs.pid_next);
+				
+				logdev_write_msg(kern_buffer[cpu],i,net);
+				
+				if (do_lock)
+					kern_spin_unlock();
+				
+				break;
+			}
+			
+			case LOGDEV_PKT_ID:
+			{
+				struct logdev_pkt rs;
+				struct tcphdr th;
+				struct iphdr iph;
+				int len = sizeof(hdr);
+				int iplen;
+				unsigned long end;
+				int tcplen;
+				char buf[16];
+				unsigned long oaddr;
+				unsigned long iaddr;
+				unsigned short oport;
+				unsigned short iport;
+				int dir;
+				
+				/*
+				 * Here we have "len" keeping track of how much data
+				 * we taken out of the device. So if there's data 
+				 * left in the device for this record, after we have
+				 * all the printable data, we know how much still needs
+				 * to be retreived.
+				 *
+				 * We also use "i" to keep track of where on the kern_buffer
+				 * we last wrote data to.
+				 */
+				
+				logdev_copy_from_dev(dev,&rs,sizeof(rs));
+				len += sizeof(rs);
+				
+				if (do_lock)
+					kern_spin_lock();
+				
+				t = ((unsigned long long)rs.high << BITS_PER_LONG) | 
+					(unsigned long long)rs.low;
+				nanosec_rem = do_div(t, 1000000000)/1000;
+				secs = (unsigned long)t;
+				
+				i = snprintf(kern_buffer[cpu],100,"[%5lu.%06lu] cpu:%d",
+					     secs,nanosec_rem,hdr.cpu);
+
+				if (rs.protocol != htons(ETH_P_IP)) {
+					sprintf(&kern_buffer[cpu][i],"Not IP packet\n");
+					goto pkt_done;
+				}
+				
+				
+				if ((hdr.size - len) < sizeof(iph)) {
+					i += sprintf(&kern_buffer[cpu][i]," IP Header corrupted\n");
+					goto pkt_done;
+				}
+				
+				logdev_copy_from_dev(dev,&iph,sizeof(iph));
+				len += sizeof(iph);
+
+				iph.tot_len = ntohs(iph.tot_len);
+				iph.id = ntohs(iph.id);
+				iph.frag_off = ntohs(iph.frag_off);
+				iph.check = ntohs(iph.check);
+				iph.saddr = ntohl(iph.saddr);
+				iph.daddr = ntohl(iph.daddr);
+	
+				/*
+				 * dir is the direction of the packet. 
+				 *  0 is incoming, 1 is outgoing.
+				 */
+				dir = rs.dir;
+
+				if (dir) {
+					/* outgoing */
+					oaddr = iph.daddr;
+					iaddr = iph.saddr;
+				} else {
+					/* incomming */
+					oaddr = iph.saddr;
+					iaddr = iph.daddr;
+				}
+			
+
+#define IPBYTE(ip,b) (unsigned char)(((ip)>>((b)*8))&0xff)
+			
+				if (iph.protocol != 6) {
+					i += sprintf(&kern_buffer[cpu][i],"%d.%d.%d.%d %s "
+						     "%d.%d.%d.%d (Not TCP packet)\n",
+						     IPBYTE(iaddr,3),
+						     IPBYTE(iaddr,2),
+						     IPBYTE(iaddr,1),
+						     IPBYTE(iaddr,0),
+						     dir ? "==>" : "<==",
+						     IPBYTE(oaddr,3),
+						     IPBYTE(oaddr,2),
+						     IPBYTE(oaddr,1),
+						     IPBYTE(oaddr,0));
+
+					goto pkt_done;
+				}
+
+				iplen = iph.ihl<<2;
+			
+				/*
+				 * Skip any ip options.
+				 */
+				while (iplen > sizeof(iph)) {
+					int count = iplen - sizeof(iph);
+					if (count > 16)
+						count = 16;
+					logdev_copy_from_dev(dev,buf,count);
+					len += count;
+					iplen -= count;
+				}
+
+				if ((hdr.size - len) < sizeof(th)) {
+					i += sprintf(&kern_buffer[cpu][i]," IP Header corrupted\n");
+					goto pkt_done;
+				}
+
+				logdev_copy_from_dev(dev,&th,sizeof(th));
+				len += sizeof(th);
+
+				tcplen = th.doff<<2;
+
+				th.source = ntohs(th.source);
+				th.dest = ntohs(th.dest);
+				th.seq = ntohl(th.seq);
+				th.ack_seq = ntohl(th.ack_seq);
+				th.window = ntohs(th.window);
+				th.check = ntohs(th.check);
+				th.urg_ptr = ntohs(th.urg_ptr);
+			
+				iplen = iph.tot_len - (iplen + tcplen);
+				end = th.seq;
+				if (th.fin || th.syn) {
+					end++;
+				}
+				end += iplen;
+			
+				if (dir) {
+					/* outgoing */
+					oport = th.dest;
+					iport = th.source;
+				} else {
+					/* incomming */
+					oport = th.source;
+					iport = th.dest;
+				}
+
+				i += sprintf(&kern_buffer[cpu][i],
+					     "%d.%d.%d.%d:%d %s %d.%d.%d.%d:%d seq:%u ack:%u "
+					     "(%c%c%c%c%c%c) len:%d win:%d end_seq:%lu\n",
+					     IPBYTE(iaddr,3),
+					     IPBYTE(iaddr,2),
+					     IPBYTE(iaddr,1),
+					     IPBYTE(iaddr,0),
+					     iport,
+					     dir ? "==>" : "<==",
+					     IPBYTE(oaddr,3),
+					     IPBYTE(oaddr,2),
+					     IPBYTE(oaddr,1),
+					     IPBYTE(oaddr,0),
+					     oport,
+					     th.seq,
+					     th.ack_seq,
+					     th.fin ? 'F' : '-',
+					     th.syn ? 'S' : '-',
+					     th.rst ? 'R' : '-',
+					     th.psh ? 'P' : '-',
+					     th.ack ? 'A' : '-',
+					     th.urg ? 'U' : '-',
+					     iplen,
+					     th.window,
+					     end
+					);
+
+			pkt_done:
+			
+				logdev_write_msg(kern_buffer[cpu],i,net);
+			
+				/*
+				 * Clean up anything left over.
+				 */
+				while (len < hdr.size) {
+					int count = hdr.size - len;
+					logdev_copy_from_dev(dev,buf,count);
+					len += count;
+				}
+				
+				if (do_lock)
+					kern_spin_unlock();
+
+				break;
+			}
+			case LOGDEV_CUSTOM:
+			{
+				struct list_head *p;
+				struct logdev_custom custom;
+				int len;
+
+				logdev_copy_from_dev(dev,&custom,sizeof(custom));
+				if (do_lock) {
+					spin_lock(&logdev_callbacks_lock);
+					kern_spin_lock();
+				}
+
+				i = len = hdr.size - sizeof(hdr) - sizeof(custom);
+				if (i > PAGE_SIZE) {
+					i = PAGE_SIZE;
+					/* show that we truncated */
+					hdr.size -= len - i;
+				}
+				logdev_copy_from_dev(dev,kern_buffer[cpu],i);
+			
+				list_for_each(p, &logdev_callbacks) {
+					struct logdev_callback *cb = list_entry(p, struct logdev_callback, list);
+					if (cb->id == custom.id) {
+						cb->func(&hdr,&custom,net,kern_buffer[cpu]);
+						break;
+					}
+				}
+
+				/* No record should be bigger than a page. Ignore all else */
+				while (i < len) {
+					int count = len - i;
+					if (count > PAGE_SIZE)
+						count = PAGE_SIZE;
+					logdev_copy_from_dev(dev,kern_buffer[cpu],count);
+					i += count;
+				}
+
+				/* check if we didn't find a call back */
+				if (p == &logdev_callbacks) {
+					i = sprintf(kern_buffer[cpu],"skipping! LOGDEV_CUSTOM id %d\n",custom.id);
+					logdev_write_msg(kern_buffer[cpu],i,net);
+				}
+
+				if (do_lock) {
+					kern_spin_unlock();
+					spin_unlock(&logdev_callbacks_lock);
+				}
+				break;
+			}
+			default:
+				if (!corrupt) {
+					corrupt = 1;
+					if (do_lock)
+						kern_spin_lock();
+					i = snprintf(kern_buffer[cpu],100,">>>>> Unknown logdev header, may be "
+						     "corrupted from this point on\n");
+					logdev_write_msg(kern_buffer[cpu],i,net);
+					if (do_lock)
+						kern_spin_unlock();
+				}
+			
+				break;
+			}
+		}
+		msg = ">>>>> done <<<<<\n";
+		i = strlen(msg);
+		logdev_write_msg(msg,i,net);
+		
+		spin_unlock_irqrestore(&dev->lock,flags);
+	}
+	atomic_set(&logdev_switch,save_switch);
+	in_logdump--;
+	started = 0;
+}
+
+void logdev_dumpnet(void)
+{
+	if (!configured)
+		return;
+
+	__logdev_dump(1);
+}
+
+void logdev_dump(void)
+{
+	__logdev_dump(0);
+}
+
+#ifdef CONFIG_NETPOLL
+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;
+}
+#endif
+
+#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)
+{
+	struct logdev_dev *dev = m->private;
+	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 (raw_irqs_disabled())
+		BUG();
+
+	raw_local_irq_disable();
+	spin_lock(&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)
+{
+	struct logdev_dev *dev = m->private;
+	spin_unlock(&dev->lock);
+	raw_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 = m->private;
+
+	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)
+{
+	struct proc_dir_entry * de;
+	int ret;
+
+	de = PDE(inode);
+
+	ret = seq_open(file, &logdev_proc_op);
+	if (!ret) {
+		struct seq_file *m = file->private_data;
+		m->private = de->data;
+	}
+	
+	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;
+	int i;
+	char buff[30];
+
+	logdev_proc_dir = proc_mkdir(LOGDEV_PROC_DIR, NULL);
+	if (!logdev_proc_dir)
+		return;
+
+	for (i=0; i < LOGDEV_CPUS; i++) {
+		if (!logdev_dev[i].init)
+			continue;
+
+		sprintf(buff,"%d",i);
+		logdev_dev[i].dir = proc_mkdir(buff,logdev_proc_dir);
+
+		entry = create_proc_entry(LOGDEV_PROC_ENTRY,
+					  S_IFREG | S_IRUGO,
+					  logdev_dev[i].dir);
+		if (entry) {
+			logdev_proc_entry[i] = entry;
+			entry->proc_fops = &logdev_proc_operations;
+			entry->data = &logdev_dev[i];
+		}
+
+		entry = create_proc_entry(LOGDEV_PROC_MINOR,
+					  S_IFREG | S_IRUGO,
+					  logdev_dev[i].dir);
+		if (entry) {
+			entry->read_proc = proc_var_read;
+			entry->data = &logdev_dev[i].minor;
+			logdev_proc_minor[i] = entry;
+		}
+	}
+
+	entry = create_proc_entry(LOGDEV_PROC_SW,
+				  S_IFREG | S_IRUGO | S_IWUGO,
+				  logdev_proc_dir);
+	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,
+				  logdev_proc_dir);
+	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,
+				  logdev_proc_dir);
+	if (entry) {
+		entry->read_proc = proc_var_read;
+		entry->write_proc = proc_var_write;
+		entry->data = &logdev_print_enabled;
+		logdev_proc_print = entry;
+	}
+}
+
+static struct miscdevice logdev_misc_dev[LOGDEV_CPUS] =
+{ [ 0 ... (LOGDEV_CPUS-1) ] = 
+	{
+		.minor = MISC_DYNAMIC_MINOR,
+		.fops = &logdev_fops
+	}
+};
+
+static int logdev_panic_handler(struct notifier_block *this,
+				unsigned long         event,
+				void                  *unused)
+{
+	LOGDUMP();
+	return NOTIFY_OK;
+}
+
+static struct notifier_block logdev_panic_notifier = {
+	.notifier_call	= logdev_panic_handler,
+	.next		= NULL,
+	.priority	= 150	/* priority: INT_MAX >= x >= 0 */
+};
+
+/*
+ * Unfortunately, the die handlers have no way to unregister, so 
+ * we don't want to do this if we are a module. We would if we could
+ * find a way to clean ourselves up when unloaded.
+ */
+#ifndef MODULE
+int logdev_die_handler(struct notifier_block *self, unsigned long val,
+		       void *data)
+{
+	switch (val) {
+	case DIE_OOPS:
+		LOGDUMP();
+		break;
+	default:
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block logdev_die_notifier = {
+	.notifier_call = logdev_die_handler,
+	.priority = 200
+};
+#endif /* !MODULE */
+
+void __exit logdev_cleanup(void)
+{
+	int i;
+	int cpu;
+	char buff[30];
+
+	notifier_chain_unregister(&panic_notifier_list, &logdev_panic_notifier);
+
+#ifdef MODULE
+	{
+		unsigned long flags;
+		spin_lock_irqsave(&logdev_add_hook_lock,flags);
+		logdev_record_switch_hook = NULL;
+		logdev_pkt_hook = NULL;
+		logdev_print_hook = NULL;
+		logdev_vprint_hook = NULL;
+		logdev_print_time_hook = NULL;
+		logdev_print_time_func_hook = NULL;
+		logdev_record_hook = NULL;
+		logdev_write_hook = NULL;
+		logdev_read_hook = NULL;
+		spin_unlock_irqrestore(&logdev_add_hook_lock,flags);
+	}
+#endif
+
+	for (i=0; i < LOGDEV_CPUS; i++) {
+		if (logdev_dev[i].dir) {
+			struct proc_dir_entry *parent = logdev_dev[i].dir;
+			if (logdev_proc_entry[i]) {
+				sprintf(buff,"%s",LOGDEV_PROC_ENTRY);
+				remove_proc_entry(buff,parent);
+			}
+			if (logdev_proc_minor[i]) {
+				sprintf(buff,"%s",LOGDEV_PROC_MINOR);
+				remove_proc_entry(buff,parent);
+			}
+			sprintf(buff,"%s/%d",LOGDEV_PROC_DIR,i);
+			remove_proc_entry(buff,0);
+		}
+	}
+
+	if (logdev_proc_sw) {
+		sprintf(buff,"%s/%s",LOGDEV_PROC_DIR,LOGDEV_PROC_SW);
+		remove_proc_entry(buff,0);
+	}
+	if (logdev_proc_level) {
+		sprintf(buff,"%s/%s",LOGDEV_PROC_DIR,LOGDEV_PROC_LEVEL);
+		remove_proc_entry(buff,0);
+	}
+	if (logdev_proc_print) {
+		sprintf(buff,"%s/%s",LOGDEV_PROC_DIR,LOGDEV_PROC_PRINT);
+		remove_proc_entry(buff,0);
+	}
+	if (logdev_proc_dir)
+		remove_proc_entry(LOGDEV_PROC_DIR,0);
+
+	for_each_present_cpu(cpu) {
+
+		if (cpu >= LOGDEV_CPUS)
+			break;
+		
+		if (!logdev_dev[cpu].init)
+			continue;
+
+		if (logdev_misc_dev[cpu].name)
+			kfree(logdev_misc_dev[cpu].name);
+
+		if (logdev_dev[cpu].entry) {
+			struct logdev_entry *entry;
+			for (i=0, entry=logdev_dev[cpu].entry; i<logdev_dev[cpu].len; i++,entry++)
+				if (entry->dat) {
+					free_page((unsigned long)entry->dat);
+					entry->dat = NULL;
+				}
+			logdev_dev[cpu].len = 0;
+			kfree(logdev_dev[cpu].entry);
+			logdev_dev[cpu].entry = NULL;
+		}
+
+		misc_deregister(&logdev_misc_dev[cpu]);
+	}
+}
+
+int __init logdev_init(void)
+{
+	int res = 0;
+	int cpu;
+	struct logdev_entry *entry;
+
+	printk("Logdevice: copyright Steven Rostedt, Kihon Technologies Inc."
+	       " (Version %s)\n",
+	       logdev_version);
+
+	for_each_present_cpu(cpu) {
+		int i;
+		char *name;
+
+		if (cpu >= LOGDEV_CPUS) {
+			printk(KERN_WARNING "More present cpus (%d) than NR_CPUS (%d)\n",
+			       cpu,LOGDEV_CPUS);
+			break;
+		}
+
+		printk("Initializing logdev for cpu: %d\n",cpu);
+
+		name = kmalloc(32,GFP_KERNEL);
+		if (!name) {
+			printk(KERN_WARNING "logdev: can't allocate misc name\n");
+			continue;
+		}
+
+		sprintf(name,"logdev%d",cpu);
+
+		logdev_misc_dev[cpu].name = name;
+
+		res = misc_register(&logdev_misc_dev[cpu]);
+		if (res) {
+			printk("Can't register misc device for Logdev CPU %d\n",cpu);
+			kfree(name);
+			logdev_misc_dev[cpu].name = NULL;
+			continue;
+		}
+
+		atomic_set(&logdev_dev[cpu].minor,logdev_misc_dev[cpu].minor);
+
+		logdev_dev[cpu].len = pages;
+		
+		res = -ENOMEM;
+
+		logdev_dev[cpu].entry = kmalloc(sizeof(struct logdev_entry)*logdev_dev[cpu].len,GFP_KERNEL);
+		if (!logdev_dev[cpu].entry)
+			goto fail;
+		
+		memset(logdev_dev[cpu].entry,0,sizeof(*logdev_dev[cpu].entry)*logdev_dev[cpu].len);
+	
+		for (i=0,entry=logdev_dev[cpu].entry; i<logdev_dev[cpu].len; i++,entry++) {
+			entry->dat = (void*)__get_free_pages(GFP_KERNEL,0);
+			if (!entry->dat)
+				goto fail;
+		}
+		init_waitqueue_head(&logdev_dev[cpu].wait);
+		spin_lock_init(&logdev_dev[cpu].lock);
+
+		logdev_dev[cpu].init = 1;
+
+#ifndef CONFIG_LOGDEV_MULTI_CPUS
+		break;
+#endif
+
+	}
+
+	logdev_proc_setup();
+
+	notifier_chain_register(&panic_notifier_list, &logdev_panic_notifier);
+
+#ifndef MODULE
+	register_die_notifier(&logdev_die_notifier);
+#endif
+
+#undef LOGDEV_SWITCHON
+#ifdef LOGDEV_SWITCHON
+	atomic_set(&logdev_switch,1);
+#endif
+
+	res = 0;
+
+
+#ifdef MODULE
+	{
+		unsigned long flags;
+		/* Make hooks last! */
+		spin_lock_irqsave(&logdev_add_hook_lock,flags);
+		logdev_record_switch_hook = logdev_record_switch;
+		logdev_pkt_hook = logdev_pkt;
+		logdev_print_hook = logdev_print;
+		logdev_vprint_hook = logdev_vprint;
+		logdev_print_time_hook = logdev_print_time;
+		logdev_record_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_irqrestore(&logdev_add_hook_lock,flags);
+	}
+#ifdef CONFIG_NETPOLL
+	logdevnet_init();
+#endif
+#endif
+
+out:
+	return res;
+
+fail:
+	logdev_cleanup();
+	goto out;
+}
+
+#ifndef MODULE
+EXPORT_SYMBOL(logdev_record_switch);
+EXPORT_SYMBOL(logdev_switch_turnoff);
+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);
+
+#ifdef CONFIG_NETPOLL
+late_initcall(logdevnet_init);
+#endif
+#endif
+
+module_init(logdev_init);
+module_exit(logdev_cleanup);
Index: drivers/char/sysrq.c
===================================================================
--- drivers/char/sysrq.c	(.../tags/starting_base_kernel_2.6.12)	(revision 300)
+++ drivers/char/sysrq.c	(.../trunk/kernel)	(revision 300)
@@ -31,6 +31,7 @@
 #include <linux/suspend.h>
 #include <linux/writeback.h>
 #include <linux/buffer_head.h>		/* for fsync_bdev() */
+#include <linux/logdev.h>
 #include <linux/swap.h>
 #include <linux/spinlock.h>
 #include <linux/vt_kern.h>
@@ -241,6 +242,39 @@
 	.enable_mask	= SYSRQ_ENABLE_SIGNAL,
 };
 
+static void sysrq_handle_dumplog(int key, struct pt_regs *pt_regs,
+				 struct tty_struct *tty) 
+{
+	LOGDUMP();
+}
+
+static struct sysrq_key_op sysrq_dumplog_op = {
+	.handler	= sysrq_handle_dumplog,
+	.help_msg	= "Dumplog",
+	.action_msg	= "Dump logdev to serial",
+};
+
+static void sysrq_handle_togglelogswitch(int key, struct pt_regs *pt_regs,
+                                         struct tty_struct *tty) 
+{
+        /*
+         * Not so atomic, but we really don't care!
+         */
+        if (atomic_read(&logdev_switch)) {
+                atomic_set(&logdev_switch,0);
+                printk("logdev_switch now off\n");
+        } else {
+                atomic_set(&logdev_switch,1);
+                printk("logdev_switch now on\n");
+        }
+}
+
+static struct sysrq_key_op sysrq_togglelogdevswitch_op = {
+	.handler	= sysrq_handle_togglelogswitch,
+	.help_msg	= "Togglelogswitch",
+	.action_msg	= "Toggling logdev_switch",
+};
+
 /* END SIGNAL SYSRQ HANDLERS BLOCK */
 
 static void sysrq_handle_unrt(int key, struct pt_regs *pt_regs,
@@ -273,11 +307,11 @@
 		 it is handled specially on the sparc
 		 and will never arrive */
 /* b */	&sysrq_reboot_op,
-/* c */ NULL,
+/* c */	&sysrq_moom_op,
 /* d */	NULL,
 /* e */	&sysrq_term_op,
-/* f */	&sysrq_moom_op,
-/* g */	NULL,
+/* f */	&sysrq_togglelogdevswitch_op,
+/* g */	&sysrq_dumplog_op,
 /* h */	NULL,
 /* i */	&sysrq_kill_op,
 /* j */	NULL,
Index: drivers/char/logdev_hooks.c
===================================================================
--- drivers/char/logdev_hooks.c	(.../tags/starting_base_kernel_2.6.12)	(revision 0)
+++ drivers/char/logdev_hooks.c	(.../trunk/kernel)	(revision 300)
@@ -0,0 +1,81 @@
+#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_vprint_hook)(const char *fmt, va_list va);
+int (*logdev_print_time_hook)(const char *fmt, ...);
+int (*logdev_print_time_func_hook)(const char *fmt, ...);
+void (*logdev_record_switch_hook)(struct task_struct *a,struct task_struct *b);
+int (*logdev_record_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_vprint_hook);
+EXPORT_SYMBOL(logdev_print_time_hook);
+EXPORT_SYMBOL(logdev_print_time_func_hook);
+EXPORT_SYMBOL(logdev_record_switch_hook);
+EXPORT_SYMBOL(logdev_record_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
+
+void logdev_time(void)
+{
+	unsigned long long t;
+	unsigned long nanosec_rem;
+	t = sched_clock();
+	nanosec_rem = do_div(t, 1000000000);
+	LOGPRINT("[%5lu.%06lu] ",
+		 (unsigned long)t,
+		 nanosec_rem/1000);
+}
+
+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
+
+int in_logdump;
+
+raw_spinlock_t logdev_add_hook_lock = RAW_SPIN_LOCK_UNLOCKED;
+
+/*
+ * sched_clock isn't exported, so we export it ourselves.
+ */
+EXPORT_SYMBOL(logdev_switch);
+EXPORT_SYMBOL(logdev_level);
+EXPORT_SYMBOL(logdev_print_enabled);
+EXPORT_SYMBOL(sched_clock);
