public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4][Diskdump]Update patches
@ 2004-06-16 12:39 Takao Indoh
  2004-06-16 12:45 ` [PATCH 1/4][Diskdump]Update patches Takao Indoh
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Takao Indoh @ 2004-06-16 12:39 UTC (permalink / raw)
  To: linux-kernel

Hi!

I fixed diskdump patches except timer problem.

- Fix some codes which Arjan van de Ven pointed out
- Replace dev_t with block_device

Source code of tool(diskdumptuils) can be downloaded from
 http://sourceforge.net/projects/lkdump

Regarding timer problem, please see previous mail I sent.
 http://marc.theaimsgroup.com/?l=linux-kernel&m=108722344204595&w=2
In short, Timer problem is as follows.
> What is a problem?  Scsi driver uses timer/tasklet to complete I/O.
> But, diskdump disables interrupt, so timer/taslket doesn't work!

There are three ways to solve this problem so far.

(1) Redefine timer/taslket routines. This method was adopted in the
    first patch.

+#if defined(CONFIG_DISKDUMP) || defined(CONFIG_DISKDUMP_MODULE)
+#undef  add_timer
+#define add_timer       diskdump_add_timer
+#undef  del_timer_sync
+#define del_timer_sync  diskdump_del_timer
+#undef  del_timer
+#define del_timer       diskdump_del_timer
+#undef  mod_timer
+#define mod_timer       diskdump_mod_timer
+
+#define tasklet_schedule        diskdump_tasklet_schedule
+#endif


(2) Add new code into the timer/taslket routines themselves.

 static inline void add_timer(struct timer_list * timer)
 {
-	__mod_timer(timer, timer->expires);
+	if(crashdump_mode())
+		diskdump_add_timer(timer);
+	else
+		__mod_timer(timer, timer->expires);
 }


 int del_timer_sync(struct timer_list *timer)
 {
 	tvec_base_t *base;
 	int i, ret = 0;

+	if(crashdump_mode()) {
+		diskdump_del_timer(timer);
+		return 0;
+	}
+
 	check_timer(timer);


 int mod_timer(struct timer_list *timer, unsigned long expires)
 {
 	BUG_ON(!timer->function);
 
+	if(crashdump_mode()) {
+		diskdump_mod_timer(timer, expires);
+		return 0;
+	}
+
 	check_timer(timer);


 static inline void tasklet_schedule(struct tasklet_struct *t)
 {
+	if(crashdump_mode()) {
+		diskdump_tasklet_schedule(t);
+		return;
+	}
+
 	if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
 		__tasklet_schedule(t);
 }


(3) Change polling handler of driver not to use timer/tasklet.


The method (1) was already rejected because of its ugliness. The
method (3) needs many extra codes and makes handler of driver too big
and complex. I think the method (2) is the simplest though it make
common codes dirty.

Please feel free to comment.

Best Regards,
Takao Indoh

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

* [PATCH 1/4][Diskdump]Update patches
  2004-06-16 12:39 [PATCH 0/4][Diskdump]Update patches Takao Indoh
@ 2004-06-16 12:45 ` Takao Indoh
  2004-06-16 12:46 ` [PATCH 2/4][Diskdump]Update patches Takao Indoh
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Takao Indoh @ 2004-06-16 12:45 UTC (permalink / raw)
  To: linux-kernel

This is a patch for diskdump common layer.

diff -Nur linux-2.6.6.org/arch/i386/kernel/nmi.c linux-2.6.6/arch/i386/kernel/nmi.c
--- linux-2.6.6.org/arch/i386/kernel/nmi.c	2004-06-04 21:21:51.000000000 +0900
+++ linux-2.6.6/arch/i386/kernel/nmi.c	2004-06-16 19:34:16.000000000 +0900
@@ -517,3 +517,4 @@
 EXPORT_SYMBOL(release_lapic_nmi);
 EXPORT_SYMBOL(disable_timer_nmi_watchdog);
 EXPORT_SYMBOL(enable_timer_nmi_watchdog);
+EXPORT_SYMBOL(touch_nmi_watchdog);
diff -Nur linux-2.6.6.org/arch/i386/kernel/reboot.c linux-2.6.6/arch/i386/kernel/reboot.c
--- linux-2.6.6.org/arch/i386/kernel/reboot.c	2004-06-04 21:21:51.000000000 +0900
+++ linux-2.6.6/arch/i386/kernel/reboot.c	2004-06-16 19:34:16.000000000 +0900
@@ -252,7 +252,8 @@
 	 * Stop all CPUs and turn off local APICs and the IO-APIC, so
 	 * other OSs see a clean IRQ state.
 	 */
-	smp_send_stop();
+	if (!crashdump_mode())
+		smp_send_stop();
 #elif defined(CONFIG_X86_LOCAL_APIC)
 	if (cpu_has_apic) {
 		local_irq_disable();
diff -Nur linux-2.6.6.org/arch/i386/kernel/traps.c linux-2.6.6/arch/i386/kernel/traps.c
--- linux-2.6.6.org/arch/i386/kernel/traps.c	2004-06-04 21:21:51.000000000 +0900
+++ linux-2.6.6/arch/i386/kernel/traps.c	2004-06-16 19:34:16.000000000 +0900
@@ -258,7 +258,8 @@
 	int nl = 0;
 
 	console_verbose();
-	spin_lock_irq(&die_lock);
+	if (!crashdump_mode())
+		spin_lock_irq(&die_lock);
 	bust_spinlocks(1);
 	handle_BUG(regs);
 	printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);
@@ -277,6 +278,7 @@
 	if (nl)
 		printk("\n");
 	show_registers(regs);
+	try_crashdump(regs);
 	bust_spinlocks(0);
 	spin_unlock_irq(&die_lock);
 	if (in_interrupt())
diff -Nur linux-2.6.6.org/arch/i386/mm/init.c linux-2.6.6/arch/i386/mm/init.c
--- linux-2.6.6.org/arch/i386/mm/init.c	2004-06-04 21:21:52.000000000 +0900
+++ linux-2.6.6/arch/i386/mm/init.c	2004-06-16 19:34:16.000000000 +0900
@@ -168,7 +168,7 @@
 
 extern int is_available_memory(efi_memory_desc_t *);
 
-static inline int page_is_ram(unsigned long pagenr)
+int page_is_ram(unsigned long pagenr)
 {
 	int i;
 	unsigned long addr, end;
@@ -205,6 +205,7 @@
 	}
 	return 0;
 }
+EXPORT_SYMBOL(page_is_ram);
 
 #ifdef CONFIG_HIGHMEM
 pte_t *kmap_pte;
diff -Nur linux-2.6.6.org/drivers/block/Kconfig linux-2.6.6/drivers/block/Kconfig
--- linux-2.6.6.org/drivers/block/Kconfig	2004-06-04 21:22:15.000000000 +0900
+++ linux-2.6.6/drivers/block/Kconfig	2004-06-16 19:34:16.000000000 +0900
@@ -346,6 +346,11 @@
 	  your machine, or if you want to have a raid or loopback device
 	  bigger than 2TB.  Otherwise say N.
 
+config DISKDUMP
+	tristate "Disk dump support"
+	---help---
+	  Disk dump support.
+
 source "drivers/s390/block/Kconfig"
 
 endmenu
diff -Nur linux-2.6.6.org/drivers/block/Makefile linux-2.6.6/drivers/block/Makefile
--- linux-2.6.6.org/drivers/block/Makefile	2004-06-04 21:22:15.000000000 +0900
+++ linux-2.6.6/drivers/block/Makefile	2004-06-16 19:34:16.000000000 +0900
@@ -43,3 +43,4 @@
 obj-$(CONFIG_VIODASD)		+= viodasd.o
 obj-$(CONFIG_BLK_DEV_CARMEL)	+= carmel.o
 
+obj-$(CONFIG_DISKDUMP)		+= diskdump.o diskdumplib.o
diff -Nur linux-2.6.6.org/drivers/block/diskdump.c linux-2.6.6/drivers/block/diskdump.c
--- linux-2.6.6.org/drivers/block/diskdump.c	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.6/drivers/block/diskdump.c	2004-06-16 19:34:16.000000000 +0900
@@ -0,0 +1,973 @@
+/*
+ *  linux/drivers/block/diskdump.c
+ *
+ *  Copyright (C) 2004  FUJITSU LIMITED
+ *  Copyright (C) 2002  Red Hat, Inc.
+ *  Written by Nobuhiro Tachino (ntachino@jp.fujitsu.com)
+ *
+ *  Some codes were derived from netdump and copyright belongs to
+ *  Red Hat, Inc.
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/file.h>
+#include <linux/reboot.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/highmem.h>
+#include <linux/utsname.h>
+#include <linux/console.h>
+#include <linux/smp_lock.h>
+#include <linux/nmi.h>
+#include <linux/genhd.h>
+#include <linux/crc32.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/diskdump.h>
+#include <linux/diskdumplib.h>
+#include <asm/diskdump.h>
+
+#define Dbg(x, ...)	pr_debug("disk_dump: " x "\n", ## __VA_ARGS__)
+#define Err(x, ...)	pr_err  ("disk_dump: " x "\n", ## __VA_ARGS__)
+#define Warn(x, ...)	pr_warn ("disk_dump: " x "\n", ## __VA_ARGS__)
+#define Info(x, ...)	pr_info ("disk_dump: " x "\n", ## __VA_ARGS__)
+
+#ifndef TRUE
+#define TRUE	!0
+#endif
+#ifndef FALSE
+#define FALSE	0
+#endif
+
+#define ROUNDUP(x, y)	(((x) + ((y)-1))/(y))
+
+/* 512byte sectors to blocks */
+#define SECTOR_BLOCK(s)	((s) >> (DUMP_BLOCK_SHIFT - 9))
+
+static unsigned int fallback_on_err = 1;
+static unsigned int allow_risky_dumps = 1;
+static unsigned int block_order = 2;
+static int sample_rate = 8;
+module_param(fallback_on_err, uint, 0);
+module_param(allow_risky_dumps, uint, 0);
+module_param(block_order, uint, 0);
+module_param(sample_rate, int, 0);
+
+static unsigned long timestamp_1sec;
+static uint32_t module_crc;
+static char *scratch;
+static struct disk_dump_header dump_header;
+static struct disk_dump_sub_header dump_sub_header;
+
+/* Registered dump devices */
+static LIST_HEAD(disk_dump_devices);
+
+/* Registered dump types, e.g. SCSI, ... */
+static LIST_HEAD(disk_dump_types);
+
+static spinlock_t disk_dump_lock = SPIN_LOCK_UNLOCKED;
+
+static unsigned int header_blocks;		/* The size of all headers */
+static unsigned int bitmap_blocks;		/* The size of bitmap header */
+static unsigned int total_ram_blocks;		/* The size of memory */
+static unsigned int total_blocks;		/* The sum of above */
+
+struct notifier_block *disk_dump_notifier_list;
+
+extern int panic_timeout;
+extern unsigned long max_pfn;
+
+#if CONFIG_SMP
+static void freeze_cpu(void *dummy)
+{
+	unsigned int cpu = smp_processor_id();
+
+	dump_header.tasks[cpu] = current;
+
+	platform_freeze_cpu();
+}
+#endif
+
+static int lapse = 0;		/* 200msec unit */
+
+static inline unsigned long eta(unsigned long nr, unsigned long maxnr)
+{
+	unsigned long long eta;
+
+	eta = ((maxnr << 8) / nr) * (unsigned long long)lapse;
+
+	return (unsigned long)(eta >> 8) - lapse;
+}
+
+static inline void print_status(unsigned int nr, unsigned int maxnr)
+{
+	static char *spinner = "/|\\-";
+	static unsigned long long prev_timestamp = 0;
+	unsigned long long timestamp;
+
+	platform_timestamp(timestamp);
+
+	if (timestamp - prev_timestamp > (timestamp_1sec/5)) {
+		prev_timestamp = timestamp;
+		lapse++;
+		printk("%u/%u    %lu ETA %c          \r",
+			nr, maxnr, eta(nr, maxnr) / 5, spinner[lapse & 3]);
+	}
+}
+
+static inline void clear_status(int nr, int maxnr)
+{
+	printk("                                       \r");
+	lapse = 0;
+}
+
+/*
+ * Checking the signature on a block. The format is as follows.
+ *
+ * 1st word = 'disk'
+ * 2nd word = 'dump'
+ * 3rd word = block number
+ * 4th word = ((block number + 7) * 11) & 0xffffffff
+ * 5th word = ((4th word + 7)* 11) & 0xffffffff
+ * ..
+ *
+ * Return TRUE if the signature is correct, else return FALSE
+ */
+static int check_block_signature(void *buf, unsigned int block_nr)
+{
+	int word_nr = PAGE_SIZE / sizeof(int);
+	int *words = buf;
+	unsigned int val;
+	int i;
+
+	if (memcmp(buf, DUMP_PARTITION_SIGNATURE, sizeof(*words)))
+		return FALSE;
+
+	val = block_nr;
+	for (i = 2; i < word_nr; i++) {
+		if (words[i] != val)
+			return FALSE;
+		val = (val + 7) * 11;
+	}
+
+	return TRUE;
+}
+
+/*
+ * Read one block into the dump partition
+ */
+static int read_blocks(struct disk_dump_partition *dump_part, unsigned int nr, char *buf, int len)
+{
+	struct disk_dump_device *device = dump_part->device;
+	int ret;
+
+	touch_nmi_watchdog();
+	ret = device->ops.rw_block(dump_part, READ, nr, buf, len);
+	if (ret < 0) {
+		Err("read error on block %u", nr);
+		return ret;
+	}
+	return 0;
+}
+
+static int write_blocks(struct disk_dump_partition *dump_part, unsigned int offs, char *buf, int len)
+{
+	struct disk_dump_device *device = dump_part->device;
+	int ret;
+
+	touch_nmi_watchdog();
+	ret = device->ops.rw_block(dump_part, WRITE, offs, buf, len);
+	if (ret < 0) {
+		Err("write error on block %u", offs);
+		return ret;
+	}
+	return 0;
+}
+
+/*
+ * Initialize the common header
+ */
+
+/*
+ * Write the common header
+ */
+static int write_header(struct disk_dump_partition *dump_part)
+{
+	memset(scratch, '\0', PAGE_SIZE);
+	memcpy(scratch, &dump_header, sizeof(dump_header));
+
+	return write_blocks(dump_part, 1, scratch, 1);
+}
+
+/*
+ * Check the signaures in all blocks of the dump partition
+ * Return TRUE if the signature is correct, else return FALSE
+ */
+static int check_dump_partition(struct disk_dump_partition *dump_part, unsigned int partition_size)
+{
+	unsigned int blk;
+	int ret;
+	unsigned int chunk_blks, skips;
+	int i;
+
+	if (sample_rate < 0)		/* No check */
+		return TRUE;
+
+	/*
+	 * If the device has limitations of transfer size, use it.
+	 */
+	chunk_blks = 1 << block_order;
+	if (dump_part->device->max_blocks)
+		 chunk_blks = min(chunk_blks, dump_part->device->max_blocks);
+	skips = chunk_blks << sample_rate;
+
+	lapse = 0;
+	for (blk = 0; blk < partition_size; blk += skips) {
+		unsigned int len;
+redo:
+		len = min(chunk_blks, partition_size - blk);
+		if ((ret = read_blocks(dump_part, blk, scratch, len)) < 0)
+			return FALSE;
+		print_status(blk + 1, partition_size);
+		for (i = 0; i < len; i++)
+			if (!check_block_signature(scratch + i * DUMP_BLOCK_SIZE, blk + i)) {
+				Err("bad signature in block %u", blk + i);
+				return FALSE;
+			}
+	}
+	/* Check the end of the dump partition */
+	if (blk - skips + chunk_blks < partition_size) {
+		blk = partition_size - chunk_blks;
+		goto redo;
+	}
+	clear_status(blk, partition_size);
+	return TRUE;
+}
+
+/*
+ * Write memory bitmap after location of dump headers.
+ */
+#define IDX2PAGENR(nr, byte, bit)	(((nr) * PAGE_SIZE + (byte)) * 8 + (bit))
+static int write_bitmap(struct disk_dump_partition *dump_part, unsigned int bitmap_offset, unsigned int bitmap_blocks)
+{
+	unsigned int nr;
+	int bit, byte;
+	int ret = 0;
+	unsigned char val;
+
+	for (nr = 0; nr < bitmap_blocks; nr++) {
+		for (byte = 0; byte < PAGE_SIZE; byte++) {
+			val = 0;
+			for (bit = 0; bit < 8; bit++)
+				if (page_is_ram(IDX2PAGENR(nr, byte, bit)))
+					val |= (1 << bit);
+			scratch[byte] = (char)val;
+		}
+		if ((ret = write_blocks(dump_part, bitmap_offset + nr, scratch, 1)) < 0) {
+			Err("I/O error %d on block %u", ret, bitmap_offset + nr);
+			break;
+		}
+	}
+	return ret;
+}
+
+/*
+ * Write whole memory to dump partition.
+ * Return value is the number of writen blocks.
+ */
+static int write_memory(struct disk_dump_partition *dump_part, int offset, unsigned int max_blocks_written, unsigned int *blocks_written)
+{
+	char *kaddr;
+	unsigned int blocks = 0;
+	struct page *page;
+	unsigned int nr;
+	int ret = 0;
+	int blk_in_chunk = 0;
+
+	for (nr = 0; nr < max_pfn; nr++) {
+		print_status(blocks, max_blocks_written);
+
+		if (!page_is_ram(nr))
+			continue;
+
+		if (blocks >= max_blocks_written) {
+			Warn("dump device is too small. %lu pages were not saved", max_pfn - blocks);
+			goto out;
+		}
+		page = pfn_to_page(nr);
+		kaddr = (char *)kmap_atomic(page, KM_DISKDUMP);
+		/*
+		 * need to copy because adapter drivers use virt_to_bus()
+		 */
+		memcpy(scratch + blk_in_chunk * PAGE_SIZE, kaddr, PAGE_SIZE);
+		blk_in_chunk++;
+		blocks++;
+		kunmap_atomic(kaddr, KM_DISKDUMP);
+
+		if (blk_in_chunk >= (1 << block_order)) {
+			ret = write_blocks(dump_part, offset, scratch, blk_in_chunk);
+			if (ret < 0) {
+				Err("I/O error %d on block %u", ret, offset);
+				break;
+			}
+			offset += blk_in_chunk;
+			blk_in_chunk = 0;
+		}
+	}
+	if (ret >= 0 && blk_in_chunk > 0) {
+		ret = write_blocks(dump_part, offset, scratch, blk_in_chunk);
+		if (ret < 0)
+			Err("I/O error %d on block %u", ret, offset);
+	}
+
+out:
+	clear_status(nr, max_blocks_written);
+
+	*blocks_written = blocks;
+	return ret;
+}
+
+/*
+ * Select most suitable dump device. sanity_check() returns the state
+ * of each dump device. 0 means OK, negative value means NG, and
+ * positive value means it maybe work. select_dump_partition() first
+ * try to select a sane device and if it has no sane device and
+ * allow_risky_dumps is set, it select one from maybe OK devices.
+ *
+ * XXX We cannot handle multiple partitions yet.
+ */
+static struct disk_dump_partition *select_dump_partition(void)
+{
+	struct disk_dump_device *dump_device;
+	struct disk_dump_partition *dump_part;
+	int sanity;
+	int strict_check = 1;
+
+redo:
+	/*
+	 * Select a sane polling driver.
+	 */
+	list_for_each_entry(dump_device, &disk_dump_devices, list) {
+		sanity = 0;
+		if (dump_device->ops.sanity_check)
+			sanity = dump_device->ops.sanity_check(dump_device);
+		if (sanity < 0 || (sanity > 0 && strict_check))
+			continue;
+		list_for_each_entry(dump_part, &dump_device->partitions, list)
+				return dump_part;
+	}
+	if (allow_risky_dumps && strict_check) {
+		strict_check = 0;
+		goto redo;
+	}
+	return NULL;
+}
+
+static void disk_dump(struct pt_regs *regs, void *platform_arg)
+{
+	unsigned long flags;
+	int ret = -EIO;
+	struct pt_regs myregs;
+	unsigned int max_written_blocks, written_blocks;
+	int i;
+	struct disk_dump_device *dump_device = NULL;
+	struct disk_dump_partition *dump_part = NULL;
+
+	/* Inhibit interrupt and stop other CPUs */
+	local_save_flags(flags);
+	local_irq_disable();
+
+	diskdump_lib_init();
+
+	/*
+	 * Check the checksum of myself
+	 */
+	if (!spin_trylock(&disk_dump_lock)) {
+		Err("disk_dump_lock failed.");
+		goto done;
+	}
+
+	spin_trylock(&disk_dump_lock);
+	if (!check_crc_module()) {
+		Err("checksum error. diskdump common module may be compromised.");
+		goto done;
+	}
+
+	diskdump_mode = 1;
+
+	Dbg("notify dump start.");
+	notifier_call_chain(&disk_dump_notifier_list, 0, NULL);
+
+	dump_header.tasks[smp_processor_id()] = current;
+#if CONFIG_SMP
+	smp_call_function(freeze_cpu, NULL, 1, 0);
+	mdelay(3000);
+	printk("CPU frozen: ");
+	for (i = 0; i < NR_CPUS; i++) {
+		if (dump_header.tasks[i] != NULL)
+			printk("#%d", i);
+
+	}
+	printk("\n");
+	printk("CPU#%d is executing diskdump.\n", smp_processor_id());
+#else
+	mdelay(1000);
+#endif
+
+	platform_fix_regs();
+
+	if (list_empty(&disk_dump_devices)) {
+		Err("adapter driver is not registered.");
+		goto done;
+	}
+
+	printk("start dumping\n");
+
+	if (!(dump_part = select_dump_partition())) {
+		Err("No sane dump device found");
+		goto done;
+	}
+	dump_device = dump_part->device;
+
+	/*
+	 * Stop ongoing I/O with polling driver and make the shift to I/O mode
+	 * for dump
+	 */
+	Dbg("do quiesce");
+	if (dump_device->ops.quiesce)
+		if ((ret = dump_device->ops.quiesce(dump_device)) < 0) {
+			Err("quiesce failed. error %d", ret);
+			goto done;
+		}
+
+	if (SECTOR_BLOCK(dump_part->nr_sects) < header_blocks + bitmap_blocks) {
+		Warn("dump partition is too small. Aborted");
+		goto done;
+	}
+
+	/* Check dump partition */
+	printk("check dump partition...\n");
+	if (!check_dump_partition(dump_part, total_blocks)) {
+		Err("check partition failed.");
+		goto done;
+	}
+
+	/*
+	 * Write the common header
+	 */
+	memcpy(dump_header.signature, DISK_DUMP_SIGNATURE, sizeof(dump_header.signature));
+	dump_header.utsname	     = system_utsname;
+	dump_header.timestamp	     = xtime;
+	dump_header.status	     = DUMP_HEADER_INCOMPLETED;
+	dump_header.block_size	     = PAGE_SIZE;
+	dump_header.sub_hdr_size     = size_of_sub_header();
+	dump_header.bitmap_blocks    = bitmap_blocks;
+	dump_header.max_mapnr	     = max_pfn;
+	dump_header.total_ram_blocks = total_ram_blocks;
+	dump_header.device_blocks    = SECTOR_BLOCK(dump_part->nr_sects);
+	dump_header.current_cpu	     = smp_processor_id();
+	dump_header.nr_cpus	     = num_online_cpus();
+	dump_header.written_blocks   = 2;
+
+	write_header(dump_part);
+
+	/*
+	 * Write the architecture dependent header
+	 */
+	Dbg("write sub header");
+	if ((ret = write_sub_header()) < 0) {
+		Err("writing sub header failed. error %d", ret);
+		goto done;
+	}
+
+	Dbg("writing memory bitmaps..");
+	if ((ret = write_bitmap(dump_part, header_blocks, bitmap_blocks)) < 0)
+		goto done;
+
+	max_written_blocks = total_ram_blocks;
+	if (dump_header.device_blocks < total_blocks) {
+		Warn("dump partition is too small. actual blocks %u. expected blocks %u. whole memory will not be saved",
+				dump_header.device_blocks, total_blocks);
+		max_written_blocks -= (total_blocks - dump_header.device_blocks);
+	}
+
+	dump_header.written_blocks += dump_header.sub_hdr_size;
+	dump_header.written_blocks += dump_header.bitmap_blocks;
+	write_header(dump_part);
+
+	printk("dumping memory..\n");
+	if ((ret = write_memory(dump_part, header_blocks + bitmap_blocks,
+				max_written_blocks, &written_blocks)) < 0)
+		goto done;
+
+	/*
+	 * Set the number of block that is written into and write it
+	 * into partition again.
+	 */
+	dump_header.written_blocks += written_blocks;
+	dump_header.status = DUMP_HEADER_COMPLETED;
+	write_header(dump_part);
+
+	ret = 0;
+
+done:
+	Dbg("do adapter shutdown.");
+	if (dump_device && dump_device->ops.shutdown)
+		if (dump_device->ops.shutdown(dump_device))
+			Err("adapter shutdown failed.");
+
+	/*
+	 * If diskdump failed and fallback_on_err is set,
+	 * We just return and leave panic to netdump.
+	 */
+	if (fallback_on_err && ret != 0)
+		return;
+
+	Dbg("notify panic.");
+	notifier_call_chain(&panic_notifier_list, 0, NULL);
+
+	diskdump_lib_exit();
+
+	if (panic_timeout > 0) {
+		int i;
+
+		printk(KERN_EMERG "Rebooting in %d second%s..",
+			panic_timeout, "s" + (panic_timeout == 1));
+		for (i = 0; i < panic_timeout; i++) {
+			touch_nmi_watchdog();
+			mdelay(1000);
+		}
+		printk("\n");
+		machine_restart(NULL);
+	}
+	printk(KERN_EMERG "halt\n");
+	for (;;) {
+		touch_nmi_watchdog();
+		machine_halt();
+		mdelay(1000);
+	}
+}
+
+static struct disk_dump_partition *find_dump_partition(dev_t dev)
+{
+	struct disk_dump_device *dump_device;
+	struct disk_dump_partition *dump_part;
+
+	list_for_each_entry(dump_device, &disk_dump_devices, list)
+		list_for_each_entry(dump_part, &dump_device->partitions, list)
+			if (dump_part->dentry->d_inode->i_rdev == dev)
+				return dump_part;
+	return NULL;
+}
+
+static struct disk_dump_device *find_dump_device(void *real_device)
+{
+	struct disk_dump_device *dump_device;
+
+	list_for_each_entry(dump_device, &disk_dump_devices, list)
+		if (real_device == dump_device->device)
+			return  dump_device;
+	return NULL;
+}
+
+static void *find_real_device(struct block_device *bdev, struct disk_dump_type **_dump_type)
+{
+	void *real_device;
+	struct disk_dump_type *dump_type;
+
+	list_for_each_entry(dump_type, &disk_dump_types, list)
+		if ((real_device = dump_type->probe(bdev)) != NULL) {
+			*_dump_type = dump_type;
+			return real_device;
+		}
+	return NULL;
+}
+
+/*
+ * Add dump partition structure corresponding to file to the dump device
+ * structure.
+ */
+static int add_dump_partition(struct disk_dump_device *dump_device, struct file *file)
+{
+	struct disk_dump_partition *dump_part;
+	struct inode *inode = file->f_dentry->d_inode;
+	dev_t dev = inode->i_rdev;
+	struct block_device *bdev = inode->i_bdev;
+	char buffer[32];
+
+	if (!(dump_part = kmalloc(sizeof(*dump_part), GFP_KERNEL)))
+		return -ENOMEM;
+
+	dump_part->device   = dump_device;
+	dump_part->vfsmount = mntget(file->f_vfsmnt);
+	dump_part->dentry   = dget(file->f_dentry);
+
+	if (!bdev || !bdev->bd_part)
+		return -EINVAL;
+	dump_part->nr_sects   = bdev->bd_part->nr_sects;
+	dump_part->start_sect = bdev->bd_part->start_sect;
+
+	if (SECTOR_BLOCK(dump_part->nr_sects) < total_blocks) {
+		format_dev_t(buffer, dev);
+		Warn("%s is too small to save whole system memory\n", buffer);
+	}
+
+	list_add(&dump_part->list, &dump_device->partitions);
+
+	return 0;
+}
+
+/*
+ * Add dump partition corresponding to file.
+ * Must be called with disk_dump_lock held.
+ */
+static int add_dump(struct file *file)
+{
+	struct disk_dump_type *dump_type = NULL;
+	struct disk_dump_device *dump_device;
+	void *real_device;
+	struct inode *dump_inode = file->f_dentry->d_inode;
+	dev_t dev = dump_inode->i_rdev;
+	int ret;
+
+	/* Check whether this inode is already registered */
+	if (find_dump_partition(dev))
+		return -EEXIST;
+
+	/* find dump_type and real device for this inode */
+	if (!(real_device = find_real_device(dump_inode->i_bdev, &dump_type)))
+		return -ENXIO;
+
+	dump_device = find_dump_device(real_device);
+	if (dump_device == NULL) {
+		/* real_device is not registered. create new dump_device */
+		if (!(dump_device = kmalloc(sizeof(*dump_device), GFP_KERNEL)))
+			return -ENOMEM;
+
+		memset(dump_device, 0, sizeof(*dump_device));
+		INIT_LIST_HEAD(&dump_device->partitions);
+
+		dump_device->dump_type = dump_type;
+		dump_device->device = real_device;
+		if ((ret = dump_type->add_device(dump_device)) < 0) {
+			kfree(dump_device);
+			return ret;
+		}
+		if (!try_module_get(dump_type->owner))
+			return -EINVAL;
+		list_add(&dump_device->list, &disk_dump_devices);
+	}
+
+	ret = add_dump_partition(dump_device, file);
+	if (ret < 0) {
+		dump_type->remove_device(dump_device);
+		module_put(dump_type->owner);
+		list_del(&dump_device->list);
+		kfree(dump_device);
+	}
+
+	return ret;
+}
+
+/*
+ * Remove dump partition corresponding to file.
+ * Must be called with disk_dump_lock held.
+ */
+static int remove_dump(struct file *file)
+{
+	struct disk_dump_device *dump_device;
+	struct disk_dump_partition *dump_part;
+	struct disk_dump_type *dump_type;
+	dev_t dev = file->f_dentry->d_inode->i_rdev;
+
+	if (!(dump_part = find_dump_partition(dev)))
+		return -ENOENT;
+
+	dump_device = dump_part->device;
+
+	list_del(&dump_part->list);
+	mntput(dump_part->vfsmount);
+	dput(dump_part->dentry);
+	kfree(dump_part);
+
+	if (list_empty(&dump_device->partitions)) {
+		dump_type = dump_device->dump_type;
+		dump_type->remove_device(dump_device);
+		module_put(dump_type->owner);
+		module_put(dump_type->owner);
+		list_del(&dump_device->list);
+		kfree(dump_device);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+static int proc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long param)
+{
+	int fd = (int)param;
+	int ret;
+	struct file *dump_file;
+	struct inode *dump_inode;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	dump_file = fget(fd);
+	if (!dump_file)
+		return -EBADF;
+	dump_inode = dump_file->f_dentry->d_inode;
+	if (!dump_inode->i_bdev) {
+		fput(dump_file);
+		return -EBADF;
+	}
+
+	spin_lock(&disk_dump_lock);
+	switch (cmd) {
+	case BLKADDDUMPDEVICE:
+		ret = add_dump(dump_file);
+		break;
+	case BLKREMOVEDUMPDEVICE:
+		ret = remove_dump(dump_file);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	set_crc_modules();
+	spin_unlock(&disk_dump_lock);
+
+	fput(dump_file);
+
+	return ret;
+}
+
+static void *disk_dump_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct disk_dump_device *dump_device;
+	struct disk_dump_partition *dump_part;
+	loff_t n = *pos;
+
+	spin_lock(&disk_dump_lock);
+	list_for_each_entry(dump_device, &disk_dump_devices, list) {
+		seq->private = dump_device;
+		list_for_each_entry(dump_part, &dump_device->partitions, list) {
+			if (!n--)
+				return dump_part;
+		}
+	}
+	return NULL;
+}
+
+static void *disk_dump_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct list_head *partition = v;
+	struct list_head *device = seq->private;
+	struct disk_dump_device *dump_device;
+
+	dump_device = list_entry(device, struct disk_dump_device, list);
+
+	(*pos)++;
+	partition = partition->next;
+	if (partition != &dump_device->partitions)
+		return partition;
+
+	device = device->next;
+	seq->private = device;
+	if (device == &disk_dump_devices)
+		return NULL;
+
+	dump_device = list_entry(device, struct disk_dump_device, list);
+
+	return dump_device->partitions.next;
+}
+
+static void disk_dump_seq_stop(struct seq_file *seq, void *v)
+{
+	spin_unlock(&disk_dump_lock);
+}
+
+static int disk_dump_seq_show(struct seq_file *seq, void *v)
+{
+	struct disk_dump_partition *dump_part = v;
+	char *page;
+	char *path;
+
+	if (!(page = (char *)__get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+	path = d_path(dump_part->dentry, dump_part->vfsmount, page, PAGE_SIZE);
+	seq_printf(seq, "%s %lu %lu\n",
+		path, dump_part->start_sect, dump_part->nr_sects);
+	free_page((unsigned long)page);
+	return 0;
+}
+
+static struct seq_operations disk_dump_seq_ops = {
+	.start	= disk_dump_seq_start,
+	.next	= disk_dump_seq_next,
+	.stop	= disk_dump_seq_stop,
+	.show	= disk_dump_seq_show,
+};
+
+static int disk_dump_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &disk_dump_seq_ops);
+}
+
+static struct file_operations disk_dump_fops = {
+	.owner		= THIS_MODULE,
+	.open		= disk_dump_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+	.ioctl		= proc_ioctl,
+};
+#endif
+
+
+int register_disk_dump_type(struct disk_dump_type *dump_type)
+{
+	spin_lock(&disk_dump_lock);
+	list_add(&dump_type->list, &disk_dump_types);
+	set_crc_modules();
+	spin_unlock(&disk_dump_lock);
+
+	return 0;
+}
+
+int unregister_disk_dump_type(struct disk_dump_type *dump_type)
+{
+	spin_lock(&disk_dump_lock);
+	list_del(&dump_type->list);
+	set_crc_modules();
+	spin_unlock(&disk_dump_lock);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(register_disk_dump_type);
+EXPORT_SYMBOL(unregister_disk_dump_type);
+EXPORT_SYMBOL(disk_dump_notifier_list);
+
+
+static void compute_total_blocks(void)
+{
+	unsigned int nr;
+
+	/*
+	 * the number of block of the common header and the header
+	 * that is depend on the architecture
+	 *
+	 * block 0:		dump partition header
+	 * block 1:		dump header
+	 * block 2:		dump subheader
+	 * block 3..n:		memory bitmap
+	 * block (n + 1)...:	saved memory
+	 *
+	 * We never overwrite block 0
+	 */
+	header_blocks = 2 + size_of_sub_header();
+
+	total_ram_blocks = 0;
+	for (nr = 0; nr < max_pfn; nr++) {
+		if (page_is_ram(nr))
+			total_ram_blocks++;
+	}
+
+	bitmap_blocks = ROUNDUP(max_pfn, 8 * PAGE_SIZE);
+
+	/*
+	 * The necessary size of area for dump is:
+	 * 1 block for common header
+	 * m blocks for architecture dependent header
+	 * n blocks for memory bitmap
+	 * and whole memory
+	 */
+	total_blocks = header_blocks + bitmap_blocks + total_ram_blocks;
+
+	Info("total blocks required: %u (header %u + bitmap %u + memory %u)",
+		total_blocks, header_blocks, bitmap_blocks, total_ram_blocks);
+}
+
+static int init_diskdump(void)
+{
+	unsigned long long t0;
+	unsigned long long t1;
+	struct page *page;
+
+	if (!platform_supports_diskdump) {
+		Err("platform does not support diskdump.");
+		return -1;
+	}
+
+	/* Allocate one block that is used temporally */
+	do {
+		page = alloc_pages(GFP_KERNEL, block_order);
+		if (page != NULL)
+			break;
+	} while (--block_order >= 0);
+	if (!page) {
+		Err("alloc_pages failed.");
+		return -1;
+	}
+	scratch = page_address(page);
+	Info("Maximum block size: %lu", PAGE_SIZE << block_order);
+
+	if (diskdump_register_hook(disk_dump)) {
+		Err("failed to register hooks.");
+		return -1;
+	}
+
+	compute_total_blocks();
+
+	platform_timestamp(t0);
+	mdelay(1);
+	platform_timestamp(t1);
+	timestamp_1sec = (unsigned long)(t1 - t0) * 1000;
+
+#ifdef CONFIG_PROC_FS
+	{
+		struct proc_dir_entry *p;
+
+		p = create_proc_entry("diskdump", S_IRUGO|S_IWUSR, NULL);
+		if (p)
+			p->proc_fops = &disk_dump_fops;
+	}
+#endif
+
+	return 0;
+}
+
+static void cleanup_diskdump(void)
+{
+	Info("shut down.");
+	diskdump_unregister_hook();
+	free_pages((unsigned long)scratch, block_order);
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("diskdump", NULL);
+#endif
+}
+
+module_init(init_diskdump);
+module_exit(cleanup_diskdump);
+MODULE_LICENSE("GPL");
diff -Nur linux-2.6.6.org/drivers/block/diskdumplib.c linux-2.6.6/drivers/block/diskdumplib.c
--- linux-2.6.6.org/drivers/block/diskdumplib.c	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.6/drivers/block/diskdumplib.c	2004-06-16 19:34:16.000000000 +0900
@@ -0,0 +1,203 @@
+/*
+ *  linux/drivers/block/diskdumplib.c
+ *
+ *  Copyright (C) 2004  FUJITSU LIMITED
+ *  Written by Nobuhiro Tachino (ntachino@jp.fujitsu.com)
+ *
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/file.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+#include <linux/smp_lock.h>
+#include <linux/genhd.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/diskdump.h>
+#include <linux/diskdumplib.h>
+#include <asm/diskdump.h>
+
+/*
+ * timer list and tasklet_struct holder
+ */
+unsigned long volatile diskdump_base_jiffies;
+static unsigned long long timestamp_base;
+static unsigned long timestamp_hz;
+
+#define DISKDUMP_NUM_TASKLETS	8
+
+/*
+ * We can't use next field of tasklet because it breaks the original
+ * tasklets chain and we have no way to know which chain the tasklet is
+ * linked.
+ */
+static struct tasklet_struct	*diskdump_tasklets[DISKDUMP_NUM_TASKLETS];
+
+static LIST_HEAD(diskdump_timers);
+static LIST_HEAD(diskdump_workq);
+
+
+static int store_tasklet(struct tasklet_struct *tasklet)
+{
+	int i;
+
+	for (i = 0; i < DISKDUMP_NUM_TASKLETS; i++)
+		if (diskdump_tasklets[i] == NULL) {
+			diskdump_tasklets[i] = tasklet;
+			return 0;
+		}
+	return -1;
+}
+
+static struct tasklet_struct *find_tasklet(struct tasklet_struct *tasklet)
+{
+	int i;
+
+	for (i = 0; i < DISKDUMP_NUM_TASKLETS; i++)
+		if (diskdump_tasklets[i] == tasklet)
+			return diskdump_tasklets[i];
+	return NULL;
+}
+
+void _diskdump_tasklet_schedule(struct tasklet_struct *tasklet)
+{
+	if (!find_tasklet(tasklet))
+		if (store_tasklet(tasklet))
+			printk(KERN_ERR "diskdumplib: too many tasklet. Ignored\n");
+	set_bit(TASKLET_STATE_SCHED, &tasklet->state);
+}
+
+int _diskdump_schedule_work(struct work_struct *work)
+{
+	list_add_tail(&work->entry, &diskdump_workq);
+	return 1;
+}
+
+void _diskdump_add_timer(struct timer_list *timer)
+{
+	timer->base = (void *)1;
+	list_add(&timer->entry, &diskdump_timers);
+}
+
+int _diskdump_del_timer(struct timer_list *timer)
+{
+	if (timer->base != NULL) {
+		list_del(&timer->entry);
+		return 1;
+	} else {
+		timer->base = NULL;
+		return 0;
+	}
+}
+
+int _diskdump_mod_timer(struct timer_list *timer, unsigned long expires)
+{
+	int ret;
+
+	ret = _diskdump_del_timer(timer);
+	timer->expires = expires;
+	_diskdump_add_timer(timer);
+
+	return ret;
+}
+
+static void update_jiffies(void)
+{
+	unsigned long long t;
+
+	platform_timestamp(t);
+	while (t > timestamp_base + timestamp_hz) {
+		timestamp_base += timestamp_hz;
+		jiffies++;
+		platform_timestamp(t);
+	}
+}
+
+void diskdump_update(void)
+{
+	struct tasklet_struct *tasklet;
+	struct work_struct *work;
+	struct timer_list *timer;
+	struct list_head *t, *n, head;
+	int i;
+
+	update_jiffies();
+
+	/* run timers */
+	list_for_each_safe(t, n, &diskdump_timers) {
+		timer = list_entry(t, struct timer_list, entry);
+		if (time_before_eq(timer->expires, jiffies)) {
+			list_del(t);
+			timer->function(timer->data);
+		}
+	}
+
+	/* run tasklet */
+	for (i = 0; i < DISKDUMP_NUM_TASKLETS; i++)
+		if ((tasklet = diskdump_tasklets[i]))
+			if (!atomic_read(&tasklet->count))
+				if (test_and_clear_bit(TASKLET_STATE_SCHED, &tasklet->state))
+					tasklet->func(tasklet->data);
+
+	/* run work queue */
+	list_add(&head, &diskdump_workq);
+	list_del_init(&diskdump_workq);
+	n = head.next;
+	while (n != &head) {
+		work = list_entry(t, struct work_struct, entry);
+		n = n->next;
+		if (work->func)
+			work->func(work->wq_data);
+	}
+}
+
+void diskdump_lib_init(void)
+{
+	unsigned long long t;
+
+	/* Save original jiffies value */
+	diskdump_base_jiffies = jiffies;
+
+	platform_timestamp(timestamp_base);
+	udelay(1000000/HZ);
+	platform_timestamp(t);
+	timestamp_hz = (unsigned long)(t - timestamp_base);
+
+	diskdump_update();
+}
+
+void diskdump_lib_exit(void)
+{
+	/* Resotre original jiffies. */
+	jiffies = diskdump_base_jiffies;
+}
+
+EXPORT_SYMBOL(diskdump_lib_init);
+EXPORT_SYMBOL(diskdump_lib_exit);
+EXPORT_SYMBOL(diskdump_update);
+EXPORT_SYMBOL(_diskdump_add_timer);
+EXPORT_SYMBOL(_diskdump_del_timer);
+EXPORT_SYMBOL(_diskdump_mod_timer);
+EXPORT_SYMBOL(_diskdump_tasklet_schedule);
+EXPORT_SYMBOL(_diskdump_schedule_work);
+
+MODULE_LICENSE("GPL");
diff -Nur linux-2.6.6.org/drivers/block/genhd.c linux-2.6.6/drivers/block/genhd.c
--- linux-2.6.6.org/drivers/block/genhd.c	2004-06-04 21:22:15.000000000 +0900
+++ linux-2.6.6/drivers/block/genhd.c	2004-06-16 19:34:16.000000000 +0900
@@ -629,3 +629,28 @@
 }
 
 EXPORT_SYMBOL(invalidate_partition);
+
+/*
+ *  Dump  stuff.
+ */
+void (*diskdump_func) (struct pt_regs *regs, void *platform_arg) = NULL;
+EXPORT_SYMBOL_GPL(diskdump_func);
+
+int diskdump_register_hook(void (*dump_func) (struct pt_regs *, void *))
+{
+	if (diskdump_func)
+		return -EEXIST;
+
+	diskdump_func = dump_func;
+
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(diskdump_register_hook);
+
+void diskdump_unregister_hook(void)
+{
+	diskdump_func = NULL;
+}
+
+EXPORT_SYMBOL_GPL(diskdump_unregister_hook);
diff -Nur linux-2.6.6.org/drivers/char/sysrq.c linux-2.6.6/drivers/char/sysrq.c
--- linux-2.6.6.org/drivers/char/sysrq.c	2004-06-04 21:22:29.000000000 +0900
+++ linux-2.6.6/drivers/char/sysrq.c	2004-06-16 19:34:16.000000000 +0900
@@ -107,6 +107,19 @@
 	.action_msg	= "Resetting",
 };
 
+/* crash sysrq handler */
+static void sysrq_handle_crash(int key, struct pt_regs *pt_regs,
+			      struct tty_struct *tty) 
+{
+	*( (char *) 0) = 0;
+}
+
+static struct sysrq_key_op sysrq_crash_op = {
+	.handler	= sysrq_handle_crash,
+	.help_msg	= "Crash",
+	.action_msg	= "Crashing the kernel by request",
+};
+
 static void sysrq_handle_sync(int key, struct pt_regs *pt_regs,
 			      struct tty_struct *tty) 
 {
@@ -235,7 +248,7 @@
 		 it is handled specially on the sparc
 		 and will never arrive */
 /* b */	&sysrq_reboot_op,
-/* c */ NULL,
+/* c */ &sysrq_crash_op,
 /* d */	NULL,
 /* e */	&sysrq_term_op,
 /* f */	NULL,
diff -Nur linux-2.6.6.org/include/asm-i386/diskdump.h linux-2.6.6/include/asm-i386/diskdump.h
--- linux-2.6.6.org/include/asm-i386/diskdump.h	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.6/include/asm-i386/diskdump.h	2004-06-16 19:34:16.000000000 +0900
@@ -0,0 +1,82 @@
+#ifndef _ASM_I386_DISKDUMP_H
+#define _ASM_I386_DISKDUMP_H
+
+/*
+ * linux/include/asm-i386/diskdump.h
+ *
+ * Copyright (c) 2004 FUJITSU LIMITED
+ * Copyright (c) 2003 Red Hat, Inc. All rights reserved.
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifdef __KERNEL__
+
+#include <linux/elf.h>
+
+extern int page_is_ram(unsigned long);
+const static int platform_supports_diskdump = 1;
+
+#define platform_fix_regs() \
+{                                                                      \
+       unsigned long esp;                                              \
+       unsigned short ss;                                              \
+       esp = (unsigned long) ((char *)regs + sizeof (struct pt_regs)); \
+       ss = __KERNEL_DS;                                               \
+       if (regs->xcs & 3) {                                            \
+               esp = regs->esp;                                        \
+               ss = regs->xss & 0xffff;                                \
+       }                                                               \
+       myregs = *regs;                                                 \
+       myregs.esp = esp;                                               \
+       myregs.xss = (myregs.xss & 0xffff0000) | ss;                    \
+}
+
+struct disk_dump_sub_header {
+	elf_gregset_t		elf_regs;
+};
+
+#define platform_timestamp(x) rdtscll(x)
+
+#define size_of_sub_header()	((sizeof(struct disk_dump_sub_header) + PAGE_SIZE - 1) / DUMP_BLOCK_SIZE)
+
+#define write_sub_header() 						\
+({									\
+ 	int ret;							\
+									\
+	ELF_CORE_COPY_REGS(dump_sub_header.elf_regs, (&myregs));	\
+	clear_page(scratch);						\
+	memcpy(scratch, &dump_sub_header, sizeof(dump_sub_header));	\
+ 									\
+	if ((ret = write_blocks(dump_part, 2, scratch, 1)) >= 0)	\
+		ret = 1; /* size of sub header in page */;		\
+	ret;								\
+})
+
+#define platform_freeze_cpu()					\
+{								\
+	for (;;) local_irq_disable();				\
+}
+
+#define platform_start_diskdump(func, regs)			\
+{								\
+	func(regs, NULL);					\
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_I386_DISKDUMP_H */
diff -Nur linux-2.6.6.org/include/asm-i386/kmap_types.h linux-2.6.6/include/asm-i386/kmap_types.h
--- linux-2.6.6.org/include/asm-i386/kmap_types.h	2004-06-04 21:22:02.000000000 +0900
+++ linux-2.6.6/include/asm-i386/kmap_types.h	2004-06-16 19:34:16.000000000 +0900
@@ -24,7 +24,8 @@
 D(11)	KM_IRQ1,
 D(12)	KM_SOFTIRQ0,
 D(13)	KM_SOFTIRQ1,
-D(14)	KM_TYPE_NR
+D(14)	KM_DISKDUMP,
+D(15)	KM_TYPE_NR
 };
 
 #undef D
diff -Nur linux-2.6.6.org/include/linux/diskdump.h linux-2.6.6/include/linux/diskdump.h
--- linux-2.6.6.org/include/linux/diskdump.h	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.6/include/linux/diskdump.h	2004-06-16 19:34:16.000000000 +0900
@@ -0,0 +1,157 @@
+#ifndef _LINUX_DISKDUMP_H
+#define _LINUX_DISKDUMP_H
+
+/*
+ * linux/include/linux/diskdump.h
+ *
+ * Copyright (c) 2004 FUJITSU LIMITED
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/list.h>
+#include <linux/mount.h>
+#include <linux/dcache.h>
+#include <linux/blkdev.h>
+#include <linux/utsname.h>
+
+/* The minimum Dump I/O unit. Must be the same of PAGE_SIZE */
+#define DUMP_BLOCK_SIZE		PAGE_SIZE
+#define DUMP_BLOCK_SHIFT	PAGE_SHIFT
+
+/* Dump ioctls */
+#define BLKADDDUMPDEVICE	0xdf00		/* Add a dump device */
+#define BLKREMOVEDUMPDEVICE	0xdf01		/* Delete a dump device */
+
+int diskdump_register_hook(void (*dump_func)(struct pt_regs *, void *));
+void diskdump_unregister_hook(void);
+
+/*
+ * The handler that adapter driver provides for the common module of
+ * dump
+ */
+struct disk_dump_partition;
+struct disk_dump_device;
+
+struct disk_dump_type {
+	void *(*probe)(struct block_device *);
+	int (*add_device)(struct disk_dump_device *);
+	void (*remove_device)(struct disk_dump_device *);
+	struct module *owner;
+	struct list_head list;
+};
+
+struct disk_dump_device_ops {
+	int (*sanity_check)(struct disk_dump_device *);
+	int (*quiesce)(struct disk_dump_device *);
+	int (*shutdown)(struct disk_dump_device *);
+	int (*rw_block)(struct disk_dump_partition *, int rw, unsigned long block_nr, void *buf, int len);
+};
+
+/* The data structure for a dump device */
+struct disk_dump_device {
+	struct list_head list;
+	struct disk_dump_device_ops ops;
+	struct disk_dump_type *dump_type;
+	void *device;
+	unsigned int max_blocks;
+	struct list_head partitions;
+};
+
+/* The data structure for a dump partition */
+struct disk_dump_partition {
+	struct list_head list;
+	struct disk_dump_device *device;
+	struct vfsmount *vfsmount;
+	struct dentry *dentry;
+	unsigned long start_sect;
+	unsigned long nr_sects;
+};
+
+
+int register_disk_dump_type(struct disk_dump_type *);
+int unregister_disk_dump_type(struct disk_dump_type *);
+
+
+/*
+ * Architecture-independent dump header
+ */
+
+/* The signature which is written in each block in the dump partition */
+#define DISK_DUMP_SIGNATURE		"DISKDUMP"
+#define DISK_DUMP_HEADER_VERSION	1
+
+#define DUMP_PARTITION_SIGNATURE	"diskdump"
+
+#define DUMP_HEADER_COMPLETED	0
+#define DUMP_HEADER_INCOMPLETED	1
+
+struct disk_dump_header {
+	char			signature[8];	/* = "DISKDUMP" */
+	int			header_version;	/* Dump header version */
+	struct new_utsname	utsname;	/* copy of system_utsname */
+	struct timespec		timestamp;	/* Time stamp */
+	unsigned int		status;		/* Above flags */
+	int			block_size;	/* Size of a block in byte */
+	int			sub_hdr_size;	/* Size of arch dependent
+						   header in blocks */
+	unsigned int		bitmap_blocks;	/* Size of Memory bitmap in
+						   block */
+	unsigned int		max_mapnr;	/* = max_mapnr */
+	unsigned int		total_ram_blocks;/* Size of Memory in block */
+	unsigned int		device_blocks;	/* Number of total blocks in
+						 * the dump device */
+	unsigned int		written_blocks;	/* Number of written blocks */
+	unsigned int		current_cpu;	/* CPU# which handles dump */
+	int			nr_cpus;	/* Number of CPUs */
+	struct task_struct	*tasks[NR_CPUS];
+};
+
+/*
+ * Calculate the check sum of the whole module
+ */
+#define get_crc_module()						\
+({									\
+	struct module *module = &__this_module;				\
+	crc32_le(0, (char *)(module->module_core),			\
+	  ((unsigned long)module - (unsigned long)(module->module_core))); \
+})
+
+/* Calculate the checksum of the whole module */
+#define set_crc_modules()						\
+({									\
+	module_crc = 0;							\
+	module_crc = get_crc_module();					\
+})
+
+/*
+ * Compare the checksum value that is stored in module_crc to the check
+ * sum of current whole module. Must be called with holding disk_dump_lock.
+ * Return TRUE if they are the same, else return FALSE
+ *
+ */
+#define check_crc_module()						\
+({									\
+	uint32_t orig_crc, cur_crc;					\
+									\
+	orig_crc = module_crc; module_crc = 0;				\
+	cur_crc = get_crc_module();					\
+	module_crc = orig_crc;						\
+	orig_crc == cur_crc;						\
+})
+
+
+#endif /* _LINUX_DISKDUMP_H */
diff -Nur linux-2.6.6.org/include/linux/diskdumplib.h linux-2.6.6/include/linux/diskdumplib.h
--- linux-2.6.6.org/include/linux/diskdumplib.h	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.6/include/linux/diskdumplib.h	2004-06-16 19:34:16.000000000 +0900
@@ -0,0 +1,67 @@
+#ifndef _LINUX_DISKDUMPLIB_H
+#define _LINUX_DISKDUMPLIB_H
+
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+
+void diskdump_lib_init(void);
+void diskdump_lib_exit(void);
+void diskdump_update(void);
+
+void _diskdump_add_timer(struct timer_list *);
+int _diskdump_del_timer(struct timer_list *);
+int _diskdump_mod_timer(struct timer_list *, unsigned long);
+void _diskdump_tasklet_schedule(struct tasklet_struct *);
+int _diskdump_schedule_work(struct work_struct *);
+
+static inline void diskdump_add_timer(struct timer_list *timer)
+{
+	if (crashdump_mode())
+		_diskdump_add_timer(timer);
+	else
+		add_timer(timer);
+}
+
+static inline int diskdump_del_timer(struct timer_list *timer)
+{
+	if (crashdump_mode())
+		return _diskdump_del_timer(timer);
+	else
+		return del_timer(timer);
+}
+
+static inline int diskdump_mod_timer(struct timer_list *timer, unsigned long expires)
+{
+	if (crashdump_mode())
+		return _diskdump_mod_timer(timer, expires);
+	else
+		return mod_timer(timer, expires);
+}
+
+static inline void diskdump_tasklet_schedule(struct tasklet_struct *tasklet)
+{
+	if (crashdump_mode())
+		return _diskdump_tasklet_schedule(tasklet);
+	else
+		return tasklet_schedule(tasklet);
+}
+
+static inline int diskdump_schedule_work(struct work_struct *work)
+{
+	if (crashdump_mode())
+		return _diskdump_schedule_work(work);
+	else
+		return schedule_work(work);
+}
+
+static inline long diskdump_schedule_timeout(signed long timeout)
+{
+	if (crashdump_mode()) {
+		mdelay(timeout);
+		set_current_state(TASK_RUNNING);
+		return timeout;
+	} else
+		return schedule_timeout(timeout);
+}
+
+#endif /* _LINUX_DISKDUMPLIB_H */
diff -Nur linux-2.6.6.org/include/linux/kernel.h linux-2.6.6/include/linux/kernel.h
--- linux-2.6.6.org/include/linux/kernel.h	2004-06-04 21:22:08.000000000 +0900
+++ linux-2.6.6/include/linux/kernel.h	2004-06-16 19:34:16.000000000 +0900
@@ -112,6 +112,11 @@
 extern int system_state;		/* See values below */
 extern int tainted;
 extern const char *print_tainted(void);
+struct pt_regs;
+extern void try_crashdump(struct pt_regs *);
+extern void (*diskdump_func) (struct pt_regs *regs, void *platform_arg);
+extern int diskdump_mode;
+#define crashdump_mode()       (diskdump_mode)
 
 /* Values used for system_state */
 #define SYSTEM_BOOTING 0
@@ -136,6 +141,12 @@
 #define pr_info(fmt,arg...) \
 	printk(KERN_INFO fmt,##arg)
 
+#define pr_err(fmt,arg...) \
+	printk(KERN_ERR fmt,##arg)
+
+#define pr_warn(fmt,arg...) \
+	printk(KERN_WARNING fmt,##arg)
+
 /*
  *      Display an IP address in readable format.
  */
diff -Nur linux-2.6.6.org/kernel/panic.c linux-2.6.6/kernel/panic.c
--- linux-2.6.6.org/kernel/panic.c	2004-06-04 21:21:58.000000000 +0900
+++ linux-2.6.6/kernel/panic.c	2004-06-16 19:34:16.000000000 +0900
@@ -23,8 +23,10 @@
 int panic_timeout;
 int panic_on_oops;
 int tainted;
+int diskdump_mode = 0;
 
 EXPORT_SYMBOL(panic_timeout);
+EXPORT_SYMBOL_GPL(diskdump_mode);
 
 struct notifier_block *panic_notifier_list;
 
@@ -60,6 +62,8 @@
 	vsnprintf(buf, sizeof(buf), fmt, args);
 	va_end(args);
 	printk(KERN_EMERG "Kernel panic: %s\n",buf);
+	if (diskdump_func)
+		BUG();
 	if (in_interrupt())
 		printk(KERN_EMERG "In interrupt handler - not syncing\n");
 	else if (!current->pid)
@@ -134,3 +138,23 @@
 		snprintf(buf, sizeof(buf), "Not tainted");
 	return(buf);
 }
+
+/*
+ * Try crashdump. Diskdump is first, netdump is second.
+ * We clear diskdump_func before call of diskdump_func, so
+ * If double panic would occur in diskdump, netdump can handle
+ * it.
+ */
+#include <asm/diskdump.h>
+void try_crashdump(struct pt_regs *regs)
+{
+	void (*func)(struct pt_regs *, void *);
+
+	if (diskdump_func) {
+		func = diskdump_func;
+		diskdump_func = NULL;
+		platform_start_diskdump(func, regs);
+	}
+	if (panic_on_oops)
+		panic("Fatal exception");
+}
diff -Nur linux-2.6.6.org/mm/bootmem.c linux-2.6.6/mm/bootmem.c
--- linux-2.6.6.org/mm/bootmem.c	2004-06-04 21:22:37.000000000 +0900
+++ linux-2.6.6/mm/bootmem.c	2004-06-16 19:34:16.000000000 +0900
@@ -16,6 +16,7 @@
 #include <linux/init.h>
 #include <linux/bootmem.h>
 #include <linux/mmzone.h>
+#include <linux/module.h>
 #include <asm/dma.h>
 #include <asm/io.h>
 
@@ -26,6 +27,7 @@
 unsigned long max_low_pfn;
 unsigned long min_low_pfn;
 unsigned long max_pfn;
+EXPORT_SYMBOL(max_pfn);
 
 /* return the number of _pages_ that will be allocated for the boot bitmap */
 unsigned long __init bootmem_bootmap_pages (unsigned long pages)

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

* [PATCH 2/4][Diskdump]Update patches
  2004-06-16 12:39 [PATCH 0/4][Diskdump]Update patches Takao Indoh
  2004-06-16 12:45 ` [PATCH 1/4][Diskdump]Update patches Takao Indoh
