diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/arch/i386/Kconfig 240-lkcd/arch/i386/Kconfig
--- 231-thread_under_page/arch/i386/Kconfig	Fri May 30 19:24:44 2003
+++ 240-lkcd/arch/i386/Kconfig	Fri May 30 19:24:56 2003
@@ -1528,6 +1528,39 @@ source "arch/i386/oprofile/Kconfig"
 
 menu "Kernel hacking"
 
+config CRASH_DUMP
+	tristate "Crash dump support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	default n
+	---help---
+	  Say Y here to enable saving an image of system memory when a panic
+	  or other error occurs. Dumps can also be forced with the SysRq+d
+	  key if MAGIC_SYSRQ is enabled.
+
+config CRASH_DUMP_BLOCKDEV
+	tristate "Crash dump block device driver"
+	depends on CRASH_DUMP
+	help
+	  Say Y to allow saving crash dumps directly to a disk device.
+
+config CRASH_DUMP_NETDEV
+	tristate "Crash dump network device driver"
+	depends on CRASH_DUMP
+	help
+	  Say Y to allow saving crash dumps over a network device.
+
+config CRASH_DUMP_COMPRESS_RLE
+	tristate "Crash dump RLE compression"
+	depends on CRASH_DUMP
+	help
+	  Say Y to allow saving dumps with Run Length Encoding compression.
+
+config CRASH_DUMP_COMPRESS_GZIP
+	tristate "Crash dump GZIP compression"
+	depends on CRASH_DUMP
+	help
+	  Say Y to allow saving dumps with Gnu Zip compression.
+
 config DEBUG_KERNEL
 	bool "Kernel debugging"
 	help
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/arch/i386/boot/Makefile 240-lkcd/arch/i386/boot/Makefile
--- 231-thread_under_page/arch/i386/boot/Makefile	Mon Mar 17 21:43:38 2003
+++ 240-lkcd/arch/i386/boot/Makefile	Fri May 30 19:24:56 2003
@@ -101,3 +101,4 @@ zlilo: $(BOOTIMAGE)
 
 install: $(BOOTIMAGE)
 	sh $(src)/install.sh $(KERNELRELEASE) $(BOOTIMAGE) System.map "$(INSTALL_PATH)"
+	if [ -f init/kerntypes.o ]; then cp init/kerntypes.o $(INSTALL_PATH)/Kerntypes; fi
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/arch/i386/kernel/i386_ksyms.c 240-lkcd/arch/i386/kernel/i386_ksyms.c
--- 231-thread_under_page/arch/i386/kernel/i386_ksyms.c	Fri May 30 19:24:43 2003
+++ 240-lkcd/arch/i386/kernel/i386_ksyms.c	Fri May 30 19:24:56 2003
@@ -16,6 +16,7 @@
 #include <linux/tty.h>
 #include <linux/highmem.h>
 #include <linux/time.h>
+#include <linux/nmi.h>
 
 #include <asm/semaphore.h>
 #include <asm/processor.h>
@@ -33,6 +34,8 @@
 #include <asm/tlbflush.h>
 #include <asm/nmi.h>
 #include <asm/edd.h>
+#include <asm/kdebug.h>
+#include <asm/e820.h>
 
 extern void dump_thread(struct pt_regs *, struct user *);
 extern spinlock_t rtc_lock;
@@ -193,6 +196,7 @@ EXPORT_SYMBOL(rtc_lock);
 
 EXPORT_SYMBOL_GPL(set_nmi_callback);
 EXPORT_SYMBOL_GPL(unset_nmi_callback);
+EXPORT_SYMBOL_GPL(die_chain);
  
 #undef memcpy
 #undef memset
@@ -221,6 +225,23 @@ EXPORT_SYMBOL(kmap_atomic_to_page);
 #ifdef CONFIG_EDD_MODULE
 EXPORT_SYMBOL(edd);
 EXPORT_SYMBOL(eddnr);
+#endif
+
+#ifdef CONFIG_CRASH_DUMP_MODULE
+#ifdef CONFIG_SMP
+extern irq_desc_t irq_desc[NR_IRQS];
+extern unsigned long irq_affinity[NR_IRQS];
+extern void stop_this_cpu(void *);
+EXPORT_SYMBOL(irq_desc);
+EXPORT_SYMBOL(irq_affinity);
+EXPORT_SYMBOL(stop_this_cpu);
+EXPORT_SYMBOL(dump_send_ipi);
+#endif
+extern int pfn_is_ram(unsigned long);
+EXPORT_SYMBOL(pfn_is_ram);
+#ifdef ARCH_HAS_NMI_WATCHDOG
+EXPORT_SYMBOL(touch_nmi_watchdog);
+#endif
 #endif
 
 #ifdef CONFIG_X86_STACK_CHECK
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/arch/i386/kernel/nmi.c 240-lkcd/arch/i386/kernel/nmi.c
--- 231-thread_under_page/arch/i386/kernel/nmi.c	Fri May 30 19:13:53 2003
+++ 240-lkcd/arch/i386/kernel/nmi.c	Fri May 30 19:24:56 2003
@@ -25,6 +25,7 @@
 #include <linux/module.h>
 #include <linux/gdb.h>
 
+#include <asm/kdebug.h>
 #include <asm/smp.h>
 #include <asm/mtrr.h>
 #include <asm/mpspec.h>
@@ -466,6 +467,7 @@ void nmi_watchdog_tick (struct pt_regs *
 			bust_spinlocks(1);
 			printk("NMI Watchdog detected LOCKUP on CPU%d, eip %08lx, registers:\n", cpu, regs->eip);
 			show_registers(regs);
+			notify_die(DIE_WATCHDOG, "nmi_watchdog", regs, 0);
 			printk("console shuts up ...\n");
 			console_silent();
 			spin_unlock(&nmi_print_lock);
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/arch/i386/kernel/reboot.c 240-lkcd/arch/i386/kernel/reboot.c
--- 231-thread_under_page/arch/i386/kernel/reboot.c	Fri May 30 19:01:59 2003
+++ 240-lkcd/arch/i386/kernel/reboot.c	Fri May 30 19:24:56 2003
@@ -8,8 +8,10 @@
 #include <linux/interrupt.h>
 #include <linux/mc146818rtc.h>
 #include <asm/uaccess.h>
+#include <asm/kdebug.h>
 #include "mach_reboot.h"
 
+
 /*
  * Power off function, if any
  */
@@ -248,7 +250,8 @@ void machine_restart(char * __unused)
 	 * Stop all CPUs and turn off local APICs and the IO-APIC, so
 	 * other OSs see a clean IRQ state.
 	 */
-	smp_send_stop();
+	if (notify_die(DIE_STOP,"cpustop",0,0) != NOTIFY_BAD)
+		smp_send_stop();
 	disable_IO_APIC();
 #endif
 
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/arch/i386/kernel/smp.c 240-lkcd/arch/i386/kernel/smp.c
--- 231-thread_under_page/arch/i386/kernel/smp.c	Fri May 30 19:24:45 2003
+++ 240-lkcd/arch/i386/kernel/smp.c	Fri May 30 19:24:56 2003
@@ -19,6 +19,7 @@
 #include <linux/mc146818rtc.h>
 #include <linux/cache.h>
 #include <linux/interrupt.h>
+#include <linux/dump.h>
 
 #include <asm/mtrr.h>
 #include <asm/pgalloc.h>
@@ -144,6 +145,13 @@ inline void __send_IPI_shortcut(unsigned
 	 */
 	cfg = __prepare_ICR(shortcut, vector);
 
+	if (vector == DUMP_VECTOR) {
+		/*
+		 * Setup DUMP IPI to be delivered as an NMI
+		 */
+		cfg = (cfg&~APIC_VECTOR_MASK)|APIC_DM_NMI;
+	}
+
 	/*
 	 * Send the IPI. The write to APIC_ICR fires this off.
 	 */
@@ -452,6 +460,11 @@ void flush_tlb_all(void)
 	on_each_cpu(do_flush_tlb_all, 0, 1, 1);
 }
 
+void dump_send_ipi(void)
+{
+	send_IPI_allbutself(DUMP_VECTOR);
+}
+
 /*
  * this function sends a 'reschedule' IPI to another CPU.
  * it goes straight through and wastes no time serializing
@@ -563,7 +576,7 @@ int smp_call_function (void (*func) (voi
 	return 0;
 }
 
-static void stop_this_cpu (void * dummy)
+void stop_this_cpu (void * dummy)
 {
 	/*
 	 * Remove this CPU:
@@ -628,4 +641,3 @@ struct pt_regs * smp_call_function_inter
 	}
 	return regs;
 }
-
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/arch/i386/kernel/smpboot.c 240-lkcd/arch/i386/kernel/smpboot.c
--- 231-thread_under_page/arch/i386/kernel/smpboot.c	Fri May 30 19:24:20 2003
+++ 240-lkcd/arch/i386/kernel/smpboot.c	Fri May 30 19:24:56 2003
@@ -48,6 +48,7 @@
 #include <asm/pgalloc.h>
 #include <asm/tlbflush.h>
 #include <asm/desc.h>
+#include <asm/kdebug.h>
 #include <asm/arch_hooks.h>
 
 #include <mach_apic.h>
@@ -416,6 +417,8 @@ void __init smp_callin(void)
 	 * Save our processor parameters
 	 */
  	smp_store_cpu_info(cpuid);
+
+	notify_die(DIE_CPUINIT, "cpuinit", NULL, 0);
 
 	disable_APIC_timer();
 	local_irq_disable();
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/arch/i386/kernel/traps.c 240-lkcd/arch/i386/kernel/traps.c
--- 231-thread_under_page/arch/i386/kernel/traps.c	Fri May 30 19:15:19 2003
+++ 240-lkcd/arch/i386/kernel/traps.c	Fri May 30 19:24:56 2003
@@ -43,6 +43,7 @@
 #include <asm/debugreg.h>
 #include <asm/desc.h>
 #include <asm/i387.h>
+#include <asm/kdebug.h>
 #include <asm/nmi.h>
 
 #include <asm/smp.h>
@@ -268,12 +269,16 @@ bug:
 }
 
 spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
