linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] 2.5.44: lkcd (9/9): dump driver and build files
@ 2002-10-21 10:16 Matt D. Robinson
  2002-10-21 21:21 ` Christoph Hellwig
  0 siblings, 1 reply; 16+ messages in thread
From: Matt D. Robinson @ 2002-10-21 10:16 UTC (permalink / raw)
  To: linux-kernel, yakker

This is the complete set of dump drivers for creating crash dumps
during panic/exception situations in the Linux kernel.  It can be
built as a module or as a built-in with the kernel.

 drivers/Makefile             |    1
 drivers/dump/Makefile        |   30
 drivers/dump/dump_base.c     | 1860 +++++++++++++++++++++++++++++++++++++++++++
 drivers/dump/dump_blockdev.c |  392 +++++++++
 drivers/dump/dump_i386.c     |  315 +++++++
 include/asm-i386/dump.h      |   94 ++
 include/linux/dump.h         |  438 ++++++++++

diff -Naur linux-2.5.44.orig/drivers/Makefile linux-2.5.44.lkcd/drivers/Makefile
--- linux-2.5.44.orig/drivers/Makefile	Fri Oct 18 21:02:28 2002
+++ linux-2.5.44.lkcd/drivers/Makefile	Sat Oct 19 12:39:15 2002
@@ -41,5 +41,6 @@
 obj-$(CONFIG_BT)		+= bluetooth/
 obj-$(CONFIG_HOTPLUG_PCI)	+= hotplug/
 obj-$(CONFIG_ISDN_BOOL)		+= isdn/
+obj-$(CONFIG_CRASH_DUMP)	+= dump/
 
 include $(TOPDIR)/Rules.make
diff -Naur linux-2.5.44.orig/drivers/dump/Makefile linux-2.5.44.lkcd/drivers/dump/Makefile
--- linux-2.5.44.orig/drivers/dump/Makefile	Wed Dec 31 16:00:00 1969
+++ linux-2.5.44.lkcd/drivers/dump/Makefile	Sat Oct 19 12:39:15 2002
@@ -0,0 +1,30 @@
+#
+# Makefile for the dump device drivers.
+#
+# 12 June 2000, Christoph Hellwig <schch@pe.tu-clausthal.de>
+# Rewritten by Matt D. Robinson (yakker@sourceforge.net) for
+# the dump directory.
+#
+export-objs				:= dump_base.o
+
+obj-$(CONFIG_CRASH_DUMP)		+= dump.o
+
+dump-y					:= dump_base.o
+obj-$(CONFIG_CRASH_DUMP_BLOCKDEV)	+= dump_blockdev.o
+obj-$(CONFIG_CRASH_DUMP_COMPRESS_RLE)	+= dump_rle.o
+obj-$(CONFIG_CRASH_DUMP_COMPRESS_GZIP)	+= dump_gzip.o
+dump-objs				+= $(dump-y)
+
+ifeq ($(ARCH),i386)
+dump-objs				+= dump_i386.o
+endif
+
+ifeq ($(ARCH),alpha)
+dump-objs				+= dump_alpha.o
+endif
+
+ifeq ($(ARCH),ia64)
+dump-objs				+= dump_ia64.o
+endif
+
+include $(TOPDIR)/Rules.make
diff -Naur linux-2.5.44.orig/drivers/dump/dump_base.c linux-2.5.44.lkcd/drivers/dump/dump_base.c
--- linux-2.5.44.orig/drivers/dump/dump_base.c	Wed Dec 31 16:00:00 1969
+++ linux-2.5.44.lkcd/drivers/dump/dump_base.c	Sat Oct 19 12:39:15 2002
@@ -0,0 +1,1860 @@
+/*
+ * Standard kernel functions 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.
+ *
+ * -----------------------------------------------------------------------
+ *
+ * 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.
+ * -----------------------------------------------------------------------
+ *
+ * DUMP IMPLEMENTATION
+ *
+ * Dumps are implemented using a "start at the top and work your way
+ * to the bottom" method.  The starting location of kernel memory is
+ * determined, and each successive page is passed through to the
+ * dump_add_page() function, which determines whether to compress the
+ * page, throw it out, add the page header, etc.  This mechanism is
+ * going to change over time as non-disruptive dumps are created, so
+ * it is best to read through the code (it is commented pretty well),
+ * and let the developers know if it isn't clear enough.  We believe
+ * in well-documented, well-commented code.
+ *
+ * -----------------------------------------------------------------------
+ *
+ * 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 is almost
+ *     always the primary swap partition for disruptive dumps.
+ *
+ * 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.
+ *
+ *      REMIND: How about we change these to 1, 3, 7, 15, 31.
+ *	        If we reserve the bits for individual passes
+ *		it's easer to test things like non-conventional
+ *		memory dump on systems with limited disk space.
+ *		If would be more consistent with dump_level being 
+ *		a bitmask. We might also consider changing the name 
+ *		to 'dump_passes' to make it more clear that the bitmask 
+ *		is not a index.
+ * 
+ *
+ * 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.
+ * 
+ *     1:  Try to keep the system running _after_ we are done
+ *         dumping -- for non-disruptive dumps.  (DUMP_FLAGS_NONDISRUPT)
+ *
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * -----------------------------------------------------------------------
+ *                      H E A D E R   F I L E S
+ * -----------------------------------------------------------------------
+ */
+
+/* header files */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/dump.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/file.h>
+#include <linux/sysctl.h>
+#include <linux/mman.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/utsname.h>
+#include <linux/highmem.h>
+#include <linux/version.h>
+
+#include <asm/hardirq.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+/*
+ * -----------------------------------------------------------------------
+ *                       D E F I N I T I O N S
+ * -----------------------------------------------------------------------
+ */
+/* 
+ * Handle printing of 64-bit values  
+ *
+ * NOTE: on ia64 %llx is recommended for ia32. 
+ *       on RedHat 7.2 	%llx work in user space but not in the kernel.
+ *	 Perhaps this is dependent on the kernel version.
+ */
+#if BITS_PER_LONG == 64
+#define PU64X "%lx"
+#else
+#define PU64X "%Lx"
+#endif
+
+/*
+ * -----------------------------------------------------------------------
+ *                         V A R I A B L E S
+ * -----------------------------------------------------------------------
+ */
+
+/* Dump tunables */
+kdev_t dump_device;                /* the actual kdev_t device number      */
+int dump_level;                    /* the current dump level               */
+int dump_compress;                 /* whether to try to compress each page */
+int dump_flags;                    /* whether to try to compress each page */
+
+/* Dump Notifier Tunables */
+long dump_scheduler_enabled = 0;     /* Default: scheduler is disabled     */
+long dump_interrupts_enabled = 1;    /* Default: interrupts stay enabled   */
+long dump_nondisruptive_enabled = 1; /* Default: non-disruptive enabled    */
+
+/* Other global fields */
+void *dump_page_buf;               /* dump page buffer for memcpy()!       */
+void *dump_page_buf_0;             /* dump page buffer returned by kmalloc */
+dump_header_t dump_header;         /* the primary dump header              */
+dump_header_asm_t dump_header_asm; /* the arch-specific dump header        */
+loff_t dump_fpos;                  /* the offset in the output device      */
+int dump_mbanks;		   /* number of  physical memory banks     */
+dump_mbank_t dump_mbank[MAXCHUNKS];/* describes layout of physical memory  */
+long dump_unreserved_mem = 0;      /* Save Pages even if it isn't reserved */
+long dump_unreferenced_mem = 0;    /* Save Pages even if page_count == 0   */
+long dump_nonconventional_mem = 0; /* Save non-conventional mem (firmware) */
+volatile int dump_started = 0;	   /* Indicated we are about to dump       */
+static struct dump_operations *dump_device_ops = 0;
+
+static int dump_compress_none(char *old, int oldsize, char *new, int newsize);
+
+static dump_compress_t dump_none_compression = {
+	compress_type:	DUMP_COMPRESS_NONE,
+	compress_func:	dump_compress_none,
+};
+
+/* our device operations and functions */
+static int dump_open(struct inode *i, struct file *f);
+static int dump_release(struct inode *i, struct file *f);
+static int dump_ioctl(struct inode *i, struct file *f,
+	unsigned int cmd, unsigned long arg);
+
+static struct file_operations dump_fops = {
+	open:		dump_open,
+	release:	dump_release,
+	ioctl:		dump_ioctl,
+};
+
+/* function pointers and prototypes */
+int (*dump_compress_func)(char *old, int oldsize, char *new, int newsize);
+
+/* proc entries */
+static struct proc_dir_entry *dump_root;    /* /proc/sys/dump root dir     */
+static struct proc_dir_entry *dump_dd;      /* dump_device tunable         */
+static struct proc_dir_entry *dump_cp;      /* dump_compress tunable       */
+static struct proc_dir_entry *dump_l;       /* dump_level tunable          */
+static struct proc_dir_entry *dump_f;       /* dump_flags tunable          */
+
+/* static variables                                                        */
+static int dump_okay = FALSE;      	   /* can we dump out to disk?     */
+static char dpcpage[DUMP_DPC_PAGE_SIZE];  /* buffer used for compression   */
+static unsigned long dump_save_flags;     /* save_flags()/restore_flags()  */
+
+/* used for dump compressors */
+static struct list_head dump_compress_list = LIST_HEAD_INIT(dump_compress_list);
+
+/* external variables                                                      */
+extern int panic_timeout;          /* time before reboot                   */
+
+/* lkcd info structure -- this is used by lcrash for basic system data     */
+lkcdinfo_t lkcdinfo = {
+	0,
+	(sizeof(void *) * 8),
+#if defined(__LITTLE_ENDIAN) 
+	__LITTLE_ENDIAN,
+#else
+	__BIG_ENDIAN,
+#endif
+	0,
+	PAGE_SHIFT,
+	PAGE_SIZE,
+	PAGE_MASK,
+	PAGE_OFFSET,
+	0
+};
+
+#if DUMP_DEBUG
+void dump_bp(void) {}
+#endif
+
+/*
+ * -----------------------------------------------------------------------
+ *            / P R O C   T U N A B L E   F U N C T I O N S
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * Name: dump_read_proc()
+ * Func: Read the proc data for dump tunables.
+ */
+static int
+dump_read_proc(char *page, char **start, off_t off,
+	int count, int *eof, void *data)
+{
+	int len;
+	char *out = page;
+	struct proc_dir_entry *p = (struct proc_dir_entry *)data;
+
+
+	if (0 == strcmp(p->name, DUMP_LEVEL_NAME)) {
+		out += sprintf(out, "%d\n", dump_level);
+		len = out - page;
+	} else if (0 == strcmp(p->name, DUMP_FLAGS_NAME)) {
+		out += sprintf(out, "%d\n", dump_flags);
+		len = out - page;
+	} else if (0 == strcmp(p->name, DUMP_COMPRESS_NAME)) {
+		out += sprintf(out, "%d\n", dump_compress);
+		len = out - page;
+	} else if (0 == strcmp(p->name, DUMP_DEVICE_NAME)) {
+		out += sprintf(out, "0x%x\n", kdev_val(dump_device));
+		len = out - page;
+	} else {
+		return (0);
+	}
+	len -= off;
+	if (len < count) {
+		*eof = 1;
+		if (len <= 0) return 0;
+	} else {
+		len = count;
+	}
+	*start = page + off;
+	return (len);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *              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 int
+dump_compress_none(char *old, int oldsize, char *new, int newsize)
+{
+	/* just return the old size */
+	return (oldsize);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *                  U T I L I T Y   F U N C T I O N S
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * Dump Notifier Callbacks:
+ *    Used by disk drivers during the dump notification
+ *    to tune the dump driver. Names and functions are obvious.
+ */
+void dump_scheduler_enable(void)
+{
+	DUMP_DPRINTF("setting dump_scheduler_enabled = 1\n");
+	dump_scheduler_enabled = 1;
+}
+
+void dump_interrupts_disable(void)
+{
+	DUMP_DPRINTF("setting dump_interrupts_enabled = 0\n");
+	dump_interrupts_enabled = 0;
+}
+
+void dump_nondisruptive_disable(void)
+{
+	DUMP_DPRINTF("setting dump_nondisruptive_enabled = 0\n");
+	dump_nondisruptive_enabled = 0;
+}
+
+/*
+ * Name: dump_kernel_write()
+ *
+ * Func: Write out kernel information, check the device limitations,
+ *       block sizes, etc. 
+ * 	 Writes DUMP_BUFFER_SIZE bytes in page buffer
+ *
+ * Returns: number of bytes written or -ERRNO. 
+ *	    At EOF it returns 0.
+ */
+static ssize_t
+dump_kernel_write(int *eof)
+{
+	int err = 0, err1 = 0;
+	loff_t offset=0;
+	
+	if (dump_device_ops == 0)
+		return (-EINVAL);
+
+	if (eof)
+		offset = (loff_t) (*eof);
+
+	/* wait till the dump device is ready to accept IO */
+	err1 = dump_device_ops->dump_ready(0);
+	if (err1 < 0) {
+		DUMP_PRINT("dump device not ready for IO\n");
+		return (err1);
+	}
+
+	err = dump_device_ops->dump_seek(dump_fpos, 0);
+	if (err < 0) {
+		DUMP_PRINT("seek to dump device failed, err %d\n", err);
+		return (-EFAULT);
+	}
+
+	/* write to the dump device */ 
+	err = dump_device_ops->dump_write(dump_page_buf, DUMP_BUFFER_SIZE,
+			&offset);
+	if (eof)
+		*eof = (int) offset;
+	if (err < 0) {
+		DUMP_PRINT("write to dump device failed, err %d\n", err);
+		return (err);
+	}
+
+	/* wait till the dump device is ready to accept next IO */
+	err1 = dump_device_ops->dump_ready(0);
+	if (err1 < 0) {
+		DUMP_PRINT("dump device not ready for IO\n");
+		return (err1);
+	}
+
+	/* err is the transferred bytes -- return those */
+	dump_fpos += err;
+	return (err);
+}
+
+/*
+ * Name: dump_add_end_marker()
+ * Func: add last dump page marker to the end of the dump buffer.
+ */
+static void
+dump_add_end_marker(unsigned long *toffset, int flags)
+{
+	dump_page_t dp;
+
+	/* set the end marker */
+	dp.dp_address = dp.dp_size = 0x0;
+	dp.dp_flags = flags;
+
+#if DUMP_DEBUG >= 6
+	dp.dp_byte_offset = dump_header.dh_num_bytes + DUMP_BUFFER_SIZE + PAGE_SIZE;
+	dp.dp_page_index = dump_header.dh_num_dump_pages;
+#endif
+
+	/* clear the buffer and copy the page header */
+	memcpy((void *)(dump_page_buf + *toffset),
+		(const void *)&dp, sizeof(dump_page_t));
+	*toffset += sizeof(dump_page_t);
+
+#if DUMP_DEBUG >= 6
+	dump_header.dh_num_bytes += sizeof(dump_page_t);
+#endif
+	dump_header.dh_num_dump_pages++;
+
+	return;
+}
+
+static inline int
+kernel_page(struct page *p)
+{
+	if (PageReserved(p) || (!PageLRU(p) && PageInuse(p)))
+		return 1;
+	else
+		return 0;
+}
+
+static inline int
+referenced_page(struct page *p)
+{
+	if (PageInuse(p)) {
+		if (!PageReserved(p) && PageLRU(p))
+			return 1;
+		else
+			return 0;
+	} else
+		return 0;
+}
+
+static inline int
+unreferenced_page(struct page *p)
+{
+	if (!PageInuse(p)) {
+		if (!PageReserved(p))
+			return 1;
+		else
+			return 0;
+	} else
+		return 0;
+}
+
+long dump_add_page_debug = 0;
+long dump_add_page_test_pattern = 0;
+
+/*
+ * Name: dump_add_page()
+ * Func: Add a hardware page to the dump buffer.
+ *
+ * NB: page is a DUMP_PAGE_SIZE page, 
+ *     not a system page (PAGE_SIZE).
+ * 
+ * REMIND:
+ *	We should likely move some of this to
+ *	arch specific code and/or clean it up.
+ */
+static int
+dump_add_page(unsigned long page_index, unsigned long *toffset, int pass)
+{
+#if defined(CONFIG_X86) || defined(CONFIG_ALPHA)
+	extern int page_is_ram(unsigned long);
+#endif
+	unsigned long size;
+	dump_page_t dp;
+	void *vaddr;
+	u64 paddr = (((u64)page_index) << DUMP_PAGE_SHIFT);
+	struct page *p;
+
+#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_IA64)
+	p = (struct page *) &(mem_map[page_index]);
+
+	/*
+	 * If the address is in highmem, map it before copying to
+	 * the dump buffer.  In essence, the dump buffer is the
+	 * bounce buffer.
+	 */
+#ifdef CONFIG_HIGHMEM
+	if (PageHighMem(p)) {
+		/*
+		 * Since this can be executed from IRQ context,
+		 * reentrance on the same CPU must be avoided:
+		 */
+		vaddr = kmap_atomic(p, KM_USER0);
+	}
+	else
+#endif
+#if defined(CONFIG_X86) || defined(CONFIG_ALPHA)
+	if (!page_is_ram(page_index)) {
+		return (1);
+	}
+	else
+#endif
+	/* low memory */
+	vaddr = page_address(p);
+
+#else /*  CONFIG_DISCONTIGMEM || CONFIG_IA64 */
+	vaddr = __va(paddr);
+	p = virt_to_page(vaddr);
+#endif
+	dp.dp_address = paddr;
+	dp.dp_flags = DUMP_DH_RAW;
+
+#if DUMP_DEBUG >= 6
+	/*
+	 * Helpful when looking at hex dump of /dev/vdump.
+	 * Dump Header         Swap Header
+	 */
+	dp.dp_byte_offset = dump_header.dh_num_bytes + DUMP_BUFFER_SIZE + 
+		DUMP_HEADER_OFFSET;
+	dp.dp_page_index = dump_header.dh_num_dump_pages;
+
+	switch(pass) {
+	    case 1:	break;
+	    case 2:	return(1); /* Already Dumped */
+	    case 3:	return(1); /* Already Dumped */
+	    case 4:	break;
+	}
+#else
+	/*
+ 	 * Selective dump:
+	 *  	Some systems, have huge memories, NUMA for example, where
+	 *	a dump of all of memory isn't likely to fit on a swap partition.
+	 *	This is a simple 1st attempt at ordering the dump so the most 
+	 *	important pages are dumped first.
+	 *
+	 *	pass1: Kernel Pages
+	 *	pass2: Remaining referenced pages
+	 *	pass3: the rest of conventional memory
+	 *	pass4: non-conventional memory,
+	 */
+	switch (pass) {
+	case 1:
+		if (kernel_page(p))
+			break;
+		else
+			return(1);
+	case 2: 
+		if (referenced_page(p))
+			break;
+		else
+			return(1);
+	case 3:
+		if (unreferenced_page(p))
+			break;
+		else
+			return(1);
+	case 4:
+		break;
+
+	}
+#endif
+
+#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_IA64)
+	if (!kern_addr_valid(dp.dp_address)) {
+		/* dump of I/O memory not supported yet */
+		printk(KERN_ALERT "dump_add_page: !kern_addr_valid"
+				"(dp.dp_address: " PU64X "\n", dp.dp_address);
+		return(1);
+	}
+#endif
+
+#if DUMP_DEBUG
+	if (dump_add_page_debug)
+		printk(KERN_ALERT "dump_add_page(page_index:%lx, "
+			"*toffset:%lx):\n", page_index, *toffset);
+#endif
+
+	/*
+	 * Don't compress the page if any part of it overlaps
+	 * with the current stack.
+	 */
+	if ((((unsigned long)vaddr < (unsigned long)current->thread_info + 
+		THREAD_SIZE)  && ((unsigned long)vaddr + DUMP_PAGE_SIZE >
+		(unsigned long)current->thread_info))) {
+			size = DUMP_PAGE_SIZE;
+	}
+	else {
+		size = dump_compress_func((char *)vaddr, DUMP_PAGE_SIZE,
+			(char *)dpcpage, DUMP_DPC_PAGE_SIZE);
+		if (!size) {
+			/* dump compression failed -- default to raw mode */
+			size = DUMP_PAGE_SIZE;
+		}
+		/* set the compressed flag if the page did compress */
+		if (size < DUMP_PAGE_SIZE)
+			dp.dp_flags = DUMP_DH_COMPRESSED;
+	}
+
+	/* set the universal size */
+	dp.dp_size = size;
+
+	/* copy the page header */
+	memcpy((void *)(dump_page_buf + *toffset), (const void *)&dp,
+		sizeof(dump_page_t));
+
+	*toffset += sizeof(dump_page_t);
+
+	if (dp.dp_flags & DUMP_DH_COMPRESSED) {
+		/* copy the compressed page */
+		memcpy((void *)(dump_page_buf + *toffset),
+			(const void *)dpcpage, size);
+	} else {
+		/* copy directly from memory */
+		DUMP_memcpy((void *)(dump_page_buf + *toffset),
+			(const void *)vaddr, size);
+	}
+
+#if DUMP_DEBUG
+	/*
+	 * Write a Test Pattern:
+	 *	Page data is full of bytes with the page number.
+	 */	
+	if (dump_add_page_test_pattern) {
+		unsigned char num_pages = (char) (dump_header.dh_num_dump_pages
+			       	& 0Xff);
+		char *cp = (dump_page_buf + *toffset);
+		int i;
+
+		for (i = 0; i < size; i++)
+			*cp++ = num_pages;	
+	}
+#endif
+
+#ifdef CONFIG_HIGHMEM
+	if (PageHighMem(p)) {
+		/*
+		 * Since this can be executed from IRQ context,
+		 * reentrance on the same CPU must be avoided:
+		 */
+		kunmap_atomic(vaddr, KM_USER0);
+	}
+#endif
+	*toffset += size;
+
+#if DUMP_DEBUG >= 6
+	if (dump_add_page_debug)
+		printk(KERN_ALERT "dump_add_page: *toffset:%lx += size:%lx:\n",
+			*toffset, size);
+
+	dump_header.dh_num_bytes += (size + sizeof(dump_page_t));
+#endif
+	dump_header.dh_num_dump_pages++;
+
+	return (0);
+}
+
+/*
+ * Name: dump_silence_system()
+ * Func: Silence the system, or make CPUs spin, etc.  The intention
+ *       here is to get things to a "quiet" state, so we can dump.
+ */
+void
+dump_silence_system(void)
+{
+	unsigned int stage = 0;
+	int cpu = smp_processor_id();
+
+	if (in_interrupt()) {
+		printk(KERN_ALERT "Dumping from interrupt handler !\n");
+		printk(KERN_ALERT "Uncertain scenario - but will try my best\n");
+		/* 
+		 * Must be an unrelated interrupt, not in the middle of io ! 
+		 * If we've panic'ed in the middle of io we should take 
+		 * another approach 
+		 */
+	}
+	/* see if there's something to do before we re-enable interrupts */
+	(void)__dump_silence_system(stage);
+
+	/* we set this to FALSE so we don't ever re-enter this code! */
+	dump_okay = FALSE;
+	dumping_cpu = cpu;
+
+	local_irq_save(dump_save_flags);
+	local_irq_enable(); /* enable interrupts just in case ... */
+
+	/* now increment the stage and do stuff after interrupts are enabled */
+	stage++;
+	(void)__dump_silence_system(stage);
+
+	/* time to leave */
+	return;
+}
+
+/*
+ * Name: dump_resume_system()
+ * Func: Resume the system state.
+ */
+void
+dump_resume_system(void)
+{
+	unsigned int stage = 0;
+
+	/* let the architectures return their proper system state */
+	(void)__dump_resume_system(stage);
+
+	local_irq_restore(dump_save_flags);
+	dump_in_progress = FALSE;
+	dump_okay = TRUE;
+
+	return;
+}
+
+static void
+dump_speedo(void)
+{
+	static int i = 0;
+
+	switch (++i%4) {
+	case 0:
+		printk("|\b");
+		break;
+	case 1:
+		printk("\\\b");
+		break;
+	case 2:
+		printk("-\b");
+		break;
+	case 3:
+		printk("/\b");
+		break;
+	}
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *                     H E A D E R   F U N C T I O N S
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * Name: dump_configure_header()
+ * Func: Update the header with the appropriate information.
+ */
+static int
+dump_configure_header(char *panic_str, struct pt_regs *regs)
+{
+	struct timeval dh_time;
+	/* make sure the dump header isn't TOO big */
+	if ((sizeof(dump_header_t) +
+		sizeof(dump_header_asm_t)) > DUMP_BUFFER_SIZE) {
+			DUMP_PRINTN("dump_configure_header(): combined "
+				"headers larger than DUMP_BUFFER_SIZE!");
+			return (0);
+	}
+
+	/* 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(dump_header_t);
+	dump_header.dh_page_size = PAGE_SIZE;
+	dump_header.dh_dump_level = dump_level;
+	dump_header.dh_current_task = (unsigned long) current;
+	dump_header.dh_dump_compress = dump_compress;
+	dump_header.dh_dump_flags = dump_flags;
+	dump_header.dh_dump_device = kdev_val(dump_device);
+
+#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;
+
+#ifndef UTSNAME_ENTRY_SZ
+#define UTSNAME_ENTRY_SZ 65
+#endif
+	memcpy((void *)&(dump_header.dh_utsname_sysname), 
+		(const void *)&(system_utsname.sysname), UTSNAME_ENTRY_SZ);
+	memcpy((void *)&(dump_header.dh_utsname_nodename), 
+		(const void *)&(system_utsname.nodename), UTSNAME_ENTRY_SZ);
+	memcpy((void *)&(dump_header.dh_utsname_release), 
+		(const void *)&(system_utsname.release), UTSNAME_ENTRY_SZ);
+	memcpy((void *)&(dump_header.dh_utsname_version), 
+		(const void *)&(system_utsname.version), UTSNAME_ENTRY_SZ);
+	memcpy((void *)&(dump_header.dh_utsname_machine), 
+		(const void *)&(system_utsname.machine), UTSNAME_ENTRY_SZ);
+	memcpy((void *)&(dump_header.dh_utsname_domainname), 
+		(const void *)&(system_utsname.domainname), UTSNAME_ENTRY_SZ);
+
+	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_t);
+
+	/* copy the registers if they are valid */
+	if (regs) {
+		memcpy((void *)&(dump_header_asm.dha_regs),
+			(const void *)regs, sizeof(struct pt_regs));
+	}
+
+	/* configure architecture-specific dump header values */
+	if (!__dump_configure_header(regs))
+		return (0);
+
+	return (1);
+}
+
+/*
+ * Name: dump_write_header()
+ * Func: Write out the dump header.
+ *
+ * Returns:
+ *	-1: failed
+ *	 0: wrote o bytes (truncated)
+ *	+1: wrote header
+ */
+static int
+dump_write_header(void)
+{
+	int state;
+	loff_t toffset;
+
+	/* clear the dump page buffer */
+	memset(dump_page_buf, 0, DUMP_BUFFER_SIZE);
+
+	/* copy the dump header directly into the dump page buffer */
+	memcpy(dump_page_buf, (const void *)&dump_header,
+		sizeof(dump_header_t));
+
+	memcpy((void *)(dump_page_buf + sizeof(dump_header_t)),
+		(const void *)&dump_header_asm, sizeof(dump_header_asm_t));
+
+	/* save our file pointer */
+	toffset = dump_fpos;
+
+	/* 
+	 * ALWAYS write out the dump header at DUMP_HEADER_OFFSET, 
+	 * this is after the longest swap header possibly written by mkswap;
+	 * likely the largest PAGE_SIZE supported by the architecture.
+	 */
+	dump_fpos = DUMP_HEADER_OFFSET;
+
+	/* do the real write here */
+	state = dump_kernel_write(NULL);
+	dump_fpos = toffset;
+	if (state < 0) {
+		DUMP_PRINTF("dump_kernel_write() failed!\n");
+		return (-1);
+	} else if (!state) {
+		/* wrote zero bytes - failed */
+		return (0);
+	}
+	/* Wrote the header - success */
+	return (1);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *                  E X E C U T E   F U N C T I O N S
+ * -----------------------------------------------------------------------
+ */
+
+static int
+dump_write_mbank(int *dump_truncated, int pass, u64 mbank_start,
+		u64 mbank_end, unsigned long *buf_loc)
+{
+	int counter = 0, n_bytes = 0;
+	u64 mem_loc;
+
+	for (mem_loc = mbank_start; mem_loc < mbank_end;
+			mem_loc += DUMP_PAGE_SIZE) {
+  
+		/* add the page (if it's real RAM) */
+		if (dump_add_page((mem_loc >> DUMP_PAGE_SHIFT), 
+			buf_loc, pass)) {
+			/* didn't add a page to buffer */
+			continue;
+		}
+
+		/* see if we've filled the buffer */
+		if (*buf_loc >= DUMP_BUFFER_SIZE) {
+			int near_eof = 0;
+
+			/* write out the dump page buffer */
+			n_bytes = dump_kernel_write(&near_eof);
+			if (n_bytes < 0) {
+				DUMP_PRINTN("Write of dump pages failed!");
+				return(-1);
+			} else if (n_bytes == 0) {
+				DUMP_PRINTN("EOF on Write of dump pages; "
+						"dump terminated!");
+				return(0);
+			}
+			if (near_eof) {
+				DUMP_PRINTN("Near EOF on Write of dump pages; "
+						"truncating dump,");
+				*dump_truncated = 1;
+			}
+
+			/* bump the counter for writing out the header */
+			counter++;
+
+		/*
+	 	* Update the header every once in a while -- this
+		 * _must_ be done before we write the overflow end of
+		 * the dump_page_buf into the top of dump_page_buf,
+		 * as dump_write_header() uses dump_page_buf to
+		 * write the default/asm dump headers.  After we are
+		 * done updating the header, _then_ we can move the
+		 * leftover dump data into the top of dump_page_buf.
+		 *
+		 * NOTE: a period is printed to indicate that the header
+		 *	 was updated. Speedo shows page outactivity.
+		 */
+			if ((counter & 0x3f) == 0) {
+				n_bytes = dump_write_header();
+				if (n_bytes < 0) {
+					DUMP_PRINTN("Dump header update "
+							"failed!");
+					return (-1);
+				} else if (n_bytes == 0) {
+					DUMP_PRINTN("Dump header update "
+							"failed; bizarre");
+					return (0);
+				}
+				DUMP_PRINT(".");
+			} else {
+				if ((counter & 0x07) == 0)
+					dump_speedo();
+			}
+
+			/* clear the dump page buffer */
+			memset(dump_page_buf, 0, DUMP_BUFFER_SIZE);
+
+			/* adjust leftover data back to the top of the page */
+			if (*buf_loc > DUMP_BUFFER_SIZE) {
+				/* copy the dump page buffer remnants */
+				memcpy((void *)dump_page_buf,
+					(const void *)(dump_page_buf +
+						DUMP_BUFFER_SIZE),
+					*buf_loc - DUMP_BUFFER_SIZE);
+
+				/* set the new buffer location */
+				*buf_loc -= DUMP_BUFFER_SIZE;
+			} else {
+				/* reset the buffer location counter */
+				*buf_loc = 0;
+			}
+		}
+		if (*dump_truncated) {
+			break;
+		}
+	}
+	DUMP_PRINT(" ");	
+	return 1;
+}
+
+/*
+ * Name: dump_execute_memdump()
+ * Func: Perform the actual memory dump.  This walks through the
+ *       memory pages and dumps the data to disk (using other functions).
+ *
+ * Returns:
+ *	-1: failed
+ *	 0: dump truncated	
+ *	 1: success
+ */
+static int
+dump_execute_memdump(void)
+{
+	int n_bytes = 0, i, retval = 1;
+	unsigned long buf_loc;
+	int dump_truncated = 0;
+	int pass = 0;
+
+	DUMP_PRINTN("Compression value is 0x%x, Writing dump header ",
+			dump_compress);
+
+	/* update the header to disk the first time */
+	n_bytes = dump_write_header();
+	DUMP_PRINT("\n");
+
+	if (n_bytes < 0) {
+		DUMP_PRINTF("Initial dump header update failed!\n");
+		return (-1);
+	} else if (n_bytes == 0) {
+		return (0);
+	}
+
+	/* if we only want the header, return */
+	if (dump_level & DUMP_LEVEL_HEADER)
+		return (1);
+
+	/* set beginning offset to keep compats right with swap devices */
+	dump_fpos = DUMP_HEADER_OFFSET + DUMP_BUFFER_SIZE;
+
+	/* clear the dump page buffer */
+	memset(dump_page_buf, 0, DUMP_BUFFER_SIZE);
+
+	/* set the buffer location */
+	buf_loc = 0;
+
+	for (pass = 1; pass <= 4; pass++) {
+		if ((pass == 2) && dump_unreserved_mem == 0)
+			 continue;
+		if ((pass == 3) && dump_unreferenced_mem == 0)
+			 continue;
+		if (pass == 4) { 
+			if (dump_nonconventional_mem == 0) break;
+			/* Contiguous Mem Sys have 1 mbank */
+			if (dump_mbanks == 1) break;
+		} 
+		/* Algorithm suggested by Jack Steiner <steiner@sgi.com */
+		switch(pass) {
+			case 1:	DUMP_PRINTN("Pass 1: Saving Kernel Pages: ");
+				break;
+			case 2:	DUMP_PRINTN("Pass 2: Saving Remaining "
+						"Referenced Pages: ");
+				break;
+			case 3:	DUMP_PRINTN("Pass 3: Saving Remaining "
+						"Unreferenced Pages: ");
+				break;
+			case 4:	DUMP_PRINTN("Pass 4: Saving Unconventional "
+						"Memory: ");
+				break;
+		}
+		for (i = 0; i < dump_mbanks; i++) {
+			u64 mem_bank_start = dump_mbank[i].start;
+			u64 mem_bank_end = dump_mbank[i].end;
+			int type = dump_mbank[i].type;
+
+			if ((type != DUMP_MBANK_TYPE_CONVENTIONAL_MEMORY) &&
+				  (pass != 4)) continue;
+			if ((type == DUMP_MBANK_TYPE_CONVENTIONAL_MEMORY) &&
+				  (pass == 4)) continue;
+	
+			DUMP_PRINTN("Memory Bank[%d]: " PU64X " ... " PU64X ": "
+					,i, mem_bank_start, mem_bank_end);
+
+			if ((retval = dump_write_mbank(&dump_truncated, pass,
+				mem_bank_start, mem_bank_end, &buf_loc)) <= 0)
+				return retval;
+			if (dump_truncated)
+				break;
+		}
+		if (dump_truncated)
+			break;
+		DUMP_PRINT("\n");
+	}
+
+	/* 
+	 * we have written out most of the dump pages, a few may still be in the
+	 * page buffer and it's possible we had to truncate the dump because we
+	 * got very close to the end of the dump partition. Now we add a EOF
+	 * marker to the page out buffer and flush out the remaining pages.
+	 *
+	 * We need to write the DUMP_DH_END even for truncated data because
+	 * the reader won't get an EOF if he reads the data in smaller chunks
+	 * than the DUMP_BUFFER_SIZE it's written in.
+	 */
+	if (dump_truncated)
+		dump_add_end_marker(&buf_loc, (DUMP_DH_END | DUMP_DH_TRUNCATED));
+	else
+		dump_add_end_marker(&buf_loc, DUMP_DH_END);
+
+	n_bytes = dump_kernel_write(NULL);
+
+	if (n_bytes < 0) {
+                DUMP_PRINTN("Final write of last of page buffer and "
+				"DUMP_DH_END failed!");
+                return (-1);
+        } else if (n_bytes == 0) {
+		DUMP_PRINTN("Hit EOF writing DUMP_DH_END; bad luck\n");
+                return (0);
+        }
+
+	/*
+	 * Writing out DUMP_DH_END may have pushed us past the end of the first
+	 * part of the page buffer, if it did we have to write out one last 
+	 * DUMP_BUFFER with the spill over page.
+	 */
+	if (buf_loc > DUMP_BUFFER_SIZE) {
+		/* clear the first part of the buffer */
+		memset(dump_page_buf, 0, DUMP_BUFFER_SIZE);
+
+		/*
+		 * Copy the dump page buffer remnants in the second 
+		 * part of the buffer to the first part.
+		 */
+		memcpy((void *)dump_page_buf,
+			(const void *)(dump_page_buf + DUMP_BUFFER_SIZE),
+			buf_loc - DUMP_BUFFER_SIZE);
+
+		n_bytes = dump_kernel_write(NULL);
+		if (n_bytes < 0) {
+			DUMP_PRINTN("Final write of spillover page failed!");
+			return (-1);
+		} else if (n_bytes == 0) {
+			DUMP_PRINTN("Hit EOF writing spill over page with "
+					"DUMP_DH_END; very bad luck!\n");
+			return (0);
+		}
+	}
+	if (dump_truncated)
+		return(0);
+
+	/* success */
+	return (1);
+}
+
+/*
+ * 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.
+ * 
+ * if( dump_flags & DUMP_FLAGS_NONDISRUPT ) {
+ *    Returns:
+ *      -1: failed
+ *       0: dump truncated
+ *       1: success
+ * }
+ */
+int
+dump_execute(char *panic_str, struct pt_regs *regs)
+{
+	int state = 1;
+	int scheduler_disabled = 1;
+
+	dump_started = 1;
+
+	/* make sure we can dump */
+	if (dump_okay == FALSE)
+		return(-1);
+
+	/* 
+	 * we can delay this till arch-specific header configuration
+	 * where we use NMI ipi to mark the current process
+	 * for rescheduling and schedular is disabled based on this value
+	 * However setting this early here only...
+	 */
+	dump_in_progress = TRUE;
+
+	if(!dump_configure_header(panic_str, regs)) {
+		DUMP_PRINTN("dump header could not be configured!");
+		dump_in_progress = FALSE;
+		return(-1);
+	}
+
+	/* silence the system */
+	dump_silence_system();
+
+	/* tell interested parties that a dump is about to start */
+	if (dump_flags & DUMP_FLAGS_NONDISRUPT) {
+		notifier_call_chain(&dump_notifier_list, DUMP_BEGIN_NONDISRUPT,
+				&dump_device);
+	} else {
+		notifier_call_chain(&dump_notifier_list, DUMP_BEGIN,
+				&dump_device);
+	}
+
+	/*
+	 * if dump device notifier indicated the scheduler needs to be 
+	 * enabled then we need to turn it back on.
+	 */
+	if (dump_scheduler_enabled) {
+		dump_resume_system();
+		scheduler_disabled = 0;
+	}
+
+        /*
+	 * Make sure dump device notifier indicated that Non-Disruptive
+	 * Dumps are Supported.
+	 */
+	if (!dump_nondisruptive_enabled && (dump_flags &
+				DUMP_FLAGS_NONDISRUPT) ) {
+		DUMP_PRINTN("Non-Disruptive Dumps are not Supported by "
+			"Dump Device 0x%x\n", kdev_val(dump_device));
+		state = -1;	/* Dump Aborted */
+	} else {
+		/* bail out if we're not going to do any dumping. */
+		if (dump_level != DUMP_LEVEL_NONE) {
+			/* inform users of what we are about to do */
+			DUMP_PRINTN("Dumping to device 0x%x on CPU %d ...",
+				kdev_val(dump_device), smp_processor_id());
+	
+			dump_device_ops->dump_start(0);
+
+			/* start walking through the page tables */
+			state = dump_execute_memdump();
+	
+			DUMP_PRINT("\n");
+	
+			/* update header to disk for the last time */
+			if (dump_write_header() < 0) {
+				DUMP_PRINTF("Final dump header update "
+						"failed!\n");
+			}
+			
+			dump_device_ops->dump_end(0);
+
+			if (state < 0) {
+				DUMP_PRINTF("Dump Failed!\n");
+			} else if (state == 0) {
+				DUMP_PRINTF("Dump Truncated (likely out of "
+						"space).\n");
+			} else {
+				DUMP_PRINTF("Dump Complete; %d dump pages "
+						"saved.\n", 
+					dump_header.dh_num_dump_pages);
+			}
+		}
+	}
+
+	/* tell interested parties that a dump has completed */
+	notifier_call_chain(&dump_notifier_list, DUMP_END, &dump_device);
+
+	/* put the system state back */
+	if (scheduler_disabled)
+		dump_resume_system();
+
+	/* 
+	 * Reboot the system if this is a disrupted dump.
+	 * Non-disruptive dumps have to be set up special 
+	 * swap partition that isn't being used and can be
+	 * used for testing.
+	 */
+	if (!(dump_flags & DUMP_FLAGS_NONDISRUPT)) {
+		int timeout = (panic_timeout > 0) ? panic_timeout : 10;
+
+		DUMP_PRINTF("Dump: Rebooting in %d seconds ...", timeout);
+		mdelay(timeout * 1000);
+		machine_restart(NULL);
+	}
+	dump_started = 0;
+	return (state);
+}
+
+/*
+ * Name: dump_register_compression()
+ * Func: Register a dump compression mechanism.
+ */
+void
+dump_register_compression(dump_compress_t *item)
+{
+	/* let's make sure our list is valid */
+	if (!item)
+		return;
+
+	/* add our item */
+	list_add(&(item->list), &dump_compress_list);
+
+	/* print information to callers */
+	DUMP_PRINTF("Registering dump compression type 0x%x\n",
+			item->compress_type);
+}
+
+/*
+ * 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;
+	dump_compress_t *dc;
+
+	/* let's make sure our list is valid */
+	if (compression_type == DUMP_COMPRESS_NONE) {
+		DUMP_PRINTN("Compression list is invalid!");
+		return;
+	}
+
+	/* try to remove the compression item */
+	list_for_each(tmp, &dump_compress_list) {
+		dc = list_entry(tmp, dump_compress_t, list);
+		if (dc->compress_type == compression_type) {
+			list_del(&(dc->list));
+			DUMP_PRINTN("De-registering dump compression type "
+					"0x%x\n", compression_type);
+			return;
+		}
+	}
+}
+
+/*
+ * Name: dump_compress_init()
+ * Func: Initialize (or re-initialize) compression scheme.
+ */
+static int
+dump_compress_init(int compression_type)
+{
+	struct list_head *tmp;
+	dump_compress_t *dc;
+
+	/* try to remove the compression item */
+	list_for_each(tmp, &dump_compress_list) {
+		dc = list_entry(tmp, dump_compress_t, list);
+		if (dc->compress_type == compression_type) {
+			dump_compress_func = dc->compress_func;
+			dump_compress = compression_type;
+
+			DUMP_PRINTF("dump_compress = %d\n", dump_compress);
+			DUMP_BP();
+			return (0);
+		}
+	}
+
+	/* 
+	 * nothing on the list -- return ENODATA to indicate an error 
+	 *
+	 * NB: 
+	 *	EAGAIN: reports "Resource temporarily unavailable" which
+	 *		isn't very enlightening.
+	 */
+	DUMP_PRINTF("compression_type:%d not found\n", compression_type);
+	DUMP_BP();
+
+	return (-ENODATA);
+}
+
+/*
+ * Name: dump_open_kdev()
+ * Func: Try to open the kdev_t argument as the real dump device.
+ *       This is where all the work is done for setting up the dump
+ *       device.  It's assumed at this point that by passing in the
+ *       dump device's major/minor number, we can open it up, check
+ *       it out, and use it for whatever purposes.
+ */
+static int
+dump_open_kdev(kdev_t tmp_dump_device)
+{
+	unsigned long dump_page_addr;
+	int err;
+
+	if (dump_device_ops == 0) {
+		DUMP_PRINT("dump device driver is not loaded\n");
+		return (-EINVAL);
+	}
+
+	/* if this is the second call to this function, clean up ... */
+	if ((dump_okay == TRUE) && (dump_page_buf_0 != (void *)NULL))
+		kfree((const void *)dump_page_buf_0);
+
+	/* 
+	 * Allocate buffer to be used for copying pages (only once ...) 
+	 *
+	 * An extra:
+	 * 2 * DUMP_PAGE_SIZE:
+	 * is needed for the page overflow of last page, it's page header
+	 * and the EOF page header.
+	 *
+	 * and an  extra:
+	 * 1 * PAGE_SIZE:
+	 * is needed for rounding up the start of the dump_page_buf to
+	 * a system page (PAGE_SIZE) boundry.
+	 * 
+	 */
+	dump_page_buf = dump_page_buf_0 = (void *)kmalloc(DUMP_BUFFER_SIZE + 
+			2 * DUMP_PAGE_SIZE + PAGE_SIZE, GFP_KERNEL);
+
+	if (dump_page_buf_0 == (void *)0) {
+		DUMP_PRINTF("Cannot kmalloc() dump page buffer!\n");
+		dump_okay = FALSE;
+		return (-ENOMEM);
+	}
+
+	/* align the dump page addresses */
+	dump_page_addr = (unsigned long) dump_page_buf;
+	if (dump_page_addr % PAGE_SIZE)
+		dump_page_buf = (void *) PAGE_ALIGN(dump_page_addr);
+
+	/* assign the new dump file structure */
+	dump_device = tmp_dump_device;
+
+	err = dump_device_ops->dump_open();
+	if (err < 0) {
+		dump_okay = FALSE;
+		return (-EINVAL);
+	}
+	err = dump_device_ops->dump_ioctl(DIOSDUMPMEM,
+				(unsigned long)dump_page_buf);
+	if (err < 0) {
+		dump_okay = FALSE;
+		return (-EINVAL);
+	}
+
+	err = dump_device_ops->dump_ioctl(DIOSDUMPDEV,
+			kdev_val(tmp_dump_device));
+	if (err < 0) {
+		dump_okay = FALSE;
+		return (-EINVAL);
+	}
+	dump_okay = TRUE;
+
+	/* after opening the block device, return */
+	return 0;
+}
+
+/*
+ * Name: dump_release()
+ * Func: Release the dump device -- it's never necessary to call
+ *       this function, but it's here regardless.
+ */
+static int
+dump_release(struct inode *i, struct file *f)
+{
+	return (0);
+}
+
+/*
+ * 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);
+
+	/*
+	 * 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) {
+		/* set dump_device */
+		case DIOSDUMPDEV:
+			/* check flags */
+			if (!(f->f_flags & O_RDWR))
+				return (-EPERM);
+
+			__dump_open();
+			return (dump_open_kdev(to_kdev_t((dev_t)arg)));
+
+		/* get dump_device */
+		case DIOGDUMPDEV:
+			return (put_user((long)kdev_val(dump_device), (long *)arg));
+
+		/* set dump_level */
+		case DIOSDUMPLEVEL:
+			/* check flags */
+			if (!(f->f_flags & O_RDWR))
+				return (-EPERM);
+
+			/* make sure we have a positive value */
+			if (arg < 0)
+				return (-EINVAL);
+			dump_level = (int)arg;
+
+			/*
+			 * REMIND: Still in development:
+			 * We will consider reserved pages a initial proxy 
+			 * for kernel pages.
+			 */
+			if (dump_level > DUMP_LEVEL_KERN)
+				dump_unreserved_mem = 1;
+			else
+				dump_unreserved_mem = 0;
+
+			/*
+			 * REMIND: Still in development:
+			 * Using refcount > 1 as a proxy for kernel & user 
+			 * pages.
+			 */
+			if (dump_level > DUMP_LEVEL_USED)
+				dump_unreferenced_mem = 1;
+			else
+				dump_unreferenced_mem = 0;
+
+			/*
+			 *  REMIND: Still in development:
+			 *	This may not be 100% stable on all ia64
+			 *	memory banks. Accessing uncached memory
+			 *	with cached accesses can cause subsequent
+			 *	bus errors when the cache is flushed.
+			 */
+			if (dump_level > DUMP_LEVEL_ALL_RAM)
+				dump_nonconventional_mem = 1;
+			else
+				dump_nonconventional_mem = 0;
+			break;
+
+		/* get dump_level */
+		case DIOGDUMPLEVEL:
+			return (put_user((long)dump_level, (long *)arg));
+
+		/* set dump_flags */
+		case DIOSDUMPFLAGS:
+			/* check flags */
+			if (!(f->f_flags & O_RDWR))
+				return (-EPERM);
+
+			/* make sure we have a positive value */
+			if (arg < 0)
+				return (-EINVAL);
+
+			dump_flags = (int)arg;
+			break;
+
+		/* get dump_flags */
+		case DIOGDUMPFLAGS:
+			return (put_user((long)dump_flags, (long *)arg));
+
+		/* set the dump_compress status */
+		case DIOSDUMPCOMPRESS:
+			/* check flags */
+			if (!(f->f_flags & O_RDWR))
+				return (-EPERM);
+
+			return (dump_compress_init((int)arg));
+
+		/* get the dump_compress status */
+		case DIOGDUMPCOMPRESS:
+			return (put_user((long)dump_compress, (long *)arg));
+
+	}
+	return (0);
+}
+
+/*
+ * Name: dump_open()
+ * Func: Open the dump device for use when the system crashes.
+ */
+static int
+dump_open(struct inode *i, struct file *f)
+{
+	/* opening a device is straightforward -- nothing to do here */
+	return (0);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *                     I N I T   F U N C T I O N S
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * Name: dump_init_proc_entry()
+ * Func: Create a dump proc entry based on the /proc dump root.
+ *       Returns 1 on failure, 0 on success.
+ */
+int
+dump_init_proc_entry(char *name, struct proc_dir_entry *dirent)
+{
+	if (!(dirent = create_proc_entry(name,
+		S_IFREG|S_IRUGO|S_IWUSR, dump_root))) {
+			DUMP_PRINT("unable to initialize "
+				"/proc/%s/%s!\n", DUMP_ROOT_NAME, name);
+		return (1);
+	}
+	dirent->data = (void *)dirent;
+	dirent->read_proc = &dump_read_proc;
+	dirent->write_proc = NULL;
+	dirent->size = 16;
+	return (0);
+}
+
+/*
+ * Name: dump_proc_init()
+ * Func: Initialize the /proc interfaces for dumping.
+ * 
+ * Typically:
+ *	/proc/sys/dump/
+ *		dump_compress  dump_device  dump_flags  dump_level
+ */
+int
+dump_proc_init(void)
+{
+	/* create the proc entries for the various tunables */
+	dump_root = create_proc_entry(DUMP_ROOT_NAME, S_IFDIR, 0);
+	if (dump_root) {
+		dump_root->owner = THIS_MODULE;
+	} else {
+		DUMP_PRINTF("unable to initialize /proc/%s!\n", DUMP_ROOT_NAME);
+		return (-EBUSY);
+	}
+
+	if (dump_init_proc_entry(DUMP_DEVICE_NAME, dump_dd)) {
+		remove_proc_entry(DUMP_ROOT_NAME, 0);
+		return (-EBUSY);
+	}
+	if (dump_init_proc_entry(DUMP_LEVEL_NAME, dump_l)) {
+		remove_proc_entry(DUMP_ROOT_NAME, 0);
+		remove_proc_entry(DUMP_DEVICE_NAME, dump_root);
+		return (-EBUSY);
+	}
+	if (dump_init_proc_entry(DUMP_FLAGS_NAME, dump_f)) {
+		remove_proc_entry(DUMP_ROOT_NAME, 0);
+		remove_proc_entry(DUMP_DEVICE_NAME, dump_root);
+		remove_proc_entry(DUMP_LEVEL_NAME, dump_root);
+		return (-EBUSY);
+	}
+	if (dump_init_proc_entry(DUMP_COMPRESS_NAME, dump_cp)) {
+		remove_proc_entry(DUMP_ROOT_NAME, 0);
+		remove_proc_entry(DUMP_DEVICE_NAME, dump_root);
+		remove_proc_entry(DUMP_LEVEL_NAME, dump_root);
+		remove_proc_entry(DUMP_FLAGS_NAME, dump_root);
+		return (-EBUSY);
+	}
+	return (0);
+}
+
+/*
+ * Name: dump_proc_cleanup()
+ * Func: Cleanup the /proc interfaces for dumping.
+ */
+void
+dump_proc_cleanup(void)
+{
+	/* remove the proc entries */
+	remove_proc_entry(DUMP_DEVICE_NAME, dump_root);
+	remove_proc_entry(DUMP_LEVEL_NAME, dump_root);
+	remove_proc_entry(DUMP_FLAGS_NAME, dump_root);
+	remove_proc_entry(DUMP_COMPRESS_NAME, dump_root);
+	remove_proc_entry(DUMP_ROOT_NAME, 0);
+	return;
+}
+
+/*
+ * These register and unregister routines are exported for modules
+ * to register their dump drivers (like block, net etc)
+ */
+int
+dump_register_device(struct dump_operations *dump_ops)
+{
+	if (dump_device_ops)
+		return (-1);
+	dump_device_ops = dump_ops;
+	return (0);
+}
+
+void
+dump_unregister_device(void)
+{
+	dump_device_ops = 0;
+}
+
+/*
+ * 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.
+ */
+int __init
+dump_init(void)
+{
+	struct sysinfo info;
+	int i; 
+
+	/* try to initialize /proc interfaces */
+	if (dump_proc_init() < 0) {
+		DUMP_PRINTF("dump_proc_init failed!; dump not initialized\n");
+		return (-EBUSY);
+	}
+
+	/* try to create our dump device */
+	if (register_chrdev(DUMP_MAJOR, "dump", &dump_fops)) {
+		DUMP_PRINTF("cannot register dump character device!\n");
+		return (-EBUSY);
+	}
+
+	/* initialize the dump headers to zero */
+	memset(&dump_header, 0, sizeof(dump_header));
+	memset(&dump_header_asm, 0, sizeof(dump_header_asm));
+
+#if    !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_IA64)
+	/* 
+	 * CONFIG_DISCONTIGMEM and CONFIG_IA64 systems are responsible 
+	 * for initializing dump_mbank[] in __dump_init().
+	 */
+	dump_mbanks = 1;
+	dump_mbank[0].start = 0;
+	dump_mbank[0].end  = (((u64) max_mapnr) << PAGE_SHIFT) - 1;
+	dump_mbank[0].type = DUMP_MBANK_TYPE_CONVENTIONAL_MEMORY;
+#endif
+
+	/* 
+	 * initialize the dump device at the arch level.
+ 	 *
+	 * if defined(CONFIG_DISCONTIGMEM) {
+	 *       __dump_init() must set up dump_mbank[].
+	 * }
+	 */
+	__dump_init((u64)PAGE_OFFSET);
+
+	/* initialize the dump page buffer */
+	dump_page_buf = (void *)0;
+
+	/* set the dump function pointer for dump execution */
+	dump_function_ptr = dump_execute;
+
+	/* set the dump_compression_list structure up */
+	dump_compress = DUMP_COMPRESS_NONE;
+	dump_compress_func = dump_compress_none;
+	dump_register_compression(&dump_none_compression);
+
+	/* initialize the dump flags, dump level and dump_compress fields */
+	dump_flags = DUMP_FLAGS_NONE;
+	dump_level = DUMP_LEVEL_ALL;
+
+	/* 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;
+
+	for (i = 0; i < dump_mbanks; i++) {
+		DUMP_PRINTF("mbank[%d]: type:%d, phys_addr" PU64X " ... " 
+			PU64X "\n" ,i, dump_mbank[i].type, dump_mbank[i].start,
+			dump_mbank[i].end);
+		if (dump_mbank[i].start % DUMP_PAGE_SIZE)
+			DUMP_PRINTF("oops, start is not DUMP_PAGE_SIZE:%x "
+				"aligned!\n", (int) DUMP_PAGE_SIZE);
+
+		if ((dump_mbank[i].end + 1) % DUMP_PAGE_SIZE)
+			DUMP_PRINTF("oops, end is not DUMP_PAGE_SIZE:%x "
+				"aligned!\n", (int) DUMP_PAGE_SIZE);
+	}
+	DUMP_PRINTF("Crash dump driver initialized.\n");
+	return (0);
+}
+
+void __exit
+dump_cleanup(void)
+{
+	/* get rid of the allocated dump page buffer */
+	if (dump_page_buf_0)
+		kfree((const void *)dump_page_buf_0);
+
+	/* arch-specific cleanup routine */
+	__dump_cleanup();
+
+	/* remove the proc entries */
+	dump_proc_cleanup();
+
+	/* try to create our dump device */
+	if (unregister_chrdev(DUMP_MAJOR, "dump"))
+		DUMP_PRINT("cannot unregister dump character device!\n");
+
+	/* reset the dump function pointer */
+	dump_function_ptr = NULL;
+
+	return;
+}
+
+EXPORT_SYMBOL(dump_register_compression);
+EXPORT_SYMBOL(dump_unregister_compression);
+EXPORT_SYMBOL(dump_register_device);
+EXPORT_SYMBOL(dump_unregister_device);
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,16))
+#if !defined(MODULE_LICENSE)
+#define MODULE_LICENSE(str)
+#endif
+#endif
+
+MODULE_AUTHOR("Matt D. Robinson <yakker@sourceforge.net>");
+MODULE_DESCRIPTION("Linux Kernel Crash Dump (LKCD) driver");
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,16))
+MODULE_LICENSE("GPL");
+#endif
+#endif /* MODULE */
+
+module_init(dump_init);
+module_exit(dump_cleanup);
diff -Naur linux-2.5.44.orig/drivers/dump/dump_blockdev.c linux-2.5.44.lkcd/drivers/dump/dump_blockdev.c
--- linux-2.5.44.orig/drivers/dump/dump_blockdev.c	Wed Dec 31 16:00:00 1969
+++ linux-2.5.44.lkcd/drivers/dump/dump_blockdev.c	Sat Oct 19 12:39:15 2002
@@ -0,0 +1,392 @@
+/*
+ * Block device driver for LKCD
+ * This code is released under version 2 of the GNU GPL.
+ */
+
+#include <linux/types.h>
+#include <linux/dump.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/bio.h>
+
+#define DUMP_MODULE_NAME "dump_blockdev"
+
+/*
+ * -----------------------------------------------------------------------
+ *                         V A R I A B L E S
+ * -----------------------------------------------------------------------
+ */
+kdev_t dump_dev;   	           /* the actual kdev_t device number      */
+void *dump_page_buffer;            /* dump page buffer for memcpy()!       */
+int dump_blk_size;                 /* sector size for dump_device          */
+int dump_blk_shift;                /* shift need to convert to sector size */
+struct bio *dump_bio;              /* bio structure for io request         */
+loff_t dump_offset;                /* the offset in the output device      */
+struct block_device *dump_bdev = NULL;
+unsigned long dumpdev_limit;	   /* the size limit of the block device   */
+volatile int bio_complete = 1;/* Indicates the completion of each bio request */
+int dump_io_abort = 0;		   /* set by end_io routine during io error */
+
+static void
+dump_free_bio(void)
+{
+	if (dump_bio && dump_bio->bi_io_vec)
+		kfree(dump_bio->bi_io_vec);
+	if (dump_bio)
+		kfree(dump_bio);
+}
+
+int
+dump_bio_end_io(struct bio *bio, unsigned int bytes_done, int error)
+{
+	/* No wakeup needed since we've stopped scheduling */
+	/* TODO: take care of the case when scheduler is enabled */
+
+	/* 
+	 * IO is not fully complete, return without setting bio_complete
+	 * so that caller continues to wait for completion.
+	 */
+	if (bio->bi_size)
+		return 1;
+
+	if (error) {
+		DUMP_PRINT("IO error while writing the dump, aborting\n");
+		dump_io_abort = 1;
+	}
+	bio_complete = 1;
+	clear_bit(BIO_UPTODATE, &dump_bio->bi_flags);
+
+	return 0;
+}
+
+/*
+ * Name: dump_open_kdev()
+ * Func: Try to open the kdev_t argument as the real dump device.
+ *       This is where all the work is done for setting up the dump
+ *       device.  It's assumed at this point that by passing in the
+ *       dump device's major/minor number, we can open it up, check
+ *       it out, and use it for whatever purposes.
+ */
+static int
+dump_open_kdev(kdev_t tmp_dump_device)
+{
+	int i, retval = 0;
+	unsigned long a;
+	struct bio_vec *bvec;
+
+	/* make sure this is a valid block device */
+	if (!kdev_val(tmp_dump_device)) {
+		retval = -EINVAL;
+		goto err;
+	}
+
+	/* release earlier dump_bdev if present */
+	if (dump_bdev) {
+		blkdev_put(dump_bdev, BDEV_RAW);
+		dump_bdev = NULL;
+	}
+
+	dump_bdev = bdget(kdev_t_to_nr(tmp_dump_device));
+	if (!dump_bdev) {
+		retval = -ENODEV;
+		goto err;
+	}
+
+	if (blkdev_get(dump_bdev, O_RDWR | O_LARGEFILE, 0, BDEV_RAW)) {
+		retval = -ENODEV;
+		goto err1;
+	}
+	
+	if ((dump_page_buffer== (void *)NULL)) {
+		retval = -EINVAL;
+		goto err2;
+	}
+
+	if (dump_bio)
+		dump_free_bio();
+
+	if ((dump_bio = kmalloc(sizeof(struct bio), GFP_KERNEL)) == NULL) {
+		DUMP_PRINTF("Cannot allocate bio\n");
+		retval = -ENOMEM;
+		goto err2;
+	}
+
+	bio_init(dump_bio);
+
+	if ((bvec = kmalloc(sizeof(struct bio_vec) * 
+		(DUMP_BUFFER_SIZE >> PAGE_SHIFT), GFP_KERNEL)) == NULL) {
+		retval = -ENOMEM;
+		goto err3;
+	}
+
+	/* setup bio structure */
+	dump_bio->bi_io_vec = bvec;
+
+	a = (unsigned long) dump_page_buffer;
+	for (i = 0; i < (DUMP_BUFFER_SIZE >> PAGE_SHIFT); i++, bvec++,
+			a += PAGE_SIZE) {
+		dump_bio->bi_vcnt++;
+		bvec->bv_page = (struct page *)virt_to_page(a);
+		bvec->bv_len = PAGE_SIZE;
+		bvec->bv_offset = 0;
+	}
+	dump_bio->bi_size = DUMP_BUFFER_SIZE;
+	dump_bio->bi_end_io = dump_bio_end_io;
+	dump_bio->bi_bdev = dump_bdev;
+
+	/* assign the new dump file structure */
+	dump_dev = tmp_dump_device;
+
+	/* note the max size of the dump device */
+	dumpdev_limit = dump_bdev->bd_inode->i_size;
+
+	DUMP_PRINTN("dump device 0x%x opened; Ready to take a "
+			"core dump\n", kdev_val(dump_dev));
+
+	/* after opening the block device, return */
+	return 0;
+
+err3:	dump_free_bio();
+err2:	if (dump_bdev) {
+		blkdev_put(dump_bdev, BDEV_RAW);
+		dump_bdev = NULL;
+	}
+err1:	if (dump_bdev) {
+		bdput(dump_bdev);
+		dump_bdev = NULL;
+	}
+err:	return retval;
+}
+
+/*
+ * Name: dev_dump_release()
+ * Func: Release the dump device -- it's never necessary to call
+ *       this function, but it's here regardless.
+ */
+static int
+dev_dump_release(void)
+{
+	return (0);
+}
+
+/*
+ * Name: dev_dump_end()
+ * Func: end the dump device -- it's never necessary to call
+ *       this function, but it's here regardless.
+ */
+static int
+dev_dump_end(unsigned long arg)
+{
+	return (0);
+}
+
+/*
+ * Name: dev_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
+dev_dump_ioctl(unsigned int cmd, unsigned long arg)
+{
+	int ret = 0;
+
+	/*
+	 * 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:
+			ret = dump_open_kdev(to_kdev_t((dev_t)arg));
+			break;	
+		case DIOSDUMPMEM:
+			dump_page_buffer = (void *)arg;
+			break;
+		default:
+			break;
+
+	}
+	return (ret);
+}
+
+/*
+ * Name: dev_dump_seek()
+ * Func: seek to the dump device for use when the system crashes.
+ */
+static loff_t
+dev_dump_seek(loff_t off, int tmp)
+{
+	if (off & (PAGE_SIZE - 1)) {
+		DUMP_PRINT("seek, non-aligned page\n");
+		return (-EINVAL);
+	}
+	
+	if (off & (bdev_hardsect_size(dump_bio->bi_bdev) - 1)) {
+		DUMP_PRINT("seek, sector not aligned\n");
+		return (-EINVAL);
+	}
+
+	if (off > dumpdev_limit) {
+		DUMP_PRINT("seek, not enough space left on device!\n");
+		return (-ENOSPC);
+	}
+	dump_offset = off;
+	return (0);
+}
+
+/*
+ * Name: dev_dump_write()
+ *
+ * Func: Write out kernel information, check the device limitations etc
+ * 	 Writes DUMP_BUFFER_SIZE bytes in page buffer
+ *
+ * Returns: number of bytes written or -ERRNO. 
+ *	    At EOF it returns 0.
+ */
+static ssize_t
+dev_dump_write(const char *buff, size_t len, loff_t *offset)
+{
+	/* this condition will not be true for now */
+	if (len > DUMP_BUFFER_SIZE) {
+		DUMP_PRINTN("max dump buffer size exceeded");
+		return (-EINVAL);
+	}
+	
+	if (dump_offset >= dumpdev_limit) {
+		DUMP_PRINTN("no space left on device!");
+		return (-ENOSPC);
+	}
+
+	/* 
+	 * If near the end of the dump device we set *eof (if ptr provided) to
+	 * let the caller know that it's near the EOF on the dump device.
+	 */
+	if ((dump_offset + (4 * DUMP_BUFFER_SIZE)) > dumpdev_limit) {
+		if (offset != NULL) *offset = 1;
+		DUMP_BP();
+	}
+
+	if (dump_offset + len > dumpdev_limit)
+		len = dumpdev_limit - dump_offset;
+
+	/* make sure we have space to write! */
+	if (!len) {
+		DUMP_PRINTN("no space left on dump device!");
+		return (0);
+	}
+
+	/* re-initialize the fields of bio structure */
+	dump_bio->bi_sector = dump_offset >> 9;
+	dump_bio->bi_bdev = dump_bdev;
+	dump_bio->bi_vcnt = len >> PAGE_SHIFT;
+	dump_bio->bi_idx = 0;
+	dump_bio->bi_size = len;
+
+	/*
+	 * write out the data to disk.
+	 * here we submit a bio request for size = 64k(DUMP_BUFFER_SIZE)
+	 * and wait for io completion. In future, we can think of increasing
+	 * the dump buffer size and issuing multiple bios parallelly.
+	 */
+	bio_complete = 0;
+	submit_bio(WRITE, dump_bio);
+
+	return len;
+}
+
+/*
+ * Name: dev_dump_open()
+ * Func: Open the dump device for use when the system crashes.
+ */
+static int
+dev_dump_open(void)
+{
+	return (0);
+}
+
+/*
+ * Name: dev_dump_start()
+ * Func: start the dump device for use when the system crashes.
+ */
+static int
+dev_dump_start(unsigned long arg)
+{
+	return (0);
+}
+
+/*
+ * Name: dev_dump_ready()
+ * Func: check the dump device for use when the system crashes.
+ */
+static int
+dev_dump_ready(unsigned long arg)
+{
+	request_queue_t *q = bdev_get_queue(dump_bio->bi_bdev);
+
+	/* wait for io completion */
+	while (!bio_complete)
+		q->unplug_fn(q);
+
+	if (dump_io_abort)
+		return (-EIO);
+	return (1);
+}
+
+static struct dump_operations dump_blockdev_ops = {
+	dump_open:		dev_dump_open,
+	dump_release:	        dev_dump_release,
+	dump_ioctl:		dev_dump_ioctl,
+        dump_write:             dev_dump_write,
+	dump_seek:              dev_dump_seek,
+	dump_start:             dev_dump_start,
+        dump_end:               dev_dump_end,
+	dump_ready:             dev_dump_ready,
+};
+
+int __init
+dump_blockdev_init(void)
+{        
+	dump_page_buffer = NULL;
+	dump_offset = 0;
+
+	if (dump_register_device(&dump_blockdev_ops) < 0) {
+		DUMP_PRINTN("block device driver registration failed\n");
+		return (-1);
+	}
+		
+	DUMP_PRINTN("block device driver for LKCD unregistered\n");
+	return (0);
+}
+
+void __exit
+dump_blockdev_cleanup(void)
+{
+	dump_unregister_device();
+	DUMP_PRINTN("block device driver for LKCD unregistered\n");
+	if (dump_bdev) {
+		blkdev_put(dump_bdev, BDEV_RAW);
+		dump_bdev = NULL;
+	}
+	if (dump_bio)
+		dump_free_bio();
+}
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,16))
+#if !defined(MODULE_LICENSE)
+#define MODULE_LICENSE(str)
+#endif
+#endif
+
+MODULE_AUTHOR("Mohamed. Abbas");
+MODULE_DESCRIPTION("Block Device Driver for Linux Kernel Crash Dump (LKCD)");
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,16))
+MODULE_LICENSE("GPL");
+#endif
+#endif /* MODULE */
+module_init(dump_blockdev_init);
+module_exit(dump_blockdev_cleanup);
diff -Naur linux-2.5.44.orig/drivers/dump/dump_i386.c linux-2.5.44.lkcd/drivers/dump/dump_i386.c
--- linux-2.5.44.orig/drivers/dump/dump_i386.c	Wed Dec 31 16:00:00 1969
+++ linux-2.5.44.lkcd/drivers/dump/dump_i386.c	Sat Oct 19 12:39:15 2002
@@ -0,0 +1,315 @@
+/*
+ * 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/dump.h>
+#include <linux/mm.h>
+#include <linux/irq.h>
+
+#include <asm/processor.h>
+#include <asm/hardirq.h>
+
+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] = (uint32_t)((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]);	
+	return 0;
+}
+
+/* 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.
+ */
+static inline void 
+fix_ssesp(struct pt_regs *regs, int cpu)
+{
+	if (!user_mode(regs)) {
+		if ((cpu == dump_header_asm.dha_dumping_cpu) &&
+			(0xffff & regs->xss) == __KERNEL_DS) 
+			return;
+		dump_header_asm.dha_smp_regs[cpu].esp = 
+				(unsigned long)&(regs->esp);
+		__asm__ __volatile__ ("movw %%ss, %%ax;"
+			:"=a"(dump_header_asm.dha_smp_regs[cpu].xss));
+	}
+}
+
+static void
+save_this_cpu_state(int cpu, struct pt_regs *regs, struct task_struct *tsk)
+{
+	dump_header_asm.dha_smp_regs[cpu] = *regs;
+	dump_header_asm.dha_smp_current_task[cpu] = (uint32_t)tsk;
+	fix_ssesp(regs, 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);
+	return;
+}
+
+#ifdef CONFIG_SMP
+extern irq_desc_t irq_desc[];
+extern unsigned long irq_affinity[];
+extern void dump_send_ipi(void);
+static int dump_expect_ipi[NR_CPUS];
+static atomic_t waiting_for_dump_ipi;
+static int wait_for_dump_ipi = 1; /* always wait for ipi to to be handled */
+extern void (*dump_trace_ptr)(struct pt_regs *);
+static unsigned long saved_affinity[NR_IRQS];
+__s32 saved_irq_count, saved_bh_count;
+
+static int
+dump_ipi_handler(struct pt_regs *regs) 
+{
+	int cpu = smp_processor_id();
+
+	if (!dump_expect_ipi[cpu]) {
+		return 0;
+	}
+	
+	save_this_cpu_state(cpu, regs, current);
+
+	dump_expect_ipi[cpu] = 0;
+	atomic_dec(&waiting_for_dump_ipi);
+
+	/* Mark the task for rescheduling, so that it task spins in schedule */
+	set_tsk_thread_flag(current, TIF_NEED_RESCHED);
+
+	return 1;
+}
+
+/* save registers on other processors */
+void 
+save_other_cpu_states(void)
+{
+	int i;
+
+	if (num_online_cpus() > 1) {
+		atomic_set(&waiting_for_dump_ipi, num_online_cpus()-1);
+		for (i = 0; i < NR_CPUS; i++)
+			dump_expect_ipi[i] = 1;
+		
+		dump_ipi_function_ptr = dump_ipi_handler;
+		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 untill then, there probably
+		   is a problem and we just fail to capture state of 
+		   other cpus. */
+		if (wait_for_dump_ipi) {
+			while(atomic_read(&waiting_for_dump_ipi))
+				barrier();
+			dump_ipi_function_ptr = NULL;
+		}
+	}
+	return;
+}
+
+/*
+ * Routine to save the old irq affinities and change affinities of all irqs to
+ * the dumping cpu.
+ */
+static void
+__dump_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
+__dump_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]);
+	}
+}
+
+/* 
+ * 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 = hardirq_count();
+	saved_bh_count = softirq_count();
+	preempt_count() &= ~HARDIRQ_MASK;
+	preempt_count() &= ~SOFTIRQ_MASK;
+}
+
+static inline void irq_bh_restore(void)
+{
+	preempt_count() |= saved_irq_count;
+	preempt_count() |= saved_bh_count;
+}
+
+#else /* !CONFIG_SMP */
+#define save_other_cpu_states()
+#endif /* !CONFIG_SMP */
+
+/*
+ * Name: __dump_silence_system()
+ * Func: Do an architecture-specific silencing of the system.
+ *     - Change irq affinities
+ *     - Wait for other cpus to come out of irq handling
+ *     - Send CALL_FUNCTION_VECTOR ipi to other cpus to put them to spin
+ */
+unsigned int
+__dump_silence_system(unsigned int stage)
+{
+#ifdef CONFIG_SMP
+	if (stage) {	/* Do this after interrupts are enabled */
+		__dump_set_irq_affinity();
+		irq_bh_save();
+		/* synchronize_irq(); TODO: needed ? */
+	}
+#endif
+	/* return */
+	return (0);
+}
+
+/*
+ * Name: __dump_resume_system()
+ * Func: Resume the system state in an architecture-specific way.
+ */
+unsigned int
+__dump_resume_system(unsigned int stage)
+{
+#ifdef CONFIG_SMP
+	/* put the irq affinity tables back */
+	__dump_reset_irq_affinity();
+	irq_bh_restore();
+#endif
+	/* return */
+	return (0);
+}
+
+/*
+ * Name: __dump_configure_header()
+ * Func: Configure the dump header with all proper values.
+ */
+int
+__dump_configure_header(struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+
+	dump_header_asm.dha_smp_num_cpus = num_online_cpus();
+	dump_header_asm.dha_dumping_cpu = cpu;
+
+	save_this_cpu_state(cpu, regs, current);
+
+	save_other_cpu_states();
+
+	return (1);
+}
+
+/*
+ * Name: __dump_init()
+ * Func: Initialize the dumping routine process.  This is in case
+ *       it's necessary in the future.
+ */
+void __init
+__dump_init(uint64_t local_memory_start)
+{
+	/* return */
+	return;
+}
+
+/*
+ * Name: __dump_open()
+ * Func: Open the dump device (architecture specific).  This is in
+ *       case it's necessary in the future.
+ */
+void
+__dump_open(void)
+{
+	alloc_dha_stack();
+	/* return */
+	return;
+}
+
+/*
+ * Name: __dump_cleanup()
+ * Func: Free any architecture specific data structures. This is called
+ *       when the dump module is being removed.
+ */
+void __exit
+__dump_cleanup(void)
+{
+	free_dha_stack();
+	/* return */
+	return;
+}
diff -Naur linux-2.5.44.orig/include/asm-i386/dump.h linux-2.5.44.lkcd/include/asm-i386/dump.h
--- linux-2.5.44.orig/include/asm-i386/dump.h	Wed Dec 31 16:00:00 1969
+++ linux-2.5.44.lkcd/include/asm-i386/dump.h	Sat Oct 19 12:39:15 2002
@@ -0,0 +1,94 @@
+/*
+ * 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>                          /* for pt_regs             */
+#include <linux/threads.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_t
+ *  Function: This is the header for architecture-specific stuff.  It
+ *            follows right after the dump header.
+ */
+typedef struct _dump_header_asm_s {
+
+        /* the dump magic number -- unique to verify dump is valid */
+        uint64_t             dha_magic_number;
+
+        /* the version number of this dump */
+        uint32_t             dha_version;
+
+        /* the size of this header (in case we can't read it) */
+        uint32_t             dha_header_size;
+
+	/* the esp for i386 systems */
+	uint32_t             dha_esp;
+
+	/* the eip for i386 systems */
+	uint32_t             dha_eip;
+
+	/* the dump registers */
+	struct pt_regs       dha_regs;
+
+	/* smp specific */
+	uint32_t	     dha_smp_num_cpus;
+	uint32_t	     dha_dumping_cpu;	
+	struct pt_regs	     dha_smp_regs[DUMP_MAX_NUM_CPUS];
+	uint32_t	     dha_smp_current_task[DUMP_MAX_NUM_CPUS];
+	uint32_t	     dha_stack[DUMP_MAX_NUM_CPUS];
+	uint32_t	     dha_stack_ptr[DUMP_MAX_NUM_CPUS];
+} __attribute__((packed)) dump_header_asm_t;
+
+#ifdef __KERNEL__
+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();
+	
+}
+
+extern volatile int dump_in_progress;
+extern unsigned long irq_affinity[];
+extern dump_header_asm_t dump_header_asm;
+
+#ifdef CONFIG_SMP
+extern int (*dump_ipi_function_ptr)(struct pt_regs *);
+extern void dump_send_ipi(void);
+#else
+#define dump_send_ipi()
+#endif
+
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_DUMP_H */
diff -Naur linux-2.5.44.orig/include/linux/dump.h linux-2.5.44.lkcd/include/linux/dump.h
--- linux-2.5.44.orig/include/linux/dump.h	Wed Dec 31 16:00:00 1969
+++ linux-2.5.44.lkcd/include/linux/dump.h	Sat Oct 19 12:39:15 2002
@@ -0,0 +1,438 @@
+/*
+ * 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
+
+#include <linux/list.h>
+#include <linux/notifier.h>
+
+/* define TRUE and FALSE for use in our dump modules */
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+
+/*
+ * DUMP_DEBUG: a debug level for the kernel dump code and
+ *             the supporting lkcd libraries in user space.
+ *
+ * 0: FALSE: No Debug Added
+ * 1: TRUE:  Break Points
+ * .
+ * .
+ * .
+ * 6: Add Debug Data to Structures
+ * .
+ * .
+ * 9: Max
+ */
+#define DUMP_DEBUG FALSE
+
+#if DUMP_DEBUG
+void dump_bp(void);			/* Called when something exceptional occurs */
+#define DUMP_BP() dump_bp()			/* Breakpoint */
+#else
+#define DUMP_BP()
+#endif
+
+/* 
+ * 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 */
+
+/* necessary header definitions in all cases */
+#define DUMP_KIOBUF_NUMBER  0xdeadbeef  /* special number for kiobuf maps   */
+
+/* 
+ * 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 (0x10000) */
+#define DUMP_HEADER_SIZE	 DUMP_BUFFER_SIZE
+
+/* header definitions for s390 dump */
+#define DUMP_MAGIC_S390     0xa8190173618f23fdULL  /* s390 magic number     */
+#define S390_DUMP_HEADER_SIZE     4096
+
+/* 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_NONDISRUPT  0x1      /* try to keep running after dump   */
+
+/* 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 tunables (they are now all read-only) */
+#define DUMP_ROOT_NAME         "sys/dump"
+#define DUMP_DEVICE_NAME       "dump_device"
+#define DUMP_COMPRESS_NAME     "dump_compress"
+#define DUMP_LEVEL_NAME        "dump_level"
+#define DUMP_FLAGS_NAME        "dump_flags"
+
+/* 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      */
+#define DIOSDUMPMEM		9	/* set the dump buffer memory	    */	
+#define DUMPCONSOLE_MAJOR       245
+#define DUMPCONSOLE_NAME        "netconsole"
+
+struct dump_operations {
+	loff_t (*dump_seek) (loff_t, int);
+	ssize_t (*dump_write) (const char *, size_t, loff_t *);
+	int (*dump_ioctl) (unsigned int, unsigned long);
+	int (*dump_open) (void);
+	int (*dump_release) (void);
+	int (*dump_start) (unsigned long);
+	int (*dump_end) (unsigned long);
+	int (*dump_ready) (unsigned long);
+};
+
+extern int dump_register_device(struct dump_operations *);
+extern void dump_unregister_device(void);
+
+/* the major number used for the dumping device */
+#ifndef DUMP_MAJOR
+#define DUMP_MAJOR              227
+#endif
+
+/*
+ * Structure: dump_header_t
+ *  Function: This is the header dumped at the top of every valid crash
+ *            dump.  
+ *            easy reassembly of each crash dump page.  The address bits
+ *            are split to make things easier for 64-bit/32-bit system
+ *            conversions.
+ */
+typedef struct _dump_header_s {
+	/* the dump magic number -- unique to verify dump is valid */
+	uint64_t             dh_magic_number;
+
+	/* the version number of this dump */
+	uint32_t             dh_version;
+
+	/* the size of this header (in case we can't read it) */
+	uint32_t             dh_header_size;
+
+	/* the level of this dump (just a header?) */
+	uint32_t             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.) 
+	 */
+	uint32_t             dh_page_size;
+
+	/* the size of all physical memory */
+	uint64_t             dh_memory_size;
+
+	/* the start of physical memory */
+	uint64_t             dh_memory_start;
+
+	/* the end of physical memory */
+	uint64_t             dh_memory_end;
+
+#if DUMP_DEBUG >= 6
+	/* the number of bytes in this dump specifically */
+	uint64_t             dh_num_bytes;
+#endif
+
+	/* the number of hardware/physical pages in this dump specifically */
+	uint32_t             dh_num_dump_pages;
+
+	/* the panic string, if available */
+	char                 dh_panic_string[DUMP_PANIC_LEN];
+
+	/* timeval depends on architecture, two long values */
+	struct {uint64_t tv_sec;
+		uint64_t 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 = uint64_t) */
+	uint64_t             dh_current_task;
+
+	/* what type of compression we're using in this dump (if any) */
+	uint32_t             dh_dump_compress;
+
+	/* any additional flags */
+	uint32_t             dh_dump_flags;
+
+	/* any additional flags */
+	uint32_t             dh_dump_device;
+
+} __attribute__((packed)) dump_header_t;
+
+/*
+ * Structure: dump_page_t
+ *  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,
+ */
+typedef struct _dump_page_s {
+
+#if DUMP_DEBUG >= 6
+	/* byte offset */
+	uint64_t		dp_byte_offset;
+
+	/* page index */
+	uint64_t		dp_page_index;
+#endif
+	/* the address of this dump page */
+	uint64_t             dp_address;
+
+	/* the size of this dump page */
+	uint32_t             dp_size;
+
+	/* flags (currently DUMP_COMPRESSED, DUMP_RAW or DUMP_END) */
+	uint32_t             dp_flags;
+} __attribute__((packed)) dump_page_t;
+
+/*
+ * This structure contains information needed for the lkcdutils
+ * package (particularly lcrash) to determine what information is
+ * associated to this kernel, specifically.
+ */
+typedef struct lkcdinfo_s {
+	int             arch;
+	int             ptrsz;
+	int             byte_order;
+	int             linux_release;
+	int             page_shift;
+	int             page_size;
+	uint64_t        page_mask;
+	uint64_t        page_offset;
+	int             stack_offset;
+} lkcdinfo_t;
+
+#ifdef __KERNEL__
+
+/*
+ * Structure: dump_compress_t
+ *  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.
+ */
+typedef struct dump_compress_s {
+	/* the list_head structure for list storage */
+	struct list_head list;
+
+	/* the type of compression to use (DUMP_COMPRESS_XXX) */
+        int compress_type;
+
+	/* the compression function to call */
+        int (*compress_func)(char *, int, char *, int);
+} dump_compress_t;
+
+/* functions for dump compression registration */
+extern void dump_register_compression(dump_compress_t *);
+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.
+ */
+typedef struct dump_mbank {
+        u64 		start;
+        u64 		end;
+	int		type;
+	int		pad1;
+	long		pad2;
+} dump_mbank_t;
+
+#define DUMP_MBANK_TYPE_CONVENTIONAL_MEMORY		1
+#define DUMP_MBANK_TYPE_OTHER				2
+
+
+#define MAXCHUNKS 256
+extern int dump_mbanks;
+extern dump_mbank_t dump_mbank[MAXCHUNKS];
+
+extern struct notifier_block *dump_notifier_list;
+extern int register_dump_notifier(struct notifier_block *);
+extern int unregister_dump_notifier(struct notifier_block *);
+
+/* notification event codes */
+#define DUMP_BEGIN_NONDISRUPT	0x0000	/* Notify of non-disruptive dump beginning */
+#define DUMP_BEGIN		0x0001	/* Notify of distuptive dump beginning */
+#define DUMP_END		0x0002	/* Notify of dump ending */
+#define DUMP_TEST		0x0003	/* Notify Just probeing for capability callbacks */
+
+extern int dump_init(void);
+int dump_execute(char *, struct pt_regs *);
+extern volatile int dump_in_progress;
+extern volatile int dumping_cpu;
+extern int (*dump_function_ptr)(char *, struct pt_regs *);
+extern volatile int dump_started;
+
+/*
+ * dump notifier callbacks to tune the dump driver for 
+ * the disk driver that will be used for the dump. They
+ * are used to modify the dump driver from it's default
+ * behavior.
+ *
+ * NOTE: To support changing the dump device between a
+ *       non-disruptive dump and a future dump we would
+ *	 need to provide the inverse functions also.
+ */
+extern void dump_scheduler_enable(void);
+extern void dump_interrupts_disable(void);
+extern void dump_nondisruptive_disable(void);
+
+static inline void dump(char * str, struct pt_regs * regs)
+{
+	if (dump_function_ptr) {
+		dump_function_ptr((char *)str, regs);
+	}
+}
+
+/*
+ * 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(uint64_t);
+extern int 		__dump_configure_header(struct pt_regs *);
+extern unsigned int  	__dump_silence_system(unsigned int);
+extern unsigned int  	__dump_resume_system(unsigned int);
+
+#define DUMP_MODULE_NAME "dump"
+#if DUMP_DEBUG
+#define DUMP_PREFIX __func__
+#else
+#define DUMP_PREFIX DUMP_MODULE_NAME
+#endif
+
+#define DUMP_PRINTN(args...)  {						\
+	printk("\n");							\
+	DUMP_PRINTF(args);						\
+}
+
+#define DUMP_PRINTF(args...)  {						\
+	printk("%s: ", DUMP_PREFIX);					\
+	printk (args);							\
+}
+
+#ifdef DUMP_DEBUG
+#define DUMP_DPRINTF(args...)  {					\
+	printk("%s: ", DUMP_PREFIX);					\
+	printk (args);							\
+}
+#else
+#define DUMP_DPRINTF(args...)
+#endif
+
+#define DUMP_PRINT(args...) {						\
+	printk(args);							\
+}
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c))
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* _DUMP_H */

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2002-10-22 22:50 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2002-10-21 10:16 [PATCH] 2.5.44: lkcd (9/9): dump driver and build files Matt D. Robinson
2002-10-21 21:21 ` Christoph Hellwig
2002-10-21 19:07   ` Matt D. Robinson
2002-10-21 22:06   ` Piet Delaney
2002-10-22 18:47     ` Christoph Hellwig
2002-10-22 11:57       ` Keith Owens
2002-10-22 22:21         ` Piet Delaney
2002-10-22 22:56           ` Keith Owens
2002-10-22 16:49       ` Matt D. Robinson
2002-10-22  2:04   ` Nicholas Wourms
2002-10-22  5:45     ` Matt D. Robinson
2002-10-22 13:00     ` Christoph Hellwig
2002-10-22 15:05   ` Suparna Bhattacharya
2002-10-22  9:49     ` Jens Axboe
2002-10-22 14:43       ` Randy.Dunlap
2002-10-22 14:59         ` Jens Axboe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).