@ 2004-06-16 12:46 ` Takao Indoh
  2004-06-16 12:48 ` [PATCH 3/4][Diskdump]Update patches Takao Indoh
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Takao Indoh @ 2004-06-16 12:46 UTC (permalink / raw)
  To: linux-kernel

This is a patch for scsi common layer.


diff -Nur linux-2.6.6.org/drivers/scsi/Kconfig linux-2.6.6/drivers/scsi/Kconfig
--- linux-2.6.6.org/drivers/scsi/Kconfig	2004-06-04 21:22:20.000000000 +0900
+++ linux-2.6.6/drivers/scsi/Kconfig	2004-06-16 19:34:16.000000000 +0900
@@ -55,6 +55,12 @@
 	  In this case, do not compile the driver for your SCSI host adapter
 	  (below) as a module either.
 
+config SCSI_DUMP
+	tristate "SCSI dump support"
+	depends on DISKDUMP && SCSI
+	help
+	   SCSI dump support
+
 config CHR_DEV_ST
 	tristate "SCSI tape support"
 	depends on SCSI
diff -Nur linux-2.6.6.org/drivers/scsi/Makefile linux-2.6.6/drivers/scsi/Makefile
--- linux-2.6.6.org/drivers/scsi/Makefile	2004-06-04 21:22:21.000000000 +0900
+++ linux-2.6.6/drivers/scsi/Makefile	2004-06-16 19:34:16.000000000 +0900
@@ -133,6 +133,8 @@
 obj-$(CONFIG_BLK_DEV_SR)	+= sr_mod.o
 obj-$(CONFIG_CHR_DEV_SG)	+= sg.o
 