+struct notifier_block *die_chain;
 
 void die(const char * str, struct pt_regs * regs, long err)
 {
 	static int die_counter;
 
 	console_verbose();
+	if (notify_die(DIE_DIE, str, regs, err) == NOTIFY_BAD)
+		goto exit_segv;
+
 	spin_lock_irq(&die_lock);
 	bust_spinlocks(1);
 	handle_BUG(regs);
@@ -282,6 +287,10 @@ void die(const char * str, struct pt_reg
 	show_registers(regs);
 	bust_spinlocks(0);
 	spin_unlock_irq(&die_lock);
+
+	notify_die(DIE_OOPS, str, regs, err);
+
+exit_segv:
 	if (in_interrupt())
 		panic("Fatal exception in interrupt");
 
@@ -400,6 +409,10 @@ asmlinkage void do_general_protection(st
 	if (kprobe_running() && kprobe_fault_handler(regs, 13))
 		return;
 
+	if (notify_die(DIE_PROTFAULT, "general protection", regs, error_code) 
+		== NOTIFY_BAD)
+		return;
+
 	if (!(regs->xcs & 3))
 		goto gp_in_kernel;
 
@@ -444,6 +457,7 @@ static void io_check_error(unsigned char
 	outb(reason, 0x61);
 }
 
+
 static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
 {
 #ifdef CONFIG_MCA
@@ -478,10 +492,14 @@ static void default_do_nmi(struct pt_reg
 		unknown_nmi_error(reason, regs);
 		return;
 	}
+
+	if (notify_die(DIE_NMI, "nmi", regs, reason) == NOTIFY_BAD)
+		return;
 	if (reason & 0x80)
 		mem_parity_error(reason, regs);
 	if (reason & 0x40)
 		io_check_error(reason, regs);
+
 	/*
 	 * Reassert NMI in case it became active meanwhile
 	 * as it's edge-triggered.
@@ -523,10 +541,13 @@ void unset_nmi_callback(void)
 
 asmlinkage int do_int3(struct pt_regs *regs, long error_code)
 {
-	if (kprobe_handler(regs))
-		return 1;
 	/* This is an interrupt gate, because kprobes wants interrupts
            disabled.  Normal trap handlers don't. */
+	if (kprobe_handler(regs))
+		return 1;
+	if (notify_die(DIE_INT3, "int3", regs, error_code) == NOTIFY_BAD)
+		return 1;
+
 	restore_interrupts(regs);
 	CHK_REMOTE_DEBUG(3,SIGTRAP,error_code,regs,return 0)
 	do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL);
@@ -573,6 +594,12 @@ asmlinkage int do_debug(struct pt_regs *
 	/* Interrupts not disabled for normal trap handling. */
 	restore_interrupts(regs);
 
+	if (notify_die(DIE_DEBUG, "debug", regs, error_code) == NOTIFY_BAD)
+		return 1; 
+
+	/* Interrupts not disabled for normal trap handling. */
+	restore_interrupts(regs);
+
 	/* Mask out spurious debug traps due to lazy DR7 setting */
 	if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) {
 		if (!tsk->thread.debugreg[7])
@@ -808,6 +835,9 @@ asmlinkage void math_state_restore(struc
 
 	if (kprobe_running() && kprobe_fault_handler(&regs, 7))
 		return;
+	if (notify_die(DIE_FPUTRAP, "fpu", &regs, 0) == NOTIFY_BAD)
+		return;
+
 	if (!tsk->used_math)
 		init_fpu(tsk);
 	restore_fpu(tsk);
@@ -840,6 +870,39 @@ void __init trap_init_f00f_bug(void)
 }
 #endif
 
+static inline void get_current_regs(struct pt_regs *regs)
+{
+	__asm__ __volatile__("movl %%ebx,%0" : "=m"(regs->ebx));
+	__asm__ __volatile__("movl %%ecx,%0" : "=m"(regs->ecx));
+	__asm__ __volatile__("movl %%edx,%0" : "=m"(regs->edx));
+	__asm__ __volatile__("movl %%esi,%0" : "=m"(regs->esi));
+	__asm__ __volatile__("movl %%edi,%0" : "=m"(regs->edi));
+	__asm__ __volatile__("movl %%ebp,%0" : "=m"(regs->ebp));
+	__asm__ __volatile__("movl %%eax,%0" : "=m"(regs->eax));
+	__asm__ __volatile__("movl %%esp,%0" : "=m"(regs->esp));
+	__asm__ __volatile__("movw %%ss, %%ax;" :"=a"(regs->xss));
+	__asm__ __volatile__("movw %%cs, %%ax;" :"=a"(regs->xcs));
+	__asm__ __volatile__("movw %%ds, %%ax;" :"=a"(regs->xds));
+	__asm__ __volatile__("movw %%es, %%ax;" :"=a"(regs->xes));
+	__asm__ __volatile__("pushfl; popl %0" :"=m"(regs->eflags));
+	regs->eip = (unsigned long)current_text_addr();
+}
+
+static int panic_event(struct notifier_block *this, 
+				unsigned long event, void *ptr)
+{
+	struct pt_regs regs;
+	get_current_regs(&regs);
+
+	return notify_die(DIE_PANIC, (const char *)ptr, &regs, event);
+}
+
+extern struct notifier_block *panic_notifier_list;
+static int panic_event(struct notifier_block *, unsigned long, void *);
+static struct notifier_block panic_block = {
+	.notifier_call = panic_event,
+};
+
 #define _set_gate(gate_addr,type,dpl,addr,seg) \
 do { \
   int __d0, __d1; \
@@ -934,10 +997,14 @@ void __init trap_init(void)
 	set_call_gate(&default_ldt[0],lcall7);
 	set_call_gate(&default_ldt[4],lcall27);
 
+	notify_die(DIE_TRAPINIT, "traps initialized", 0, 0); 
 	/*
 	 * Should be a barrier for any external CPU state.
 	 */
 	cpu_init();
 
 	trap_init_hook();
+
+	notifier_chain_register(&panic_notifier_list, &panic_block);
 }
+
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/arch/i386/mm/fault.c 240-lkcd/arch/i386/mm/fault.c
--- 231-thread_under_page/arch/i386/mm/fault.c	Fri May 30 19:15:14 2003
+++ 240-lkcd/arch/i386/mm/fault.c	Fri May 30 19:24:56 2003
@@ -35,6 +35,7 @@
 #include <asm/pgalloc.h>
 #include <asm/hardirq.h>
 #include <asm/desc.h>
+#include <asm/kdebug.h>
 
 extern void die(const char *,struct pt_regs *,long);
 
@@ -88,6 +89,12 @@ asmlinkage void do_page_fault(struct pt_
 
 	/* get the address */
 	__asm__("movl %%cr2,%0":"=r" (address));
+
+	/* for many cases list will be empty, so optimize for that case */
+	if (unlikely(die_chain != NULL) 
+	    && notify_die(DIE_PAGEFAULT, "page fault", regs, error_code) 
+	        == NOTIFY_BAD)
+		return;
 
 	if (kprobe_running() && kprobe_fault_handler(regs, 14))
 		return;
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/arch/i386/mm/init.c 240-lkcd/arch/i386/mm/init.c
--- 231-thread_under_page/arch/i386/mm/init.c	Fri May 30 19:01:59 2003
+++ 240-lkcd/arch/i386/mm/init.c	Fri May 30 19:24:56 2003
@@ -166,7 +166,7 @@ static inline int page_kills_ppro(unsign
 	return 0;
 }
 
-static inline int page_is_ram(unsigned long pagenr)
+int page_is_ram(unsigned long pagenr)
 {
 	int i;
 
@@ -186,6 +186,12 @@ static inline int page_is_ram(unsigned l
 			return 1;
 	}
 	return 0;
+}
+
+/* To enable modules to check if a page is in RAM */
+int pfn_is_ram(unsigned long pfn)
+{
+	return (page_is_ram(pfn));
 }
 
 #ifdef CONFIG_HIGHMEM
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/arch/s390/boot/Makefile 240-lkcd/arch/s390/boot/Makefile
--- 231-thread_under_page/arch/s390/boot/Makefile	Sun Apr 20 19:34:58 2003
+++ 240-lkcd/arch/s390/boot/Makefile	Fri May 30 19:24:56 2003
@@ -16,4 +16,4 @@ $(obj)/image: vmlinux FORCE
 
 install: $(CONFIGURE) $(obj)/image
 	sh -x $(obj)/install.sh $(KERNELRELEASE) $(obj)/image \
-	      System.map Kerntypes "$(INSTALL_PATH)"
+	      System.map init/kerntypes.o "$(INSTALL_PATH)"
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/arch/s390/boot/install.sh 240-lkcd/arch/s390/boot/install.sh
--- 231-thread_under_page/arch/s390/boot/install.sh	Sun Nov 17 20:29:48 2002
+++ 240-lkcd/arch/s390/boot/install.sh	Fri May 30 19:24:56 2003
@@ -16,7 +16,8 @@
 #   $1 - kernel version
 #   $2 - kernel image file
 #   $3 - kernel map file
-#   $4 - default install path (blank if root directory)
+#   $4 - kernel type file
+#   $5 - default install path (blank if root directory)
 #
 
 # User may have a custom install script
@@ -26,13 +27,22 @@ if [ -x /sbin/installkernel ]; then exec
 
 # Default install - same as make zlilo
 
-if [ -f $4/vmlinuz ]; then
-	mv $4/vmlinuz $4/vmlinuz.old
+if [ -f $5/vmlinuz ]; then
+	mv $5/vmlinuz $5/vmlinuz.old
 fi
 
-if [ -f $4/System.map ]; then
-	mv $4/System.map $4/System.old
+if [ -f $5/System.map ]; then
+	mv $5/System.map $5/System.old
 fi
 
-cat $2 > $4/vmlinuz
-cp $3 $4/System.map
+if [ -f $5/Kerntypes ]; then
+	mv $5/Kerntypes $5/Kerntypes.old
+fi
+
+cat $2 > $5/vmlinuz
+cp $3 $5/System.map
+
+# copy the kernel type file if it exists
+if [ -f $4 ]; then
+	cp $4 $5/Kerntypes
+fi
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/Makefile 240-lkcd/drivers/Makefile
--- 231-thread_under_page/drivers/Makefile	Fri May 30 19:02:02 2003
+++ 240-lkcd/drivers/Makefile	Fri May 30 19:24:56 2003
@@ -51,3 +51,4 @@ obj-$(CONFIG_ISDN_BOOL)		+= isdn/
 obj-$(CONFIG_MCA)		+= mca/
 obj-$(CONFIG_EISA)		+= eisa/
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq/
+obj-$(CONFIG_CRASH_DUMP)	+= dump/
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/dump/Makefile 240-lkcd/drivers/dump/Makefile
--- 231-thread_under_page/drivers/dump/Makefile	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/drivers/dump/Makefile	Fri May 30 19:24:56 2003
@@ -0,0 +1,14 @@
+#
+# Makefile for the dump device drivers.
+#
+export-objs				:= dump_setup.o
+
+dump-y					:= dump_setup.o dump_fmt.o dump_filters.o dump_scheme.o dump_execute.o
+dump-$(CONFIG_X86)			+= dump_i386.o
+dump-objs				+= $(dump-y)
+
+obj-$(CONFIG_CRASH_DUMP)		+= dump.o
+obj-$(CONFIG_CRASH_DUMP_BLOCKDEV)	+= dump_blockdev.o
+obj-$(CONFIG_CRASH_DUMP_NETDEV)	+= dump_netdev.o
+obj-$(CONFIG_CRASH_DUMP_COMPRESS_RLE)	+= dump_rle.o
+obj-$(CONFIG_CRASH_DUMP_COMPRESS_GZIP)	+= dump_gzip.o
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/dump/dump_blockdev.c 240-lkcd/drivers/dump/dump_blockdev.c
--- 231-thread_under_page/drivers/dump/dump_blockdev.c	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/drivers/dump/dump_blockdev.c	Fri May 30 19:24:56 2003
@@ -0,0 +1,447 @@
+/*
+ * Implements the dump driver interface for saving a dump to 
+ * a block device through the kernel's generic low level block i/o
+ * routines.
+ *
+ * Started: June 2002 - Mohamed Abbas <mohamed.abbas@intel.com>
+ * 	Moved original lkcd kiobuf dump i/o code from dump_base.c
+ * 	to use generic dump device interfaces
+ *
+ * Sept 2002 - Bharata B. Rao <bharata@in.ibm.com>
+ * 	Convert dump i/o to directly use bio instead of kiobuf for 2.5
+ *
+ * Oct 2002  - Suparna Bhattacharya <suparna@in.ibm.com>
+ * 	Rework to new dumpdev.h structures, implement open/close/
+ * 	silence, misc fixes (blocknr removal, bio_add_page usage)  
+ *
+ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 2001 - 2002 Matt D. Robinson.  All rights reserved.
+ * Copyright (C) 2002 International Business Machines Corp. 
+ *
+ * This code is released under version 2 of the GNU GPL.
+ */
+
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/bio.h>
+#include <asm/hardirq.h>
+#include <linux/dump.h>
+#include "dump_methods.h"
+
+extern void *dump_page_buf;
+
+/* The end_io callback for dump i/o completion */
+static int
+dump_bio_end_io(struct bio *bio, unsigned int bytes_done, int error)
+{
+	struct dump_blockdev *dump_bdev;
+
+	if (bio->bi_size) {
+		/* some bytes still left to transfer */
+		return 1; /* not complete */
+	}
+
+	dump_bdev = (struct dump_blockdev *)bio->bi_private;
+	if (error) {
+		printk("IO error while writing the dump, aborting\n");
+	}
+
+	dump_bdev->err = error;
+
+	/* no wakeup needed, since caller polls for completion */
+	return 0;
+}
+
+/* Check if the dump bio is already mapped to the specified buffer */
+static int
+dump_block_map_valid(struct dump_blockdev *dev, struct page *page, 
+	int len) 
+{
+	struct bio *bio = dev->bio;
+
+	if (!bio->bi_vcnt)
+		return 0; /* first time, not mapped */
+
+
+	if ((bio_page(bio) != page) || (len != bio->bi_vcnt << PAGE_SHIFT)) 
+		return 0; /* buffer not mapped */
+
+	/* quick check to decide if we need to redo bio_add_page */
+	if (bdev_get_queue(bio->bi_bdev)->merge_bvec_fn)
+		return 0; /* device may have other restrictions */
+
+	return 1; /* already mapped */
+}
+
+/* 
+ * Set up the dump bio for i/o from the specified buffer 
+ * Return value indicates whether the full buffer could be mapped or not
+ */
+static int
+dump_block_map(struct dump_blockdev *dev, void *buf, int len)
+{
+	struct page *page = virt_to_page(buf);
+	struct bio *bio = dev->bio;
+	unsigned long bsize = 0;
+
+	bio->bi_bdev = dev->bdev;
+	bio->bi_sector = (dev->start_offset + dev->ddev.curr_offset) >> 9;
+	bio->bi_idx = 0; /* reset index to the beginning */
+
+	if (dump_block_map_valid(dev, page, len)) {
+		/* already mapped and usable rightaway */
+		bio->bi_size = len; /* reset size to the whole bio */
+	} else {
+		/* need to map the bio */
+		bio->bi_size = 0;
+		bio->bi_vcnt = 0;
+		bsize = bdev_hardsect_size(bio->bi_bdev);
+
+		/* first a few sanity checks */
+		if (len < bsize) {
+			printk("map: len less than hardsect size \n");
+			return -EINVAL;
+		}
+
+		if ((unsigned long)buf & bsize) {
+			printk("map: not aligned \n");
+			return -EINVAL;
+		}
+
+		/* assume contig. page aligned low mem buffer( no vmalloc) */
+		if ((page_address(page) != buf) || (len & (PAGE_SIZE - 1))) {
+			printk("map: invalid buffer alignment!\n");
+			return -EINVAL; 
+		}
+		/* finally we can go ahead and map it */
+		while (bio->bi_size < len)
+			if (bio_add_page(bio, page++, PAGE_SIZE, 0) == 0) {
+				break;
+			}
+
+		bio->bi_end_io = dump_bio_end_io;
+		bio->bi_private = dev;
+	}
+
+	if (bio->bi_size != len) {
+		printk("map: bio size = %d not enough for len = %d!\n",
+			bio->bi_size, len);
+		return -E2BIG;
+	}
+	return 0;
+}
+
+static void
+dump_free_bio(struct bio *bio)
+{
+	if (bio)
+		kfree(bio->bi_io_vec);
+	kfree(bio);
+}
+
+/*
+ * Prepares the dump device so we can take a dump later. 
+ * The caller is expected to have filled up the kdev_id field in the 
+ * block dump dev structure.
+ *
+ * At dump time when dump_block_write() is invoked it will be too 
+ * late to recover, so as far as possible make sure obvious errors 
+ * get caught right here and reported back to the caller.
+ */
+static int
+dump_block_open(struct dump_dev *dev, unsigned long arg)
+{
+	struct dump_blockdev *dump_bdev = DUMP_BDEV(dev);
+	struct block_device *bdev;
+	int retval = 0;
+	struct bio_vec *bvec;
+
+	/* make sure this is a valid block device */
+	if (!arg) {
+		retval = -EINVAL;
+		goto err;
+	}
+
+	/* get a corresponding block_dev struct for this */
+	bdev = bdget((dev_t)arg);
+	if (!bdev) {
+		retval = -ENODEV;
+		goto err;
+	}
+
+	/* get the block device opened */
+	if ((retval = blkdev_get(bdev, O_RDWR | O_LARGEFILE, 0, BDEV_RAW))) {
+		goto err1;
+	}
+
+	if ((dump_bdev->bio = kmalloc(sizeof(struct bio), GFP_KERNEL)) 
+		== NULL) {
+		printk("Cannot allocate bio\n");
+		retval = -ENOMEM;
+		goto err2;
+	}
+
+	bio_init(dump_bdev->bio);
+
+	if ((bvec = kmalloc(sizeof(struct bio_vec) * 
+		(DUMP_BUFFER_SIZE >> PAGE_SHIFT), GFP_KERNEL)) == NULL) {
+		retval = -ENOMEM;
+		goto err3;
+	}
+
+	/* assign the new dump dev structure */
+	dump_bdev->kdev_id = to_kdev_t((dev_t)arg);
+	dump_bdev->bdev = bdev;
+
+	/* make a note of the limit */
+	dump_bdev->limit = bdev->bd_inode->i_size;
+	
+	/* now make sure we can map the dump buffer */
+	dump_bdev->bio->bi_io_vec = bvec;
+	dump_bdev->bio->bi_max_vecs = DUMP_BUFFER_SIZE >> PAGE_SHIFT;
+
+	retval = dump_block_map(dump_bdev, dump_config.dumper->dump_buf, 
+		DUMP_BUFFER_SIZE);
+		
+	if (retval) {
+		printk("open: dump_block_map failed, ret %d\n", retval);
+		goto err3;
+	}
+
+	printk("Block device (%d,%d) successfully configured for dumping\n",
+	       major(dump_bdev->kdev_id),
+	       minor(dump_bdev->kdev_id));
+
+
+	/* after opening the block device, return */
+	return retval;
+
+err3:	dump_free_bio(dump_bdev->bio);
+	dump_bdev->bio = NULL;
+err2:	if (bdev) blkdev_put(bdev, BDEV_RAW);
+		goto err;
+err1:	if (bdev) bdput(bdev);
+	dump_bdev->bdev = NULL;
+err:	return retval;
+}
+
+/*
+ * Close the dump device and release associated resources
+ * Invoked when unconfiguring the dump device.
+ */
+static int
+dump_block_release(struct dump_dev *dev)
+{
+	struct dump_blockdev *dump_bdev = DUMP_BDEV(dev);
+
+	/* release earlier bdev if present */
+	if (dump_bdev->bdev) {
+		blkdev_put(dump_bdev->bdev, BDEV_RAW);
+		dump_bdev->bdev = NULL;
+	}
+
+	dump_free_bio(dump_bdev->bio);
+	dump_bdev->bio = NULL;
+
+	return 0;
+}
+
+
+/*
+ * Prepare the dump device for use (silence any ongoing activity
+ * and quiesce state) when the system crashes.
+ */
+static int
+dump_block_silence(struct dump_dev *dev)
+{
+	struct dump_blockdev *dump_bdev = DUMP_BDEV(dev);
+
+	/* For now we assume we have the device to ourselves */
+	/* Just a quick sanity check */
+	if (!blk_queue_empty(bdev_get_queue(dump_bdev->bdev))) {
+		/* i/o in flight - safer to quit */
+		return -EBUSY;
+	}
+
+	/* 
+	 * Move to a softer level of silencing where no spin_lock_irqs 
+	 * are held on other cpus
+	 */
+	dump_silence_level = DUMP_SOFT_SPIN_CPUS;	
+
+	__dump_irq_enable();
+
+	printk("Dumping to block device (%d,%d) on CPU %d ...\n",
+	       major(dump_bdev->kdev_id), minor(dump_bdev->kdev_id),
+	       smp_processor_id());
+	
+	return 0;
+}
+
+/*
+ * Invoked when dumping is done. This is the time to put things back 
+ * (i.e. undo the effects of dump_block_silence) so the device is 
+ * available for normal use.
+ */
+static int
+dump_block_resume(struct dump_dev *dev)
+{
+	__dump_irq_restore();
+	return 0;
+}
+
+
+/*
+ * Seek to the specified offset in the dump device.
+ * Makes sure this is a valid offset, otherwise returns an error.
+ */
+static int
+dump_block_seek(struct dump_dev *dev, loff_t off)
+{
+	struct dump_blockdev *dump_bdev = DUMP_BDEV(dev);
+	loff_t offset = off + dump_bdev->start_offset;
+	
+	if (offset & ( PAGE_SIZE - 1)) {
+		printk("seek: non-page aligned\n");
+		return -EINVAL;
+	}
+
+	if (offset & (bdev_hardsect_size(dump_bdev->bdev) - 1)) {
+		printk("seek: not sector aligned \n");
+		return -EINVAL;
+	}
+
+	if (offset > dump_bdev->limit) {
+		printk("seek: not enough space left on device!\n");
+		return -ENOSPC; 
+	}
+	dev->curr_offset = off;
+	return 0;
+}
+
+/*
+ * Write out a buffer after checking the device limitations, 
+ * sector sizes, etc. Assumes the buffer is in directly mapped 
+ * kernel address space (not vmalloc'ed).
+ *
+ * Returns: number of bytes written or -ERRNO. 
+ */
+static int
+dump_block_write(struct dump_dev *dev, void *buf, 
+	unsigned long len)
+{
+	struct dump_blockdev *dump_bdev = DUMP_BDEV(dev);
+	loff_t offset = dev->curr_offset + dump_bdev->start_offset;
+	int retval = -ENOSPC;
+
+	if (offset >= dump_bdev->limit) {
+		printk("write: not enough space left on device!\n");
+		goto out;
+	}
+
+	/* don't write more blocks than our max limit */
+	if (offset + len > dump_bdev->limit) 
+		len = dump_bdev->limit - offset;
+
+
+	retval = dump_block_map(dump_bdev, buf, len);
+	if (retval){
+		printk("write: dump_block_map failed! err %d\n", retval);
+		goto out;
+	}
+
+	/*
+	 * Write out the data to disk.
+	 * Assumes the entire buffer mapped to a single bio, which we can
+	 * submit and wait for io completion. In the future, may consider
+	 * increasing the dump buffer size and submitting multiple bio s 
+	 * for better throughput.
+	 */
+	dump_bdev->err = -EAGAIN;
+	submit_bio(WRITE, dump_bdev->bio);
+
+	dump_bdev->ddev.curr_offset += len;
+	retval = len;
+ out:
+	return retval;
+}
+
+/*
+ * Name: dump_block_ready()
+ * Func: check if the last dump i/o is over and ready for next request
+ */
+static int
+dump_block_ready(struct dump_dev *dev, void *buf)
+{
+	struct dump_blockdev *dump_bdev = DUMP_BDEV(dev);
+	request_queue_t *q = bdev_get_queue(dump_bdev->bio->bi_bdev);
+
+	/* check for io completion */
+	if (dump_bdev->err == -EAGAIN) {
+		q->unplug_fn(q);
+		return -EAGAIN;
+	}
+
+	if (dump_bdev->err) {
+		printk("dump i/o err\n");
+		return dump_bdev->err;
+	}
+
+	return 0;
+}
+
+
+struct dump_dev_ops dump_blockdev_ops = {
+	.open 		= dump_block_open,
+	.release	= dump_block_release,
+	.silence	= dump_block_silence,
+	.resume 	= dump_block_resume,
+	.seek		= dump_block_seek,
+	.write		= dump_block_write,
+	/* .read not implemented */
+	.ready		= dump_block_ready
+};
+
+static struct dump_blockdev default_dump_blockdev = {
+	.ddev = {.type_name = "blockdev", .ops = &dump_blockdev_ops, 
+			.curr_offset = 0},
+	/* 
+	 * leave enough room for the longest swap header possibly written 
+	 * written by mkswap (likely the largest page size supported by
+	 * the arch
+	 */
+	.start_offset 	= DUMP_HEADER_OFFSET,
+	.err 		= 0
+	/* assume the rest of the fields are zeroed by default */
+};	
+	
+struct dump_blockdev *dump_blockdev = &default_dump_blockdev;
+
+static int __init
+dump_blockdev_init(void)
+{
+	if (dump_register_device(&dump_blockdev->ddev) < 0) {
+		printk("block device driver registration failed\n");
+		return -1;
+	}
+		
+	printk("block device driver for LKCD registered\n");
+	return 0;
+}
+
+static void __exit
+dump_blockdev_cleanup(void)
+{
+	dump_unregister_device(&dump_blockdev->ddev);
+	printk("block device driver for LKCD unregistered\n");
+}
+
+MODULE_AUTHOR("LKCD Development Team <lkcd-devel@lists.sourceforge.net>");
+MODULE_DESCRIPTION("Block Dump Driver for Linux Kernel Crash Dump (LKCD)");
+MODULE_LICENSE("GPL");
+
+module_init(dump_blockdev_init);
+module_exit(dump_blockdev_cleanup);
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/dump/dump_execute.c 240-lkcd/drivers/dump/dump_execute.c
--- 231-thread_under_page/drivers/dump/dump_execute.c	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/drivers/dump/dump_execute.c	Fri May 30 19:24:56 2003
@@ -0,0 +1,124 @@
+/*
+ * The file has the common/generic dump execution code 
+ *
+ * Started: Oct 2002 -  Suparna Bhattacharya <suparna@in.ibm.com>
+ * 	Split and rewrote high level dump execute code to make use 
+ * 	of dump method interfaces.
+ *
+ * Derived from original code in dump_base.c created by 
+ * 	Matt Robinson <yakker@sourceforge.net>)
+ * 	
+ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 2001 - 2002 Matt D. Robinson.  All rights reserved.
+ * Copyright (C) 2002 International Business Machines Corp. 
+ *
+ * Assumes dumper and dump config settings are in place
+ * (invokes corresponding dumper specific routines as applicable)
+ *
+ * This code is released under version 2 of the GNU GPL.
+ */
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+#include <linux/dump.h>
+#include "dump_methods.h"
+
+extern int dump_device;
+
+struct notifier_block *dump_notifier_list; /* dump started/ended callback */
+
+/* Dump progress indicator */
+void 
+dump_speedo(int i)
+{
+	static const char twiddle[4] =  { '|', '\\', '-', '/' };
+	printk("%c\b", twiddle[i&3]);
+}
+
+/* Make the device ready and write out the header */
+int dump_begin(void)
+{
+	int err = 0;
+
+	/* dump_dev = dump_config.dumper->dev; */
+	dumper_reset();
+	if ((err = dump_dev_silence())) {
+		/* quiesce failed, can't risk continuing */
+		/* Todo/Future: switch to alternate dump scheme if possible */
+		printk("dump silence dev failed ! error %d\n", err);
+		return err;
+	}
+
+	pr_debug("Writing dump header\n");
+	if ((err = dump_update_header())) {
+		printk("dump update header failed ! error %d\n", err);
+		dump_dev_resume();
+		return err;
+	}
+
+	dump_config.dumper->curr_offset = DUMP_BUFFER_SIZE;
+
+	return 0;
+}
+
+/* 
+ * Write the dump terminator, a final header update and let go of 
+ * exclusive use of the device for dump.
+ */
+int dump_complete(void)
+{
+	int ret = 0;
+
+	if (dump_config.level != DUMP_LEVEL_HEADER) {
+		if ((ret = dump_update_end_marker())) {
+			printk("dump update end marker error %d\n", ret);
+		}
+		if ((ret = dump_update_header())) {
+			printk("dump update header error %d\n", ret);
+		}
+	}
+	ret = dump_dev_resume();
+
+	return ret;
+}
+
+/* Saves all dump data */
+int dump_execute_savedump(void)
+{
+	int ret = 0;
+
+	if ((ret = dump_begin()))  {
+		return ret;
+	}
+
+	if (dump_config.level != DUMP_LEVEL_HEADER) { 
+		ret = dump_sequencer();
+	}
+	dump_complete();
+
+	return ret;
+}
+
+/* Does all the real work:  Capture and save state */
+int dump_generic_execute(const char *panic_str, const struct pt_regs *regs)
+{
+	int ret = 0;
+
+	if ((ret = dump_configure_header(panic_str, regs))) {
+		printk("dump config header failed ! error %d\n", ret);
+		return ret;	
+	}
+
+	/* tell interested parties that a dump is about to start */
+	notifier_call_chain(&dump_notifier_list, DUMP_BEGIN, &dump_device);
+
+	if (dump_config.level != DUMP_LEVEL_NONE)
+		ret = dump_execute_savedump();
+
+	pr_debug("dumped %ld blocks of %d bytes each\n", 
+		dump_config.dumper->count, DUMP_BUFFER_SIZE);
+	
+	/* tell interested parties that a dump has completed */
+	notifier_call_chain(&dump_notifier_list, DUMP_END, &dump_device);
+
+	return ret;
+}
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/dump/dump_filters.c 240-lkcd/drivers/dump/dump_filters.c
--- 231-thread_under_page/drivers/dump/dump_filters.c	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/drivers/dump/dump_filters.c	Fri May 30 19:24:56 2003
@@ -0,0 +1,145 @@
+/*
+ * Default filters to select data to dump for various passes.
+ *
+ * Started: Oct 2002 -  Suparna Bhattacharya <suparna@in.ibm.com>
+ * 	Split and rewrote default dump selection logic to generic dump 
+ * 	method interfaces 
+ * Derived from a portion of dump_base.c created by 
+ * 	Matt Robinson <yakker@sourceforge.net>)
+ *
+ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 2001 - 2002 Matt D. Robinson.  All rights reserved.
+ * Copyright (C) 2002 International Business Machines Corp. 
+ *
+ * Used during single-stage dumping and during stage 1 of the 2-stage scheme
+ * (Stage 2 of the 2-stage scheme uses the fully transparent filters
+ * i.e. passthru filters in dump_overlay.c)
+ *
+ * Future: Custom selective dump may involve a different set of filters.
+ *
+ * This code is released under version 2 of the GNU GPL.
+ */
+
+#include <linux/kernel.h>
+#include <linux/bootmem.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/dump.h>
+#include "dump_methods.h"
+
+
+/* Copied from mm/bootmem.c - FIXME */
+/* return the number of _pages_ that will be allocated for the boot bitmap */
+unsigned long dump_calc_bootmap_pages (void)
+{
+	unsigned long mapsize;
+	unsigned long pages = num_physpages;
+
+	mapsize = (pages+7)/8;
+	mapsize = (mapsize + ~PAGE_MASK) & PAGE_MASK;
+	mapsize >>= PAGE_SHIFT;
+
+	return mapsize;
+}
+
+
+#define DUMP_PFN_SAFETY_MARGIN	1024  /* 4 MB */
+/* temporary */
+extern unsigned long min_low_pfn;
+
+/* to track all used (compound + zero order) pages */
+#define PageInuse(p)	(PageCompound(p) || page_count(p))
+
+int dump_low_page(struct page *p)
+{
+	return page_to_pfn(p) < min_low_pfn + dump_calc_bootmap_pages() 
+			+ 1 + DUMP_PFN_SAFETY_MARGIN;
+}
+
+static inline int kernel_page(struct page *p)
+{
+	/* FIXME: Need to exclude hugetlb pages. Clue: reserved but inuse */
+	return PageReserved(p) || (!PageLRU(p) && PageInuse(p));
+}
+
+static inline int user_page(struct page *p)
+{
+	return PageInuse(p) && (!PageReserved(p) && PageLRU(p));
+}
+
+static inline int unreferenced_page(struct page *p)
+{
+	return !PageInuse(p) && !PageReserved(p);
+}
+
+
+/* loc marks the beginning of a range of pages */
+int dump_filter_kernpages(int pass, unsigned long loc, unsigned long sz)
+{
+	struct page *page = (struct page *)loc;
+	/* if any of the pages is a kernel page, select this set */	
+	while (sz) {
+		if (dump_low_page(page) || kernel_page(page))
+			return 1;
+		sz -= PAGE_SIZE;
+		page++;
+	}	
+	return 0;
+}
+
+
+/* loc marks the beginning of a range of pages */
+int dump_filter_userpages(int pass, unsigned long loc, unsigned long sz)
+{
+	struct page *page = (struct page *)loc;
+	int ret = 0;
+	/* select if the set has any user page, and no kernel pages  */	
+	while (sz) {
+		if (user_page(page) && !dump_low_page(page)) {
+			ret = 1;
+		} else if (kernel_page(page) || dump_low_page(page)) {
+			return 0;
+		}
+		page++;
+		sz -= PAGE_SIZE;
+	}	
+	return ret;
+}
+
+
+
+/* loc marks the beginning of a range of pages */
+int dump_filter_unusedpages(int pass, unsigned long loc, unsigned long sz)
+{
+	struct page *page = (struct page *)loc;
+
+	/* select if the set does not have any used pages  */	
+	while (sz) {
+		if (!unreferenced_page(page) || dump_low_page(page)) {
+			return 0;
+		}
+		page++;
+		sz -= PAGE_SIZE;
+	}	
+	return 1;
+}
+
+/* dummy: last (non-existent) pass */
+int dump_filter_none(int pass, unsigned long loc, unsigned long sz)
+{
+	return 0;
+}
+
+/* TBD: resolve level bitmask ? */
+struct dump_data_filter dump_filter_table[] = {
+	{ .name = "kern", .selector = dump_filter_kernpages, 
+		.level_mask = DUMP_MASK_KERN},
+	{ .name = "user", .selector = dump_filter_userpages, 
+		.level_mask = DUMP_MASK_USED},
+	{ .name = "unused", .selector = dump_filter_unusedpages, 
+		.level_mask = DUMP_MASK_UNUSED},
+	{ .name = "none", .selector = dump_filter_none, 
+		.level_mask = DUMP_MASK_REST},
+	{ .name = "", .selector = NULL, .level_mask = 0}
+};
+
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/dump/dump_fmt.c 240-lkcd/drivers/dump/dump_fmt.c
--- 231-thread_under_page/drivers/dump/dump_fmt.c	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/drivers/dump/dump_fmt.c	Fri May 30 19:24:56 2003
@@ -0,0 +1,395 @@
+/*
+ * Implements the routines which handle the format specific
+ * aspects of dump for the default dump format.
+ *
+ * Used in single stage dumping and stage 1 of soft-boot based dumping 
+ * Saves data in LKCD (lcrash) format 
+ *
+ * Previously a part of dump_base.c
+ *
+ * Started: Oct 2002 -  Suparna Bhattacharya <suparna@in.ibm.com>
+ *	Split off and reshuffled LKCD dump format code around generic
+ *	dump method interfaces.
+ *
+ * Derived from original code created by 
+ * 	Matt Robinson <yakker@sourceforge.net>)
+ *
+ * Contributions from SGI, IBM, HP, MCL, and others.
+ *
+ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 2000 - 2002 TurboLinux, Inc.  All rights reserved.
+ * Copyright (C) 2001 - 2002 Matt D. Robinson.  All rights reserved.
+ * Copyright (C) 2002 International Business Machines Corp. 
+ *
+ * This code is released under version 2 of the GNU GPL.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/utsname.h>
+#include <asm/dump.h>
+#include <linux/dump.h>
+#include "dump_methods.h"
+
+/*
+ * SYSTEM DUMP LAYOUT
+ * 
+ * System dumps are currently the combination of a dump header and a set
+ * of data pages which contain the system memory.  The layout of the dump
+ * (for full dumps) is as follows:
+ *
+ *             +-----------------------------+
+ *             |     generic dump header     |
+ *             +-----------------------------+
+ *             |   architecture dump header  |
+ *             +-----------------------------+
+ *             |         page header         |
+ *             +-----------------------------+
+ *             |          page data          |
+ *             +-----------------------------+
+ *             |         page header         |
+ *             +-----------------------------+
+ *             |          page data          |
+ *             +-----------------------------+
+ *             |              |              |
+ *             |              |              |
+ *             |              |              |
+ *             |              |              |
+ *             |              V              |
+ *             +-----------------------------+
+ *             |        PAGE_END header      |
+ *             +-----------------------------+
+ *
+ * There are two dump headers, the first which is architecture
+ * independent, and the other which is architecture dependent.  This
+ * allows different architectures to dump different data structures
+ * which are specific to their chipset, CPU, etc.
+ *
+ * After the dump headers come a succession of dump page headers along
+ * with dump pages.  The page header contains information about the page
+ * size, any flags associated with the page (whether it's compressed or
+ * not), and the address of the page.  After the page header is the page
+ * data, which is either compressed (or not).  Each page of data is
+ * dumped in succession, until the final dump header (PAGE_END) is
+ * placed at the end of the dump, assuming the dump device isn't out
+ * of space.
+ *
+ * This mechanism allows for multiple compression types, different
+ * types of data structures, different page ordering, etc., etc., etc.
+ * It's a very straightforward mechanism for dumping system memory.
+ */
+
+struct __dump_header dump_header;  /* the primary dump header              */
+struct __dump_header_asm dump_header_asm; /* the arch-specific dump header */
+
+/*
+ *  Set up common header fields (mainly the arch indep section) 
+ *  Per-cpu state is handled by lcrash_save_context
+ */
+static int lcrash_init_dump_header(const char *panic_str)
+{
+	struct timeval dh_time;
+	/* make sure the dump header isn't TOO big */
+	if ((sizeof(struct __dump_header) +
+		sizeof(struct __dump_header_asm)) > DUMP_BUFFER_SIZE) {
+			printk("lcrash_init_header(): combined "
+				"headers larger than DUMP_BUFFER_SIZE!\n");
+			return -E2BIG;
+	}
+
+	/* initialize the dump headers to zero */
+	memset(&dump_header, 0, sizeof(dump_header));
+	memset(&dump_header_asm, 0, sizeof(dump_header_asm));
+
+	/* configure dump header values */
+	dump_header.dh_magic_number = DUMP_MAGIC_NUMBER;
+	dump_header.dh_version = DUMP_VERSION_NUMBER;
+	dump_header.dh_memory_start = PAGE_OFFSET;
+	dump_header.dh_memory_end = DUMP_MAGIC_NUMBER;
+	dump_header.dh_header_size = sizeof(struct __dump_header);
+	dump_header.dh_page_size = PAGE_SIZE;
+	dump_header.dh_dump_level = dump_config.level;
+	dump_header.dh_current_task = (unsigned long) current;
+	dump_header.dh_dump_compress = dump_config.dumper->compress->
+		compress_type;
+	dump_header.dh_dump_flags = dump_config.flags;
+	dump_header.dh_dump_device = dump_config.dumper->dev->device_id; 
+
+#if DUMP_DEBUG >= 6
+	dump_header.dh_num_bytes = 0;
+#endif
+	dump_header.dh_num_dump_pages = 0;
+	do_gettimeofday(&dh_time);
+	dump_header.dh_time.tv_sec = dh_time.tv_sec;
+	dump_header.dh_time.tv_usec = dh_time.tv_usec;
+
+	memcpy((void *)&(dump_header.dh_utsname_sysname), 
+		(const void *)&(system_utsname.sysname), __NEW_UTS_LEN + 1);
+	memcpy((void *)&(dump_header.dh_utsname_nodename), 
+		(const void *)&(system_utsname.nodename), __NEW_UTS_LEN + 1);
+	memcpy((void *)&(dump_header.dh_utsname_release), 
+		(const void *)&(system_utsname.release), __NEW_UTS_LEN + 1);
+	memcpy((void *)&(dump_header.dh_utsname_version), 
+		(const void *)&(system_utsname.version), __NEW_UTS_LEN + 1);
+	memcpy((void *)&(dump_header.dh_utsname_machine), 
+		(const void *)&(system_utsname.machine), __NEW_UTS_LEN + 1);
+	memcpy((void *)&(dump_header.dh_utsname_domainname), 
+		(const void *)&(system_utsname.domainname), __NEW_UTS_LEN + 1);
+
+	if (panic_str) {
+		memcpy((void *)&(dump_header.dh_panic_string),
+			(const void *)panic_str, DUMP_PANIC_LEN);
+	}
+
+        dump_header_asm.dha_magic_number = DUMP_ASM_MAGIC_NUMBER;
+        dump_header_asm.dha_version = DUMP_ASM_VERSION_NUMBER;
+        dump_header_asm.dha_header_size = sizeof(dump_header_asm);
+
+	dump_header_asm.dha_smp_num_cpus = num_online_cpus();
+	pr_debug("smp_num_cpus in header %d\n", 
+		dump_header_asm.dha_smp_num_cpus);
+
+	dump_header_asm.dha_dumping_cpu = smp_processor_id();
+	
+	return 0;
+}
+
+
+int dump_lcrash_configure_header(const char *panic_str, 
+	const struct pt_regs *regs)
+{
+	int retval = 0;
+
+	if ((retval = lcrash_init_dump_header(panic_str))) 
+		return retval;
+
+	/* capture register states for all processors */
+	dump_save_this_cpu(regs);
+	__dump_save_other_cpus(); /* side effect:silence cpus */
+
+	/* configure architecture-specific dump header values */
+	if ((retval = __dump_configure_header(regs))) 
+		return retval;
+
+	dump_config.dumper->header_dirty++;
+	return 0;
+}
+
+/* save register and task context */
+void dump_lcrash_save_context(int cpu, const struct pt_regs *regs, 
+	struct task_struct *tsk)
+{
+	dump_header_asm.dha_smp_current_task[cpu] = (uint32_t) tsk;
+
+	__dump_save_regs(&dump_header_asm.dha_smp_regs[cpu], regs);
+
+	/* take a snapshot of the stack */
+	/* doing this enables us to tolerate slight drifts on this cpu */
+	if (dump_header_asm.dha_stack[cpu]) {
+		memcpy((void *)dump_header_asm.dha_stack[cpu],
+				tsk->thread_info, THREAD_SIZE);
+	}
+	dump_header_asm.dha_stack_ptr[cpu] = (uint32_t)(tsk->thread_info);
+}
+
+/* write out the header */
+int dump_write_header(void)
+{
+	int retval = 0, size;
+	void *buf = dump_config.dumper->dump_buf;
+
+	/* accounts for DUMP_HEADER_OFFSET if applicable */
+	if ((retval = dump_dev_seek(0))) {
+		printk("Unable to seek to dump header offset: %d\n", 
+			retval);
+		return retval;
+	}
+
+	memcpy(buf, (void *)&dump_header, sizeof(dump_header));
+	size = sizeof(dump_header);
+	memcpy(buf + size, (void *)&dump_header_asm, sizeof(dump_header_asm));
+	size += sizeof(dump_header_asm);
+	/* assuming header is dump buffer size always ? */
+	retval = dump_ll_write(buf , DUMP_BUFFER_SIZE);
+
+	if (retval < DUMP_BUFFER_SIZE) 
+		return (retval >= 0) ? ENOSPC : retval;
+
+	return 0;
+}
+
+int dump_generic_update_header(void)
+{
+	int err = 0;
+
+	if (dump_config.dumper->header_dirty) {
+		if ((err = dump_write_header())) {
+			printk("dump write header failed !err %d\n", err);
+		} else {
+			dump_config.dumper->header_dirty = 0;
+		}
+	}
+
+	return err;
+}
+
+static inline int is_curr_stack_page(struct page *page, unsigned long size)
+{
+	unsigned long thread_addr = (unsigned long)current_thread_info();
+	unsigned long addr = (unsigned long)page_address(page);
+
+	return !PageHighMem(page) && (addr < thread_addr + THREAD_SIZE)
+		&& (addr + size > thread_addr);
+}
+
+static inline int is_dump_page(struct page *page, unsigned long size)
+{
+	unsigned long addr = (unsigned long)page_address(page);
+	unsigned long dump_buf = (unsigned long)dump_config.dumper->dump_buf;
+
+	return !PageHighMem(page) && (addr < dump_buf + DUMP_BUFFER_SIZE)
+		&& (addr + size > dump_buf);
+}
+
+int dump_allow_compress(struct page *page, unsigned long size)
+{
+	/*
+	 * Don't compress the page if any part of it overlaps
+	 * with the current stack or dump buffer (since the contents
+	 * in these could be changing while compression is going on)
+	 */
+	return !is_curr_stack_page(page, size) && !is_dump_page(page, size);
+}
+
+void lcrash_init_pageheader(struct __dump_page *dp, struct page *page, 
+	unsigned long sz)
+{
+	memset(dp, sizeof(struct __dump_page), 0);
+	dp->dp_flags = 0; 
+	dp->dp_size = 0;
+	if (sz > 0)
+		dp->dp_address = page_to_pfn(page) << PAGE_SHIFT;
+
+#if DUMP_DEBUG > 6
+	dp->dp_page_index = dump_header.dh_num_dump_pages;
+	dp->dp_byte_offset = dump_header.dh_num_bytes + DUMP_BUFFER_SIZE
+		+ DUMP_HEADER_OFFSET; /* ?? */
+#endif /* DUMP_DEBUG */
+}
+
+int dump_lcrash_add_data(unsigned long loc, unsigned long len)
+{
+	struct page *page = (struct page *)loc;
+	void *addr, *buf = dump_config.dumper->curr_buf;
+	struct __dump_page *dp = (struct __dump_page *)buf; 
+	int bytes, size;
+
+	if (buf > dump_config.dumper->dump_buf + DUMP_BUFFER_SIZE)
+		return -ENOMEM;
+
+	lcrash_init_pageheader(dp, page, len);
+	buf += sizeof(struct __dump_page);
+
+	while (len) {
+		addr = kmap_atomic(page, KM_DUMP);
+		size = bytes = (len > PAGE_SIZE) ? PAGE_SIZE : len;	
+		/* check for compression */
+		if (dump_allow_compress(page, bytes)) {
+			size = dump_compress_data((char *)addr, bytes, (char *)buf);
+		}
+		/* set the compressed flag if the page did compress */
+		if (size && (size < bytes)) {
+			dp->dp_flags |= DUMP_DH_COMPRESSED;
+		} else {
+			/* compression failed -- default to raw mode */
+			dp->dp_flags |= DUMP_DH_RAW;
+			memcpy(buf, addr, bytes);
+			size = bytes;
+		}
+		/* memset(buf, 'A', size); temporary: testing only !! */
+		kunmap_atomic(addr, KM_DUMP);
+		dp->dp_size += size;
+		buf += size;
+		len -= bytes;
+		page++;
+	}
+
+	/* now update the header */
+#if DUMP_DEBUG > 6
+	dump_header.dh_num_bytes += dp->dp_size + sizeof(*dp);
+#endif
+	dump_header.dh_num_dump_pages++;
+	dump_config.dumper->header_dirty++;
+
+	dump_config.dumper->curr_buf = buf;	
+
+	return len;
+}
+
+int dump_lcrash_update_end_marker(void)
+{
+	struct __dump_page *dp = 
+		(struct __dump_page *)dump_config.dumper->curr_buf;
+	unsigned long left;
+	int ret = 0;
+		
+	lcrash_init_pageheader(dp, NULL, 0);
+	dp->dp_flags |= DUMP_DH_END; /* tbd: truncation test ? */
+	
+	/* now update the header */
+#if DUMP_DEBUG > 6
+	dump_header.dh_num_bytes += sizeof(*dp);
+#endif
+	dump_config.dumper->curr_buf += sizeof(*dp);
+	left = dump_config.dumper->curr_buf - dump_config.dumper->dump_buf;
+
+	printk("\n");
+
+	while (left) {
+		if ((ret = dump_dev_seek(dump_config.dumper->curr_offset))) {
+			printk("Seek failed at offset 0x%llx\n", 
+			dump_config.dumper->curr_offset);
+			return ret;
+		}
+
+		if (DUMP_BUFFER_SIZE > left) 
+			memset(dump_config.dumper->curr_buf, 'm', 
+				DUMP_BUFFER_SIZE - left);
+
+		if ((ret = dump_ll_write(dump_config.dumper->dump_buf, 
+			DUMP_BUFFER_SIZE)) < DUMP_BUFFER_SIZE) {
+			return (ret < 0) ? ret : -ENOSPC;
+		}
+
+		dump_config.dumper->curr_offset += DUMP_BUFFER_SIZE;
+	
+		if (left > DUMP_BUFFER_SIZE) {
+			left -= DUMP_BUFFER_SIZE;
+			memcpy(dump_config.dumper->dump_buf, 
+			dump_config.dumper->dump_buf + DUMP_BUFFER_SIZE, left);
+			dump_config.dumper->curr_buf -= DUMP_BUFFER_SIZE;
+		} else {
+			left = 0;
+		}
+	}
+	return 0;
+}
+
+
+/* Default Formatter (lcrash) */
+struct dump_fmt_ops dump_fmt_lcrash_ops = {
+	.configure_header	= dump_lcrash_configure_header,
+	.update_header		= dump_generic_update_header,
+	.save_context		= dump_lcrash_save_context,
+	.add_data		= dump_lcrash_add_data,
+	.update_end_marker	= dump_lcrash_update_end_marker
+};
+
+struct dump_fmt dump_fmt_lcrash = {
+	.name	= "lcrash",
+	.ops	= &dump_fmt_lcrash_ops
+};
+
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/dump/dump_gzip.c 240-lkcd/drivers/dump/dump_gzip.c
--- 231-thread_under_page/drivers/dump/dump_gzip.c	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/drivers/dump/dump_gzip.c	Fri May 30 19:24:56 2003
@@ -0,0 +1,118 @@
+/*
+ * GZIP Compression functions for kernel crash dumps.
+ *
+ * Created by: Matt Robinson (yakker@sourceforge.net)
+ * Copyright 2001 Matt D. Robinson.  All rights reserved.
+ *
+ * This code is released under version 2 of the GNU GPL.
+ */
+
+/* header files */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/dump.h>
+#include <linux/zlib.h>
+#include <linux/vmalloc.h>
+
+static void *deflate_workspace;
+
+/*
+ * Name: dump_compress_gzip()
+ * Func: Compress a DUMP_PAGE_SIZE page using gzip-style algorithms (the.
+ *       deflate functions similar to what's used in PPP).
+ */
+static u16
+dump_compress_gzip(const u8 *old, u16 oldsize, u8 *new, u16 newsize)
+{
+	/* error code and dump stream */
+	int err;
+	z_stream dump_stream;
+	
+	dump_stream.workspace = deflate_workspace;
+	
+	if ((err = zlib_deflateInit(&dump_stream, Z_BEST_COMPRESSION)) != Z_OK) {
+		/* fall back to RLE compression */
+		printk("dump_compress_gzip(): zlib_deflateInit() "
+			"failed (%d)!\n", err);
+		return 0;
+	}
+
+	/* use old (page of memory) and size (DUMP_PAGE_SIZE) as in-streams */
+	dump_stream.next_in = (u8 *) old;
+	dump_stream.avail_in = oldsize;
+
+	/* out streams are new (dpcpage) and new size (DUMP_DPC_PAGE_SIZE) */
+	dump_stream.next_out = new;
+	dump_stream.avail_out = newsize;
+
+	/* deflate the page -- check for error */
+	err = zlib_deflate(&dump_stream, Z_FINISH);
+	if (err != Z_STREAM_END) {
+		/* zero is return code here */
+		(void)zlib_deflateEnd(&dump_stream);
+		printk("dump_compress_gzip(): zlib_deflate() failed (%d)!\n",
+			err);
+		return 0;
+	}
+
+	/* let's end the deflated compression stream */
+	if ((err = zlib_deflateEnd(&dump_stream)) != Z_OK) {
+		printk("dump_compress_gzip(): zlib_deflateEnd() "
+			"failed (%d)!\n", err);
+	}
+
+	/* return the compressed byte total (if it's smaller) */
+	if (dump_stream.total_out >= oldsize) {
+		return oldsize;
+	}
+	return dump_stream.total_out;
+}
+
+/* setup the gzip compression functionality */
+static struct __dump_compress dump_gzip_compression = {
+	.compress_type = DUMP_COMPRESS_GZIP,
+	.compress_func = dump_compress_gzip,
+	.compress_name = "GZIP",
+};
+
+/*
+ * Name: dump_compress_gzip_init()
+ * Func: Initialize gzip as a compression mechanism.
+ */
+static int __init
+dump_compress_gzip_init(void)
+{
+	deflate_workspace = vmalloc(zlib_deflate_workspacesize());
+	if (!deflate_workspace) {
+		printk("dump_compress_gzip_init(): Failed to "
+			"alloc %d bytes for deflate workspace\n",
+			zlib_deflate_workspacesize());
+		return -ENOMEM;
+	}
+	dump_register_compression(&dump_gzip_compression);
+	return 0;
+}
+
+/*
+ * Name: dump_compress_gzip_cleanup()
+ * Func: Remove gzip as a compression mechanism.
+ */
+static void __exit
+dump_compress_gzip_cleanup(void)
+{
+	vfree(deflate_workspace);
+	dump_unregister_compression(DUMP_COMPRESS_GZIP);
+}
+
+/* module initialization */
+module_init(dump_compress_gzip_init);
+module_exit(dump_compress_gzip_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("LKCD Development Team <lkcd-devel@lists.sourceforge.net>");
+MODULE_DESCRIPTION("Gzip compression module for crash dump driver");
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/dump/dump_i386.c 240-lkcd/drivers/dump/dump_i386.c
--- 231-thread_under_page/drivers/dump/dump_i386.c	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/drivers/dump/dump_i386.c	Fri May 30 19:24:56 2003
@@ -0,0 +1,358 @@
+/*
+ * Architecture specific (i386) functions for Linux crash dumps.
+ *
+ * Created by: Matt Robinson (yakker@sgi.com)
+ *
+ * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
+ *
+ * 2.3 kernel modifications by: Matt D. Robinson (yakker@turbolinux.com)
+ * Copyright 2000 TurboLinux, Inc.  All rights reserved.
+ * 
+ * This code is released under version 2 of the GNU GPL.
+ */
+
+/*
+ * The hooks for dumping the kernel virtual memory to disk are in this
+ * file.  Any time a modification is made to the virtual memory mechanism,
+ * these routines must be changed to use the new mechanisms.
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/dump.h>
+#include "dump_methods.h"
+#include <linux/irq.h>
+
+#include <asm/processor.h>
+#include <asm/e820.h>
+#include <asm/hardirq.h>
+#include <asm/nmi.h>
+#include <asm/kdebug.h>
+
+static __s32 	     saved_irq_count;	/* saved preempt_count() flags */
+
+static int
+alloc_dha_stack(void)
+{
+	int i;
+	void *ptr;
+	
+	if (dump_header_asm.dha_stack[0])
+		return 0;
+
+	ptr = vmalloc(THREAD_SIZE * num_online_cpus());
+	if (!ptr) {
+		printk("vmalloc for dha_stacks failed\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < num_online_cpus(); i++) {
+		dump_header_asm.dha_stack[i] = (u32)((unsigned long)ptr +
+				(i * THREAD_SIZE));
+	}
+	return 0;
+}
+
+static int
+free_dha_stack(void) 
+{
+	if (dump_header_asm.dha_stack[0]) {
+		vfree((void *)dump_header_asm.dha_stack[0]);	
+		dump_header_asm.dha_stack[0] = 0;
+	}
+	return 0;
+}
+
+
+void 
+__dump_save_regs(struct pt_regs *dest_regs, const struct pt_regs *regs)
+{
+	*dest_regs = *regs;
+
+	/* In case of panic dumps, we collects regs on entry to panic.
+	 * so, we shouldn't 'fix' ssesp here again. But it is hard to
+	 * tell just looking at regs whether ssesp need fixing. We make
+	 * this decision by looking at xss in regs. If we have better
+	 * means to determine that ssesp are valid (by some flag which
+	 * tells that we are here due to panic dump), then we can use
+	 * that instead of this kludge.
+	 */
+	if (!user_mode(regs)) {
+		if ((0xffff & regs->xss) == __KERNEL_DS) 
+			/* already fixed up */
+			return;
+		dest_regs->esp = (unsigned long)&(regs->esp);
+		__asm__ __volatile__ ("movw %%ss, %%ax;"
+			:"=a"(dest_regs->xss));
+	}
+}
+
+
+#ifdef CONFIG_SMP
+extern unsigned long irq_affinity[];
+extern irq_desc_t irq_desc[];
+extern void dump_send_ipi(void);
+
+static int dump_expect_ipi[NR_CPUS];
+static atomic_t waiting_for_dump_ipi;
+static unsigned long saved_affinity[NR_IRQS];
+
+extern void stop_this_cpu(void *); /* exported by i386 kernel */
+
+static int
+dump_nmi_callback(struct pt_regs *regs, int cpu) 
+{
+	if (!dump_expect_ipi[cpu])
+		return 0;
+
+	dump_expect_ipi[cpu] = 0;
+	
+	dump_save_this_cpu(regs);
+	atomic_dec(&waiting_for_dump_ipi);
+
+ level_changed:
+	switch (dump_silence_level) {
+	case DUMP_HARD_SPIN_CPUS:	/* Spin until dump is complete */
+		while (dump_oncpu) {
+			barrier();    	/* paranoia */
+			if (dump_silence_level != DUMP_HARD_SPIN_CPUS)
+				goto level_changed;
+
+			cpu_relax();	/* kill time nicely */
+		}
+		break;
+
+	case DUMP_HALT_CPUS:		/* Execute halt */
+		stop_this_cpu(NULL);
+		break;
+		
+	case DUMP_SOFT_SPIN_CPUS:
+		/* Mark the task so it spins in schedule */
+		set_tsk_thread_flag(current, TIF_NEED_RESCHED);
+		break;
+	}
+
+	return 1;
+}
+
+/* save registers on other processors */
+void 
+__dump_save_other_cpus(void)
+{
+	int i, cpu = smp_processor_id();
+	int other_cpus = num_online_cpus()-1;
+	
+	if (other_cpus > 0) {
+		atomic_set(&waiting_for_dump_ipi, other_cpus);
+
+		for (i = 0; i < NR_CPUS; i++) {
+			dump_expect_ipi[i] = (i != cpu && cpu_online(i));
+		}
+
+		/* short circuit normal NMI handling temporarily */
+		set_nmi_callback(dump_nmi_callback);
+		wmb();
+
+		dump_send_ipi();
+		/* may be we dont need to wait for NMI to be processed. 
+		   just write out the header at the end of dumping, if
+		   this IPI is not processed until then, there probably
+		   is a problem and we just fail to capture state of 
+		   other cpus. */
+		while(atomic_read(&waiting_for_dump_ipi) > 0) {
+			cpu_relax();
+		}
+
+		unset_nmi_callback();
+	}
+}
+
+/*
+ * Routine to save the old irq affinities and change affinities of all irqs to
+ * the dumping cpu.
+ */
+static void 
+set_irq_affinity(void)
+{
+	int i;
+	int cpu = smp_processor_id();
+
+	memcpy(saved_affinity, irq_affinity, NR_IRQS * sizeof(unsigned long));
+	for (i = 0; i < NR_IRQS; i++) {
+		if (irq_desc[i].handler == NULL)
+			continue;
+		irq_affinity[i] = 1UL << cpu;
+		if (irq_desc[i].handler->set_affinity != NULL)
+			irq_desc[i].handler->set_affinity(i, irq_affinity[i]);
+	}
+}
+
+/*
+ * Restore old irq affinities.
+ */
+static void 
+reset_irq_affinity(void)
+{
+	int i;
+
+	memcpy(irq_affinity, saved_affinity, NR_IRQS * sizeof(unsigned long));
+	for (i = 0; i < NR_IRQS; i++) {
+		if (irq_desc[i].handler == NULL)
+			continue;
+		if (irq_desc[i].handler->set_affinity != NULL)
+			irq_desc[i].handler->set_affinity(i, saved_affinity[i]);
+	}
+}
+
+#else /* !CONFIG_SMP */
+#define set_irq_affinity()	do { } while (0)
+#define reset_irq_affinity()	do { } while (0)
+#define save_other_cpu_states() do { } while (0)
+#endif /* !CONFIG_SMP */
+
+/* 
+ * Kludge - dump from interrupt context is unreliable (Fixme)
+ *
+ * We do this so that softirqs initiated for dump i/o 
+ * get processed and we don't hang while waiting for i/o
+ * to complete or in any irq synchronization attempt.
+ *
+ * This is not quite legal of course, as it has the side 
+ * effect of making all interrupts & softirqs triggered 
+ * while dump is in progress complete before currently 
+ * pending softirqs and the currently executing interrupt 
+ * code. 
+ */
+static inline void
+irq_bh_save(void)
+{
+	saved_irq_count = irq_count();
+	preempt_count() &= ~(HARDIRQ_MASK|SOFTIRQ_MASK);
+}
+
+static inline void
+irq_bh_restore(void)
+{
+	preempt_count() |= saved_irq_count;
+}
+
+/*
+ * Name: __dump_irq_enable
+ * Func: Reset system so interrupts are enabled.
+ *	 This is used for dump methods that require interrupts
+ *	 Eventually, all methods will have interrupts disabled
+ *	 and this code can be removed.
+ *
+ *     Change irq affinities
+ *     Re-enable interrupts
+ */
+void 
+__dump_irq_enable(void)
+{
+	set_irq_affinity();
+	irq_bh_save();
+	local_irq_enable();
+}
+
+/*
+ * Name: __dump_irq_restore
+ * Func: Resume the system state in an architecture-specific way.
+
+ */
+void 
+__dump_irq_restore(void)
+{
+	local_irq_disable();
+	reset_irq_affinity();
+	irq_bh_restore();
+}
+
+/*
+ * Name: __dump_configure_header()
+ * Func: Meant to fill in arch specific header fields except per-cpu state
+ * already captured via __dump_save_context for all CPUs.
+ */
+int
+__dump_configure_header(const struct pt_regs *regs)
+{
+	return (0);
+}
+
+/*
+ * Name: dump_nmi_handler
+ * Func: Called from notify_die 
+ */
+static int dump_die_event(struct notifier_block *this,
+			  unsigned long event,
+			  void *arg)
+{
+	const struct die_args *args = (const struct die_args *) arg;
+
+	switch (event) {
+	case DIE_PANIC:
+	case DIE_OOPS:
+	case DIE_WATCHDOG:
+		dump_execute(args->str, args->regs);
+		break;
+	}
+	return NOTIFY_DONE;
+
+}
+
+static struct notifier_block dump_die_block = {
+	.notifier_call = dump_die_event,
+};
+
+/*
+ * Name: __dump_init()
+ * Func: Initialize the dumping routine process.
+ */
+void
+__dump_init(uint64_t local_memory_start)
+{
+	/* hook into NMI, Panic, and OOPS */
+	register_die_notifier(&dump_die_block);
+}
+
+/*
+ * Name: __dump_open()
+ * Func: Open the dump device (architecture specific).
+ */
+void
+__dump_open(void)
+{
+	alloc_dha_stack();
+}
+
+/*
+ * Name: __dump_cleanup()
+ * Func: Free any architecture specific data structures. This is called
+ *       when the dump module is being removed.
+ */
+void
+__dump_cleanup(void)
+{
+	free_dha_stack();
+
+	unregister_die_notifier(&dump_die_block);
+}
+
+extern int pfn_is_ram(unsigned long);
+
+/*
+ * Name: __dump_page_valid()
+ * Func: Check if page is valid to dump.
+ */ 
+int 
+__dump_page_valid(unsigned long index)
+{
+	if (!pfn_valid(index))
+		return 0;
+
+	return pfn_is_ram(index);
+}
+
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/dump/dump_methods.h 240-lkcd/drivers/dump/dump_methods.h
--- 231-thread_under_page/drivers/dump/dump_methods.h	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/drivers/dump/dump_methods.h	Fri May 30 19:24:56 2003
@@ -0,0 +1,314 @@
+/*
+ * Generic interfaces for flexible system dump 
+ *
+ * Started: Oct 2002 -  Suparna Bhattacharya (suparna@in.ibm.com)
+ *
+ * Copyright (C) 2002 International Business Machines Corp. 
+ *
+ * This code is released under version 2 of the GNU GPL.
+ */
+
+#ifndef _LINUX_DUMP_METHODS_H
+#define _LINUX_DUMP_METHODS_H
+
+/*
+ * Inspired by Matt Robinson's suggestion of introducing dump 
+ * methods as a way to enable different crash dump facilities to 
+ * coexist where each employs its own scheme or dumping policy.
+ *
+ * The code here creates a framework for flexible dump by defining 
+ * a set of methods and providing associated helpers that differentiate
+ * between the underlying mechanism (how to dump), overall scheme 
+ * (sequencing of stages and data dumped and associated quiescing), 
+ * output format (what the dump output looks like), target type 
+ * (where to save the dump; see dumpdev.h), and selection policy 
+ * (state/data to dump).
+ * 
+ * These sets of interfaces can be mixed and matched to build a 
+ * dumper suitable for a given situation, allowing for 
+ * flexibility as well appropriate degree of code reuse.
+ * For example all features and options of lkcd (including
+ * granular selective dumping in the near future) should be
+ * available even when say, the 2 stage soft-boot based mechanism 
+ * is used for taking disruptive dumps.
+ *
+ * Todo: Additionally modules or drivers may supply their own
+ * custom dumpers which extend dump with module specific
+ * information or hardware state, and can even tweak the
+ * mechanism when it comes to saving state relevant to
+ * them.
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/highmem.h>
+#include <linux/dumpdev.h>
+
+#define MAX_PASSES 	6
+#define MAX_DEVS	4
+
+
+/* To customise selection of pages to be dumped in a given pass/group */
+struct dump_data_filter{
+	char name[32];
+	int (*selector)(int, unsigned long, unsigned long);
+	ulong level_mask; /* dump level(s) for which this filter applies */
+	loff_t start, end; /* location range applicable */
+};
+
+
+/* 
+ * Determined by the kind of dump mechanism and appropriate 
+ * overall scheme 
+ */ 
+struct dump_scheme_ops {
+	/* sets aside memory, inits data structures etc */
+	int (*configure)(unsigned long devid); 
+	/* releases  resources */
+	int (*unconfigure)(void); 
+
+	/* ordering of passes, invoking iterator */
+	int (*sequencer)(void); 
+        /* iterates over system data, selects and acts on data to dump */
+	int (*iterator)(int, int (*)(unsigned long, unsigned long), 
+		struct dump_data_filter *); 
+        /* action when data is selected for dump */
+	int (*save_data)(unsigned long, unsigned long); 
+        /* action when data is to be excluded from dump */
+	int (*skip_data)(unsigned long, unsigned long); 
+	/* policies for space, multiple dump devices etc */
+	int (*write_buffer)(void *, unsigned long); 
+};
+
+struct dump_scheme {
+	/* the name serves as an anchor to locate the scheme after reboot */
+	char name[32]; 
+	struct dump_scheme_ops *ops;
+	struct list_head list;
+};
+
+/* Quiescing/Silence levels (controls IPI callback behaviour) */
+extern enum dump_silence_levels {
+	DUMP_SOFT_SPIN_CPUS	= 1,
+	DUMP_HARD_SPIN_CPUS	= 2,
+	DUMP_HALT_CPUS		= 3,
+} dump_silence_level;
+
+/* determined by the dump (file) format */
+struct dump_fmt_ops {
+	/* build header */
+	int (*configure_header)(const char *, const struct pt_regs *); 
+	int (*update_header)(void); /* update header and write it out */
+	/* save curr context  */
+	void (*save_context)(int, const struct pt_regs *, 
+		struct task_struct *); 
+	/* typically called by the save_data action */
+	/* add formatted data to the dump buffer */
+	int (*add_data)(unsigned long, unsigned long); 
+	int (*update_end_marker)(void);
+};
+
+struct dump_fmt {
+	unsigned long magic; 
+	char name[32];  /* lcrash, crash, elf-core etc */
+	struct dump_fmt_ops *ops;
+	struct list_head list;
+};
+
+/* 
+ * Modules will be able add their own data capture schemes by 
+ * registering their own dumpers. Typically they would use the 
+ * primary dumper as a template and tune it with their routines.
+ * Still Todo.
+ */
+
+/* The combined dumper profile (mechanism, scheme, dev, fmt) */
+struct dumper {
+	char name[32]; /* singlestage, overlay (stg1), passthru(stg2), pull */
+	struct dump_scheme *scheme;
+	struct dump_fmt *fmt;
+	struct __dump_compress *compress;
+	struct dump_data_filter *filter;
+	struct dump_dev *dev; 
+	/* state valid only for active dumper(s) - per instance */
+	/* run time state/context */
+	int curr_pass;
+	unsigned long count;
+	loff_t curr_offset; /* current logical offset into dump device */
+	loff_t curr_loc; /* current memory location */
+	void *curr_buf; /* current position in the dump buffer */
+	void *dump_buf; /* starting addr of dump buffer */
+	int header_dirty; /* whether the header needs to be written out */
+	struct list_head dumper_list; /* links to other dumpers */
+};	
+
+/* Starting point to get to the current configured state */
+struct dump_config {
+	ulong level;
+	ulong flags;
+	struct dumper *dumper;
+	struct list_head dump_dev_list;
+};	
+
+extern struct dump_config dump_config;
+
+
+/* Wrappers that invoke the methods for the current (active) dumper */
+
+/* Scheme operations */
+
+static inline int dump_sequencer(void)
+{
+	return dump_config.dumper->scheme->ops->sequencer();
+}
+
+static inline int dump_iterator(int pass, int (*action)(unsigned long, 
+	unsigned long), struct dump_data_filter *filter)
+{
+	return dump_config.dumper->scheme->ops->iterator(pass, action, filter);
+}
+
+#define dump_save_data dump_config.dumper->scheme->ops->save_data
+#define dump_skip_data dump_config.dumper->scheme->ops->skip_data
+
+static inline int dump_write_buffer(void *buf, unsigned long len)
+{
+	return dump_config.dumper->scheme->ops->write_buffer(buf, len);
+}
+
+static inline int dump_configure(unsigned long devid)
+{
+	return dump_config.dumper->scheme->ops->configure(devid);
+}
+
+static inline int dump_unconfigure(void)
+{
+	return dump_config.dumper->scheme->ops->unconfigure();
+}
+
+/* Format operations */
+
+static inline int dump_configure_header(const char *panic_str, 
+	const struct pt_regs *regs)
+{
+	return dump_config.dumper->fmt->ops->configure_header(panic_str, regs);
+}
+
+static inline void dump_save_context(int cpu, const struct pt_regs *regs, 
+		struct task_struct *tsk)
+{
+	dump_config.dumper->fmt->ops->save_context(cpu, regs, tsk);
+}
+
+static inline int dump_save_this_cpu(const struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+
+	dump_save_context(cpu, regs, current);
+	return 1;
+}
+
+static inline int dump_update_header(void)
+{
+	return dump_config.dumper->fmt->ops->update_header();
+}
+
+static inline int dump_update_end_marker(void)
+{
+	return dump_config.dumper->fmt->ops->update_end_marker();
+}
+
+static inline int dump_add_data(unsigned long loc, unsigned long sz)
+{
+	return dump_config.dumper->fmt->ops->add_data(loc, sz);
+}
+
+/* Compression operation */
+static inline int dump_compress_data(char *src, int slen, char *dst)
+{
+	return dump_config.dumper->compress->compress_func(src, slen, 
+		dst, DUMP_DPC_PAGE_SIZE);
+}
+
+
+/* Prototypes of some default implementations of dump methods */
+
+extern struct __dump_compress dump_none_compression;
+
+/* Default scheme methods (dump_scheme.c) */
+
+extern int dump_generic_sequencer(void);
+extern int dump_page_iterator(int pass, int (*action)(unsigned long, unsigned
+	long), struct dump_data_filter *filter);
+extern int dump_generic_save_data(unsigned long loc, unsigned long sz);
+extern int dump_generic_skip_data(unsigned long loc, unsigned long sz);
+extern int dump_generic_write_buffer(void *buf, unsigned long len);
+extern int dump_generic_configure(unsigned long);
+extern int dump_generic_unconfigure(void);
+
+/* Default scheme template */
+extern struct dump_scheme dump_scheme_singlestage;
+
+/* Default dump format methods */
+
+extern int dump_lcrash_configure_header(const char *panic_str, 
+	const struct pt_regs *regs);
+extern void dump_lcrash_save_context(int  cpu, const struct pt_regs *regs, 
+	struct task_struct *tsk);
+extern int dump_generic_update_header(void);
+extern int dump_lcrash_add_data(unsigned long loc, unsigned long sz);
+extern int dump_lcrash_update_end_marker(void);
+
+/* Default format (lcrash) template */
+extern struct dump_fmt dump_fmt_lcrash;
+
+/* Default dump selection filter table */
+
+/* 
+ * Entries listed in order of importance and correspond to passes
+ * The last entry (with a level_mask of zero) typically reflects data that 
+ * won't be dumped  -- this may for example be used to identify data 
+ * that will be skipped for certain so the corresponding memory areas can be 
+ * utilized as scratch space.
+ */   
+extern struct dump_data_filter dump_filter_table[];
+
+/* Some pre-defined dumpers */
+extern struct dumper dumper_singlestage;
+
+/* These are temporary */
+#define DUMP_MASK_HEADER	DUMP_LEVEL_HEADER
+#define DUMP_MASK_KERN		DUMP_LEVEL_KERN
+#define DUMP_MASK_USED		DUMP_LEVEL_USED
+#define DUMP_MASK_UNUSED	DUMP_LEVEL_ALL_RAM
+#define DUMP_MASK_REST		0 /* dummy for now */
+
+/* Helpers - move these to dump.h later ? */
+
+int dump_generic_execute(const char *panic_str, const struct pt_regs *regs);
+extern int dump_ll_write(void *buf, unsigned long len); 
+
+static inline void dumper_reset(void)
+{
+	dump_config.dumper->curr_buf = dump_config.dumper->dump_buf;
+	dump_config.dumper->curr_loc = 0;
+	dump_config.dumper->curr_offset = 0;
+	dump_config.dumper->count = 0;
+	dump_config.dumper->curr_pass = 0;
+}
+
+/* 
+ * May later be moulded to perform boot-time allocations so we can dump 
+ * earlier during bootup 
+ */
+static inline void *dump_alloc_mem(unsigned long size)
+{
+	return kmalloc(size, GFP_KERNEL);
+}
+
+static inline void dump_free_mem(void *buf)
+{
+	kfree(buf);
+}
+
+#endif /*  _LINUX_DUMP_METHODS_H */
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/dump/dump_netdev.c 240-lkcd/drivers/dump/dump_netdev.c
--- 231-thread_under_page/drivers/dump/dump_netdev.c	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/drivers/dump/dump_netdev.c	Fri May 30 19:24:56 2003
@@ -0,0 +1,858 @@
+/*
+ * Implements the dump driver interface for saving a dump via network
+ * interface. 
+ *
+ * Some of this code has been taken/adapted from Ingo Molnar's netconsole
+ * code. LKCD team expresses its thanks to Ingo.
+ *
+ * Started: June 2002 - Mohamed Abbas <mohamed.abbas@intel.com>
+ * 	Adapted netconsole code to implement LKCD dump over the network.
+ *
+ * Nov 2002 - Bharata B. Rao <bharata@in.ibm.com>
+ * 	Innumerable code cleanups, simplification and some fixes.
+ *	Netdump configuration done by ioctl instead of using module parameters.
+ *
+ * Copyright (C) 2001  Ingo Molnar <mingo@redhat.com>
+ * Copyright (C) 2002 International Business Machines Corp. 
+ *
+ *  This code is released under version 2 of the GNU GPL.
+ */
+
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/reboot.h>
+#include <linux/module.h>
+#include <linux/dump.h>
+#include <linux/dump_netdev.h>
+
+#include <asm/unaligned.h>
+
+static int startup_handshake;
+static int page_counter;
+static struct net_device *dump_ndev;
+static struct in_device *dump_in_dev;
+static u16 source_port, target_port;
+static u32 source_ip, target_ip;
+static unsigned char daddr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} ;
+static spinlock_t dump_skb_lock = SPIN_LOCK_UNLOCKED;
+static int dump_nr_skbs;
+static struct sk_buff *dump_skb;
+static unsigned long flags_global;
+static int netdump_in_progress;
+static char device_name[IFNAMSIZ];
+
+/*
+ * security depends on the trusted path between the netconsole
+ * server and netconsole client, since none of the packets are
+ * encrypted. The random magic number protects the protocol
+ * against spoofing.
+ */
+static u64 dump_magic;
+
+#define MAX_UDP_CHUNK 1460
+#define MAX_PRINT_CHUNK (MAX_UDP_CHUNK-HEADER_LEN)
+
+/*
+ * We maintain a small pool of fully-sized skbs,
+ * to make sure the message gets out even in
+ * extreme OOM situations.
+ */
+#define DUMP_MAX_SKBS 32
+
+#define MAX_SKB_SIZE \
+		(MAX_UDP_CHUNK + sizeof(struct udphdr) + \
+				sizeof(struct iphdr) + sizeof(struct ethhdr))
+
+static void
+dump_refill_skbs(void)
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dump_skb_lock, flags);
+	while (dump_nr_skbs < DUMP_MAX_SKBS) {
+		skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC);
+		if (!skb)
+			break;
+		if (dump_skb)
+			skb->next = dump_skb;
+		else
+			skb->next = NULL;
+		dump_skb = skb;
+		dump_nr_skbs++;
+	}
+	spin_unlock_irqrestore(&dump_skb_lock, flags);
+}
+
+static struct
+sk_buff * dump_get_skb(void)
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dump_skb_lock, flags);
+	skb = dump_skb;
+	if (skb) {
+		dump_skb = skb->next;
+		skb->next = NULL;
+		dump_nr_skbs--;
+	}
+	spin_unlock_irqrestore(&dump_skb_lock, flags);
+        
+	return skb;
+}
+
+/*
+ * Zap completed output skbs.
+ */
+static void
+zap_completion_queue(void)
+{
+	int count;
+	unsigned long flags;
+	int cpu = smp_processor_id();
+
+        count=0;
+	if (softnet_data[cpu].completion_queue) {
+		struct sk_buff *clist;
+	
+		local_irq_save(flags);
+		clist = softnet_data[cpu].completion_queue;
+		softnet_data[cpu].completion_queue = NULL;
+		local_irq_restore(flags);
+
+		while (clist != NULL) {
+			struct sk_buff *skb = clist;
+			clist = clist->next;
+			__kfree_skb(skb);
+			count++;
+			if (count > 10000)
+				printk("Error in sk list\n");
+		}
+	}
+}
+
+static void
+dump_send_skb(struct net_device *dev, const char *msg, unsigned int msg_len,
+		reply_t *reply)
+{
+	int once = 1;
+	int total_len, eth_len, ip_len, udp_len, count = 0;
+	struct sk_buff *skb;
+	struct udphdr *udph;
+	struct iphdr *iph;
+	struct ethhdr *eth; 
+
+	udp_len = msg_len + HEADER_LEN + sizeof(*udph);
+	ip_len = eth_len = udp_len + sizeof(*iph);
+	total_len = eth_len + ETH_HLEN;
+
+repeat_loop:
+	zap_completion_queue();
+	if (dump_nr_skbs < DUMP_MAX_SKBS)
+		dump_refill_skbs();
+
+	skb = alloc_skb(total_len, GFP_ATOMIC);
+	if (!skb) {
+		skb = dump_get_skb();
+		if (!skb) {
+			count++;
+			if (once && (count == 1000000)) {
+				printk("possibly FATAL: out of netconsole "
+					"skbs!!! will keep retrying.\n");
+				once = 0;
+			}
+			dev->poll_controller(dev);
+			goto repeat_loop;
+		}
+	}
+
+	atomic_set(&skb->users, 1);
+	skb_reserve(skb, total_len - msg_len - HEADER_LEN);
+	skb->data[0] = NETCONSOLE_VERSION;
+
+	put_unaligned(htonl(reply->nr), (u32 *) (skb->data + 1));
+	put_unaligned(htonl(reply->code), (u32 *) (skb->data + 5));
+	put_unaligned(htonl(reply->info), (u32 *) (skb->data + 9));
+
+	memcpy(skb->data + HEADER_LEN, msg, msg_len);
+	skb->len += msg_len + HEADER_LEN;
+
+	udph = (struct udphdr *) skb_push(skb, sizeof(*udph));
+	udph->source = source_port;
+	udph->dest = target_port;
+	udph->len = htons(udp_len);
+	udph->check = 0;
+
+	iph = (struct iphdr *)skb_push(skb, sizeof(*iph));
+
+	iph->version  = 4;
+	iph->ihl      = 5;
+	iph->tos      = 0;
+	iph->tot_len  = htons(ip_len);
+	iph->id       = 0;
+	iph->frag_off = 0;
+	iph->ttl      = 64;
+	iph->protocol = IPPROTO_UDP;
+	iph->check    = 0;
+	iph->saddr    = source_ip;
+	iph->daddr    = target_ip;
+	iph->check    = ip_fast_csum((unsigned char *)iph, iph->ihl);
+
+	eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
+
+	eth->h_proto = htons(ETH_P_IP);
+	memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+	memcpy(eth->h_dest, daddr, dev->addr_len);
+
+	count=0;
+repeat_poll:
+	spin_lock(&dev->xmit_lock);
+	dev->xmit_lock_owner = smp_processor_id();
+
+	count++;
+
+
+	if (netif_queue_stopped(dev)) {
+		dev->xmit_lock_owner = -1;
+		spin_unlock(&dev->xmit_lock);
+
+		dev->poll_controller(dev);
+		zap_completion_queue();
+
+
+		goto repeat_poll;
+	}
+
+	dev->hard_start_xmit(skb, dev);
+
+	dev->xmit_lock_owner = -1;
+	spin_unlock(&dev->xmit_lock);
+}
+
+static unsigned short
+udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr,
+	       	unsigned long base)
+{
+	return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, base);
+}
+
+static int
+udp_checksum_init(struct sk_buff *skb, struct udphdr *uh,
+			     unsigned short ulen, u32 saddr, u32 daddr)
+{
+	if (uh->check == 0) {
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	} else if (skb->ip_summed == CHECKSUM_HW) {
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+		if (!udp_check(uh, ulen, saddr, daddr, skb->csum))
+			return 0;
+		skb->ip_summed = CHECKSUM_NONE;
+	}
+	if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+		skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen,
+				IPPROTO_UDP, 0);
+	/* Probably, we should checksum udp header (it should be in cache
+	 * in any case) and data in tiny packets (< rx copybreak).
+	 */
+	return 0;
+}
+
+static __inline__ int
+__udp_checksum_complete(struct sk_buff *skb)
+{
+	return (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len,
+				skb->csum));
+}
+
+static __inline__
+int udp_checksum_complete(struct sk_buff *skb)
+{
+	return skb->ip_summed != CHECKSUM_UNNECESSARY &&
+		__udp_checksum_complete(skb);
+}
+
+int new_req = 0;
+static req_t req;
+
+static int
+dump_rx_hook(struct sk_buff *skb)
+{
+	int proto;
+	struct iphdr *iph;
+	struct udphdr *uh;
+	__u32 len, saddr, daddr, ulen;
+	req_t *__req;
+
+	/* 
+	 * First check if were are dumping or doing startup handshake, if
+	 * not quickly return.
+	 */
+	if (!netdump_in_progress)
+		return NET_RX_SUCCESS;
+
+	if (skb->dev->type != ARPHRD_ETHER)
+		goto out;
+
+	proto = ntohs(skb->mac.ethernet->h_proto);
+	if (proto != ETH_P_IP)
+		goto out;
+
+	if (skb->pkt_type == PACKET_OTHERHOST)
+		goto out;
+
+	if (skb_shared(skb))
+		goto out;
+
+	 /* IP header correctness testing: */
+	iph = (struct iphdr *)skb->data;
+	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+		goto out;
+
+	if (iph->ihl < 5 || iph->version != 4)
+		goto out;
+
+	if (!pskb_may_pull(skb, iph->ihl*4))
+		goto out;
+
+	if (ip_fast_csum((u8 *)iph, iph->ihl) != 0)
+		goto out;
+
+	len = ntohs(iph->tot_len);
+	if (skb->len < len || len < iph->ihl*4)
+		goto out;
+
+	saddr = iph->saddr;
+	daddr = iph->daddr;
+	if (iph->protocol != IPPROTO_UDP)
+		goto out;
+
+	if (source_ip != daddr)
+		goto out;
+
+	if (target_ip != saddr)
+		goto out;
+
+	len -= iph->ihl*4;
+	uh = (struct udphdr *)(((char *)iph) + iph->ihl*4);
+	ulen = ntohs(uh->len);
+
+	if (ulen != len || ulen < (sizeof(*uh) + sizeof(*__req)))
+		goto out;
+
+	if (udp_checksum_init(skb, uh, ulen, saddr, daddr) < 0)
+		goto out;
+
+	if (udp_checksum_complete(skb))
+		goto out;
+
+	if (source_port != uh->dest)
+		goto out;
+
+	if (target_port != uh->source)
+		goto out;
+
+	__req = (req_t *)(uh + 1);
+	if ((ntohl(__req->command) != COMM_GET_MAGIC) &&
+	    (ntohl(__req->command) != COMM_HELLO) &&
+	    (ntohl(__req->command) != COMM_START_WRITE_NETDUMP_ACK) &&
+	    (ntohl(__req->command) != COMM_START_NETDUMP_ACK) &&
+	    (memcmp(&__req->magic, &dump_magic, sizeof(dump_magic)) != 0))
+		goto out;
+
+	req.magic = ntohl(__req->magic);
+	req.command = ntohl(__req->command);
+	req.from = ntohl(__req->from);
+	req.to = ntohl(__req->to);
+	req.nr = ntohl(__req->nr);
+	new_req = 1;
+out:
+	return NET_RX_DROP;
+}
+
+static void
+dump_send_mem(struct net_device *dev, req_t *req, const char* buff, size_t len)
+{
+	int i;
+
+	int nr_chunks = len/1024;
+	reply_t reply;
+	
+	reply.nr = req->nr;
+	reply.info = 0;
+
+        if ( nr_chunks <= 0)
+		 nr_chunks = 1;
+	for (i = 0; i < nr_chunks; i++) {
+		unsigned int offset = i*1024;
+		reply.code = REPLY_MEM;
+		reply.info = offset;
+                dump_send_skb(dev, buff + offset, 1024, &reply);
+	}
+}
+
+/*
+ * This function waits for the client to acknowledge the receipt
+ * of the netdump startup reply, with the possibility of packets
+ * getting lost. We resend the startup packet if no ACK is received,
+ * after a 1 second delay.
+ *
+ * (The client can test the success of the handshake via the HELLO
+ * command, and send ACKs until we enter netdump mode.)
+ */
+static int
+dump_handshake(struct dump_dev *net_dev)
+{
+	char tmp[200];
+	reply_t reply;
+	int i, j;
+
+	if (startup_handshake) {
+		sprintf(tmp, "NETDUMP start, waiting for start-ACK.\n");
+		reply.code = REPLY_START_NETDUMP;
+		reply.nr = 0;
+		reply.info = 0;
+	} else {
+		sprintf(tmp, "NETDUMP start, waiting for start-ACK.\n");
+		reply.code = REPLY_START_WRITE_NETDUMP;
+		reply.nr = net_dev->curr_offset;
+		reply.info = net_dev->curr_offset;
+	}
+	
+	/* send 300 handshake packets before declaring failure */
+	for (i = 0; i < 300; i++) {
+		dump_send_skb(dump_ndev, tmp, strlen(tmp), &reply);
+
+		/* wait 1 sec */
+		for (j = 0; j < 10000; j++) {
+			udelay(100);
+			dump_ndev->poll_controller(dump_ndev);
+			zap_completion_queue();
+			if (new_req)
+				break;
+		}
+
+		/* 
+		 * if there is no new request, try sending the handshaking
+		 * packet again
+		 */
+		if (!new_req)
+			continue;
+
+		/* 
+		 * check if the new request is of the expected type,
+		 * if so, return, else try sending the handshaking
+		 * packet again
+		 */
+		if (startup_handshake) {
+			if (req.command == COMM_HELLO || req.command ==
+				COMM_START_NETDUMP_ACK) {
+				return 0;
+			} else {
+				new_req = 0;
+				continue;
+			}
+		} else {
+			if (req.command == COMM_SEND_MEM) {
+				return 0;
+			} else {
+				new_req = 0;
+				continue;
+			}
+		}
+	}
+	return -1;
+}
+
+static ssize_t
+do_netdump(struct dump_dev *net_dev, const char* buff, size_t len)
+{
+	reply_t reply;
+	char tmp[200];
+	ssize_t  ret = 0;
+	int repeatCounter, counter, total_loop;
+	
+	netdump_in_progress = 1;
+
+	if (dump_handshake(net_dev) < 0) {
+		printk("network dump failed due to handshake failure\n");
+		goto out;
+	}
+
+	/*
+	 * Ideally startup handshake should be done during dump configuration,
+	 * i.e., in dump_net_open(). This will be done when I figure out
+	 * the dependency between startup handshake, subsequent write and
+	 * various commands wrt to net-server.
+	 */
+	if (startup_handshake)
+		startup_handshake = 0;
+
+        counter = 0;
+	repeatCounter = 0;
+	total_loop = 0;
+	while (1) {
+                if (!new_req) {
+			dump_ndev->poll_controller(dump_ndev);
+			zap_completion_queue();
+		}
+		if (!new_req) {
+			repeatCounter++;
+
+			if (repeatCounter > 5) {
+				counter++;
+				if (counter > 10000) {
+					if (total_loop >= 100000) {
+						printk("Time OUT LEAVE NOW\n");
+						goto out;
+					} else {
+						total_loop++;
+						printk("Try number %d out of "
+							"10 before Time Out\n",
+							total_loop);
+					}
+				}
+				mdelay(1);
+				repeatCounter = 0;
+			}	
+			continue;
+		}
+		repeatCounter = 0;
+		counter = 0;
+		total_loop = 0;
+		new_req = 0;
+		switch (req.command) {
+		case COMM_NONE:
+			break;
+
+		case COMM_SEND_MEM:
+			dump_send_mem(dump_ndev, &req, buff, len);
+			break;
+
+		case COMM_EXIT:
+                case COMM_START_WRITE_NETDUMP_ACK:
+			ret = len;
+			goto out;
+
+		case COMM_HELLO:
+			sprintf(tmp, "Hello, this is netdump version "
+					"0.%02d\n", NETCONSOLE_VERSION);
+			reply.code = REPLY_HELLO;
+			reply.nr = req.nr;
+                        reply.info = net_dev->curr_offset;
+			dump_send_skb(dump_ndev, tmp, strlen(tmp), &reply);
+			break;
+
+		case COMM_GET_PAGE_SIZE:
+			sprintf(tmp, "PAGE_SIZE: %ld\n", PAGE_SIZE);
+			reply.code = REPLY_PAGE_SIZE;
+			reply.nr = req.nr;
+			reply.info = PAGE_SIZE;
+			dump_send_skb(dump_ndev, tmp, strlen(tmp), &reply);
+			break;
+
+		case COMM_GET_NR_PAGES:
+			reply.code = REPLY_NR_PAGES;
+			reply.nr = req.nr;
+			reply.info = max_mapnr;
+                        reply.info = page_counter;
+			sprintf(tmp, "Number of pages: %ld\n", max_mapnr);
+			dump_send_skb(dump_ndev, tmp, strlen(tmp), &reply);
+			break;
+
+		case COMM_GET_MAGIC:
+			reply.code = REPLY_MAGIC;
+			reply.nr = req.nr;
+			reply.info = NETCONSOLE_VERSION;
+			dump_send_skb(dump_ndev, (char *)&dump_magic,
+					sizeof(dump_magic), &reply);
+			break;
+
+		default:
+			reply.code = REPLY_ERROR;
+			reply.nr = req.nr;
+			reply.info = req.command;
+			sprintf(tmp, "Got unknown command code %d!\n",
+					req.command);
+			dump_send_skb(dump_ndev, tmp, strlen(tmp), &reply);
+			break;
+		}
+	}
+out:
+	netdump_in_progress = 0;
+	return ret;
+}
+
+static int
+dump_validate_config(void)
+{
+	source_ip = dump_in_dev->ifa_list->ifa_local;
+	if (!source_ip) {
+		printk("network device %s has no local address, "
+				"aborting.\n", device_name);
+		return -1;
+	}
+
+#define IP(x) ((unsigned char *)&source_ip)[x]
+	printk("Source %d.%d.%d.%d", IP(0), IP(1), IP(2), IP(3));
+#undef IP
+
+	if (!source_port) {
+		printk("source_port parameter not specified, aborting.\n");
+		return -1;
+	}
+	printk(":%i\n", source_port);
+	source_port = htons(source_port);
+
+	if (!target_ip) {
+		printk("target_ip parameter not specified, aborting.\n");
+		return -1;
+	}
+
+#define IP(x) ((unsigned char *)&target_ip)[x]
+	printk("Target %d.%d.%d.%d", IP(0), IP(1), IP(2), IP(3));
+#undef IP
+
+	if (!target_port) {
+		printk("target_port parameter not specified, aborting.\n");
+		return -1;
+	}
+	printk(":%i\n", target_port);
+	target_port = htons(target_port);
+
+	printk("Target Ethernet Address %02x:%02x:%02x:%02x:%02x:%02x",
+		daddr[0], daddr[1], daddr[2], daddr[3], daddr[4], daddr[5]);
+
+	if ((daddr[0] & daddr[1] & daddr[2] & daddr[3] & daddr[4] & 
+				daddr[5]) == 255)
+		printk("(Broadcast)");
+	printk("\n");
+	return 0;
+}
+
+/*
+ * Prepares the dump device so we can take a dump later. 
+ * Validates the netdump configuration parameters.
+ *
+ * TODO: Network connectivity check should be done here.
+ */
+static int
+dump_net_open(struct dump_dev *net_dev, unsigned long arg)
+{
+	int retval = 0;
+
+	/* get the interface name */
+	if (copy_from_user(device_name, (void *)arg, IFNAMSIZ))
+		return -EFAULT;
+
+	if (!(dump_ndev = dev_get_by_name(device_name))) {
+		printk("network device %s does not exist, aborting.\n",
+				device_name);
+		return -ENODEV;
+	}
+
+	if (!dump_ndev->poll_controller) {
+		printk("network device %s does not implement polling yet, "
+				"aborting.\n", device_name);
+		retval = -1; /* return proper error */
+		goto err1;
+	}
+
+	if (!(dump_in_dev = in_dev_get(dump_ndev))) {
+		printk("network device %s is not an IP protocol device, "
+				"aborting.\n", device_name);
+		retval = -EINVAL;
+		goto err1;
+	}
+
+	if ((retval = dump_validate_config()) < 0)
+		goto err2;
+
+	net_dev->curr_offset = 0;
+	printk("Network device %s successfully configured for dumping\n",
+			device_name);
+	return retval;
+err2:
+	in_dev_put(dump_in_dev);
+err1:
+	dev_put(dump_ndev);	
+	return retval;
+}
+
+/*
+ * Close the dump device and release associated resources
+ * Invoked when unconfiguring the dump device.
+ */
+static int
+dump_net_release(struct dump_dev *net_dev)
+{
+	if (dump_in_dev)
+		in_dev_put(dump_in_dev);
+	if (dump_ndev)
+		dev_put(dump_ndev);
+	return 0;
+}
+
+/*
+ * Prepare the dump device for use (silence any ongoing activity
+ * and quiesce state) when the system crashes.
+ */
+static int
+dump_net_silence(struct dump_dev *net_dev)
+{
+	local_irq_save(flags_global);
+	dump_ndev->rx_hook = dump_rx_hook;
+        startup_handshake = 1;
+	net_dev->curr_offset = 0;
+	printk("Dumping to network device %s on CPU %d ...\n", device_name,
+			smp_processor_id());
+	return 0;
+}
+
+/*
+ * Invoked when dumping is done. This is the time to put things back 
+ * (i.e. undo the effects of dump_block_silence) so the device is 
+ * available for normal use.
+ */
+static int
+dump_net_resume(struct dump_dev *net_dev)
+{
+	int indx;
+	reply_t reply;
+	char tmp[200];
+
+        if (!dump_ndev)
+		return (0);
+
+	sprintf(tmp, "NETDUMP end.\n");
+	for( indx = 0; indx < 6; indx++) {
+		reply.code = REPLY_END_NETDUMP;
+		reply.nr = 0;
+		reply.info = 0;
+		dump_send_skb(dump_ndev, tmp, strlen(tmp), &reply);
+	}
+	printk("NETDUMP END!\n");
+	local_irq_restore(flags_global);
+	dump_ndev->rx_hook = NULL;
+	startup_handshake = 0;
+	return 0;
+}
+
+/*
+ * Seek to the specified offset in the dump device.
+ * Makes sure this is a valid offset, otherwise returns an error.
+ */
+static  int
+dump_net_seek(struct dump_dev *net_dev, loff_t off)
+{
+	net_dev->curr_offset = off;
+	return 0;
+}
+
+/*
+ *
+ */
+static int
+dump_net_write(struct dump_dev *net_dev, void *buf, unsigned long len)
+{
+	int cnt, i, off;
+	ssize_t ret;
+
+	cnt = len/ PAGE_SIZE;
+
+	for (i = 0; i < cnt; i++) {
+		off = i* PAGE_SIZE;
+		ret = do_netdump(net_dev, buf+off, PAGE_SIZE);
+		if (ret <= 0)
+			return -1;
+		net_dev->curr_offset = net_dev->curr_offset + PAGE_SIZE;
+	}
+	return len;
+}
+
+/*
+ * check if the last dump i/o is over and ready for next request
+ */
+static int
+dump_net_ready(struct dump_dev *net_dev, void *buf)
+{
+	return 0;
+}
+
+/*
+ * ioctl function used for configuring network dump
+ */
+static int
+dump_net_ioctl(struct dump_dev *net_dev, unsigned int cmd, unsigned long arg)
+{
+	switch (cmd) {
+	case DIOSTARGETIP:
+		target_ip = arg;
+		break;
+	case DIOSTARGETPORT:
+		target_port = (u16)arg;
+		break;
+	case DIOSSOURCEPORT:
+		source_port = (u16)arg;
+		break;
+	case DIOSETHADDR:
+		return copy_from_user(daddr, (void *)arg, 6);
+		break;
+	case DIOGTARGETIP:
+	case DIOGTARGETPORT:
+	case DIOGSOURCEPORT:
+	case DIOGETHADDR:
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+struct dump_dev_ops dump_netdev_ops = {
+	.open 		= dump_net_open,
+	.release	= dump_net_release,
+	.silence	= dump_net_silence,
+	.resume 	= dump_net_resume,
+	.seek		= dump_net_seek,
+	.write		= dump_net_write,
+	/* .read not implemented */
+	.ready		= dump_net_ready,
+	.ioctl		= dump_net_ioctl
+};
+
+static struct dump_dev default_dump_netdev = {
+	.type_name = "networkdev", 
+	.ops = &dump_netdev_ops, 
+	.curr_offset = 0
+};
+
+static int __init
+dump_netdev_init(void)
+{
+        default_dump_netdev.curr_offset = 0;
+
+	if (dump_register_device(&default_dump_netdev) < 0) {
+		printk("network dump device driver registration failed\n");
+		return -1;
+	}
+	printk("network device driver for LKCD registered\n");
+ 
+	get_random_bytes(&dump_magic, sizeof(dump_magic));
+	return 0;
+}
+
+static void __exit
+dump_netdev_cleanup(void)
+{
+	dump_unregister_device(&default_dump_netdev);
+}
+
+MODULE_AUTHOR("LKCD Development Team <lkcd-devel@lists.sourceforge.net>");
+MODULE_DESCRIPTION("Network Dump Driver for Linux Kernel Crash Dump (LKCD)");
+MODULE_LICENSE("GPL");
+
+module_init(dump_netdev_init);
+module_exit(dump_netdev_cleanup);
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/dump/dump_rle.c 240-lkcd/drivers/dump/dump_rle.c
--- 231-thread_under_page/drivers/dump/dump_rle.c	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/drivers/dump/dump_rle.c	Fri May 30 19:24:56 2003
@@ -0,0 +1,175 @@
+/*
+ * RLE Compression functions for kernel crash dumps.
+ *
+ * Created by: Matt Robinson (yakker@sourceforge.net)
+ * Copyright 2001 Matt D. Robinson.  All rights reserved.
+ *
+ * This code is released under version 2 of the GNU GPL.
+ */
+
+/* header files */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <linux/dump.h>
+
+/*
+ * Name: dump_compress_rle()
+ * Func: Compress a DUMP_PAGE_SIZE (hardware) page down to something more
+ *       reasonable, if possible.  This is the same routine we use in IRIX.
+ */
+static u16
+dump_compress_rle(const u8 *old, u16 oldsize, u8 *new, u16 newsize)
+{
+	u16 ri, wi, count = 0;
+	u_char value = 0, cur_byte;
+
+	/*
+	 * If the block should happen to "compress" to larger than the
+	 * buffer size, allocate a larger one and change cur_buf_size.
+	 */
+
+	wi = ri = 0;
+
+	while (ri < oldsize) {
+		if (!ri) {
+			cur_byte = value = old[ri];
+			count = 0;
+		} else {
+			if (count == 255) {
+				if (wi + 3 > oldsize) {
+					return oldsize;
+				}
+				new[wi++] = 0;
+				new[wi++] = count;
+				new[wi++] = value;
+				value = cur_byte = old[ri];
+				count = 0;
+			} else { 
+				if ((cur_byte = old[ri]) == value) {
+					count++;
+				} else {
+					if (count > 1) {
+						if (wi + 3 > oldsize) {
+							return oldsize;
+						}
+						new[wi++] = 0;
+						new[wi++] = count;
+						new[wi++] = value;
+					} else if (count == 1) {
+						if (value == 0) {
+							if (wi + 3 > oldsize) {
+								return oldsize;
+							}
+							new[wi++] = 0;
+							new[wi++] = 1;
+							new[wi++] = 0;
+						} else {
+							if (wi + 2 > oldsize) {
+								return oldsize;
+							}
+							new[wi++] = value;
+							new[wi++] = value;
+						}
+					} else { /* count == 0 */
+						if (value == 0) {
+							if (wi + 2 > oldsize) {
+								return oldsize;
+							}
+							new[wi++] = value;
+							new[wi++] = value;
+						} else {
+							if (wi + 1 > oldsize) {
+								return oldsize;
+							}
+							new[wi++] = value;
+						}
+					} /* if count > 1 */
+
+					value = cur_byte;
+					count = 0;
+
+				} /* if byte == value */
+
+			} /* if count == 255 */
+
+		} /* if ri == 0 */
+		ri++;
+
+	}
+	if (count > 1) {
+		if (wi + 3 > oldsize) {
+			return oldsize;
+		}
+		new[wi++] = 0;
+		new[wi++] = count;
+		new[wi++] = value;
+	} else if (count == 1) {
+		if (value == 0) {
+			if (wi + 3 > oldsize)
+				return oldsize;
+			new[wi++] = 0;
+			new[wi++] = 1;
+			new[wi++] = 0;
+		} else {
+			if (wi + 2 > oldsize)
+				return oldsize;
+			new[wi++] = value;
+			new[wi++] = value;
+		}
+	} else { /* count == 0 */
+		if (value == 0) {
+			if (wi + 2 > oldsize)
+				return oldsize;
+			new[wi++] = value;
+			new[wi++] = value;
+		} else {
+			if (wi + 1 > oldsize)
+				return oldsize;
+			new[wi++] = value;
+		}
+	} /* if count > 1 */
+
+	value = cur_byte;
+	count = 0;
+	return wi;
+}
+
+/* setup the rle compression functionality */
+static struct __dump_compress dump_rle_compression = {
+	.compress_type = DUMP_COMPRESS_RLE,
+	.compress_func = dump_compress_rle,
+	.compress_name = "RLE",
+};
+
+/*
+ * Name: dump_compress_rle_init()
+ * Func: Initialize rle compression for dumping.
+ */
+static int __init
+dump_compress_rle_init(void)
+{
+	dump_register_compression(&dump_rle_compression);
+	return 0;
+}
+
+/*
+ * Name: dump_compress_rle_cleanup()
+ * Func: Remove rle compression for dumping.
+ */
+static void __exit
+dump_compress_rle_cleanup(void)
+{
+	dump_unregister_compression(DUMP_COMPRESS_RLE);
+}
+
+/* module initialization */
+module_init(dump_compress_rle_init);
+module_exit(dump_compress_rle_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("LKCD Development Team <lkcd-devel@lists.sourceforge.net>");
+MODULE_DESCRIPTION("RLE compression module for crash dump driver");
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/dump/dump_scheme.c 240-lkcd/drivers/dump/dump_scheme.c
--- 231-thread_under_page/drivers/dump/dump_scheme.c	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/drivers/dump/dump_scheme.c	Fri May 30 19:24:56 2003
@@ -0,0 +1,346 @@
+/* 
+ * Default single stage dump scheme methods
+ *
+ * Previously a part of dump_base.c
+ *
+ * Started: Oct 2002 -  Suparna Bhattacharya <suparna@in.ibm.com>
+ * 	Split and rewrote LKCD dump scheme to generic dump method 
+ * 	interfaces 
+ * Derived from original code created by
+ * 	Matt Robinson <yakker@sourceforge.net>)
+ *
+ * Contributions from SGI, IBM, HP, MCL, and others.
+ *
+ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 2001 - 2002 Matt D. Robinson.  All rights reserved.
+ * Copyright (C) 2002 International Business Machines Corp. 
+ *
+ * This code is released under version 2 of the GNU GPL.
+ */
+
+/*
+ * Implements the default dump scheme, i.e. single-stage gathering and 
+ * saving of dump data directly to the target device, which operates in
+ * a push mode, where the dumping system decides what data it saves
+ * taking into account pre-specified dump config options.
+ *
+ * Aside: The 2-stage dump scheme, where there is a soft-reset between
+ * the gathering and saving phases, also reuses some of these
+ * default routines (see dump_overlay.c) 
+ */ 
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <linux/nmi.h>
+#include <linux/dump.h>
+#include "dump_methods.h"
+
+extern int panic_timeout;  /* time before reboot */
+
+extern void dump_speedo(int);
+
+/* Default sequencer used during single stage dumping */
+/* Also invoked during stage 2 of soft-boot based dumping */
+int dump_generic_sequencer(void)
+{
+	struct dump_data_filter *filter = dump_config.dumper->filter;
+	int pass = 0, err = 0, save = 0;
+	int (*action)(unsigned long, unsigned long);
+
+	/* 
+	 * We want to save the more critical data areas first in 
+	 * case we run out of space, encounter i/o failures, or get
+	 * interrupted otherwise and have to give up midway
+	 * So, run through the passes in increasing order 
+	 */
+	for (;filter->selector; filter++, pass++)
+	{
+		/* Assumes passes are exclusive (even across dumpers) */
+		/* Requires care when coding the selection functions */
+		if ((save = filter->level_mask & dump_config.level))
+			action = dump_save_data;
+		else
+			action = dump_skip_data;
+
+		if ((err = dump_iterator(pass, action, filter)) < 0)
+			break;
+
+		printk("\n %d dump pages %s of %d each in pass %d\n", 
+		err, save ? "saved" : "skipped", DUMP_PAGE_SIZE, pass);
+
+	}
+
+	return (err < 0) ? err : 0;
+}
+
+static inline struct page *dump_get_page(loff_t loc)
+{
+	unsigned long page_index = loc >> PAGE_SHIFT;
+
+	/* todo: complete this  to account for ia64/discontig mem */
+	/* todo: and to check for validity, ram page, no i/o mem etc */
+	/* need to use pfn/physaddr equiv of kern_addr_valid */
+	if (__dump_page_valid(page_index))
+		return pfn_to_page(page_index);
+	else
+		return NULL;
+
+}
+
+/* Default iterator: for singlestage and stage 1 of soft-boot dumping */
+/* Iterates over range of physical memory pages in DUMP_PAGE_SIZE increments */
+int dump_page_iterator(int pass, int (*action)(unsigned long, unsigned long), 
+	struct dump_data_filter *filter)
+{
+	/* Todo : fix unit, type */
+	loff_t loc;
+	int count = 0, err = 0;
+	struct page *page;
+
+	/* Todo: Add membanks code */
+	/* TBD: Check if we need to address DUMP_PAGE_SIZE < PAGE_SIZE */	
+	
+	for (loc = filter->start; loc < filter->end; loc += DUMP_PAGE_SIZE) {
+		dump_config.dumper->curr_loc = loc;
+		page = dump_get_page(loc);
+		if (page && filter->selector(pass, (unsigned long) page, 
+		DUMP_PAGE_SIZE)) {
+			if ((err = action((unsigned long)page, DUMP_PAGE_SIZE)))
+			{
+				printk("dump_page_iterator: err %d for loc "
+				"0x%llx, in pass %d\n", err, loc, pass);
+				break;
+			} else
+				count++;
+		}
+	}
+
+	return err ? err : count;
+}
+
+/* 
+ * Base function that saves the selected block of data in the dump 
+ * Action taken when iterator decides that data needs to be saved 
+ */
+int dump_generic_save_data(unsigned long loc, unsigned long sz)
+{
+	void *buf;
+	void *dump_buf = dump_config.dumper->dump_buf;
+	int left, bytes, ret;
+
+	if ((ret = dump_add_data(loc, sz))) {
+		return ret;
+	}
+	buf = dump_config.dumper->curr_buf;
+
+	/* If we've filled up the buffer write it out */
+	if ((left = buf - dump_buf) >= DUMP_BUFFER_SIZE) {
+		bytes = dump_write_buffer(dump_buf, DUMP_BUFFER_SIZE);
+		if (bytes < DUMP_BUFFER_SIZE) {
+			printk("dump_write_buffer failed %d\n", bytes);
+			return bytes ? -ENOSPC : bytes;
+		}
+
+		left -= bytes;
+		
+		/* -- A few chores to do from time to time -- */
+		dump_config.dumper->count++;
+
+		if (!(dump_config.dumper->count & 0x3f)) {
+			/* Update the header every one in a while */
+			/* memset((void *)dump_buf, 'b', DUMP_BUFFER_SIZE);*/
+			if ((ret = dump_update_header()) < 0) {
+				/* issue warning */
+				return ret;
+			}
+			printk(".");
+
+			touch_nmi_watchdog();
+		} else if (!(dump_config.dumper->count & 0x7)) {
+			/* Show progress so the user knows we aren't hung */
+			dump_speedo(dump_config.dumper->count >> 3); 
+		}
+		/* Todo: Touch/Refresh watchdog */
+
+		/* --- Done with periodic chores -- */
+
+
+		/* now adjust the leftover bits back to the top of the page */
+		/* this case would not arise during stage 2 (passthru) */
+		memset(dump_buf, 'z', DUMP_BUFFER_SIZE);
+		if (left) {
+			memcpy(dump_buf, dump_buf + DUMP_BUFFER_SIZE, left);
+		}
+		buf -= DUMP_BUFFER_SIZE;
+		dump_config.dumper->curr_buf = buf;
+	}
+				
+	return 0;
+}
+
+int dump_generic_skip_data(unsigned long loc, unsigned long sz)
+{
+	/* dummy by default */
+	return 0;
+}
+
+/* 
+ * Common low level routine to write a buffer to current dump device 
+ * Expects checks for space etc to have been taken care of by the caller 
+ * Operates serially at the moment for simplicity. 
+ * TBD/Todo: Consider batching for improved throughput
+ */
+int dump_ll_write(void *buf, unsigned long len)
+{
+	long transferred = 0, last_transfer = 0;
+	int ret = 0;
+
+	/* make sure device is ready */
+	while ((ret = dump_dev_ready(NULL)) == -EAGAIN);
+	if  (ret < 0) {
+		printk("dump_dev_ready failed !err %d\n", ret);
+		return ret;
+	}
+
+	while (len) {
+		if ((last_transfer = dump_dev_write(buf, len)) <= 0)  {
+			ret = last_transfer;
+			printk("dump_dev_write failed !err %d\n", 
+			ret);
+			break;
+		}
+		/* wait till complete */
+		while ((ret = dump_dev_ready(buf)) == -EAGAIN)
+			cpu_relax();
+
+		if  (ret < 0) {
+			printk("i/o failed !err %d\n", ret);
+			break;
+		}
+
+		len -= last_transfer;
+		buf += last_transfer;
+		transferred += last_transfer;
+	}
+	return (ret < 0) ? ret : transferred;
+}
+
+/* default writeout routine for single dump device */
+/* writes out the dump data ensuring enough space is left for the end marker */
+int dump_generic_write_buffer(void *buf, unsigned long len)
+{
+	long written = 0;
+	int err = 0;
+
+	/* check for space */
+	if ((err = dump_dev_seek(dump_config.dumper->curr_offset + len + 
+			2*DUMP_BUFFER_SIZE)) < 0) {
+		printk("dump_write_buffer: insuff space after offset 0x%llx\n",
+			dump_config.dumper->curr_offset);
+		return err;
+	}
+	/* alignment check would happen as a side effect of this */
+	if ((err = dump_dev_seek(dump_config.dumper->curr_offset)) < 0)
+		return err; 
+
+	written = dump_ll_write(buf, len);
+
+	/* all or none */
+
+	if (written < len)
+		written = written ? -ENOSPC : written;
+	else
+		dump_config.dumper->curr_offset += len;
+
+	return written;
+}
+
+int dump_generic_configure(unsigned long devid)
+{
+	struct dump_dev *dev = dump_config.dumper->dev;
+	struct dump_data_filter *filter;
+	void *buf;
+	int ret = 0;
+
+	/* Allocate the dump buffer and initialize dumper state */
+	/* Assume that we get aligned addresses */
+	if (!(buf = dump_alloc_mem(DUMP_BUFFER_SIZE + 2 * DUMP_PAGE_SIZE)))
+		return -ENOMEM;
+
+	if ((unsigned long)buf & (PAGE_SIZE - 1)) {
+		/* sanity check for page aligned address */
+		dump_free_mem(buf);
+		return -ENOMEM; /* fixme: better error code */
+	}
+
+	/* Initialize the rest of the fields */
+	dump_config.dumper->dump_buf = buf;
+	dumper_reset();
+
+	/* Open the dump device */
+	if (!dev)
+		return -ENODEV;
+
+	if ((ret = dev->ops->open(dev, devid))) {
+	       return ret;
+	}
+
+	/* Initialise the memory ranges in the dump filter */
+	for (filter = dump_config.dumper->filter ;filter->selector; filter++) {
+		if (!filter->start && !filter->end) {
+			filter->start = 0;
+			filter->end = max_mapnr << PAGE_SHIFT;
+		}
+	}
+
+	return 0;
+}
+
+int dump_generic_unconfigure(void)
+{
+	struct dump_dev *dev = dump_config.dumper->dev;
+	void *buf = dump_config.dumper->dump_buf;
+	int ret = 0;
+
+	/* Close the dump device */
+	if (dev && (ret = dev->ops->release(dev)))
+		return ret;
+	
+	if (buf)
+		dump_free_mem(buf);
+
+	dump_config.dumper->curr_buf = dump_config.dumper->dump_buf = NULL;
+	return 0;
+}
+
+
+/* Set up the default dump scheme */
+
+struct dump_scheme_ops dump_scheme_singlestage_ops = {
+	.configure	= dump_generic_configure,
+	.unconfigure	= dump_generic_unconfigure,
+	.sequencer	= dump_generic_sequencer,
+	.iterator	= dump_page_iterator,
+	.save_data	= dump_generic_save_data,
+	.skip_data	= dump_generic_skip_data,
+	.write_buffer	= dump_generic_write_buffer,
+};
+
+struct dump_scheme dump_scheme_singlestage = {
+	.name		= "single-stage",
+	.ops		= &dump_scheme_singlestage_ops
+};
+
+/* The single stage dumper comprising all these */
+struct dumper dumper_singlestage = {
+	.name		= "single-stage",
+	.scheme		= &dump_scheme_singlestage,
+	.fmt		= &dump_fmt_lcrash,
+	.compress 	= &dump_none_compression,
+	.filter		= dump_filter_table,
+	.dev		= NULL,
+};		
+
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/dump/dump_setup.c 240-lkcd/drivers/dump/dump_setup.c
--- 231-thread_under_page/drivers/dump/dump_setup.c	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/drivers/dump/dump_setup.c	Fri May 30 19:24:56 2003
@@ -0,0 +1,729 @@
+/*
+ * Standard kernel function entry points for Linux crash dumps.
+ *
+ * Created by: Matt Robinson (yakker@sourceforge.net)
+ * Contributions from SGI, IBM, HP, MCL, and others.
+ *
+ * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 2000 - 2002 TurboLinux, Inc.  All rights reserved.
+ * Copyright (C) 2001 - 2002 Matt D. Robinson.  All rights reserved.
+ * Copyright (C) 2002 Free Software Foundation, Inc. All rights reserved.
+ *
+ * This code is released under version 2 of the GNU GPL.
+ */
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * DUMP HISTORY
+ *
+ * This dump code goes back to SGI's first attempts at dumping system
+ * memory on SGI systems running IRIX.  A few developers at SGI needed
+ * a way to take this system dump and analyze it, and created 'icrash',
+ * or IRIX Crash.  The mechanism (the dumps and 'icrash') were used
+ * by support people to generate crash reports when a system failure
+ * occurred.  This was vital for large system configurations that
+ * couldn't apply patch after patch after fix just to hope that the
+ * problems would go away.  So the system memory, along with the crash
+ * dump analyzer, allowed support people to quickly figure out what the
+ * problem was on the system with the crash dump.
+ *
+ * In comes Linux.  SGI started moving towards the open source community,
+ * and upon doing so, SGI wanted to take its support utilities into Linux
+ * with the hopes that they would end up the in kernel and user space to
+ * be used by SGI's customers buying SGI Linux systems.  One of the first
+ * few products to be open sourced by SGI was LKCD, or Linux Kernel Crash
+ * Dumps.  LKCD comprises of a patch to the kernel to enable system
+ * dumping, along with 'lcrash', or Linux Crash, to analyze the system
+ * memory dump.  A few additional system scripts and kernel modifications
+ * are also included to make the dump mechanism and dump data easier to
+ * process and use.
+ *
+ * As soon as LKCD was released into the open source community, a number
+ * of larger companies started to take advantage of it.  Today, there are
+ * many community members that contribute to LKCD, and it continues to
+ * flourish and grow as an open source project.
+ */
+
+/*
+ * DUMP TUNABLES
+ *
+ * This is the list of system tunables (via /proc) that are available
+ * for Linux systems.  All the read, write, etc., functions are listed
+ * here.  Currently, there are a few different tunables for dumps:
+ *
+ * dump_device (used to be dumpdev):
+ *     The device for dumping the memory pages out to.  This 
+ *     may be set to the primary swap partition for disruptive dumps,
+ *     and must be an unused partition for non-disruptive dumps.
+ *     Todo: In the case of network dumps, this may be interpreted 
+ *     as the IP address of the netdump server to connect to.
+ *
+ * dump_compress (used to be dump_compress_pages):
+ *     This is the flag which indicates which compression mechanism
+ *     to use.  This is a BITMASK, not an index (0,1,2,4,8,16,etc.).
+ *     This is the current set of values:
+ *
+ *     0: DUMP_COMPRESS_NONE -- Don't compress any pages.
+ *     1: DUMP_COMPRESS_RLE  -- This uses RLE compression.
+ *     2: DUMP_COMPRESS_GZIP -- This uses GZIP compression.
+ *
+ * dump_level:
+ *     The amount of effort the dump module should make to save
+ *     information for post crash analysis.  This value is now
+ *     a BITMASK value, not an index:
+ *
+ *     0:   Do nothing, no dumping. (DUMP_LEVEL_NONE)
+ *
+ *     1:   Print out the dump information to the dump header, and
+ *          write it out to the dump_device. (DUMP_LEVEL_HEADER)
+ *
+ *     2:   Write out the dump header and all kernel memory pages.
+ *          (DUMP_LEVEL_KERN)
+ *
+ *     4:   Write out the dump header and all kernel and user
+ *          memory pages.  (DUMP_LEVEL_USED)
+ *
+ *     8:   Write out the dump header and all conventional/cached 
+ *	    memory (RAM) pages in the system (kernel, user, free).  
+ *	    (DUMP_LEVEL_ALL_RAM)
+ *
+ *    16:   Write out everything, including non-conventional memory
+ *	    like firmware, proms, I/O registers, uncached memory.
+ *	    (DUMP_LEVEL_ALL)
+ *
+ *     The dump_level will default to 1.
+ *
+ * dump_flags:
+ *     These are the flags to use when talking about dumps.  There
+ *     are lots of possibilities.  This is a BITMASK value, not an index.
+ * 
+ * -----------------------------------------------------------------------
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <linux/fs.h>
+#include <linux/dump.h>
+#include "dump_methods.h"
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/utsname.h>
+#include <linux/highmem.h>
+#include <linux/major.h>
+#include <linux/sysrq.h>
+#include <linux/sysctl.h>
+#include <linux/nmi.h>
+
+#include <asm/hardirq.h>
+#include <asm/uaccess.h>
+
+/*
+ * -----------------------------------------------------------------------
+ *                         V A R I A B L E S
+ * -----------------------------------------------------------------------
+ */
+
+/* Dump tunables */
+struct dump_config dump_config = {
+	.level 		= 0,
+	.flags 		= 0,
+	.dumper 	= NULL
+};
+
+
+/* Global variables used in dump.h */
+/* degree of system freeze when dumping */
+enum dump_silence_levels dump_silence_level = DUMP_HARD_SPIN_CPUS;	 
+
+/* Other global fields */
+extern struct __dump_header dump_header; 
+struct dump_dev *dump_dev = NULL;  /* Active dump device                   */
+int dump_device = 0;
+static int dump_compress = 0;
+
+static u16 dump_compress_none(const u8 *old, u16 oldsize, u8 *new, u16 newsize);
+struct __dump_compress dump_none_compression = {
+	.compress_type	= DUMP_COMPRESS_NONE,
+	.compress_func	= dump_compress_none,
+	.compress_name  = "none",
+};
+
+/* our device operations and functions */
+static int dump_ioctl(struct inode *i, struct file *f,
+	unsigned int cmd, unsigned long arg);
+
+static struct file_operations dump_fops = {
+	.ioctl		=	dump_ioctl,
+};
+
+/* static variables							*/
+static int dump_okay = 0;		/* can we dump out to disk?	*/
+static spinlock_t dump_lock = SPIN_LOCK_UNLOCKED;
+
+/* used for dump compressors */
+static struct list_head dump_compress_list = LIST_HEAD_INIT(dump_compress_list);
+
+/* list of registered dump targets */
+static struct list_head dump_target_list = LIST_HEAD_INIT(dump_target_list);
+
+/* lkcd info structure -- this is used by lcrash for basic system data     */
+struct __lkcdinfo lkcdinfo = {
+	.ptrsz		= (sizeof(void *) * 8),
+#if defined(__LITTLE_ENDIAN) 
+	.byte_order	= __LITTLE_ENDIAN,
+#else
+	.byte_order	= __BIG_ENDIAN,
+#endif
+	.page_shift	= PAGE_SHIFT,
+	.page_size	= PAGE_SIZE,
+	.page_mask	= PAGE_MASK,
+	.page_offset	= PAGE_OFFSET,
+};
+
+/*
+ * -----------------------------------------------------------------------
+ *            / P R O C   T U N A B L E   F U N C T I O N S
+ * -----------------------------------------------------------------------
+ */
+
+static int proc_dump_device(ctl_table *ctl, int write, struct file *f,
+			    void *buffer, size_t *lenp);
+
+/*
+ * sysctl-tuning infrastructure.
+ */
+static ctl_table dump_table[] = {
+	{ .ctl_name = CTL_DUMP_LEVEL,
+	  .procname = DUMP_LEVEL_NAME, 
+	  .data = &dump_config.level, 	 
+	  .maxlen = sizeof(int),
+	  .mode = 0644,
+	  .proc_handler = proc_dointvec, },
+
+	{ .ctl_name = CTL_DUMP_FLAGS,
+	  .procname = DUMP_FLAGS_NAME,
+	  .data = &dump_config.flags,	
+	  .maxlen = sizeof(int),
+	  .mode = 0644,
+	  .proc_handler = proc_dointvec, },
+
+	{ .ctl_name = CTL_DUMP_COMPRESS,
+	  .procname = DUMP_COMPRESS_NAME,
+	  .data = &dump_compress, /* FIXME */
+	  .maxlen = sizeof(int),
+	  .mode = 0644,
+	  .proc_handler = proc_dointvec, },
+	  
+	{ .ctl_name = CTL_DUMP_DEVICE,
+	  .procname = DUMP_DEVICE_NAME,
+	  .mode = 0644,
+	  .data = &dump_device, /* FIXME */
+	  .maxlen = sizeof(int),
+	  .proc_handler = proc_dump_device },
+
+	{ 0, }
+};
+
+static ctl_table dump_root[] = {
+	{ .ctl_name = KERN_DUMP,
+	  .procname = "dump",
+	  .mode = 0555, 
+	  .child = dump_table },
+	{ 0, }
+};
+
+static ctl_table kernel_root[] = {
+	{ .ctl_name = CTL_KERN,
+	  .procname = "kernel",
+	  .mode = 0555,
+	  .child = dump_root, },
+	{ 0, }
+};
+
+static struct ctl_table_header *sysctl_header;
+
+/*
+ * -----------------------------------------------------------------------
+ *              C O M P R E S S I O N   F U N C T I O N S
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * Name: dump_compress_none()
+ * Func: Don't do any compression, period.
+ */
+static u16
+dump_compress_none(const u8 *old, u16 oldsize, u8 *new, u16 newsize)
+{
+	/* just return the old size */
+	return oldsize;
+}
+
+
+/*
+ * Name: dump_execute()
+ * Func: Execute the dumping process.  This makes sure all the appropriate
+ *       fields are updated correctly, and calls dump_execute_memdump(),
+ *       which does the real work.
+ */
+void
+dump_execute(const char *panic_str, const struct pt_regs *regs)
+{
+	int state = -1;
+	unsigned long flags;
+
+	/* make sure we can dump */
+	if (!dump_okay) {
+		pr_info("LKCD not yet configured, can't take dump now\n");
+		return;
+	}
+
+	/* Exclude multiple dumps at the same time,
+	 * and disable interrupts,  some drivers may re-enable
+	 * interrupts in with silence()
+	 *
+	 * Try and acquire spin lock. If successful, leave preempt
+	 * and interrupts disabled.  See spin_lock_irqsave in spinlock.h
+	 */
+	local_irq_save(flags);
+	if (!spin_trylock(&dump_lock)) {
+		local_irq_restore(flags);
+		pr_info("LKCD dump already in progress\n");
+		return;
+	}
+
+	/* Bring system into the strictest level of quiescing for min drift 
+	 * dump drivers can soften this as required in dev->ops->silence() 
+	 */
+	dump_oncpu = smp_processor_id() + 1;
+	dump_silence_level = DUMP_HARD_SPIN_CPUS; 
+
+	state = dump_generic_execute(panic_str, regs);
+	
+	dump_oncpu = 0;
+	spin_unlock_irqrestore(&dump_lock, flags);
+
+	if (state < 0) {
+		printk("Dump Incomplete or failed!\n");
+	} else {
+		printk("Dump Complete; %d dump pages saved.\n", 
+		       dump_header.dh_num_dump_pages);
+	}
+}
+
+/*
+ * Name: dump_register_compression()
+ * Func: Register a dump compression mechanism.
+ */
+void
+dump_register_compression(struct __dump_compress *item)
+{
+	if (item)
+		list_add(&(item->list), &dump_compress_list);
+}
+
+/*
+ * Name: dump_unregister_compression()
+ * Func: Remove a dump compression mechanism, and re-assign the dump
+ *       compression pointer if necessary.
+ */
+void
+dump_unregister_compression(int compression_type)
+{
+	struct list_head *tmp;
+	struct __dump_compress *dc;
+
+	/* let's make sure our list is valid */
+	if (compression_type != DUMP_COMPRESS_NONE) {
+		list_for_each(tmp, &dump_compress_list) {
+			dc = list_entry(tmp, struct __dump_compress, list);
+			if (dc->compress_type == compression_type) {
+				list_del(&(dc->list));
+				break;
+			}
+		}
+	}
+}
+
+/*
+ * Name: dump_compress_init()
+ * Func: Initialize (or re-initialize) compression scheme.
+ */
+static int
+dump_compress_init(int compression_type)
+{
+	struct list_head *tmp;
+	struct __dump_compress *dc;
+
+	/* try to remove the compression item */
+	list_for_each(tmp, &dump_compress_list) {
+		dc = list_entry(tmp, struct __dump_compress, list);
+		if (dc->compress_type == compression_type) {
+			dump_config.dumper->compress = dc;
+			dump_compress = compression_type;
+ 			pr_debug("Dump Compress %s\n", dc->compress_name);
+			return 0;
+		}
+	}
+
+	/* 
+	 * nothing on the list -- return ENODATA to indicate an error 
+	 *
+	 * NB: 
+	 *	EAGAIN: reports "Resource temporarily unavailable" which
+	 *		isn't very enlightening.
+	 */
+	printk("compression_type:%d not found\n", compression_type);
+
+	return -ENODATA;
+}
+
+static int
+dumper_setup(unsigned long flags, unsigned long devid)
+{
+	int ret = 0;
+
+	/* unconfigure old dumper if it exists */
+	dump_okay = 0;
+	if (dump_config.dumper) {
+		pr_debug("Unconfiguring current dumper\n");
+		dump_unconfigure();
+	}
+	/* set up new dumper */
+	dump_config.dumper = &dumper_singlestage;
+	dump_config.dumper->dev = dump_dev;
+
+	ret = dump_configure(devid);
+	if (!ret) {
+		dump_okay = 1;
+		pr_debug("%s dumper set up for dev 0x%lx\n", 
+			dump_config.dumper->name, devid);
+		dump_device = devid;
+	} else {
+		printk("%s dumper set up failed for dev 0x%lx\n", 
+		       dump_config.dumper->name, devid);
+	}
+	return ret;
+}
+
+static int
+dump_target_init(int target)
+{
+	char type[20];
+	struct list_head *tmp;
+	struct dump_dev *dev;
+	
+	switch (target) {
+		case DUMP_FLAGS_DISKDUMP:
+			strcpy(type, "blockdev"); break;
+		case DUMP_FLAGS_NETDUMP:
+			strcpy(type, "networkdev"); break;
+		default:
+			return -1;
+	}
+
+	/*
+	 * This is a bit stupid, generating strings from flag
+	 * and doing strcmp. This is done because 'struct dump_dev'
+	 * has string 'type_name' and not interger 'type'.
+	 */
+	list_for_each(tmp, &dump_target_list) {
+		dev = list_entry(tmp, struct dump_dev, list);
+		if (strcmp(type, dev->type_name) == 0) {
+			dump_dev = dev;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+/*
+ * Name: dump_ioctl()
+ * Func: Allow all dump tunables through a standard ioctl() mechanism.
+ *       This is far better than before, where we'd go through /proc,
+ *       because now this will work for multiple OS and architectures.
+ */
+static int
+dump_ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned long arg)
+{
+	/* check capabilities */
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (!dump_config.dumper && cmd == DIOSDUMPCOMPRESS)
+		/* dump device must be configured first */
+		return -ENODEV;
+
+	/*
+	 * This is the main mechanism for controlling get/set data
+	 * for various dump device parameters.  The real trick here
+	 * is setting the dump device (DIOSDUMPDEV).  That's what
+	 * triggers everything else.
+	 */
+	switch (cmd) {
+	case DIOSDUMPDEV:	/* set dump_device */
+		pr_debug("Configuring dump device\n"); 
+		if (!(f->f_flags & O_RDWR))
+			return -EPERM;
+
+		__dump_open();
+		return dumper_setup(dump_config.flags, arg);
+
+		
+	case DIOGDUMPDEV:	/* get dump_device */
+		return put_user((long)dump_device, (long *)arg);
+
+	case DIOSDUMPLEVEL:	/* set dump_level */
+		if (!(f->f_flags & O_RDWR))
+			return -EPERM;
+
+		/* make sure we have a positive value */
+		if (arg < 0)
+			return -EINVAL;
+
+		/* Fixme: clean this up */
+		dump_config.level = 0;
+		switch ((int)arg) {
+			case DUMP_LEVEL_ALL:
+			case DUMP_LEVEL_ALL_RAM:
+				dump_config.level |= DUMP_MASK_UNUSED;
+			case DUMP_LEVEL_USED:
+				dump_config.level |= DUMP_MASK_USED;
+			case DUMP_LEVEL_KERN:
+				dump_config.level |= DUMP_MASK_KERN;
+			case DUMP_LEVEL_HEADER:
+				dump_config.level |= DUMP_MASK_HEADER;
+			case DUMP_LEVEL_NONE:
+				break;
+			default:
+				return (-EINVAL);
+			}
+		pr_debug("Dump Level 0x%lx\n", dump_config.level);
+		break;
+
+	case DIOGDUMPLEVEL:	/* get dump_level */
+		/* fixme: handle conversion */
+		return put_user((long)dump_config.level, (long *)arg);
+
+		
+	case DIOSDUMPFLAGS:	/* set dump_flags */
+		/* check flags */
+		if (!(f->f_flags & O_RDWR))
+			return -EPERM;
+
+		/* make sure we have a positive value */
+		if (arg < 0)
+			return -EINVAL;
+			
+		if (dump_target_init(arg & DUMP_FLAGS_TARGETMASK) < 0)
+			return -EINVAL; /* return proper error */
+
+		dump_config.flags = arg;
+		
+		pr_debug("Dump Flags 0x%lx\n", dump_config.flags);
+		break;
+		
+	case DIOGDUMPFLAGS:	/* get dump_flags */
+		return put_user((long)dump_config.flags, (long *)arg);
+
+	case DIOSDUMPCOMPRESS:	/* set the dump_compress status */
+		if (!(f->f_flags & O_RDWR))
+			return -EPERM;
+
+		return dump_compress_init((int)arg);
+
+	case DIOGDUMPCOMPRESS:	/* get the dump_compress status */
+		return put_user((long)(dump_config.dumper ? 
+			dump_config.dumper->compress->compress_type : 0), 
+			(long *)arg);
+			
+	default:
+		/* 
+		 * these are network dump specific ioctls, let the
+		 * module handle them.
+		 */
+		return dump_dev_ioctl(cmd, arg);
+	}
+	return 0;
+}
+
+/*
+ * Handle special cases for dump_device 
+ * changing dump device requires doing an opening the device
+ */
+static int 
+proc_dump_device(ctl_table *ctl, int write, struct file *f,
+		 void *buffer, size_t *lenp)
+{
+	int *valp = ctl->data;
+	int oval = *valp;
+	int ret = -EPERM;
+
+	/* same permission checks as ioctl */
+	if (capable(CAP_SYS_ADMIN)) {
+		ret = proc_dointvec(ctl, write, f, buffer, lenp);
+		if (ret == 0 && write && *valp != oval) {
+			/* need to restore old value to close properly */
+			dump_device = (dev_t) oval;
+			__dump_open();
+			ret = dumper_setup(dump_config.flags, (dev_t) *valp);
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *                     I N I T   F U N C T I O N S
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * These register and unregister routines are exported for modules
+ * to register their dump drivers (like block, net etc)
+ */
+int
+dump_register_device(struct dump_dev *ddev)
+{
+	struct list_head *tmp;
+	struct dump_dev *dev;
+
+	list_for_each(tmp, &dump_target_list) {
+		dev = list_entry(tmp, struct dump_dev, list);
+		if (strcmp(ddev->type_name, dev->type_name) == 0) {
+			printk("Target type %s already registered\n",
+					dev->type_name);
+			return -1; /* return proper error */
+		}
+	}
+	list_add(&(ddev->list), &dump_target_list);
+	
+	return 0;
+}
+
+void
+dump_unregister_device(struct dump_dev *ddev)
+{
+	list_del(&(ddev->list));
+	if (ddev != dump_dev)
+		return;
+
+	dump_okay = 0;
+
+	if (dump_config.dumper)
+		dump_unconfigure();
+
+	dump_config.flags &= ~DUMP_FLAGS_TARGETMASK;
+	dump_okay = 0;
+	dump_dev = NULL;
+	dump_config.dumper = NULL;
+}
+
+
+#ifdef CONFIG_MAGIC_SYSRQ
+/* Sysrq handler */
+static void sysrq_handle_crashdump(int key, struct pt_regs *pt_regs,
+		struct tty_struct *tty) {
+	dump_execute("sysrq", pt_regs);
+}
+
+static struct sysrq_key_op sysrq_crashdump_op = {
+	.handler	=	sysrq_handle_crashdump,
+	.help_msg	=	"Dump",
+	.action_msg	=	"Starting crash dump",
+};
+#endif
+
+static inline void
+dump_sysrq_register(void) 
+{
+#ifdef CONFIG_MAGIC_SYSRQ
+	__sysrq_lock_table();
+	__sysrq_put_key_op(DUMP_SYSRQ_KEY, &sysrq_crashdump_op);
+	__sysrq_unlock_table();
+#endif
+}
+
+static inline void
+dump_sysrq_unregister(void)
+{
+#ifdef CONFIG_MAGIC_SYSRQ
+	__sysrq_lock_table();
+	if (__sysrq_get_key_op(DUMP_SYSRQ_KEY) == &sysrq_crashdump_op)
+		__sysrq_put_key_op(DUMP_SYSRQ_KEY, NULL);
+	__sysrq_unlock_table();
+#endif
+}
+
+/*
+ * Name: dump_init()
+ * Func: Initialize the dump process.  This will set up any architecture
+ *       dependent code.  The big key is we need the memory offsets before
+ *       the page table is initialized, because the base memory offset
+ *       is changed after paging_init() is called.
+ */
+static int __init
+dump_init(void)
+{
+	struct sysinfo info;
+
+	/* try to create our dump device */
+	if (register_chrdev(CRASH_DUMP_MAJOR, "dump", &dump_fops)) {
+		printk("cannot register dump character device!\n");
+		return -EBUSY;
+	}
+
+	__dump_init((u64)PAGE_OFFSET);
+
+	/* set the dump_compression_list structure up */
+	dump_register_compression(&dump_none_compression);
+
+	/* grab the total memory size now (not if/when we crash) */
+	si_meminfo(&info);
+
+	/* set the memory size */
+	dump_header.dh_memory_size = (u64)info.totalram;
+
+	sysctl_header = register_sysctl_table(kernel_root, 0);
+	dump_sysrq_register();
+
+	pr_info("Crash dump driver initialized.\n");
+	return 0;
+}
+
+static void __exit
+dump_cleanup(void)
+{
+	dump_okay = 0;
+
+	if (dump_config.dumper)
+		dump_unconfigure();
+
+	/* arch-specific cleanup routine */
+	__dump_cleanup();
+
+	/* ignore errors while unregistering -- since can't do anything */
+	unregister_sysctl_table(sysctl_header);
+	unregister_chrdev(CRASH_DUMP_MAJOR, "dump");
+	dump_sysrq_unregister();
+}
+
+EXPORT_SYMBOL(dump_register_compression);
+EXPORT_SYMBOL(dump_unregister_compression);
+EXPORT_SYMBOL(dump_register_device);
+EXPORT_SYMBOL(dump_unregister_device);
+EXPORT_SYMBOL(dump_config);
+EXPORT_SYMBOL(dump_silence_level);
+
+EXPORT_SYMBOL(__dump_irq_enable);
+EXPORT_SYMBOL(__dump_irq_restore);
+
+MODULE_AUTHOR("Matt D. Robinson <yakker@sourceforge.net>");
+MODULE_DESCRIPTION("Linux Kernel Crash Dump (LKCD) driver");
+MODULE_LICENSE("GPL");
+
+module_init(dump_init);
+module_exit(dump_cleanup);
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/net/3c59x.c 240-lkcd/drivers/net/3c59x.c
--- 231-thread_under_page/drivers/net/3c59x.c	Fri May 30 19:02:11 2003
+++ 240-lkcd/drivers/net/3c59x.c	Fri May 30 19:24:56 2003
@@ -907,6 +907,7 @@ static void set_rx_mode(struct net_devic
 static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static void vortex_tx_timeout(struct net_device *dev);
 static void acpi_set_WOL(struct net_device *dev);
+static void vorboom_poll(struct net_device *dev);
 
 /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
 /* Option count limit only -- unlimited interfaces are supported. */
@@ -1463,6 +1464,9 @@ static int __devinit vortex_probe1(struc
 	dev->set_multicast_list = set_rx_mode;
 	dev->tx_timeout = vortex_tx_timeout;
 	dev->watchdog_timeo = (watchdog * HZ) / 1000;
+#ifdef HAVE_POLL_CONTROLLER
+	dev->poll_controller = &vorboom_poll;
+#endif
 	if (pdev && vp->enable_wol) {
 		vp->pm_state_valid = 1;
  		pci_save_state(VORTEX_PCI(vp), vp->power_state);
@@ -2452,6 +2456,29 @@ handler_exit:
 	spin_unlock(&vp->lock);
 	return IRQ_HANDLED;
 }
+
+#ifdef HAVE_POLL_CONTROLLER
+
+/*
+ * Polling 'interrupt' - used by things like netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+
+static void vorboom_poll (struct net_device *dev)
+{
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+
+	disable_irq(dev->irq);
+	if (vp->full_bus_master_tx)
+		boomerang_interrupt(dev->irq, dev, 0);
+	else
+		vortex_interrupt(dev->irq, dev, 0);
+	enable_irq(dev->irq);
+}
+
+#endif
+
 
 static int vortex_rx(struct net_device *dev)
 {
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/net/e100/e100_main.c 240-lkcd/drivers/net/e100/e100_main.c
--- 231-thread_under_page/drivers/net/e100/e100_main.c	Fri May 30 19:02:12 2003
+++ 240-lkcd/drivers/net/e100/e100_main.c	Fri May 30 19:24:56 2003
@@ -558,6 +558,22 @@ e100_trigger_SWI(struct e100_private *bd
 	readw(&(bdp->scb->scb_status));	/* flushes last write, read-safe */
 }
 
+#ifdef HAVE_POLL_CONTROLLER
+
+/*
+ * Polling 'interrupt' - used by things like netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+static void
+e100_poll(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	e100intr(dev->irq, dev, NULL);
+	enable_irq(dev->irq);
+}
+#endif
+
 static int __devinit
 e100_found1(struct pci_dev *pcid, const struct pci_device_id *ent)
 {
@@ -576,6 +592,9 @@ e100_found1(struct pci_dev *pcid, const 
 
 	SET_MODULE_OWNER(dev);
 
+#ifdef HAVE_POLL_CONTROLLER
+	dev->poll_controller = &e100_poll;
+#endif
 	if (first_time) {
 		first_time = false;
         	printk(KERN_NOTICE "%s - version %s\n",
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/net/e1000/e1000_main.c 240-lkcd/drivers/net/e1000/e1000_main.c
--- 231-thread_under_page/drivers/net/e1000/e1000_main.c	Fri May 30 19:02:12 2003
+++ 240-lkcd/drivers/net/e1000/e1000_main.c	Fri May 30 19:24:56 2003
@@ -145,6 +145,7 @@ static void e1000_leave_82542_rst(struct
 static inline void e1000_rx_checksum(struct e1000_adapter *adapter,
                                      struct e1000_rx_desc *rx_desc,
                                      struct sk_buff *skb);
+static void e1000_Poll(struct net_device *dev);
 static void e1000_tx_timeout(struct net_device *dev);
 static void e1000_tx_timeout_task(struct net_device *dev);
 static void e1000_smartspeed(struct e1000_adapter *adapter);
@@ -413,6 +414,9 @@ e1000_probe(struct pci_dev *pdev,
 
 	adapter->bd_number = cards_found;
 
+#ifdef HAVE_POLL_CONTROLLER
+	netdev->poll_controller = &e1000_Poll;
+#endif
 	/* setup the private structure */
 
 	if(e1000_sw_init(adapter))
@@ -2002,6 +2006,15 @@ e1000_intr(int irq, void *data, struct p
 #endif
 	return IRQ_HANDLED;
 }
+
+#ifdef HAVE_POLL_CONTROLLER
+static void e1000_Poll(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	e1000_intr(dev->irq, dev, NULL);
+	enable_irq(dev->irq);
+}
+#endif
 
 #ifdef CONFIG_E1000_NAPI
 /**
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/net/eepro100.c 240-lkcd/drivers/net/eepro100.c
--- 231-thread_under_page/drivers/net/eepro100.c	Fri May 30 19:02:12 2003
+++ 240-lkcd/drivers/net/eepro100.c	Fri May 30 19:24:56 2003
@@ -542,6 +542,7 @@ static void speedo_refill_rx_buffers(str
 static int speedo_rx(struct net_device *dev);
 static void speedo_tx_buffer_gc(struct net_device *dev);
 static irqreturn_t speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static void poll_speedo (struct net_device *dev);
 static int speedo_close(struct net_device *dev);
 static struct net_device_stats *speedo_get_stats(struct net_device *dev);
 static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
@@ -880,6 +881,9 @@ static int __devinit speedo_found1(struc
 	dev->get_stats = &speedo_get_stats;
 	dev->set_multicast_list = &set_rx_mode;
 	dev->do_ioctl = &speedo_ioctl;
+#ifdef HAVE_POLL_CONTROLLER
+	dev->poll_controller = &poll_speedo;
+#endif
 
 	return 0;
 }
@@ -1660,6 +1664,23 @@ static irqreturn_t speedo_interrupt(int 
 	clear_bit(0, (void*)&sp->in_interrupt);
 	return IRQ_RETVAL(handled);
 }
+
+#ifdef HAVE_POLL_CONTROLLER
+
+/*
+ * Polling 'interrupt' - used by things like netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+
+static void poll_speedo (struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	speedo_interrupt (dev->irq, dev, NULL);
+	enable_irq(dev->irq);
+}
+
+#endif
 
 static inline struct RxFD *speedo_rx_alloc(struct net_device *dev, int entry)
 {
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/net/smc-ultra.c 240-lkcd/drivers/net/smc-ultra.c
--- 231-thread_under_page/drivers/net/smc-ultra.c	Wed Mar  5 07:37:02 2003
+++ 240-lkcd/drivers/net/smc-ultra.c	Fri May 30 19:24:56 2003
@@ -122,6 +122,14 @@ MODULE_DEVICE_TABLE(isapnp, ultra_device
 #define ULTRA_IO_EXTENT 32
 #define EN0_ERWCNT		0x08	/* Early receive warning count. */
 
+
+static void ultra_poll(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	ei_interrupt(dev->irq, dev, NULL);
+	enable_irq(dev->irq);
+}
+
 /*	Probe for the Ultra.  This looks like a 8013 with the station
 	address PROM at I/O ports <base>+8 to <base>+13, with a checksum
 	following.
@@ -134,6 +142,9 @@ int __init ultra_probe(struct net_device
 
 	SET_MODULE_OWNER(dev);
 
+#ifdef HAVE_POLL_CONTROLLER
+	dev->poll_controller = &ultra_poll;
+#endif
 	if (base_addr > 0x1ff)		/* Check a single specified location. */
 		return ultra_probe1(dev, base_addr);
 	else if (base_addr != 0)	/* Don't probe at all. */
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/net/tlan.c 240-lkcd/drivers/net/tlan.c
--- 231-thread_under_page/drivers/net/tlan.c	Fri May 30 19:02:13 2003
+++ 240-lkcd/drivers/net/tlan.c	Fri May 30 19:24:56 2003
@@ -345,6 +345,8 @@ static int	TLan_EeSendByte( u16, u8, int
 static void	TLan_EeReceiveByte( u16, u8 *, int );
 static int	TLan_EeReadByte( struct net_device *, u8, u8 * );
 
+static void	TLan_Poll(struct net_device *);
+
 
 static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
 	TLan_HandleInvalid,
@@ -855,6 +857,9 @@ static int TLan_Init( struct net_device 
 	dev->get_stats = &TLan_GetStats;
 	dev->set_multicast_list = &TLan_SetMulticastList;
 	dev->do_ioctl = &TLan_ioctl;
+#ifdef HAVE_POLL_CONTROLLER
+	dev->poll_controller = &TLan_Poll;
+#endif
 	dev->tx_timeout = &TLan_tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
 
@@ -1138,7 +1143,14 @@ static irqreturn_t TLan_HandleInterrupt(
 	return IRQ_HANDLED;
 } /* TLan_HandleInterrupts */
 
-
+#ifdef HAVE_POLL_CONTROLLER
+static void TLan_Poll(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	TLan_HandleInterrupt(dev->irq, dev, NULL);
+	enable_irq(dev->irq);
+}
+#endif
 
 
 	/***************************************************************
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/drivers/net/tulip/tulip_core.c 240-lkcd/drivers/net/tulip/tulip_core.c
--- 231-thread_under_page/drivers/net/tulip/tulip_core.c	Fri May 30 19:02:13 2003
+++ 240-lkcd/drivers/net/tulip/tulip_core.c	Fri May 30 19:24:56 2003
@@ -243,6 +243,7 @@ static void tulip_down(struct net_device
 static struct net_device_stats *tulip_get_stats(struct net_device *dev);
 static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static void set_rx_mode(struct net_device *dev);
+static void poll_tulip(struct net_device *dev);
 
 
 
@@ -1618,6 +1619,9 @@ static int __devinit tulip_init_one (str
 	dev->get_stats = tulip_get_stats;
 	dev->do_ioctl = private_ioctl;
 	dev->set_multicast_list = set_rx_mode;
+#ifdef HAVE_POLL_CONTROLLER
+	dev->poll_controller = &poll_tulip;
+#endif
 
 	if (register_netdev(dev))
 		goto err_out_free_ring;
@@ -1773,6 +1777,24 @@ static void __devexit tulip_remove_one (
 
 	/* pci_power_off (pdev, -1); */
 }
+
+
+#ifdef HAVE_POLL_CONTROLLER
+
+/*
+ * Polling 'interrupt' - used by things like netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+
+static void poll_tulip (struct net_device *dev)
+{
+       disable_irq(dev->irq);
+       tulip_interrupt (dev->irq, dev, NULL);
+       enable_irq(dev->irq);
+}
+
+#endif
 
 
 static struct pci_driver tulip_driver = {
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/include/asm-i386/dump.h 240-lkcd/include/asm-i386/dump.h
--- 231-thread_under_page/include/asm-i386/dump.h	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/include/asm-i386/dump.h	Fri May 30 19:24:56 2003
@@ -0,0 +1,75 @@
+/*
+ * Kernel header file for Linux crash dumps.
+ *
+ * Created by: Matt Robinson (yakker@sgi.com)
+ *
+ * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
+ *
+ * This code is released under version 2 of the GNU GPL.
+ */
+
+/* This header file holds the architecture specific crash dump header */
+#ifndef _ASM_DUMP_H
+#define _ASM_DUMP_H
+
+/* necessary header files */
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <linux/threads.h>
+#include <linux/mm.h>
+
+/* definitions */
+#define DUMP_ASM_MAGIC_NUMBER	0xdeaddeadULL	/* magic number            */
+#define DUMP_ASM_VERSION_NUMBER	0x3	/* version number          */
+
+/* max number of cpus */
+#define DUMP_MAX_NUM_CPUS 32
+
+/*
+ * Structure: __dump_header_asm
+ *  Function: This is the header for architecture-specific stuff.  It
+ *            follows right after the dump header.
+ */
+struct __dump_header_asm {
+	/* the dump magic number -- unique to verify dump is valid */
+	u64		dha_magic_number;
+
+	/* the version number of this dump */
+	u32		dha_version;
+
+	/* the size of this header (in case we can't read it) */
+	u32		dha_header_size;
+
+	/* the esp for i386 systems */
+	u32		dha_esp;
+
+	/* the eip for i386 systems */
+	u32		dha_eip;
+
+	/* the dump registers */
+	struct pt_regs	dha_regs;
+
+	/* smp specific */
+	u32		dha_smp_num_cpus;
+	u32		dha_dumping_cpu;
+	struct pt_regs	dha_smp_regs[DUMP_MAX_NUM_CPUS];
+	u32		dha_smp_current_task[DUMP_MAX_NUM_CPUS];
+	u32		dha_stack[DUMP_MAX_NUM_CPUS];
+	u32		dha_stack_ptr[DUMP_MAX_NUM_CPUS];
+} __attribute__((packed));
+
+#ifdef __KERNEL__
+
+extern struct __dump_header_asm dump_header_asm;
+
+#ifdef CONFIG_SMP
+extern unsigned long irq_affinity[];
+extern int (*dump_ipi_function_ptr)(struct pt_regs *);
+extern void dump_send_ipi(void);
+#else
+#define dump_send_ipi() do { } while(0)
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_DUMP_H */
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/include/asm-i386/e820.h 240-lkcd/include/asm-i386/e820.h
--- 231-thread_under_page/include/asm-i386/e820.h	Sun Nov 17 20:29:50 2002
+++ 240-lkcd/include/asm-i386/e820.h	Fri May 30 19:24:56 2003
@@ -35,6 +35,7 @@ struct e820map {
 };
 
 extern struct e820map e820;
+
 #endif/*!__ASSEMBLY__*/
 
 #endif/*__E820_HEADER*/
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/include/asm-i386/kdebug.h 240-lkcd/include/asm-i386/kdebug.h
--- 231-thread_under_page/include/asm-i386/kdebug.h	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/include/asm-i386/kdebug.h	Fri May 30 19:24:56 2003
@@ -0,0 +1,53 @@
+#ifndef _I386_KDEBUG_H
+#define _I386_KDEBUG_H 1
+
+#include <linux/notifier.h>
+
+struct pt_regs;
+
+struct die_args { 
+	struct pt_regs *regs;
+	const char *str;
+	long err; 
+}; 
+
+
+/* Grossly misnamed. */
+enum die_val { 
+	DIE_OOPS = 1,
+	DIE_INT3,
+	DIE_DEBUG,
+	DIE_PANIC,
+	DIE_NMI,
+	DIE_DIE,
+	DIE_CPUINIT,	/* not really a die, but .. */
+	DIE_TRAPINIT,
+	DIE_STOP,
+	DIE_PROTFAULT,
+	DIE_PAGEFAULT,
+	DIE_FPUTRAP,
+	DIE_WATCHDOG,
+}; 
+
+extern struct notifier_block *die_chain;
+
+static inline int register_die_notifier(struct notifier_block *nb)
+{
+	return notifier_chain_register(&die_chain, nb);
+
+}
+
+static inline int unregister_die_notifier(struct notifier_block *nb)
+{
+	return notifier_chain_unregister(&die_chain, nb);
+}
+
+static inline int notify_die(enum die_val val, const char *str,
+			     struct pt_regs *regs,long err)
+{ 
+	struct die_args args = { regs: regs, str: str, err: err }; 
+
+	return notifier_call_chain(&die_chain, val, &args); 
+} 
+
+#endif
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/include/asm-i386/kmap_types.h 240-lkcd/include/asm-i386/kmap_types.h
--- 231-thread_under_page/include/asm-i386/kmap_types.h	Fri May 30 19:02:20 2003
+++ 240-lkcd/include/asm-i386/kmap_types.h	Fri May 30 19:24:56 2003
@@ -24,7 +24,8 @@ D(10)	KM_IRQ0,
 D(11)	KM_IRQ1,
 D(12)	KM_SOFTIRQ0,
 D(13)	KM_SOFTIRQ1,
-D(14)	KM_TYPE_NR
+D(14)	KM_TYPE_NR,
+D(15)	KM_DUMP
 };
 
 #undef D
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/include/asm-i386/mach-default/irq_vectors.h 240-lkcd/include/asm-i386/mach-default/irq_vectors.h
--- 231-thread_under_page/include/asm-i386/mach-default/irq_vectors.h	Sun Apr 20 19:35:05 2003
+++ 240-lkcd/include/asm-i386/mach-default/irq_vectors.h	Fri May 30 19:24:56 2003
@@ -48,6 +48,7 @@
 #define INVALIDATE_TLB_VECTOR	0xfd
 #define RESCHEDULE_VECTOR	0xfc
 #define CALL_FUNCTION_VECTOR	0xfb
+#define DUMP_VECTOR		0xfa
 
 #define THERMAL_APIC_VECTOR	0xf0
 /*
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/include/asm-i386/smp.h 240-lkcd/include/asm-i386/smp.h
--- 231-thread_under_page/include/asm-i386/smp.h	Fri May 30 19:02:20 2003
+++ 240-lkcd/include/asm-i386/smp.h	Fri May 30 19:24:56 2003
@@ -39,6 +39,7 @@ extern int smp_num_siblings;
 extern int cpu_sibling_map[];
 
 extern void smp_flush_tlb(void);
+extern void dump_send_ipi(void);
 extern void smp_message_irq(int cpl, void *dev_id, struct pt_regs *regs);
 extern void smp_send_reschedule(int cpu);
 extern void smp_send_reschedule_all(void);
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/include/linux/dump.h 240-lkcd/include/linux/dump.h
--- 231-thread_under_page/include/linux/dump.h	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/include/linux/dump.h	Fri May 30 19:24:56 2003
@@ -0,0 +1,362 @@
+/*
+ * Kernel header file for Linux crash dumps.
+ *
+ * Created by: Matt Robinson (yakker@sgi.com)
+ * Copyright 1999 - 2002 Silicon Graphics, Inc. All rights reserved.
+ *
+ * vmdump.h to dump.h by: Matt D. Robinson (yakker@sourceforge.net)
+ * Copyright 2001 - 2002 Matt D. Robinson.  All rights reserved.
+ * Copyright (C) 2002 Free Software Foundation, Inc. All rights reserved.
+ *
+ * Most of this is the same old stuff from vmdump.h, except now we're
+ * actually a stand-alone driver plugged into the block layer interface,
+ * with the exception that we now allow for compression modes externally
+ * loaded (e.g., someone can come up with their own).
+ *
+ * This code is released under version 2 of the GNU GPL.
+ */
+
+/* This header file includes all structure definitions for crash dumps. */
+#ifndef _DUMP_H
+#define _DUMP_H
+
+#if defined(CONFIG_CRASH_DUMP) || defined (CONFIG_CRASH_DUMP_MODULE)
+
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <linux/dumpdev.h>
+
+/* 
+ * Predefine default DUMP_PAGE constants, asm header may override.
+ *
+ * On ia64 discontinuous memory systems it's possible for the memory
+ * banks to stop at 2**12 page alignments, the smallest possible page
+ * size. But the system page size, PAGE_SIZE, is in fact larger.
+ */
+#define DUMP_PAGE_SHIFT 	PAGE_SHIFT
+#define DUMP_PAGE_MASK		PAGE_MASK
+#define DUMP_PAGE_ALIGN(addr)	PAGE_ALIGN(addr)
+#define DUMP_HEADER_OFFSET	PAGE_SIZE
+
+/* keep DUMP_PAGE_SIZE constant to 4K = 1<<12
+ * it may be different from PAGE_SIZE then.
+ */
+#define DUMP_PAGE_SIZE		4096
+
+/* 
+ * Predefined default memcpy() to use when copying memory to the dump buffer.
+ *
+ * On ia64 there is a heads up function that can be called to let the prom
+ * machine check monitor know that the current activity is risky and it should
+ * ignore the fault (nofault). In this case the ia64 header will redefine this
+ * macro to __dump_memcpy() and use it's arch specific version.
+ */
+#define DUMP_memcpy		memcpy
+
+/* necessary header files */
+#include <asm/dump.h>			/* for architecture-specific header */
+
+/* 
+ * Size of the buffer that's used to hold:
+ *
+ *	1. the dump header (padded to fill the complete buffer)
+ *	2. the possibly compressed page headers and data
+ */
+#define DUMP_BUFFER_SIZE	(64 * 1024)  /* size of dump buffer         */
+#define DUMP_HEADER_SIZE	DUMP_BUFFER_SIZE
+
+/* standard header definitions */
+#define DUMP_MAGIC_NUMBER	0xa8190173618f23edULL  /* dump magic number */
+#define DUMP_MAGIC_LIVE		0xa8190173618f23cdULL  /* live magic number */
+#define DUMP_VERSION_NUMBER	0x8	/* dump version number              */
+#define DUMP_PANIC_LEN		0x100	/* dump panic string length         */
+
+/* dump levels - type specific stuff added later -- add as necessary */
+#define DUMP_LEVEL_NONE		0x0	/* no dumping at all -- just bail   */
+#define DUMP_LEVEL_HEADER	0x1	/* kernel dump header only          */
+#define DUMP_LEVEL_KERN		0x2	/* dump header and kernel pages     */
+#define DUMP_LEVEL_USED		0x4	/* dump header, kernel/user pages   */
+#define DUMP_LEVEL_ALL_RAM	0x8	/* dump header, all RAM pages       */
+#define DUMP_LEVEL_ALL		0x10	/* dump all memory RAM and firmware */
+
+
+/* dump compression options -- add as necessary */
+#define DUMP_COMPRESS_NONE	0x0	/* don't compress this dump         */
+#define DUMP_COMPRESS_RLE	0x1	/* use RLE compression              */
+#define DUMP_COMPRESS_GZIP	0x2	/* use GZIP compression             */
+
+/* dump flags - any dump-type specific flags -- add as necessary */
+#define DUMP_FLAGS_NONE		0x0	/* no flags are set for this dump   */
+
+#define DUMP_FLAGS_TARGETMASK	0xf0000000 /* handle special case targets   */
+#define DUMP_FLAGS_DISKDUMP	0x80000000 /* dump to local disk 	    */
+#define DUMP_FLAGS_NETDUMP	0x40000000 /* dump over the network         */
+
+/* dump header flags -- add as necessary */
+#define DUMP_DH_FLAGS_NONE	0x0	/* no flags set (error condition!)  */
+#define DUMP_DH_RAW		0x1	/* raw page (no compression)        */
+#define DUMP_DH_COMPRESSED	0x2	/* page is compressed               */
+#define DUMP_DH_END		0x4	/* end marker on a full dump        */
+#define DUMP_DH_TRUNCATED	0x8	/* dump is incomplete               */
+#define DUMP_DH_TEST_PATTERN	0x10	/* dump page is a test pattern      */
+#define DUMP_DH_NOT_USED	0x20	/* 1st bit not used in flags        */
+
+/* names for various dump parameters in /proc/kernel */
+#define DUMP_ROOT_NAME		"sys/dump"
+#define DUMP_DEVICE_NAME	"device"
+#define DUMP_COMPRESS_NAME	"compress"
+#define DUMP_LEVEL_NAME		"level"
+#define DUMP_FLAGS_NAME		"flags"
+
+#define DUMP_SYSRQ_KEY		'd'	/* key to use for MAGIC_SYSRQ key   */
+
+/* CTL_DUMP names: */
+enum
+{
+	CTL_DUMP_DEVICE=1,
+	CTL_DUMP_COMPRESS=3,
+	CTL_DUMP_LEVEL=3,
+	CTL_DUMP_FLAGS=4,
+	CTL_DUMP_TEST=5,
+};
+
+
+/* page size for gzip compression -- buffered slightly beyond hardware PAGE_SIZE used by DUMP */
+#define DUMP_DPC_PAGE_SIZE	(DUMP_PAGE_SIZE + 512)
+
+/* dump ioctl() control options */
+#define DIOSDUMPDEV		1	/* set the dump device              */
+#define DIOGDUMPDEV		2	/* get the dump device              */
+#define DIOSDUMPLEVEL		3	/* set the dump level               */
+#define DIOGDUMPLEVEL		4	/* get the dump level               */
+#define DIOSDUMPFLAGS		5	/* set the dump flag parameters     */
+#define DIOGDUMPFLAGS		6	/* get the dump flag parameters     */
+#define DIOSDUMPCOMPRESS	7	/* set the dump compress level      */
+#define DIOGDUMPCOMPRESS	8	/* get the dump compress level      */
+
+/* these ioctls are used only by netdump module */
+#define DIOSTARGETIP		9	/* set the target m/c's ip	    */
+#define DIOGTARGETIP		10	/* get the target m/c's ip	    */
+#define DIOSTARGETPORT		11	/* set the target m/c's port	    */
+#define DIOGTARGETPORT		12	/* get the target m/c's port	    */
+#define DIOSSOURCEPORT		13	/* set the source m/c's port	    */
+#define DIOGSOURCEPORT		14	/* get the source m/c's port	    */
+#define DIOSETHADDR		15	/* set ethernet address		    */
+#define DIOGETHADDR		16	/* get ethernet address		    */
+
+/*
+ * Structure: __dump_header
+ *  Function: This is the header dumped at the top of every valid crash
+ *            dump.  
+ */
+struct __dump_header {
+	/* the dump magic number -- unique to verify dump is valid */
+	u64	dh_magic_number;
+
+	/* the version number of this dump */
+	u32	dh_version;
+
+	/* the size of this header (in case we can't read it) */
+	u32	dh_header_size;
+
+	/* the level of this dump (just a header?) */
+	u32	dh_dump_level;
+
+	/* 
+	 * We assume dump_page_size to be 4K in every case.
+	 * Store here the configurable system page size (4K, 8K, 16K, etc.) 
+	 */
+	u32	dh_page_size;
+
+	/* the size of all physical memory */
+	u64	dh_memory_size;
+
+	/* the start of physical memory */
+	u64	dh_memory_start;
+
+	/* the end of physical memory */
+	u64	dh_memory_end;
+
+	/* the number of hardware/physical pages in this dump specifically */
+	u32	dh_num_dump_pages;
+
+	/* the panic string, if available */
+	char	dh_panic_string[DUMP_PANIC_LEN];
+
+	/* timeval depends on architecture, two long values */
+	struct {
+		u64 tv_sec;
+		u64 tv_usec;
+	} dh_time; /* the time of the system crash */
+
+	/* the NEW utsname (uname) information -- in character form */
+	/* we do this so we don't have to include utsname.h         */
+	/* plus it helps us be more architecture independent        */
+	/* now maybe one day soon they'll make the [65] a #define!  */
+	char	dh_utsname_sysname[65];
+	char	dh_utsname_nodename[65];
+	char	dh_utsname_release[65];
+	char	dh_utsname_version[65];
+	char	dh_utsname_machine[65];
+	char	dh_utsname_domainname[65];
+
+	/* the address of current task (OLD = void *, NEW = u64) */
+	u64	dh_current_task;
+
+	/* what type of compression we're using in this dump (if any) */
+	u32	dh_dump_compress;
+
+	/* any additional flags */
+	u32	dh_dump_flags;
+
+	/* any additional flags */
+	u32	dh_dump_device;
+} __attribute__((packed));
+
+/*
+ * Structure: __dump_page
+ *  Function: To act as the header associated to each physical page of
+ *            memory saved in the system crash dump.  This allows for
+ *            easy reassembly of each crash dump page.  The address bits
+ *            are split to make things easier for 64-bit/32-bit system
+ *            conversions.
+ *
+ * dp_byte_offset and dp_page_index are landmarks that are helpful when
+ * looking at a hex dump of /dev/vmdump,
+ */
+struct __dump_page {
+	/* the address of this dump page */
+	u64	dp_address;
+
+	/* the size of this dump page */
+	u32	dp_size;
+
+	/* flags (currently DUMP_COMPRESSED, DUMP_RAW or DUMP_END) */
+	u32	dp_flags;
+} __attribute__((packed));
+
+/*
+ * Structure: __lkcdinfo
+ * Function:  This structure contains information needed for the lkcdutils
+ *            package (particularly lcrash) to determine what information is
+ *            associated to this kernel, specifically.
+ */
+struct __lkcdinfo {
+	int	arch;
+	int	ptrsz;
+	int	byte_order;
+	int	linux_release;
+	int	page_shift;
+	int	page_size;
+	u64	page_mask;
+	u64	page_offset;
+	int	stack_offset;
+};
+
+#ifdef __KERNEL__
+
+/*
+ * Structure: __dump_compress
+ *  Function: This is what an individual compression mechanism can use
+ *            to plug in their own compression techniques.  It's always
+ *            best to build these as individual modules so that people
+ *            can put in whatever they want.
+ */
+struct __dump_compress {
+	/* the list_head structure for list storage */
+	struct list_head list;
+
+	/* the type of compression to use (DUMP_COMPRESS_XXX) */
+	int compress_type;
+	const char *compress_name;
+
+	/* the compression function to call */
+	u16 (*compress_func)(const u8 *, u16, u8 *, u16);
+};
+
+/* functions for dump compression registration */
+extern void dump_register_compression(struct __dump_compress *);
+extern void dump_unregister_compression(int);
+
+/*
+ * Structure dump_mbank[]:
+ *
+ * For CONFIG_DISCONTIGMEM systems this array specifies the
+ * memory banks/chunks that need to be dumped after a panic.
+ *
+ * For classic systems it specifies a single set of pages from
+ * 0 to max_mapnr.
+ */
+struct __dump_mbank {
+	u64 	start;
+	u64 	end;
+	int	type;
+	int	pad1;
+	long	pad2;
+};
+
+#define DUMP_MBANK_TYPE_CONVENTIONAL_MEMORY		1
+#define DUMP_MBANK_TYPE_OTHER				2
+
+#define MAXCHUNKS 256
+extern int dump_mbanks;
+extern struct __dump_mbank dump_mbank[MAXCHUNKS];
+
+/* notification event codes */
+#define DUMP_BEGIN		0x0001	/* dump beginning */
+#define DUMP_END		0x0002	/* dump ending */
+
+/* Scheduler soft spin control.
+ *
+ * 0 - no dump in progress
+ * 1 - cpu0 is dumping, ...
+ */
+extern unsigned long dump_oncpu;
+extern void dump_execute(const char *, const struct pt_regs *);
+
+/*
+ *	Notifier list for kernel code which wants to be called
+ *	at kernel dump. 
+ */
+extern struct notifier_block *dump_notifier_list;
+static inline int register_dump_notifier(struct notifier_block *nb)
+{
+	return notifier_chain_register(&dump_notifier_list, nb);
+}
+static inline int unregister_dump_notifier(struct notifier_block * nb)
+{
+	return notifier_chain_unregister(&dump_notifier_list, nb);
+}
+
+/*
+ * Common Arch Specific Functions should be declared here.
+ * This allows the C compiler to detect discrepancies.
+ */
+extern void	__dump_open(void);
+extern void	__dump_cleanup(void);
+extern void	__dump_init(u64);
+extern void	__dump_save_regs(struct pt_regs *, const struct pt_regs *);
+extern int	__dump_configure_header(const struct pt_regs *);
+extern void	__dump_irq_enable(void);
+extern void	__dump_irq_restore(void);
+extern int	__dump_page_valid(unsigned long index);
+#ifdef CONFIG_SMP
+extern void 	__dump_save_other_cpus(void);
+#else
+#define 	__dump_save_other_cpus(void)
+#endif
+
+#endif /* __KERNEL__ */
+
+#else	/* !CONFIG_CRASH_DUMP */
+
+/* If not configured then make code disappear! */
+#define register_dump_watchdog(x) 	do { } while(0)
+#define unregister_dump_watchdog(x) 	do { } while(0)
+#define register_dump_notifier(x)	do { } while(0)
+#define unregister_dump_notifier(x)	do { } while(0)
+#define dump_in_progress() 		0
+
+#endif	/* !CONFIG_CRASH_DUMP */
+
+#endif /* _DUMP_H */
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/include/linux/dump_netdev.h 240-lkcd/include/linux/dump_netdev.h
--- 231-thread_under_page/include/linux/dump_netdev.h	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/include/linux/dump_netdev.h	Fri May 30 19:24:56 2003
@@ -0,0 +1,80 @@
+/*
+ *  linux/drivers/net/netconsole.h
+ *
+ *  Copyright (C) 2001  Ingo Molnar <mingo@redhat.com>
+ *
+ *  This file contains the implementation of an IRQ-safe, crash-safe
+ *  kernel console implementation that outputs kernel messages to the
+ *  network.
+ *
+ * Modification history:
+ *
+ * 2001-09-17    started by Ingo Molnar.
+ */
+
+/****************************************************************
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2, or (at your option)
+ *      any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+#define NETCONSOLE_VERSION 0x03
+
+enum netdump_commands {
+	COMM_NONE = 0,
+	COMM_SEND_MEM = 1,
+	COMM_EXIT = 2,
+	COMM_REBOOT = 3,
+	COMM_HELLO = 4,
+	COMM_GET_NR_PAGES = 5,
+	COMM_GET_PAGE_SIZE = 6,
+	COMM_START_NETDUMP_ACK = 7,
+	COMM_GET_REGS = 8,
+	COMM_GET_MAGIC = 9,
+	COMM_START_WRITE_NETDUMP_ACK = 10,
+};
+
+typedef struct netdump_req_s {
+	u64 magic;
+	u32 nr;
+	u32 command;
+	u32 from;
+	u32 to;
+} req_t;
+
+enum netdump_replies {
+	REPLY_NONE = 0,
+	REPLY_ERROR = 1,
+	REPLY_LOG = 2,
+	REPLY_MEM = 3,
+	REPLY_RESERVED = 4,
+	REPLY_HELLO = 5,
+	REPLY_NR_PAGES = 6,
+	REPLY_PAGE_SIZE = 7,
+	REPLY_START_NETDUMP = 8,
+	REPLY_END_NETDUMP = 9,
+	REPLY_REGS = 10,
+	REPLY_MAGIC = 11,
+	REPLY_START_WRITE_NETDUMP = 12,
+};
+
+typedef struct netdump_reply_s {
+	u32 nr;
+	u32 code;
+	u32 info;
+} reply_t;
+
+#define HEADER_LEN (1 + sizeof(reply_t))
+
+
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/include/linux/dumpdev.h 240-lkcd/include/linux/dumpdev.h
--- 231-thread_under_page/include/linux/dumpdev.h	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/include/linux/dumpdev.h	Fri May 30 19:24:56 2003
@@ -0,0 +1,123 @@
+/*
+ * Generic dump device interfaces for flexible system dump 
+ * (Enables variation of dump target types e.g disk, network, memory)
+ *
+ * These interfaces have evolved based on discussions on lkcd-devel. 
+ * Eventually the intent is to support primary and secondary or 
+ * alternate targets registered at the same time, with scope for 
+ * situation based failover or multiple dump devices used for parallel 
+ * dump i/o.
+ *
+ * Started: Oct 2002 - Suparna Bhattacharya (suparna@in.ibm.com)
+ *
+ * Copyright (C) 2001 - 2002 Matt D. Robinson.  All rights reserved.
+ * Copyright (C) 2002 International Business Machines Corp. 
+ *
+ * This code is released under version 2 of the GNU GPL.
+ */
+
+#ifndef _LINUX_DUMPDEV_H
+#define _LINUX_DUMPDEV_H
+
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/bio.h>
+
+/* Determined by the dump target (device) type */
+
+struct dump_dev;
+
+struct dump_dev_ops {
+	int (*open)(struct dump_dev *, unsigned long); /* configure */
+	int (*release)(struct dump_dev *); /* unconfigure */
+	int (*silence)(struct dump_dev *); /* when dump starts */
+	int (*resume)(struct dump_dev *); /* when dump is over */
+	int (*seek)(struct dump_dev *, loff_t);
+	/* trigger a write (async in nature typically) */
+	int (*write)(struct dump_dev *, void *, unsigned long);
+	/* not usually used during dump, but option available */
+	int (*read)(struct dump_dev *, void *, unsigned long);
+	/* use to poll for completion */
+	int (*ready)(struct dump_dev *, void *); 
+	int (*ioctl)(struct dump_dev *, unsigned int, unsigned long);
+};
+
+struct dump_dev {
+	char type_name[32]; /* block, net-poll etc */
+	unsigned long device_id; /* interpreted differently for various types */
+	struct dump_dev_ops *ops;
+	struct list_head list;
+	loff_t curr_offset;
+};
+
+/*
+ * dump_dev type variations: 
+ */
+
+/* block */
+struct dump_blockdev {
+	struct dump_dev ddev;
+	kdev_t kdev_id;
+	struct block_device *bdev;
+	struct bio *bio;
+	loff_t start_offset;
+	loff_t limit;
+	int err;
+};
+
+static inline struct dump_blockdev *DUMP_BDEV(struct dump_dev *dev)
+{
+	return container_of(dev, struct dump_blockdev, ddev);
+}
+
+/* Dump device / target operation wrappers */
+/* These assume that dump_dev is initiatized to dump_config.dumper->dev */
+
+extern struct dump_dev *dump_dev;
+
+static inline int dump_dev_open(unsigned long arg)
+{
+	return dump_dev->ops->open(dump_dev, arg);
+}
+
+static inline int dump_dev_release(void)
+{
+	return dump_dev->ops->release(dump_dev);
+}
+
+static inline int dump_dev_silence(void)
+{
+	return dump_dev->ops->silence(dump_dev);
+}
+
+static inline int dump_dev_resume(void)
+{
+	return dump_dev->ops->resume(dump_dev);
+}
+
+static inline int dump_dev_seek(loff_t offset)
+{
+	return dump_dev->ops->seek(dump_dev, offset);
+}
+
+static inline int dump_dev_write(void *buf, unsigned long len)
+{
+	return dump_dev->ops->write(dump_dev, buf, len);
+}
+
+static inline int dump_dev_ready(void *buf)
+{
+	return dump_dev->ops->ready(dump_dev, buf);
+}
+
+static inline int dump_dev_ioctl(unsigned int cmd, unsigned long arg)
+{
+	if (!dump_dev->ops->ioctl)
+		return -EINVAL;
+	return dump_dev->ops->ioctl(dump_dev, cmd, arg);
+}
+
+extern int dump_register_device(struct dump_dev *);
+extern void dump_unregister_device(struct dump_dev *);
+
+#endif /*  _LINUX_DUMPDEV_H */
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/include/linux/major.h 240-lkcd/include/linux/major.h
--- 231-thread_under_page/include/linux/major.h	Fri May 30 19:02:22 2003
+++ 240-lkcd/include/linux/major.h	Fri May 30 19:24:56 2003
@@ -157,6 +157,8 @@
 
 #define OSST_MAJOR		206	/* OnStream-SCx0 SCSI tape */
 
+#define CRASH_DUMP_MAJOR	221	/* crash dump interface */
+
 #define IBM_TTY3270_MAJOR	227
 #define IBM_FS3270_MAJOR	228
 
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/include/linux/netdevice.h 240-lkcd/include/linux/netdevice.h
--- 231-thread_under_page/include/linux/netdevice.h	Fri May 30 19:02:23 2003
+++ 240-lkcd/include/linux/netdevice.h	Fri May 30 19:24:56 2003
@@ -426,6 +426,9 @@ struct net_device
 						     unsigned char *haddr);
 	int			(*neigh_setup)(struct net_device *dev, struct neigh_parms *);
 	int			(*accept_fastpath)(struct net_device *, struct dst_entry*);
+#define HAVE_POLL_CONTROLLER
+ 	void			(*poll_controller)(struct net_device *dev);
+	int			(*rx_hook)(struct sk_buff *skb);
 
 	/* bridge stuff */
 	struct net_bridge_port	*br_port;
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/include/linux/sysctl.h 240-lkcd/include/linux/sysctl.h
--- 231-thread_under_page/include/linux/sysctl.h	Fri May 30 19:05:11 2003
+++ 240-lkcd/include/linux/sysctl.h	Fri May 30 19:24:56 2003
@@ -131,6 +131,7 @@ enum
 	KERN_PIDMAX=55,		/* int: PID # limit */
   	KERN_CORE_PATTERN=56,	/* string: pattern for core-file names */
 	KERN_PANIC_ON_OOPS=57,  /* int: whether we will panic on an oops */
+	KERN_DUMP=60,           /* directory: dump parameters */
 };
 
 
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/init/Makefile 240-lkcd/init/Makefile
--- 231-thread_under_page/init/Makefile	Sat May 10 18:35:04 2003
+++ 240-lkcd/init/Makefile	Fri May 30 19:24:56 2003
@@ -1,6 +1,10 @@
 #
 # Makefile for the linux kernel.
 #
+ifdef CONFIG_CRASH_DUMP
+EXTRA_TARGETS := kerntypes.o
+CFLAGS_kerntypes.o := -gstabs
+endif
 
 obj-y				:= main.o version.o mounts.o initramfs.o
 mounts-y			:= do_mounts.o
@@ -24,3 +28,4 @@ $(obj)/version.o: include/linux/compile.
 include/linux/compile.h: FORCE
 	@echo '  CHK     $@'
 	@sh $(srctree)/scripts/mkcompile_h $@ "$(UTS_MACHINE)" "$(CONFIG_SMP)" "$(CC) $(CFLAGS)"
+
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/init/kerntypes.c 240-lkcd/init/kerntypes.c
--- 231-thread_under_page/init/kerntypes.c	Wed Dec 31 16:00:00 1969
+++ 240-lkcd/init/kerntypes.c	Fri May 30 19:24:56 2003
@@ -0,0 +1,24 @@
+/*
+ * kerntypes.c
+ *
+ * Copyright (C) 2000 Tom Morano (tjm@sgi.com) and
+ *                    Matt D. Robinson (yakker@alacritech.com)
+ *
+ * Dummy module that includes headers for all kernel types of interest. 
+ * The kernel type information is used by the lcrash utility when 
+ * analyzing system crash dumps or the live system. Using the type 
+ * information for the running system, rather than kernel header files,
+ * makes for a more flexible and robust analysis tool.
+ *
+ * This source code is released under version 2 of the GNU GPL.
+ */
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/config.h>
+#include <linux/utsname.h>
+#include <linux/dump.h>
+
+void
+kerntypes_dummy(void)
+{
+}
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/init/main.c 240-lkcd/init/main.c
--- 231-thread_under_page/init/main.c	Fri May 30 19:13:53 2003
+++ 240-lkcd/init/main.c	Fri May 30 19:24:56 2003
@@ -105,6 +105,16 @@ extern void ipc_init(void);
 int system_running = 0;
 
 /*
+ * The kernel_magic value represents the address of _end, which allows
+ * namelist tools to "match" each other respectively.  That way a tool
+ * that looks at /dev/mem can verify that it is using the right System.map
+ * file -- if kernel_magic doesn't equal the namelist value of _end,
+ * something's wrong.
+ */
+extern unsigned long _end;
+unsigned long *kernel_magic = &_end;
+
+/*
  * Boot command-line arguments
  */
 #define MAX_INIT_ARGS 8
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/kernel/ksyms.c 240-lkcd/kernel/ksyms.c
--- 231-thread_under_page/kernel/ksyms.c	Fri May 30 19:13:53 2003
+++ 240-lkcd/kernel/ksyms.c	Fri May 30 19:24:56 2003
@@ -58,6 +58,8 @@
 #include <linux/time.h>
 #include <linux/backing-dev.h>
 #include <linux/percpu_counter.h>
+#include <linux/dump.h>
+#include <linux/bootmem.h>
 #include <asm/checksum.h>
 
 #if defined(CONFIG_PROC_FS)
@@ -609,3 +611,8 @@ EXPORT_SYMBOL(dump_stack);
 EXPORT_SYMBOL(ptrace_notify);
 
 EXPORT_SYMBOL(current_kernel_time); 
+
+#ifdef CONFIG_CRASH_DUMP_MODULE
+EXPORT_SYMBOL(min_low_pfn);
+EXPORT_SYMBOL(dump_oncpu);
+#endif
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/kernel/panic.c 240-lkcd/kernel/panic.c
--- 231-thread_under_page/kernel/panic.c	Fri May 30 19:02:24 2003
+++ 240-lkcd/kernel/panic.c	Fri May 30 19:24:56 2003
@@ -54,6 +54,7 @@ NORET_TYPE void panic(const char * fmt, 
 	va_start(args, fmt);
 	vsnprintf(buf, sizeof(buf), fmt, args);
 	va_end(args);
+
 	printk(KERN_EMERG "Kernel panic: %s\n",buf);
 	if (in_interrupt())
 		printk(KERN_EMERG "In interrupt handler - not syncing\n");
@@ -63,14 +64,13 @@ NORET_TYPE void panic(const char * fmt, 
 		sys_sync();
 	bust_spinlocks(0);
 
+	notifier_call_chain(&panic_notifier_list, 0, buf);
+
 #ifdef CONFIG_SMP
 	smp_send_stop();
 #endif
 
-       notifier_call_chain(&panic_notifier_list, 0, buf);
-
-	if (panic_timeout > 0)
-	{
+	if (panic_timeout > 0) {
 		/*
 	 	 * Delay timeout seconds before rebooting the machine. 
 		 * We can't use the "normal" timers since we just panicked..
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/kernel/sched.c 240-lkcd/kernel/sched.c
--- 231-thread_under_page/kernel/sched.c	Fri May 30 19:13:53 2003
+++ 240-lkcd/kernel/sched.c	Fri May 30 19:24:56 2003
@@ -39,6 +39,9 @@
 #define cpu_to_node_mask(cpu) (cpu_online_map)
 #endif
 
+/* used to soft spin in sched while dump is in progress */
+int dump_oncpu;
+
 /*
  * Convert user-nice values [ -20 ... 0 ... 19 ]
  * to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ],
@@ -1377,6 +1380,14 @@ asmlinkage void do_schedule(void)
 	struct list_head *queue;
 	int idx, mycpu = smp_processor_id();
 
+	/* 
+	 * If crash dump is in progress, this other cpu's 
+	 * need to wait until it completes.
+	 * NB: this code is optimized away for kernels without dumping enabled.
+	 */
+	if (unlikely(dump_oncpu))
+		goto dump_scheduling_disabled;
+
 	/*
 	 * Test if we are atomic.  Since do_exit() needs to call into
 	 * schedule() atomically, we ignore that path for now.
@@ -1469,6 +1480,16 @@ switch_tasks:
 	preempt_enable_no_resched();
 	if (test_thread_flag(TIF_NEED_RESCHED))
 		goto need_resched;
+
+	return;
+
+ dump_scheduling_disabled:
+	/* allow scheduling only if this is the dumping cpu */
+	if (dump_oncpu != smp_processor_id()+1) {
+		while (dump_oncpu)
+			cpu_relax();
+	}
+	return;
 }
 
 #ifdef CONFIG_PREEMPT
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/lib/Kconfig 240-lkcd/lib/Kconfig
--- 231-thread_under_page/lib/Kconfig	Tue Apr  8 14:38:21 2003
+++ 240-lkcd/lib/Kconfig	Fri May 30 19:24:56 2003
@@ -17,14 +17,14 @@ config CRC32
 #
 config ZLIB_INFLATE
 	tristate
-	default y if CRAMFS=y || PPP_DEFLATE=y || JFFS2_FS=y || ZISOFS_FS=y || BINFMT_ZFLAT=y || CRYPTO_DEFLATE=y
-	default m if CRAMFS=m || PPP_DEFLATE=m || JFFS2_FS=m || ZISOFS_FS=m || BINFMT_ZFLAT=m || CRYPTO_DEFLATE=m
+	default y if CRAMFS=y || PPP_DEFLATE=y || JFFS2_FS=y || ZISOFS_FS=y || BINFMT_ZFLAT=y || CRYPTO_DEFLATE=y || CRASH_DUMP_COMPRESS_GZIP=y
+	default m if CRAMFS=m || PPP_DEFLATE=m || JFFS2_FS=m || ZISOFS_FS=m || BINFMT_ZFLAT=m || CRYPTO_DEFLATE=m || CRASH_DUMP_COMPRESS_GZIP=m
 
 config ZLIB_DEFLATE
 	tristate
 	default m if PPP_DEFLATE!=y && JFFS2_FS!=y && CRYPTO_DEFLATE!=y && \
-		(PPP_DEFLATE=m || JFFS2_FS=m || CRYPTO_DEFLATE=m)
-	default y if PPP_DEFLATE=y || JFFS2_FS=y || CRYPTO_DEFLATE=y
+		(PPP_DEFLATE=m || JFFS2_FS=m || CRYPTO_DEFLATE=m) || CRASH_DUMP_COMPRESS_GZIP=m
+	default y if PPP_DEFLATE=y || JFFS2_FS=y || CRYPTO_DEFLATE=y || CRASH_DUMP_COMPRESS_GZIP=y
 
 endmenu
 
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/mm/page_alloc.c 240-lkcd/mm/page_alloc.c
--- 231-thread_under_page/mm/page_alloc.c	Fri May 30 19:07:08 2003
+++ 240-lkcd/mm/page_alloc.c	Fri May 30 19:24:56 2003
@@ -85,7 +85,8 @@ static void bad_page(const char *functio
 	page->mapping = NULL;
 }
 
-#ifndef CONFIG_HUGETLB_PAGE
+#if !defined(CONFIG_HUGETLB_PAGE) && !defined(CONFIG_CRASH_DUMP) \
+	&& !defined(CONFIG_CRASH_DUMP_MODULE)
 #define prep_compound_page(page, order) do { } while (0)
 #define destroy_compound_page(page, order) do { } while (0)
 #else
@@ -267,6 +268,7 @@ void __free_pages_ok(struct page *page, 
 
 	mod_page_state(pgfree, 1 << order);
 	free_pages_check(__FUNCTION__, page);
+
 	list_add(&page->list, &list);
 	free_pages_bulk(page_zone(page), 1, &list, order);
 }
@@ -445,6 +447,7 @@ static void free_hot_cold_page(struct pa
 
 	inc_page_state(pgfree);
 	free_pages_check(__FUNCTION__, page);
+
 	pcp = &zone->pageset[get_cpu()].pcp[cold];
 	local_irq_save(flags);
 	if (pcp->count >= pcp->high)
diff -urpN -X /home/fletch/.diff.exclude 231-thread_under_page/net/core/dev.c 240-lkcd/net/core/dev.c
--- 231-thread_under_page/net/core/dev.c	Fri May 30 19:02:24 2003
+++ 240-lkcd/net/core/dev.c	Fri May 30 19:24:56 2003
@@ -1296,8 +1296,6 @@ int netif_rx(struct sk_buff *skb)
 	struct softnet_data *queue;
 	unsigned long flags;
 
-	if (!skb->stamp.tv_sec)
-		do_gettimeofday(&skb->stamp);
 
 	/*
 	 * The code is rearranged so that the path is the most
@@ -1307,6 +1305,13 @@ int netif_rx(struct sk_buff *skb)
 	this_cpu = smp_processor_id();
 	queue = &softnet_data[this_cpu];
 
+	if (skb->dev->rx_hook)
+		goto rx_hook;
+rx_hook_continue:
+
+	if (!skb->stamp.tv_sec )
+		do_gettimeofday(&skb->stamp);
+
 	netdev_rx_stat[this_cpu].total++;
 	if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
 		if (queue->input_pkt_queue.qlen) {
@@ -1349,6 +1354,15 @@ drop:
 
 	kfree_skb(skb);
 	return NET_RX_DROP;
+rx_hook:
+	{
+		int ret;
+
+		ret = skb->dev->rx_hook(skb);
+		if (ret == NET_RX_DROP)
+			goto drop;
+		goto rx_hook_continue;
+	}
 }
 
 /* Deliver skb to an old protocol, which is not threaded well