+obj-$(CONFIG_SCSI_DUMP)		+= scsi_dump.o
+
 scsi_mod-y			+= scsi.o hosts.o scsi_ioctl.o constants.o \
 				   scsicam.o scsi_error.o scsi_lib.o \
 				   scsi_scan.o scsi_syms.o scsi_sysfs.o \
diff -Nur linux-2.6.6.org/drivers/scsi/scsi.c linux-2.6.6/drivers/scsi/scsi.c
--- linux-2.6.6.org/drivers/scsi/scsi.c	2004-06-04 21:22:20.000000000 +0900
+++ linux-2.6.6/drivers/scsi/scsi.c	2004-06-16 19:34:16.000000000 +0900
@@ -691,6 +691,9 @@
 {
 	unsigned long flags;
 
+	if (crashdump_mode())
+		return;
+
 	/*
 	 * We don't have to worry about this one timing out any more.
 	 * If we are unable to remove the timer, then the command
diff -Nur linux-2.6.6.org/drivers/scsi/scsi_dump.c linux-2.6.6/drivers/scsi/scsi_dump.c
--- linux-2.6.6.org/drivers/scsi/scsi_dump.c	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.6/drivers/scsi/scsi_dump.c	2004-06-16 19:34:16.000000000 +0900
@@ -0,0 +1,579 @@
+/*
+ *  linux/drivers/scsi/scsi_dump.c
+ *
+ *  Copyright (C) 2004  FUJITSU LIMITED
+ *  Written by Nobuhiro Tachino (ntachino@jp.fujitsu.com)
+ *
+ * Some codes are derived from drivers/scsi/sd.c
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+#include <linux/blkdev.h>
+#include <linux/blkpg.h>
+
+#include <linux/genhd.h>
+#include <linux/utsname.h>
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/diskdump.h>
+#include <linux/diskdumplib.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_dump.h>
+
+#include "scsi.h"
+#include "scsi_priv.h"
+
+#define MAX_RETRIES 5
+#define SD_TIMEOUT (60 * HZ)
+
+#define Dbg(x, ...)	pr_debug("scsi_dump: " x "\n", ## __VA_ARGS__)
+#define Err(x, ...)	pr_err  ("scsi_dump: " x "\n", ## __VA_ARGS__)
+#define Warn(x, ...)	pr_warn ("scsi_dump: " x "\n", ## __VA_ARGS__)
+#define Info(x, ...)	pr_info ("scsi_dump: " x "\n", ## __VA_ARGS__)
+
+/* blocks to 512byte sectors */
+#define BLOCK_SECTOR(s)	((s) << (DUMP_BLOCK_SHIFT - 9))
+
+static int quiesce_ok = 0;
+static struct scsi_cmnd scsi_dump_cmnd;
+static struct request scsi_dump_req;
+static uint32_t module_crc;
+
+static void rw_intr(struct scsi_cmnd * scmd)
+{
+	scmd->done = NULL;
+}
+
+/*
+ * Common code to make Scsi_Cmnd
+ */
+static void init_scsi_command(struct scsi_device *sdev, struct scsi_cmnd *scmd,
+		 	      void *buf, int len, unsigned char direction,
+			      int set_lun)
+{
+	scmd->request   = &scsi_dump_req;
+	scmd->sc_magic	= SCSI_CMND_MAGIC;
+	scmd->owner	= SCSI_OWNER_MIDLEVEL;
+	scmd->device	= sdev;
+	scmd->buffer	= scmd->request_buffer = buf;
+	scmd->bufflen	= scmd->request_bufflen = len;
+
+
+	scmd->sc_data_direction = direction;
+
+	memcpy(scmd->data_cmnd, scmd->cmnd, sizeof(scmd->cmnd));
+	scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
+	scmd->old_cmd_len = scmd->cmd_len;
+
+
+	if (set_lun)
+		scmd->cmnd[1] |= (sdev->scsi_level <= SCSI_2) ?
+				  ((sdev->lun << 5) & 0xe0) : 0;
+
+	scmd->transfersize = sdev->sector_size;
+	if (direction == SCSI_DATA_WRITE)
+		scmd->underflow = len;
+
+	scmd->allowed = MAX_RETRIES;
+	scmd->timeout_per_command = SD_TIMEOUT;
+
+	/*
+	 * This is the completion routine we use.  This is matched in terms
+	 * of capability to this function.
+	 */
+	scmd->done = rw_intr;
+}
+
+/* MODE SENSE */
+static void init_mode_sense_command(struct scsi_device *sdev,
+				    struct scsi_cmnd *scmd, void *buf)
+{
+	memset(scmd, 0, sizeof(*scmd));
+	scmd->cmnd[0] = MODE_SENSE;
+	scmd->cmnd[1] = 0x00;		/* DBD=0 */
+	scmd->cmnd[2] = 0x08;		/* PCF=0 Page 8(Cache) */
+	scmd->cmnd[4] = 255;
+
+	init_scsi_command(sdev, scmd, buf, 256, SCSI_DATA_READ, 1);
+}
+
+/* MODE SELECT */
+static void init_mode_select_command(struct scsi_device *sdev,
+				     struct scsi_cmnd *scmd, void *buf, int len)
+{
+	memset(scmd, 0, sizeof(*scmd));
+	scmd->cmnd[0] = MODE_SELECT;
+	scmd->cmnd[1] = 0x10;		/* PF=1 SP=0 */
+	scmd->cmnd[4] = len;
+
+	init_scsi_command(sdev, scmd, buf, len, SCSI_DATA_WRITE, 1);
+}
+
+/* SYNCHRONIZE CACHE */
+static void init_sync_command(struct scsi_device *sdev, struct scsi_cmnd * scmd)
+{
+	memset(scmd, 0, sizeof(*scmd));
+	scmd->cmnd[0] = SYNCHRONIZE_CACHE;
+
+	init_scsi_command(sdev, scmd, NULL, 0, SCSI_DATA_NONE, 0);
+}
+
+/* REQUEST SENSE */
+static void init_sense_command(struct scsi_device *sdev, struct scsi_cmnd *scmd,
+			       void *buf)
+{
+	memset(scmd, 0, sizeof(*scmd));
+	scmd->cmnd[0] = REQUEST_SENSE;
+	scmd->cmnd[4] = 255;
+
+	init_scsi_command(sdev, scmd, buf, 256, SCSI_DATA_READ, 1);
+}
+
+/* READ/WRITE */
+static int init_rw_command(struct disk_dump_partition *dump_part,
+			   struct scsi_device *sdev, struct scsi_cmnd * scmd,
+			   int rw, int block, void *buf, unsigned int len)
+{
+	int this_count = len >> 9;
+
+	memset(scmd, 0, sizeof(*scmd));
+
+	if (block + this_count > dump_part->nr_sects) {
+		Err("block number %d is larger than %lu",
+				block + this_count, dump_part->nr_sects);
+		return -EFBIG;
+	}
+
+	block += dump_part->start_sect;
+
+	/*
+	 * If we have a 1K hardware sectorsize, prevent access to single
+	 * 512 byte sectors.  In theory we could handle this - in fact
+	 * the scsi cdrom driver must be able to handle this because
+	 * we typically use 1K blocksizes, and cdroms typically have
+	 * 2K hardware sectorsizes.  Of course, things are simpler
+	 * with the cdrom, since it is read-only.  For performance
+	 * reasons, the filesystems should be able to handle this
+	 * and not force the scsi disk driver to use bounce buffers
+	 * for this.
+	 */
+	if (sdev->sector_size == 1024) {
+		block = block >> 1;
+		this_count = this_count >> 1;
+	}
+	if (sdev->sector_size == 2048) {
+		block = block >> 2;
+		this_count = this_count >> 2;
+	}
+	if (sdev->sector_size == 4096) {
+		block = block >> 3;
+		this_count = this_count >> 3;
+	}
+	switch (rw) {
+	case WRITE:
+		if (!sdev->writeable) {
+			Err("writable media");
+			return 0;
+		}
+		scmd->cmnd[0] = WRITE_10;
+		break;
+	case READ:
+		scmd->cmnd[0] = READ_10;
+		break;
+	default:
+		Err("Unknown command %d", rw);
+		return -EINVAL;
+	}
+
+	if (this_count > 0xffff)
+		this_count = 0xffff;
+
+	scmd->cmnd[2] = (unsigned char) (block >> 24) & 0xff;
+	scmd->cmnd[3] = (unsigned char) (block >> 16) & 0xff;
+	scmd->cmnd[4] = (unsigned char) (block >> 8) & 0xff;
+	scmd->cmnd[5] = (unsigned char) block & 0xff;
+	scmd->cmnd[7] = (unsigned char) (this_count >> 8) & 0xff;
+	scmd->cmnd[8] = (unsigned char) this_count & 0xff;
+
+	init_scsi_command(sdev, scmd, buf, len,
+			(rw == WRITE ? SCSI_DATA_WRITE : SCSI_DATA_READ), 1);
+	return 0;
+}
+
+/*
+ * Check the status of scsi command and determine whether it is
+ * success, fail, or retriable.
+ *
+ * Return code
+ * 	> 0: should retry
+ * 	= 0: success
+ * 	< 0: fail
+ */
+static int cmd_result(struct scsi_cmnd *scmd)
+{
+	int status;
+
+	status = status_byte(scmd->result);
+
+	switch (scsi_decide_disposition(scmd)) {
+	case FAILED:
+		break;
+	case NEEDS_RETRY:
+	case ADD_TO_MLQUEUE:
+		return 1 /* retry */;
+	case SUCCESS:
+		if (host_byte(scmd->result) != DID_OK)
+			break;
+
+		if (status == GOOD || status == INTERMEDIATE_GOOD
+				   || status == INTERMEDIATE_C_GOOD)
+			return 0;
+		if (status == CHECK_CONDITION &&
+		    scmd->sense_buffer[2] == RECOVERED_ERROR)
+			return 0;
+		break;
+	default:
+		Err("bad disposition: %d", scmd->result);
+		return -EIO;
+	}
+
+	Err("command %x failed with 0x%x", scmd->cmnd[0], scmd->result);
+	return -EIO;
+}
+
+static int send_command(struct scsi_cmnd *scmd)
+{
+	struct Scsi_Host *host = scmd->device->host;
+	struct scsi_device *sdev = scmd->device;
+	int ret;
+
+	do {
+		if (!scsi_device_online(sdev)) {
+			Err("Scsi disk is not online");
+			return -EIO;
+		}
+		if (sdev->changed) {
+			Err("SCSI disk has been changed. Prohibiting further I/O");
+			return -EIO;
+		}
+
+		spin_lock(host->host_lock);
+		host->hostt->queuecommand(scmd, rw_intr);
+		spin_unlock(host->host_lock);
+
+		while (scmd->done != NULL) {
+			host->hostt->dump_poll(scmd->device);
+			udelay(100);
+			diskdump_update();
+		}
+		scmd->done = rw_intr;
+	} while ((ret = cmd_result(scmd)) > 0);
+
+	return ret;
+}
+
+/*
+ * If Write Cache Enable of disk device is not set, write I/O takes
+ * long long time.  So enable WCE temporary and issue SYNCHRONIZE CACHE
+ * after all write I/Os are done, Following system reboot will reset
+ * WCE bit to original value.
+ */
+static void
+enable_write_cache(struct scsi_device *sdev)
+{
+	char buf[256];
+	int ret;
+	int data_len;
+
+	Dbg("enable write cache");
+	memset(buf, 0, 256);
+
+	init_mode_sense_command(sdev, &scsi_dump_cmnd, buf);
+	if ((ret = send_command(&scsi_dump_cmnd)) < 0) {
+		Warn("MODE SENSE failed");
+		return;
+	}
+
+	if (buf[14] & 0x04)		/* WCE is already set */
+		return;
+
+	data_len = buf[0] + 1; /* Data length in mode parameter header */
+	buf[0] = 0;
+	buf[1] = 0;
+	buf[2] = 0;
+	buf[12] &= 0x7f;		/* clear PS */
+	buf[14] |= 0x04;		/* set WCE */
+
+	init_mode_select_command(sdev, &scsi_dump_cmnd, buf, data_len);
+	if ((ret = send_command(&scsi_dump_cmnd)) < 0) {
+		Warn("MODE SELECT failed");
+
+		init_sense_command(sdev, &scsi_dump_cmnd, buf);
+		if ((ret = send_command(&scsi_dump_cmnd)) < 0) {
+			Err("sense failed");
+		}
+	}
+}
+
+/*
+ * Check whether the dump device is sane enough to handle I/O.
+ *
+ * Return value:
+ * 	0:	the device is ok
+ * 	< 0:	the device is not ok
+ * 	> 0:	Cannot determine
+ */
+static int
+scsi_dump_sanity_check(struct disk_dump_device *dump_device)
+{
+	struct scsi_device *sdev = dump_device->device;
+	struct Scsi_Host *host = sdev->host;
+	int adapter_sanity = 0;
+	int sanity = 0;
+
+	if (!check_crc_module()) {
+		Err("checksum error. scsi dump module may be compromised.");
+		return -EINVAL;
+	}
+	/*
+	 * If host's spinlock is already taken, assume it's part
+	 * of crash and skip it.
+	 */
+	if (!scsi_device_online(sdev)) {
+		Warn("device not online: host %d channel %d id %d lun %d",
+			host->host_no, sdev->channel, sdev->id, sdev->lun);
+		return -EIO;
+	}
+	if (sdev->changed) {
+		Err("SCSI disk has been changed. Prohibiting further I/O: host %d channel %d id %d lun %d",
+			host->host_no, sdev->channel, sdev->id, sdev->lun);
+		return -EIO;
+	}
+
+	if (host->hostt->dump_sanity_check) {
+		adapter_sanity = host->hostt->dump_sanity_check(sdev);
+		if (adapter_sanity < 0) {
+			Warn("adapter status is not sane");
+			return adapter_sanity;
+		}
+	}
+
+	if (!spin_is_locked(host->host_lock)) {
+		sanity = 0;
+	} else {
+		Warn("host_lock is held: host %d channel %d id %d lun %d",
+			host->host_no, sdev->channel, sdev->id, sdev->lun);
+		return -EIO;
+	}
+	return sanity + adapter_sanity;
+}
+
+/*
+ * Try to reset the host adapter. If the adapter does not have its host reset
+ * handler, try to use its bus device reset handler.
+ */
+static int scsi_dump_reset(struct scsi_device *sdev)
+{
+	struct Scsi_Host *host = sdev->host;
+	struct scsi_host_template *hostt = host->hostt;
+	char buf[256];
+	int ret, i;
+
+	init_sense_command(sdev, &scsi_dump_cmnd, buf);
+
+	if (hostt->eh_host_reset_handler) {
+		spin_lock(host->host_lock);
+		ret = hostt->eh_host_reset_handler(&scsi_dump_cmnd);
+	} else if (hostt->eh_bus_reset_handler) {
+		spin_lock(host->host_lock);
+		ret = hostt->eh_bus_reset_handler(&scsi_dump_cmnd);
+	} else
+		return 0;
+	spin_unlock(host->host_lock);
+
+	if (ret != SUCCESS) {
+		Err("adapter reset failed");
+		return -EIO;
+	}
+
+	/* bus reset settle time. 5sec for old disk devices */
+	for (i = 0; i < 5000; i++) {
+		diskdump_update();
+		mdelay(1);
+	}
+
+	Dbg("request sense");
+	if ((ret = send_command(&scsi_dump_cmnd)) < 0) {
+		Err("sense failed");
+		return -EIO;
+	}
+	return 0;
+}
+
+static int
+scsi_dump_quiesce(struct disk_dump_device *dump_device)
+{
+	struct scsi_device *sdev = dump_device->device;
+	struct Scsi_Host *host = sdev->host;
+	int ret;
+
+	if (host->hostt->dump_quiesce) {
+		ret = host->hostt->dump_quiesce(sdev);
+		if (ret < 0)
+			return ret;
+	}
+
+	Dbg("do bus reset");
+	if ((ret = scsi_dump_reset(sdev)) < 0)
+		return ret;
+
+	if (sdev->scsi_level >= SCSI_2)
+		enable_write_cache(sdev);
+
+	quiesce_ok = 1;
+	return 0;
+}
+
+static int scsi_dump_rw_block(struct disk_dump_partition *dump_part, int rw,
+			      unsigned long dump_block_nr, void *buf, int len)
+{
+	struct disk_dump_device *dump_device = dump_part->device;
+	struct scsi_device *sdev = dump_device->device;
+	int block_nr = BLOCK_SECTOR(dump_block_nr);
+	int ret;
+
+	if (!quiesce_ok) {
+		Err("quiesce not called");
+		return -EIO;
+	}
+
+	ret = init_rw_command(dump_part, sdev, &scsi_dump_cmnd, rw,
+					block_nr, buf, DUMP_BLOCK_SIZE * len);
+	if (ret < 0) {
+		Err("init_rw_command failed");
+		return ret;
+	}
+	return send_command(&scsi_dump_cmnd);
+}
+
+static int
+scsi_dump_shutdown(struct disk_dump_device *dump_device)
+{
+	struct scsi_device *sdev = dump_device->device;
+	struct Scsi_Host *host = sdev->host;
+
+	if (sdev->scsi_level >= SCSI_2) {
+		init_sync_command(sdev, &scsi_dump_cmnd);
+		send_command(&scsi_dump_cmnd);
+	}
+
+	if (host->hostt->dump_shutdown)
+		return host->hostt->dump_shutdown(sdev);
+
+	return 0;
+}
+
+static void *scsi_dump_probe(struct block_device *bdev)
+{
+	struct scsi_device *sdev;
+
+	sdev = sd_find_scsi_device(bdev);
+	if (sdev == NULL)
+		return NULL;
+	if (!sdev->host->hostt->dump_poll)
+		return NULL;
+
+	return sdev;
+}
+
+
+struct disk_dump_device_ops scsi_dump_device_ops = {
+	.sanity_check	= scsi_dump_sanity_check,
+	.rw_block	= scsi_dump_rw_block,
+	.quiesce	= scsi_dump_quiesce,
+	.shutdown	= scsi_dump_shutdown,
+};
+
+static int scsi_dump_add_device(struct disk_dump_device *dump_device)
+{
+	struct scsi_device *sdev;
+	int error;
+
+	sdev = dump_device->device;
+	if (!sdev->host->hostt->dump_poll)
+		return -ENOTSUPP;
+
+	if ((error = scsi_device_get(sdev)) != 0)
+		return error;
+
+	memcpy(&dump_device->ops, &scsi_dump_device_ops,
+		sizeof(scsi_dump_device_ops));
+	if (sdev->host->max_sectors) {
+		dump_device->max_blocks =
+			(sdev->sector_size * sdev->host->max_sectors)
+			  >> DUMP_BLOCK_SHIFT;
+	}
+	return 0;
+}
+
+static void scsi_dump_remove_device(struct disk_dump_device *dump_device)
+{
+	struct scsi_device *sdev = dump_device->device;
+
+	scsi_device_put(sdev);
+}
+
+static struct disk_dump_type scsi_dump_type = {
+	.probe		= scsi_dump_probe,
+	.add_device	= scsi_dump_add_device,
+	.remove_device	= scsi_dump_remove_device,
+	.owner		= THIS_MODULE,
+};
+
+static int init_scsi_dump(void)
+{
+	int ret;
+
+	if ((ret = register_disk_dump_type(&scsi_dump_type)) < 0) {
+		Err("register failed");
+		return ret;
+	}
+	set_crc_modules();
+	return ret;
+}
+
+static void cleanup_scsi_dump(void)
+{
+	if (unregister_disk_dump_type(&scsi_dump_type) < 0)
+		Err("register failed");
+}
+
+module_init(init_scsi_dump);
+module_exit(cleanup_scsi_dump);
+MODULE_LICENSE("GPL");
diff -Nur linux-2.6.6.org/drivers/scsi/scsi_error.c linux-2.6.6/drivers/scsi/scsi_error.c
--- linux-2.6.6.org/drivers/scsi/scsi_error.c	2004-06-04 21:22:20.000000000 +0900
+++ linux-2.6.6/drivers/scsi/scsi_error.c	2004-06-16 19:34:16.000000000 +0900
@@ -402,6 +402,9 @@
  **/
 static void scsi_eh_done(struct scsi_cmnd *scmd)
 {
+	if (crashdump_mode())
+		return;
+
 	/*
 	 * if the timeout handler is already running, then just set the
 	 * flag which says we finished late, and return.  we have no
diff -Nur linux-2.6.6.org/drivers/scsi/scsi_syms.c linux-2.6.6/drivers/scsi/scsi_syms.c
--- linux-2.6.6.org/drivers/scsi/scsi_syms.c	2004-06-04 21:22:21.000000000 +0900
+++ linux-2.6.6/drivers/scsi/scsi_syms.c	2004-06-16 19:34:16.000000000 +0900
@@ -25,6 +25,7 @@
 #include "scsi.h"
 
 #include "scsi_logging.h"
+#include "scsi_priv.h"
 
 
 /*
@@ -107,3 +108,5 @@
  */
 EXPORT_SYMBOL(scsi_add_timer);
 EXPORT_SYMBOL(scsi_delete_timer);
+
+EXPORT_SYMBOL(scsi_decide_disposition);
diff -Nur linux-2.6.6.org/drivers/scsi/sd.c linux-2.6.6/drivers/scsi/sd.c
--- linux-2.6.6.org/drivers/scsi/sd.c	2004-06-04 21:22:22.000000000 +0900
+++ linux-2.6.6/drivers/scsi/sd.c	2004-06-16 19:34:16.000000000 +0900
@@ -680,6 +680,24 @@
 	.revalidate_disk	= sd_revalidate_disk,
 };
 
+#if defined(CONFIG_DISKDUMP) || defined(CONFIG_DISKDUMP_MODULE)
+struct scsi_device *sd_find_scsi_device(struct block_device *bdev)
+{
+	struct gendisk *disk = bdev->bd_disk;
+
+	/* Check whether dev is scsi or not */
+	if (!disk || (disk->fops != &sd_fops))
+		return NULL;
+
+	if(disk->private_data)
+		return scsi_disk(disk)->device;
+	else
+		return NULL;
+}
+
+EXPORT_SYMBOL(sd_find_scsi_device);
+#endif
+
 /**
  *	sd_rw_intr - bottom half handler: called when the lower level
  *	driver has completed (successfully or otherwise) a scsi command.
diff -Nur linux-2.6.6.org/include/scsi/scsi_dump.h linux-2.6.6/include/scsi/scsi_dump.h
--- linux-2.6.6.org/include/scsi/scsi_dump.h	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.6/include/scsi/scsi_dump.h	2004-06-16 19:34:16.000000000 +0900
@@ -0,0 +1,31 @@
+#ifndef _SCSI_DUMP_H
+#define _SCSI_DUMP_H
+
+/*
+ *  linux/include/scsi/scsi_dump.h
+ *
+ *  Copyright (C) 2004  FUJITSU LIMITED
+ *  Written by Nobuhiro Tachino (ntachino@jp.fujitsu.com)
+ *
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* sd.c function */
+extern struct scsi_device *sd_find_scsi_device(struct block_device *);
+
+#endif /* _SCSI_DUMP_H */
diff -Nur linux-2.6.6.org/include/scsi/scsi_host.h linux-2.6.6/include/scsi/scsi_host.h
--- linux-2.6.6.org/include/scsi/scsi_host.h	2004-06-04 21:22:02.000000000 +0900
+++ linux-2.6.6/include/scsi/scsi_host.h	2004-06-16 19:34:16.000000000 +0900
@@ -345,6 +345,47 @@
 	 * module_init/module_exit.
 	 */
 	struct list_head legacy_hosts;
+
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+	/* operations for dump */
+
+	/*
+	 * dump_sanity_check() checks if the selected device works normally.
+	 * A device which returns an error status will not be selected as
+	 * the dump device.
+	 *
+	 * Status: OPTIONAL
+	 */
+	int (* dump_sanity_check)(struct scsi_device *);
+
+	/*
+	 * dump_quiesce() is called after the device is selected as the
+	 * dump device. Usually, host reset is executed and Write Cache
+	 * Enable bit of the disk device is temporarily set for the
+	 * dump operation.
+	 *
+	 * Status: OPTIONAL
+	 */
+	int (* dump_quiesce)(struct scsi_device *);
+
+	/*
+	 * dump_shutdown() is called after dump is completed. Usually
+	 * "SYNCHRONIZE CACHE" command is issued to the disk.
+	 *
+	 * Status: OPTIONAL
+	 */
+	int (* dump_shutdown)(struct scsi_device *);
+
+	/*
+	 * dump_poll() should call the interrupt handler. It is called
+	 * repeatedly after queuecommand() is issued, and until the command
+	 * is completed. If the low level device driver support crash dump,
+	 * it must have this routine.
+	 *
+	 * Status: OPTIONAL
+	 */
+	void (* dump_poll)(struct scsi_device *);
+#endif
 };
 
 /*

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

* [PATCH 3/4][Diskdump]Update patches
  2004-06-16 12:39 [PATCH 0/4][Diskdump]Update patches Takao Indoh
  2004-06-16 12:45 ` [PATCH 1/4][Diskdump]Update patches Takao Indoh
  2004-06-16 12:46 ` [PATCH 2/4][Diskdump]Update patches Takao Indoh
@ 2004-06-16 12:48 ` Takao Indoh
  2004-06-16 12:50 ` [PATCH 4/4][Diskdump]Update patches Takao Indoh
  2004-06-16 17:25 ` [PATCH 0/4][Diskdump]Update patches Jeff Garzik
  4 siblings, 0 replies; 9+ messages in thread
From: Takao Indoh @ 2004-06-16 12:48 UTC (permalink / raw)
  To: linux-kernel

This is a patch for aic7xxx driver.


diff -Nur linux-2.6.6.org/drivers/scsi/aic7xxx/aic7xxx_osm.c linux-2.6.6/drivers/scsi/aic7xxx/aic7xxx_osm.c
--- linux-2.6.6.org/drivers/scsi/aic7xxx/aic7xxx_osm.c	2004-06-04 21:22:20.000000000 +0900
+++ linux-2.6.6/drivers/scsi/aic7xxx/aic7xxx_osm.c	2004-06-16 19:34:16.000000000 +0900
@@ -774,6 +774,10 @@
 static int	   ahc_linux_bus_reset(Scsi_Cmnd *);
 static int	   ahc_linux_dev_reset(Scsi_Cmnd *);
 static int	   ahc_linux_abort(Scsi_Cmnd *);
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+static int	   ahc_linux_sanity_check(struct scsi_device *);
+static void	   ahc_linux_poll(struct scsi_device *);
+#endif
 
 /*
  * Calculate a safe value for AHC_NSEG (as expressed through ahc_linux_nseg).
@@ -1313,6 +1317,10 @@
 	.slave_alloc		= ahc_linux_slave_alloc,
 	.slave_configure	= ahc_linux_slave_configure,
 	.slave_destroy		= ahc_linux_slave_destroy,
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+	.dump_sanity_check	= ahc_linux_sanity_check,
+	.dump_poll		= ahc_linux_poll,
+#endif
 };
 
 /**************************** Tasklet Handler *********************************/
@@ -3863,6 +3871,42 @@
 	return IRQ_RETVAL(ours);
 }
 
+static int
+ahc_linux_sanity_check(struct scsi_device *device)
+{
+	struct ahc_softc *ahc;
+	struct ahc_linux_device *dev;
+
+	ahc = *(struct ahc_softc **)device->host->hostdata;
+	dev = ahc_linux_get_device(ahc, device->channel,
+				   device->id, device->lun,
+					   /*alloc*/FALSE);
+	if (dev == NULL)
+		return -ENXIO;
+	if (ahc->platform_data->qfrozen || dev->qfrozen)
+		return -EBUSY;
+	if (spin_is_locked(&ahc->platform_data->spin_lock))
+		return -EBUSY;
+	return 0;
+}
+
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+static void
+ahc_linux_poll(struct scsi_device *device)
+{
+	struct ahc_softc *ahc;
+	struct ahc_linux_device *dev;
+
+	ahc = *(struct ahc_softc **)device->host->hostdata;
+	ahc_intr(ahc);
+	while ((dev = ahc_linux_next_device_to_run(ahc)) != NULL) {
+		TAILQ_REMOVE(&ahc->platform_data->device_runq, dev, links);
+		dev->flags &= ~AHC_DEV_ON_RUN_LIST;
+		ahc_linux_check_device_queue(ahc, dev);
+	}
+	ahc_linux_run_complete_queue(ahc);
+}
+
 void
 ahc_platform_flushwork(struct ahc_softc *ahc)
 {
@@ -3870,6 +3914,7 @@
 	while (ahc_linux_run_complete_queue(ahc) != NULL)
 		;
 }
+#endif
 
 static struct ahc_linux_target*
 ahc_linux_alloc_target(struct ahc_softc *ahc, u_int channel, u_int target)
diff -Nur linux-2.6.6.org/drivers/scsi/aic7xxx/aic7xxx_osm.h linux-2.6.6/drivers/scsi/aic7xxx/aic7xxx_osm.h
--- linux-2.6.6.org/drivers/scsi/aic7xxx/aic7xxx_osm.h	2004-06-04 21:22:20.000000000 +0900
+++ linux-2.6.6/drivers/scsi/aic7xxx/aic7xxx_osm.h	2004-06-16 19:34:16.000000000 +0900
@@ -67,6 +67,7 @@
 #include <linux/smp_lock.h>
 #include <linux/version.h>
 #include <linux/module.h>
+#include <linux/diskdumplib.h>
 #include <asm/byteorder.h>
 #include <asm/io.h>
 
@@ -82,6 +83,8 @@
 #include <linux/malloc.h>
 #endif
 
+#include <scsi/scsi_dump.h>
+
 /* Core SCSI definitions */
 #define AIC_LIB_PREFIX ahc
 #include "scsi.h"
@@ -253,6 +256,22 @@
  */
 #define ahc_dmamap_sync(ahc, dma_tag, dmamap, offset, len, op)
 
+
+/******************************** Disk dump ***********************************/
+#if defined(CONFIG_DISKDUMP) || defined(CONFIG_DISKDUMP_MODULE)
+#undef  add_timer
+#define add_timer       diskdump_add_timer
+#undef  del_timer_sync
+#define del_timer_sync  diskdump_del_timer
+#undef  del_timer
+#define del_timer       diskdump_del_timer
+#undef  mod_timer
+#define mod_timer       diskdump_mod_timer
+
+#define tasklet_schedule        diskdump_tasklet_schedule
+#endif
+
+
 /************************** Timer DataStructures ******************************/
 typedef struct timer_list ahc_timer_t;
 

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

* [PATCH 4/4][Diskdump]Update patches
  2004-06-16 12:39 [PATCH 0/4][Diskdump]Update patches Takao Indoh
                   ` (2 preceding siblings ...)
  2004-06-16 12:48 ` [PATCH 3/4][Diskdump]Update patches Takao Indoh
@ 2004-06-16 12:50 ` Takao Indoh
  2004-06-16 17:25 ` [PATCH 0/4][Diskdump]Update patches Jeff Garzik
  4 siblings, 0 replies; 9+ messages in thread
From: Takao Indoh @ 2004-06-16 12:50 UTC (permalink / raw)
  To: linux-kernel

This is a patch for aic79xx driver.


diff -Nur linux-2.6.6.org/drivers/scsi/aic7xxx/aic79xx_osm.c linux-2.6.6/drivers/scsi/aic7xxx/aic79xx_osm.c
--- linux-2.6.6.org/drivers/scsi/aic7xxx/aic79xx_osm.c	2004-06-04 21:22:20.000000000 +0900
+++ linux-2.6.6/drivers/scsi/aic7xxx/aic79xx_osm.c	2004-06-16 19:34:16.000000000 +0900
@@ -786,6 +786,10 @@
 static int	   ahd_linux_bus_reset(Scsi_Cmnd *);
 static int	   ahd_linux_dev_reset(Scsi_Cmnd *);
 static int	   ahd_linux_abort(Scsi_Cmnd *);
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+static int         ahd_linux_sanity_check(struct scsi_device *);
+static void        ahd_linux_poll(struct scsi_device *);
+#endif
 
 /*
  * Calculate a safe value for AHD_NSEG (as expressed through ahd_linux_nseg).
@@ -1684,6 +1688,10 @@
 	.slave_alloc		= ahd_linux_slave_alloc,
 	.slave_configure	= ahd_linux_slave_configure,
 	.slave_destroy		= ahd_linux_slave_destroy,
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+	.dump_sanity_check	= ahd_linux_sanity_check,
+	.dump_poll		= ahd_linux_poll,
+#endif
 };
 
 /**************************** Tasklet Handler *********************************/
@@ -4190,6 +4198,41 @@
 	return IRQ_RETVAL(ours);
 }
 
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+static int
+ahd_linux_sanity_check(struct scsi_device *device)
+{
+	struct ahd_softc *ahd;
+	struct ahd_linux_device *dev;
+
+	ahd = *(struct ahd_softc **)device->host->hostdata;
+	dev = ahd_linux_get_device(ahd, device->channel,
+				   device->id, device->lun,
+				   /*alloc*/FALSE);
+
+	if (dev == NULL)
+		return -ENXIO;
+	if (ahd->platform_data->qfrozen || dev->qfrozen)
+		return -EBUSY;
+	if (spin_is_locked(&ahd->platform_data->spin_lock))
+		return -EBUSY;
+	return 0;
+}
+
+static void
+ahd_linux_poll(struct scsi_device *device)
+{
+	struct ahd_softc *ahd;
+	int ours;
+
+	ahd = *(struct ahd_softc **)device->host->hostdata;
+	ours = ahd_intr(ahd);
+	if (ahd_linux_next_device_to_run(ahd) != NULL)
+		ahd_schedule_runq(ahd);
+	ahd_linux_run_complete_queue(ahd);
+}
+#endif
+
 void
 ahd_platform_flushwork(struct ahd_softc *ahd)
 {
diff -Nur linux-2.6.6.org/drivers/scsi/aic7xxx/aic79xx_osm.h linux-2.6.6/drivers/scsi/aic7xxx/aic79xx_osm.h
--- linux-2.6.6.org/drivers/scsi/aic7xxx/aic79xx_osm.h	2004-06-04 21:22:20.000000000 +0900
+++ linux-2.6.6/drivers/scsi/aic7xxx/aic79xx_osm.h	2004-06-16 19:34:16.000000000 +0900
@@ -50,6 +50,9 @@
 #include <linux/smp_lock.h>
 #include <linux/version.h>
 #include <linux/module.h>
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+#include <linux/diskdumplib.h>
+#endif
 #include <asm/byteorder.h>
 #include <asm/io.h>
 
@@ -65,6 +68,10 @@
 #include <linux/malloc.h>
 #endif
 
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+#include <scsi/scsi_dump.h>
+#endif
+
 /* Core SCSI definitions */
 #define AIC_LIB_PREFIX ahd
 #include "scsi.h"
@@ -96,6 +103,20 @@
 /* No debugging code. */
 #endif
 
+/********************************** Disk Dump *********************************/
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+#undef	add_timer
+#define	add_timer		diskdump_add_timer
+#undef	del_timer_sync
+#define	del_timer_sync		diskdump_del_timer
+#undef	del_timer
+#define	del_timer		diskdump_del_timer
+#undef	mod_timer
+#define	mod_timer		diskdump_mod_timer
+
+#define	tasklet_schedule	diskdump_tasklet_schedule
+#endif
+
 /********************************** Misc Macros *******************************/
 #define	roundup(x, y)   ((((x)+((y)-1))/(y))*(y))
 #define	powerof2(x)	((((x)-1)&(x))==0)

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

* Re: [PATCH 0/4][Diskdump]Update patches
  2004-06-16 12:39 [PATCH 0/4][Diskdump]Update patches Takao Indoh
                   ` (3 preceding siblings ...)
  2004-06-16 12:50 ` [PATCH 4/4][Diskdump]Update patches Takao Indoh
@ 2004-06-16 17:25 ` Jeff Garzik
  4 siblings, 0 replies; 9+ messages in thread
From: Jeff Garzik @ 2004-06-16 17:25 UTC (permalink / raw)
  To: Takao Indoh; +Cc: linux-kernel

Takao Indoh wrote:
>  static inline void tasklet_schedule(struct tasklet_struct *t)
>  {
> +	if(crashdump_mode()) {
> +		diskdump_tasklet_schedule(t);
> +		return;
> +	}
> +


I would suggest

	if (unlikely(crashdump_mode()))


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

* [PATCH 0/4][Diskdump]Update patches
@ 2004-06-22 13:47 Takao Indoh
  0 siblings, 0 replies; 9+ messages in thread
From: Takao Indoh @ 2004-06-22 13:47 UTC (permalink / raw)
  To: linux-kernel; +Cc: Ingo Molnar, Christoph Hellwig, Andi Kleen

Hi!

I update the Diskdump patches!

- fix timer problem
- support kernel 2.6.7

Source code of tool(diskdumptuils) can be downloaded from
 http://sourceforge.net/projects/lkdump

TODO:
- Replace proc interface with sysfs.
- Merge scsi_dump with scsi_mod.


I solved the timer problem by the method which Ingo Molnar proposed.

On Thu, 17 Jun 2004 14:13:56 +0200, Ingo Molnar wrote:

>but there's another possible method (suggested by Alan Cox) that
>requires no changes to the timer API hotpaths: 'clear' all timer lists
>upon a crash [once all CPUs have stopped and irqs are disabled] and just
>let the drivers use the normal timer APIs. Drive timer execution via a
>polling method.
>
>this basically approximates your polling based implementation but uses
>the existing kernel timer data structures and timer mechanism so should
>be robust and compatible. It doesnt rely on any previous state (because
>all currently pending timers are discarded) so it's as crash-safe as
>possible.


The following is core part of patches related to timer problem.
The complete patch is posted later.


diff -Nur linux-2.6.7.org/include/linux/interrupt.h linux-2.6.7/include/linux/interrupt.h
--- linux-2.6.7.org/include/linux/interrupt.h	2004-06-22 10:27:34.000000000 +0900
+++ linux-2.6.7/include/linux/interrupt.h	2004-06-22 22:26:39.000000000 +0900
@@ -246,4 +246,8 @@
 extern int probe_irq_off(unsigned long);	/* returns 0 or negative on failure */
 extern unsigned int probe_irq_mask(unsigned long);	/* returns mask of ISA interrupts */
 
+
+extern void dump_clear_tasklet(void);
+extern void dump_run_tasklet(void);
+
 #endif
diff -Nur linux-2.6.7.org/include/linux/timer.h linux-2.6.7/include/linux/timer.h
--- linux-2.6.7.org/include/linux/timer.h	2004-06-22 10:27:31.000000000 +0900
+++ linux-2.6.7/include/linux/timer.h	2004-06-22 22:26:39.000000000 +0900
@@ -99,4 +99,7 @@
 extern void run_local_timers(void);
 extern void it_real_fn(unsigned long);
 
+extern void dump_clear_timers(void);
+extern void dump_run_timers(void);
+
 #endif
diff -Nur linux-2.6.7.org/include/linux/workqueue.h linux-2.6.7/include/linux/workqueue.h
--- linux-2.6.7.org/include/linux/workqueue.h	2004-06-22 10:27:35.000000000 +0900
+++ linux-2.6.7/include/linux/workqueue.h	2004-06-22 22:26:39.000000000 +0900
@@ -84,4 +84,7 @@
 	return ret;
 }
 
+extern void dump_clear_workqueue(void);
+extern void dump_run_workqueue(void);
+
 #endif
diff -Nur linux-2.6.7.org/kernel/softirq.c linux-2.6.7/kernel/softirq.c
--- linux-2.6.7.org/kernel/softirq.c	2004-06-22 10:27:25.000000000 +0900
+++ linux-2.6.7/kernel/softirq.c	2004-06-22 22:26:39.000000000 +0900
@@ -314,6 +314,38 @@
 
 EXPORT_SYMBOL(tasklet_kill);
 
+struct tasklet_head saved_tasklet;
+
+void dump_clear_tasklet(void)
+{
+	saved_tasklet.list = __get_cpu_var(tasklet_vec).list;
+	__get_cpu_var(tasklet_vec).list = NULL;
+}
+
+EXPORT_SYMBOL(dump_clear_tasklet);
+
+void dump_run_tasklet(void)
+{
+	struct tasklet_struct *list;
+
+	list = __get_cpu_var(tasklet_vec).list;
+	__get_cpu_var(tasklet_vec).list = NULL;
+
+	while (list) {
+		struct tasklet_struct *t = list;
+		list = list->next;
+
+		if (!atomic_read(&t->count) &&
+		    (test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)))
+				t->func(t->data);
+
+		t->next = __get_cpu_var(tasklet_vec).list;
+		__get_cpu_var(tasklet_vec).list = t;
+	}
+}
+
+EXPORT_SYMBOL(dump_run_tasklet);
+
 void __init softirq_init(void)
 {
 	open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
diff -Nur linux-2.6.7.org/kernel/timer.c linux-2.6.7/kernel/timer.c
--- linux-2.6.7.org/kernel/timer.c	2004-06-22 10:27:25.000000000 +0900
+++ linux-2.6.7/kernel/timer.c	2004-06-22 22:26:39.000000000 +0900
@@ -31,6 +31,7 @@
 #include <linux/time.h>
 #include <linux/jiffies.h>
 #include <linux/cpu.h>
+#include <linux/delay.h>
 
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
@@ -424,7 +425,6 @@
 {
 	struct timer_list *timer;
 
-	spin_lock_irq(&base->lock);
 	while (time_after_eq(jiffies, base->timer_jiffies)) {
 		struct list_head work_list = LIST_HEAD_INIT(work_list);
 		struct list_head *head = &work_list;
@@ -460,6 +460,12 @@
 		}
 	}
 	set_running_timer(base, NULL);
+}
+
+static inline void _run_timers(tvec_base_t *base)
+{
+	spin_lock_irq(&base->lock);
+	__run_timers(base);
 	spin_unlock_irq(&base->lock);
 }
 
@@ -909,7 +915,7 @@
 	tvec_base_t *base = &__get_cpu_var(tvec_bases);
 
 	if (time_after_eq(jiffies, base->timer_jiffies))
-		__run_timers(base);
+		_run_timers(base);
 }
 
 /*
@@ -1105,6 +1111,12 @@
 	struct timer_list timer;
 	unsigned long expire;
 
+	if (unlikely(crashdump_mode())) {
+		mdelay(timeout);
+		set_current_state(TASK_RUNNING);
+		return timeout;
+	}
+
 	switch (timeout)
 	{
 	case MAX_SCHEDULE_TIMEOUT:
@@ -1308,7 +1320,7 @@
 	return 0;
 }
 
-static void __devinit init_timers_cpu(int cpu)
+static void /* __devinit */ init_timers_cpu(int cpu)
 {
 	int j;
 	tvec_base_t *base;
@@ -1327,6 +1339,27 @@
 	base->timer_jiffies = jiffies;
 }
 
+static tvec_base_t saved_tvec_base;
+
+void dump_clear_timers(void)
+{
+	tvec_base_t *base = &per_cpu(tvec_bases, smp_processor_id());
+
+	memcpy(&saved_tvec_base, base, sizeof(saved_tvec_base));
+	init_timers_cpu(smp_processor_id());
+}
+
+EXPORT_SYMBOL(dump_clear_timers);
+
+void dump_run_timers(void)
+{
+	tvec_base_t *base = &__get_cpu_var(tvec_bases);
+
+	__run_timers(base);
+}
+
+EXPORT_SYMBOL(dump_run_timers);
+
 #ifdef CONFIG_HOTPLUG_CPU
 static int migrate_timer_list(tvec_base_t *new_base, struct list_head *head)
 {
diff -Nur linux-2.6.7.org/kernel/workqueue.c linux-2.6.7/kernel/workqueue.c
--- linux-2.6.7.org/kernel/workqueue.c	2004-06-22 10:27:25.000000000 +0900
+++ linux-2.6.7/kernel/workqueue.c	2004-06-22 22:26:39.000000000 +0900
@@ -424,6 +424,37 @@
 
 }
 
+struct cpu_workqueue_struct saved_cwq;
+
+void dump_clear_workqueue(void)
+{
+	int cpu = smp_processor_id();
+	struct cpu_workqueue_struct *cwq = keventd_wq->cpu_wq + cpu;
+
+	memcpy(&saved_cwq, cwq, sizeof(saved_cwq));
+	spin_lock_init(&cwq->lock);
+	INIT_LIST_HEAD(&cwq->worklist);
+	init_waitqueue_head(&cwq->more_work);
+	init_waitqueue_head(&cwq->work_done);
+}
+
+void dump_run_workqueue(void)
+{
+	struct cpu_workqueue_struct *cwq;
+
+	cwq = keventd_wq->cpu_wq + smp_processor_id();
+	while (!list_empty(&cwq->worklist)) {
+		struct work_struct *work = list_entry(cwq->worklist.next,
+						struct work_struct, entry);
+		void (*f) (void *) = work->func;
+		void *data = work->data;
+
+		list_del_init(cwq->worklist.next);
+		clear_bit(0, &work->pending);
+		f(data);
+	}
+}
+
 #ifdef CONFIG_HOTPLUG_CPU
 /* Take the work from this (downed) CPU. */
 static void take_over_work(struct workqueue_struct *wq, unsigned int cpu)
@@ -507,3 +538,6 @@
 EXPORT_SYMBOL(schedule_delayed_work);
 EXPORT_SYMBOL(flush_scheduled_work);
 
+EXPORT_SYMBOL(dump_clear_workqueue);
+EXPORT_SYMBOL(dump_run_workqueue);
+

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

* [PATCH 0/4][Diskdump]Update patches
@ 2004-07-07  8:26 Takao Indoh
  0 siblings, 0 replies; 9+ messages in thread
From: Takao Indoh @ 2004-07-07  8:26 UTC (permalink / raw)
  To: linux-kernel

Hi!

I release new diskdump patches.

- Support sysfs interface instead of proc
- Fix some bugs

Source code can be downloaded from
 http://sourceforge.net/projects/lkdump


The main change from previous version is replacing proc interface with
sysfs.
If you want to add a new dump device /dev/sda3, 

  # echo 3 > /sys/block/sda/device/dump

Like this, you need write the partition number into the appropriate
sysfs entry. You can also do the same thing using diskdumpctl command, 
which is included in the diskdumputils.

  # diskdumpctl /dev/sda3

If you want to remove the dump device /dev/sda3, 

  # echo -3 > /sys/block/sda/device/dump

or

  # diskdumpctl -u /dev/sda1

Here is a part of patch of sysfs interface.
I added a new attribute "dump" to the /sys/block/sdX/device. The handler
of show/store operation calls a function which is registered via
sdev_dump_handler_register(). When the scsi_dump module is installed,
sysfs handler of scsi_dump is registered using 
sdev_dump_handler_register().

Please feel free to comment!


diff -Nur linux-2.6.7.org/drivers/scsi/scsi_sysfs.c linux-2.6.7/drivers/scsi/scsi_sysfs.c
--- linux-2.6.7.org/drivers/scsi/scsi_sysfs.c	2004-06-22 10:27:50.000000000 +0900
+++ linux-2.6.7/drivers/scsi/scsi_sysfs.c	2004-07-07 17:01:26.146353152 +0900
@@ -375,6 +375,66 @@
 
 DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_state_field, store_state_field);
 
+static DECLARE_MUTEX(sdev_dump_mutex);
+
+static struct device_attribute sdev_dump_attr = {
+	.show  = NULL,
+	.store = NULL,
+};
+
+int sdev_dump_handler_register(struct device_attribute* attr)
+{
+	if (sdev_dump_attr.show || sdev_dump_attr.store)
+		return -EEXIST;
+
+	down(&sdev_dump_mutex);
+	sdev_dump_attr = *attr;
+	up(&sdev_dump_mutex);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(sdev_dump_handler_register);
+
+void sdev_dump_handler_unregister(void)
+{
+	down(&sdev_dump_mutex);
+	sdev_dump_attr.show  = NULL;
+	sdev_dump_attr.store = NULL;
+	up(&sdev_dump_mutex);
+}
+
+EXPORT_SYMBOL(sdev_dump_handler_unregister);
+
+static ssize_t
+sdev_store_dump(struct device *dev, const char *buf, size_t count)
+{
+	ssize_t ret = count;
+
+	down(&sdev_dump_mutex);
+	if (sdev_dump_attr.store)
+		ret = sdev_dump_attr.store(dev, buf, count);
+	up(&sdev_dump_mutex);
+
+	return ret;
+}
+
+static ssize_t
+sdev_show_dump(struct device *dev, char *buf)
+{
+	ssize_t ret;
+
+	down(&sdev_dump_mutex);
+	if (sdev_dump_attr.show)
+		ret = sdev_dump_attr.show(dev, buf);
+	else
+		ret = snprintf(buf, 20, "handler not found\n");
+	up(&sdev_dump_mutex);
+
+	return ret;
+}
+
+DEVICE_ATTR(dump, S_IRUGO | S_IWUSR, sdev_show_dump, sdev_store_dump);
 
 /* Default template for device attributes.  May NOT be modified */
 static struct device_attribute *scsi_sysfs_sdev_attrs[] = {
@@ -389,6 +449,7 @@
 	&dev_attr_delete,
 	&dev_attr_state,
 	&dev_attr_timeout,
+	&dev_attr_dump,
 	NULL
 };
 
 

Best Regards,
Takao Indoh

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

* [PATCH 0/4][Diskdump]Update patches
@ 2004-07-09  7:15 Takao Indoh
  0 siblings, 0 replies; 9+ messages in thread
From: Takao Indoh @ 2004-07-09  7:15 UTC (permalink / raw)
  To: linux-kernel; +Cc: arjanv, hch

Hi all,

I improved the patches which I posted on 7th Jul.

- Improve sysfs interface code
- Bug fix related to eh_timeout of scsi_cmd

Source code can be downloaded from
 http://sourceforge.net/projects/lkdump


On Mon, 21 Jun 2004 10:01:29 +0200, Arjan van de Ven wrote:

>On Mon, Jun 21, 2004 at 04:59:52PM +0900, Takao Indoh wrote:
>> Hi,
>> 
>> Now I am fixing diskdump according to comments by you and Christoph.
>> 
>> On Fri, 11 Jun 2004 13:50:45 +0200, Arjan van de Ven wrote:
>> 
>> >> +#ifdef CONFIG_PROC_FS
>> >> +static int proc_ioctl(struct inode *inode, struct file *file, unsigned 
>> >> int cmd, unsigned long param)
>> >
>> >
>> >ehhh this looks evil
>> 
>> Do you mean I should use not ioctl but the following style?
>> 
>> echo "add /dev/hda1" > /proc/diskdump
>> echo "delete /dev/hda1" > /proc/diskdump
>
>well no since /dev/hda is pointless; major/minor pairs maybe.
>But why in /proc???? it sounds like a sysfs job to me, where you probably
>want to represent a dump relationship with a symlink, and use "rm" to remove
>an entry..

As I wrote in the previous mail, I replaced proc interface with
sysfs.

If you want to add a new dump device /dev/sda3,

  # echo 3 > /sys/block/sda/device/dump

If you want to remove the dump device /dev/sda3,

  # echo -3 > /sys/block/sda/device/dump


I added a new attribute "dump" to the /sys/block/sdX/device.

diff -Nur linux-2.6.7.org/drivers/scsi/scsi_sysfs.c linux-2.6.7/drivers/scsi/scsi_sysfs.c
--- linux-2.6.7.org/drivers/scsi/scsi_sysfs.c	2004-06-22 10:27:50.000000000 +0900
+++ linux-2.6.7/drivers/scsi/scsi_sysfs.c	2004-07-09 11:36:41.451983216 +0900
@@ -11,6 +11,7 @@
 #include <linux/init.h>
 #include <linux/blkdev.h>
 #include <linux/device.h>
+#include <linux/diskdump.h>
 
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport.h>
@@ -375,6 +376,7 @@
 
 DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_state_field, store_state_field);
 
+DEVICE_ATTR(dump, S_IRUGO | S_IWUSR, diskdump_sysfs_show, diskdump_sysfs_store);
 
 /* Default template for device attributes.  May NOT be modified */
 static struct device_attribute *scsi_sysfs_sdev_attrs[] = {
@@ -389,6 +391,7 @@
 	&dev_attr_delete,
 	&dev_attr_state,
 	&dev_attr_timeout,
+	&dev_attr_dump,
 	NULL
 };
 

diskdump_sysfs_show/store is the sysfs handler of diskdump. These
functions passes struct device to the diskdump module.
Diskdump changes "struct device" to "struct scsi_device" like this:

+static void *scsi_dump_probe(struct device *dev)
+{
+	struct scsi_device *sdev;
+
+	if ((dev->bus == NULL) || (dev->bus->name == NULL) ||
+	    strncmp(dev->bus->name, "scsi", 4))
+		return NULL;
+
+	sdev =  to_scsi_device(dev);
+	if (!sdev->host->hostt->dump_poll)
+		return NULL;
+
+	return sdev;
+}

I used device->bus->name to confirm device is scsi device or not.

Please comment.

Best Regards,
Takao Indoh

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

end of thread, other threads:[~2004-07-09  7:13 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-06-16 12:39 [PATCH 0/4][Diskdump]Update patches Takao Indoh
2004-06-16 12:45 ` [PATCH 1/4][Diskdump]Update patches Takao Indoh
2004-06-16 12:46 ` [PATCH 2/4][Diskdump]Update patches Takao Indoh
2004-06-16 12:48 ` [PATCH 3/4][Diskdump]Update patches Takao Indoh
2004-06-16 12:50 ` [PATCH 4/4][Diskdump]Update patches Takao Indoh
2004-06-16 17:25 ` [PATCH 0/4][Diskdump]Update patches Jeff Garzik
  -- strict thread matches above, loose matches on Subject: below --
2004-06-22 13:47 Takao Indoh
2004-07-07  8:26 Takao Indoh
2004-07-09  7:15 Takao Indoh

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox