Hi,
I update the Diskdump patch for kernel 2.6.6. I fixed some codes
according to comments from Christoph Hellwig.
http://marc.theaimsgroup.com/?l=linux-kernel&m=108566610109915&w=2
Source code can be downloaded from
http://sourceforge.net/projects/lkdump
Please feel free to comment.
Next week, I'll try the following and release new patch.
1) Change codes around add_timer()/del_timer()/etc.
2) Replace dev_t with block_device
3) Clean code more
On Thu, 27 May 2004 14:51:34 +0100, Christoph Hellwig wrote:
>> +/******************************** 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
>
>Yikes. No way in hell we'll place code like this in drivers. This needs
>to be handled in common code.
Another approach is insering some codes into the core timer and tasklet
routines.
For example,
static inline void add_timer(struct timer_list * timer)
{
if(crashdump_mode())
__diskdump_add_timer(timer);
else
__mod_timer(timer, timer->expires);
}
But I do not want to make common codes dirty...
Please let me know more good idea!
Best Regards,
Takao Indoh
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-05-10 11:32:37.000000000 +0900
+++ linux-2.6.6/arch/i386/kernel/nmi.c 2004-06-09 19:17:46.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-05-10 11:32:27.000000000 +0900
+++ linux-2.6.6/arch/i386/kernel/reboot.c 2004-06-09 19:17:46.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-05-10 11:32:02.000000000 +0900
+++ linux-2.6.6/arch/i386/kernel/traps.c 2004-06-09 19:17:46.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-05-10 11:33:08.000000000 +0900
+++ linux-2.6.6/arch/i386/mm/init.c 2004-06-09 19:17:46.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-05-10 11:32:26.000000000 +0900
+++ linux-2.6.6/drivers/block/Kconfig 2004-06-09 19:17:46.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-05-10 11:33:13.000000000 +0900
+++ linux-2.6.6/drivers/block/Makefile 2004-06-09 19:17:46.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-09 20:10:05.000000000 +0900
@@ -0,0 +1,967 @@
+/*
+ * linux/drivers/block/diskdump.c
+ *
+ * Copyright (C) 2004 FUJITSU LIMITED
+ * Copyright (C) 2002 Red Hat, Inc.
+ * Written by Nobuhiro Tachino ([email protected])
+ *
+ * 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/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 1
+#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 unsigned int sample_rate = 8;
+MODULE_PARM(fallback_on_err, "i");
+MODULE_PARM(allow_risky_dumps, "i");
+MODULE_PARM(block_order, "i");
+MODULE_PARM(sample_rate, "i");
+
+static unsigned long timestamp_1sec;
+static uint32_t module_crc;
+static char *scratch;
+static struct disk_dump_header dump_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;
+
+#if CONFIG_SMP
+static void freeze_cpu(void *dummy)
+{
+ unsigned int cpu = smp_processor_id();
+
+ dump_header.tasks[cpu] = current;
+
+ for (;;) local_irq_disable();
+}
+#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_mapnr; nr++) {
+ if (!page_is_ram(nr))
+ continue;
+
+ if (blocks >= max_blocks_written) {
+ Warn("dump device is too small. %lu pages were not saved", max_mapnr - blocks);
+ goto out;
+ }
+ page = mem_map + 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;
+ print_status(blocks, max_blocks_written);
+ }
+ }
+ 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)
+{
+ 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
+ */
+ 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;
+ }
+
+ Info("start dumping");
+
+ 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 */
+ Info("check dump partition...");
+ 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_mapnr;
+ dump_header.total_ram_blocks = total_ram_blocks;
+ dump_header.device_blocks = SECTOR_BLOCK(dump_part->nr_sects);
+ dump_header.written_blocks = 0;
+ dump_header.current_cpu = smp_processor_id();
+ dump_header.nr_cpus = num_online_cpus();
+
+ Dbg("write header");
+ write_header(dump_part);
+
+ /*
+ * Write the architecture dependent header
+ */
+ Dbg("write sub header");
+ if ((ret = write_sub_header()) < 0) {
+ Err("writing 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);
+ }
+
+ Info("dumping memory..");
+ 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 = 1 /* partition header */
+ + 1 /* common header */
+ + dump_header.sub_hdr_size
+ + dump_header.bitmap_blocks
+ + written_blocks;
+ 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(dev_t dev, 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(dev)) != 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 gendisk *gd;
+ int part;
+ 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);
+
+ gd = get_gendisk(inode->i_rdev, &part);
+ if (gd) part--;
+ if ((!gd) || (!gd->part) || (!gd->part[part]))
+ return -EINVAL;
+ dump_part->nr_sects = gd->part[part]->nr_sects;
+ dump_part->start_sect = gd->part[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;
+ dev_t dev = file->f_dentry->d_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(dev, &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 user specified dump partition.
+ * Must be called with disk_dump_lock held.
+ */
+static int remove_dump(dev_t dev)
+{
+ struct disk_dump_device *dump_device;
+ struct disk_dump_partition *dump_part;
+ struct disk_dump_type *dump_type;
+
+ 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 (!S_ISBLK(dump_inode->i_mode)) {
+ 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_inode->i_rdev);
+ 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_mapnr; nr++) {
+ if (page_is_ram(nr))
+ total_ram_blocks++;
+ }
+
+ bitmap_blocks = ROUNDUP(max_mapnr, 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("header blocks: %u", header_blocks);
+ Info("bitmap blocks: %u", bitmap_blocks);
+ Info("total number of memory blocks: %u", total_ram_blocks);
+ Info("total blocks written: %u", total_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-09 19:17:46.000000000 +0900
@@ -0,0 +1,203 @@
+/*
+ * linux/drivers/block/diskdumplib.c
+ *
+ * Copyright (C) 2004 FUJITSU LIMITED
+ * Written by Nobuhiro Tachino ([email protected])
+ *
+ */
+/*
+ * 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-05-10 11:32:29.000000000 +0900
+++ linux-2.6.6/drivers/block/genhd.c 2004-06-09 19:17:46.000000000 +0900
@@ -224,6 +224,8 @@
return kobj ? to_disk(kobj) : NULL;
}
+EXPORT_SYMBOL(get_gendisk);
+
#ifdef CONFIG_PROC_FS
/* iterator */
static void *part_start(struct seq_file *part, loff_t *pos)
@@ -629,3 +631,28 @@
}
EXPORT_SYMBOL(invalidate_partition);
+
+/*
+ * Dump stuff.
+ */
+void (*diskdump_func) (struct pt_regs *regs) = NULL;
+EXPORT_SYMBOL_GPL(diskdump_func);
+
+int diskdump_register_hook(void (*dump_func) (struct pt_regs *))
+{
+ 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-05-10 11:33:13.000000000 +0900
+++ linux-2.6.6/drivers/char/sysrq.c 2004-06-09 19:17:46.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-09 19:26:26.000000000 +0900
@@ -0,0 +1,71 @@
+#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; \
+ struct disk_dump_sub_header *header; \
+ \
+ header = (struct disk_dump_sub_header *)scratch; \
+ ELF_CORE_COPY_REGS(header->elf_regs, (&myregs)); \
+ if ((ret = write_blocks(dump_part, 2, scratch, 1)) >= 0)\
+ ret = 1; /* size of sub header in page */; \
+ ret; \
+})
+
+#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-05-10 11:32:54.000000000 +0900
+++ linux-2.6.6/include/asm-i386/kmap_types.h 2004-06-09 19:17:46.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-09 19:29:07.000000000 +0900
@@ -0,0 +1,154 @@
+#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>
+
+/* Dump I/O unit. Must be the same of PAGE_SIZE */
+#define DUMP_BLOCK_SIZE PAGE_SIZE
+#define DUMP_BLOCK_SHIFT PAGE_SHIFT
+
+/* Dump ioctl */
+#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 diskdump_unregister_hook(void);
+
+/*
+ * The handler that adapter driver provide for the common module of
+ * dump
+ */
+struct disk_dump_partition;
+struct disk_dump_device;
+
+struct disk_dump_type {
+ void *(*probe)(dev_t);
+ 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);
+};
+
+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;
+};
+
+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 *);
+
+
+/*
+ * Dump header that doesn't depend on the architecture
+ */
+
+#define DISK_DUMP_SIGNATURE "DISKDUMP"
+
+#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;/* Number of blocks should be
+ written */
+ 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 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-09 20:12:04.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()) {
+ diskdump_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-05-10 11:31:56.000000000 +0900
+++ linux-2.6.6/include/linux/kernel.h 2004-06-09 19:17:46.000000000 +0900
@@ -112,6 +112,10 @@
extern int system_state; /* See values below */
extern int tainted;
extern const char *print_tainted(void);
+extern void try_crashdump(struct pt_regs *);
+extern void (*diskdump_func) (struct pt_regs *regs);
+extern int diskdump_mode;
+#define crashdump_mode() (diskdump_mode)
/* Values used for system_state */
#define SYSTEM_BOOTING 0
@@ -136,6 +140,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-05-10 11:33:20.000000000 +0900
+++ linux-2.6.6/kernel/panic.c 2004-06-09 19:17:46.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,22 @@
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.
+ */
+void try_crashdump(struct pt_regs *regs)
+{
+ void (*func)(struct pt_regs *);
+
+ if (diskdump_func) {
+ func = diskdump_func;
+ diskdump_func = NULL;
+ func(regs);
+ }
+ if (panic_on_oops)
+ panic("Fatal exception");
+}
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-05-10 11:33:19.000000000 +0900
+++ linux-2.6.6/drivers/scsi/Kconfig 2004-06-09 22:24:30.590629105 +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-05-10 11:33:13.000000000 +0900
+++ linux-2.6.6/drivers/scsi/Makefile 2004-06-09 22:24:30.591605668 +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-05-10 11:32:37.000000000 +0900
+++ linux-2.6.6/drivers/scsi/scsi.c 2004-06-09 22:24:30.592582230 +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-09 22:31:51.418748705 +0900
@@ -0,0 +1,574 @@
+/*
+ * linux/drivers/scsi/scsi_dump.c
+ *
+ * Copyright (C) 2004 FUJITSU LIMITED
+ * Written by Nobuhiro Tachino ([email protected])
+ *
+ * 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/diskdump.h>
+#include <linux/diskdumplib.h>
+#include <linux/delay.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;
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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_ops->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_ops->sanity_check) {
+ adapter_sanity = host->hostt->dump_ops->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_ops->quiesce) {
+ ret = host->hostt->dump_ops->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_ops->shutdown)
+ return host->hostt->dump_ops->shutdown(sdev);
+
+ return 0;
+}
+
+static void *scsi_dump_probe(dev_t dev)
+{
+ struct scsi_device *sdev;
+
+ sdev = sd_find_scsi_device(dev);
+ if (sdev == NULL)
+ return NULL;
+ if (!sdev->host->hostt->dump_ops)
+ 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_ops)
+ 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-05-10 11:31:58.000000000 +0900
+++ linux-2.6.6/drivers/scsi/scsi_error.c 2004-06-09 22:24:30.595511918 +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-05-10 11:32:37.000000000 +0900
+++ linux-2.6.6/drivers/scsi/scsi_syms.c 2004-06-09 22:24:30.595511918 +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-05-10 11:33:05.000000000 +0900
+++ linux-2.6.6/drivers/scsi/sd.c 2004-06-09 22:24:30.596488480 +0900
@@ -192,6 +192,21 @@
up(&sd_ref_sem);
}
+#if defined(CONFIG_DISKDUMP) || defined(CONFIG_DISKDUMP_MODULE)
+struct scsi_device *sd_find_scsi_device(dev_t dev)
+{
+ struct gendisk *disk;
+ int part;
+ disk = get_gendisk(dev, &part);
+ if(disk && disk->private_data)
+ return scsi_disk(disk)->device;
+ else
+ return NULL;
+}
+
+EXPORT_SYMBOL(sd_find_scsi_device);
+#endif
+
/**
* sd_init_command - build a scsi (read or write) command from
* information in the request structure.
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-09 22:24:30.596488480 +0900
@@ -0,0 +1,38 @@
+#ifndef _SCSI_DUMP_H
+#define _SCSI_DUMP_H
+
+/*
+ * linux/include/scsi/scsi_dump.h
+ *
+ * Copyright (C) 2004 FUJITSU LIMITED
+ * Written by Nobuhiro Tachino ([email protected])
+ *
+ */
+/*
+ * 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.
+ *
+ */
+
+struct scsi_dump_ops {
+ int (*sanity_check)(struct scsi_device *);
+ int (*quiesce)(struct scsi_device *);
+ int (*shutdown)(struct scsi_device *);
+ void (*poll)(struct scsi_device *);
+};
+
+/* sd.c function */
+extern struct scsi_device *sd_find_scsi_device(dev_t);
+
+#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-05-10 11:32:01.000000000 +0900
+++ linux-2.6.6/include/scsi/scsi_host.h 2004-06-09 22:24:30.597465043 +0900
@@ -345,6 +345,11 @@
* module_init/module_exit.
*/
struct list_head legacy_hosts;
+
+ /*
+ * operations for dump
+ */
+ struct scsi_dump_ops *dump_ops;
};
/*
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-05-10 11:32:00.000000000 +0900
+++ linux-2.6.6/drivers/scsi/aic7xxx/aic7xxx_osm.c 2004-06-07 16:12:40.000000000 +0900
@@ -774,6 +774,8 @@
static int ahc_linux_bus_reset(Scsi_Cmnd *);
static int ahc_linux_dev_reset(Scsi_Cmnd *);
static int ahc_linux_abort(Scsi_Cmnd *);
+static int ahc_linux_sanity_check(struct scsi_device *);
+static void ahc_linux_poll(struct scsi_device *);
/*
* Calculate a safe value for AHC_NSEG (as expressed through ahc_linux_nseg).
@@ -1294,6 +1296,11 @@
return SUCCESS;
}
+static struct scsi_dump_ops ahc_dump_ops = {
+ .sanity_check = ahc_linux_sanity_check,
+ .poll = ahc_linux_poll,
+};
+
Scsi_Host_Template aic7xxx_driver_template = {
.module = THIS_MODULE,
.name = "aic7xxx",
@@ -1313,6 +1320,7 @@
.slave_alloc = ahc_linux_slave_alloc,
.slave_configure = ahc_linux_slave_configure,
.slave_destroy = ahc_linux_slave_destroy,
+ .dump_ops = &ahc_dump_ops,
};
/**************************** Tasklet Handler *********************************/
@@ -3863,6 +3871,41 @@
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;
+}
+
+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)
{
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-05-10 11:32:28.000000000 +0900
+++ linux-2.6.6/drivers/scsi/aic7xxx/aic7xxx_osm.h 2004-06-07 16:13:25.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;
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-05-10 11:32:38.000000000 +0900
+++ linux-2.6.6/drivers/scsi/aic7xxx/aic79xx_osm.c 2004-06-07 16:09:10.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).
@@ -1665,6 +1669,13 @@
return (SUCCESS);
}
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+static struct scsi_dump_ops ahd_dump_ops = {
+ .sanity_check = ahd_linux_sanity_check,
+ .poll = ahd_linux_poll,
+};
+#endif /* CONFIG_SCSI_DUMP || CONFIG_SCSI_DUMP_MODULE */
+
Scsi_Host_Template aic79xx_driver_template = {
.module = THIS_MODULE,
.name = "aic79xx",
@@ -1684,6 +1695,9 @@
.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_ops = &ahd_dump_ops,
+#endif /* CONFIG_SCSI_DUMP || CONFIG_SCSI_DUMP_MODULE */
};
/**************************** Tasklet Handler *********************************/
@@ -4190,6 +4204,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 /* CONFIG_SCSI_DUMP || CONFIG_SCSI_DUMP_MODULE */
+
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-05-10 11:32:52.000000000 +0900
+++ linux-2.6.6/drivers/scsi/aic7xxx/aic79xx_osm.h 2004-06-07 16:09:59.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)
> 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-05-10 11:32:02.000000000 +0900
> +++ linux-2.6.6/arch/i386/kernel/traps.c 2004-06-09 19:17:46.000000000 +0900
> @@ -258,7 +258,8 @@
> int nl = 0;
>
> console_verbose();
> - spin_lock_irq(&die_lock);
> + if (!crashdump_mode())
> + spin_lock_irq(&die_lock);
why is this??
> +#ifndef TRUE
> +#define TRUE 1
> +#endif
> +#ifndef FALSE
> +#define FALSE 0
> +#endif
it's kernel convention to just use 0 and !0
> +MODULE_PARM(fallback_on_err, "i");
> +MODULE_PARM(allow_risky_dumps, "i");
> +MODULE_PARM(block_order, "i");
> +MODULE_PARM(sample_rate, "i");
please exclusively use the 2.6 module parameter mechanism for new code.
> + page = mem_map + nr;
this is not safe for discontig mem and thus broken
> + /*
> + * Check the checksum of myself
> + */
> + spin_trylock(&disk_dump_lock);
you need to handle the failure case of trylock of course
> +#ifdef CONFIG_PROC_FS
> +static int proc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long param)
ehhh this looks evil
> 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-05-10 11:32:29.000000000 +0900
> +++ linux-2.6.6/drivers/block/genhd.c 2004-06-09 19:17:46.000000000 +0900
> @@ -224,6 +224,8 @@
> return kobj ? to_disk(kobj) : NULL;
> }
>
> +EXPORT_SYMBOL(get_gendisk);
this is WRONG. VERY WRONG.
Hi,
On Fri, 11 Jun 2004 13:50:45 +0200, Arjan van de Ven wrote:
>> console_verbose();
>> - spin_lock_irq(&die_lock);
>> + if (!crashdump_mode())
>> + spin_lock_irq(&die_lock);
>
>
>why is this??
It is for safety.
If diskdump dies during dumping, die() is called again.
In such a case, system falls into the deadlock by die_lock.
User cannot't know what happened to diskdump.
By this correction, spin_lock_irq() is skipped and show_registers()
shows stack trace. User may be able to know what happened even if dump
fails.
>
>
>> +#ifndef TRUE
>> +#define TRUE 1
>> +#endif
>> +#ifndef FALSE
>> +#define FALSE 0
>> +#endif
>
>it's kernel convention to just use 0 and !0
OK.
>
>> +MODULE_PARM(fallback_on_err, "i");
>> +MODULE_PARM(allow_risky_dumps, "i");
>> +MODULE_PARM(block_order, "i");
>> +MODULE_PARM(sample_rate, "i");
>
>please exclusively use the 2.6 module parameter mechanism for new code.
I see. I'll change this code using __setup.
>> + page = mem_map + nr;
>
>this is not safe for discontig mem and thus broken
Ok, I fix it.
- page = mem_map + nr;
+ page = pfn_to_page(nr);
I also need fix this.
- for (nr = 0; nr < max_mapnr; nr++) {
+ for (nr = 0; nr < max_pfn; nr++) {
>> + /*
>> + * Check the checksum of myself
>> + */
>> + spin_trylock(&disk_dump_lock);
>
>you need to handle the failure case of trylock of course
I see, thanks.
>> +#ifdef CONFIG_PROC_FS
>> +static int proc_ioctl(struct inode *inode, struct file *file, unsigned
>> int cmd, unsigned long param)
>
>
>ehhh this looks evil
Sorry, but would you please explain?
>> 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-05-10 11:32:29.000000000
+>> 0900
>> +++ linux-2.6.6/drivers/block/genhd.c 2004-06-09 19:17:46.000000000 +
0900
>> @@ -224,6 +224,8 @@
>> return kobj ? to_disk(kobj) : NULL;
>> }
>>
>> +EXPORT_SYMBOL(get_gendisk);
>
>
>this is WRONG. VERY WRONG.
I used get_gendisk() to get gendisk from inode.
gd = get_gendisk(inode->i_rdev, &part);
But, I can get gendiskd by i_bdev->bd_disk, so this export
is not needed. I remove it.
Best Regards,
Takao Indoh
> Ok, I fix it.
>
> - page = mem_map + nr;
> + page = pfn_to_page(nr);
That's correct now ...
> I also need fix this.
>
> - for (nr = 0; nr < max_mapnr; nr++) {
> + for (nr = 0; nr < max_pfn; nr++) {
... but that's not (at least AFAICS from this snippet). You need to iterate
over pgdats, and then over the lmem_map inside each pgdat.
M.
Hi,
On Fri, 11 Jun 2004 08:17:40 -0700, "Martin J. Bligh" wrote:
>> Ok, I fix it.
>>
>> - page = mem_map + nr;
>> + page = pfn_to_page(nr);
>
>That's correct now ...
>
>> I also need fix this.
>>
>> - for (nr = 0; nr < max_mapnr; nr++) {
>> + for (nr = 0; nr < max_pfn; nr++) {
>
>... but that's not (at least AFAICS from this snippet). You need to iterate
>over pgdats, and then over the lmem_map inside each pgdat.
How about this?
for_each_pgdat(pgdat) {
for (i = 0; i < pgdat->node_spanned_pages; ++i) {
page = pgdat->node_mem_map + i;
...
}
}
>>> Ok, I fix it.
>>>
>>> - page = mem_map + nr;
>>> + page = pfn_to_page(nr);
>>
>> That's correct now ...
>>
>>> I also need fix this.
>>>
>>> - for (nr = 0; nr < max_mapnr; nr++) {
>>> + for (nr = 0; nr < max_pfn; nr++) {
>>
>> ... but that's not (at least AFAICS from this snippet). You need to iterate
>> over pgdats, and then over the lmem_map inside each pgdat.
>
> How about this?
>
> for_each_pgdat(pgdat) {
> for (i = 0; i < pgdat->node_spanned_pages; ++i) {
> page = pgdat->node_mem_map + i;
> ...
> }
> }
Yes, looks OK. I started writing out what it'd take to use the actual
pfn_to_page macros, but it just makes a mess ... what you have is fine.
M.
On Fri, 11 Jun 2004 20:34:01 +0900, Takao Indoh wrote:
>On Thu, 27 May 2004 14:51:34 +0100, Christoph Hellwig wrote:
>
>>> +/******************************** 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
>>
>>Yikes. No way in hell we'll place code like this in drivers. This needs
>>to be handled in common code.
>
>Another approach is insering some codes into the core timer and tasklet
>routines.
>
>For example,
>
>static inline void add_timer(struct timer_list * timer)
>{
> if(crashdump_mode())
> __diskdump_add_timer(timer);
> else
> __mod_timer(timer, timer->expires);
>}
>
>But I do not want to make common codes dirty...
>Please let me know more good idea!
I forgot to explain what is problem. At first, I explain how diskdump
writes the system memory to the disk.
disk_dump() in drivers/block/diskdump.c is main routine of diskdump.
It is called from die()/panic().
First, disk_dump() silences system as follows.
local_save_flags(flags);
local_irq_disable();
smp_call_function(freeze_cpu, NULL, 1, 0);
Diskdump disables interrupt and stops other cpus.
Next, after preparing for dump, diskdump starts dumping. Diskdump calls
driver's handler via scis_dump module. Driver's handler writes data to
the disk with polling mode.
What is a problem? Scsi driver uses timer/tasklet to complete I/O.
But, diskdump disables interrupt, so timer/taslket doesn't work!
The current diskdump uses the following macros to solve this problem.
#define add_timer diskdump_add_timer
#define del_timer_sync diskdump_del_timer
#define del_timer diskdump_del_timer
#define mod_timer diskdump_mod_timer
#define tasklet_schedule diskdump_tasklet_schedule
ex.
static inline void diskdump_add_timer(struct timer_list *timer)
{
if (crashdump_mode())
_diskdump_add_timer(timer);
else
add_timer(timer);
}
It's very easy way. If diskdump is working, timer is registered with
diskdump. Otherwise timer is registered with normal kernel timer. The
timer registered with diskdump is called by diskdump itself during
dumping.
This way is easy but ugly. Another way is add new codes into the core
timer and tasklet routines as I wrote in the previous mail. I wondered
whether it is possible to insert general-purpose hooks into the timer/
taslket routine.
Please let me know if there is another good idea!
Best Regards,
Takao Indoh
my old comments for this are still valid, please add the actual dumping
methods directly to scsi_host_template instead of a pointer to another
method vector, please make it not a module of it's own but part of the
scsi code, please don't use "scsi.h" in new code, and the find gendisk
by dev_t code in sd.c is still no good.
On Thu, 17 Jun 2004 13:49:57 +0100, Christoph Hellwig wrote:
>my old comments for this are still valid, please add the actual dumping
>methods directly to scsi_host_template instead of a pointer to another
>method vector,
I have already done in the latest patch.
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 *********************************/
>please make it not a module of it's own but part of the
>scsi code,
Do you mean scsi_dump module should be merged with sd_mod.o or scsi_mod.o?
>please don't use "scsi.h" in new code,
Oh, I missed it. Thanks.
>and the find gendisk
>by dev_t code in sd.c is still no good.
In the latest source, sd_find_scsi_device, which I added, does not use
dev_t. Here is the latest version.
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;
}
I'll try sysfs attribute instead of this.
>On Thu, 27 May 2004 14:48:40 +0100, Christoph Hellwig wrote:
>>Not the kind of interface we want exported. IMHO you shouldn't find
>>device by dev_t but add a dumpdevice sysfs attribute to the scsi_device
>>where you can echo 1 to to make it a possible dump device.
Best Regards,
Takao Indoh
On Thu, Jun 17, 2004 at 10:21:27PM +0900, Takao Indoh wrote:
> On Thu, 17 Jun 2004 13:49:57 +0100, Christoph Hellwig wrote:
>
> >my old comments for this are still valid, please add the actual dumping
> >methods directly to scsi_host_template instead of a pointer to another
> >method vector,
>
> I have already done in the latest patch.
Okay. Looks like I looked at an older patch, sorry.
>
> 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
I'd say implement these unconditionally, it's not _that_ much code..
> >please make it not a module of it's own but part of the
> >scsi code,
>
> Do you mean scsi_dump module should be merged with sd_mod.o or scsi_mod.o?
scsi_mod.o.
> I'll try sysfs attribute instead of this.
Okay, thanks. Or some other way to find by host/channel/target/lun using
scsi_device_lookup.
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
Best Regards,
Takao Indoh
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..
On Mon, Jun 21, 2004 at 04:59:52PM +0900, Takao Indoh wrote:
> >> 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
the right interface would probably be a dump attribute for the scsi_device
where you can echo enable or disable (or 1/0) to. The also nicely solves
then need to find an associated scsi_device for a given block_dev.
On Mon, 21 Jun 2004 10:01:29 +0200, Arjan van de Ven wrote:
>> 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..
Plural devices(partitions) can be registered to the diskdump as dump
device, so it seems difficult to represent a dump relationship with a
symlink.
Possible idea is adding new attribute to somewhere of sysfs entry,
for example /sys/block/sda/sda1/dumpdev.
When new device is added, echo 1 to this entry.
# echo 1 > /sys/block/sda/sda1/dumpdev
Its store function is as follows.
static ssize_t store(struct kobject * kobj, struct attribute * attr,
const char * buf, size_t count)
{
ssize_t ret;
struct gendisk *disk =
container_of(p->kobj.parent, struct gendisk, kobj);
return dump_register_device(disk);
}
The problem is how to get scsi_device from gendisk.
It seems that the method using sysfs is more complex than current method
using proc...
It may be better to use simply module parameter like netdump.
Best Regards,
Takao Indoh
On Thu, 17 Jun 2004 14:39:06 +0100, Christoph Hellwig wrote:
>> >please make it not a module of it's own but part of the
>> >scsi code,
>>
>> Do you mean scsi_dump module should be merged with sd_mod.o or scsi_mod.o?
>
>scsi_mod.o.
It is difficult because disk_dump and scsi_dump try to check checksum of
itself using check_crc_module so as to confirm whether module is
compromised or not.
drivers/scsi/scsi_dump.c:
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;
}
Therefore, scsi_dump need to be always compiled as independent module.
If scsi_dump is merged with scsi_mod, scsi_mod is not able to be
compiled statically.
Best Regards,
Takao Indoh
On Tue, Jun 22, 2004 at 09:01:43PM +0900, Takao Indoh wrote:
> On Thu, 17 Jun 2004 14:39:06 +0100, Christoph Hellwig wrote:
>
> >> >please make it not a module of it's own but part of the
> >> >scsi code,
> >>
> >> Do you mean scsi_dump module should be merged with sd_mod.o or scsi_mod.o?
> >
> >scsi_mod.o.
>
> It is difficult because disk_dump and scsi_dump try to check checksum of
> itself using check_crc_module so as to confirm whether module is
> compromised or not.
> Therefore, scsi_dump need to be always compiled as independent module.
> If scsi_dump is merged with scsi_mod, scsi_mod is not able to be
> compiled statically.
So check if it's a module and otherwise not.
On Tue, 22 Jun 2004 13:12:01 +0100, Christoph Hellwig wrote:
>On Tue, Jun 22, 2004 at 09:01:43PM +0900, Takao Indoh wrote:
>> On Thu, 17 Jun 2004 14:39:06 +0100, Christoph Hellwig wrote:
>>
>> >> >please make it not a module of it's own but part of the
>> >> >scsi code,
>> >>
>> >> Do you mean scsi_dump module should be merged with sd_mod.o or
>> >> scsi_mod.o?
>> >
>> >scsi_mod.o.
>>
>> It is difficult because disk_dump and scsi_dump try to check checksum of
>> itself using check_crc_module so as to confirm whether module is
>> compromised or not.
>
>
>> Therefore, scsi_dump need to be always compiled as independent module.
>> If scsi_dump is merged with scsi_mod, scsi_mod is not able to be
>> compiled statically.
>
>So check if it's a module and otherwise not.
It means checksum is not used when scsi_mod is compiled statically.
I think it is very important to check the checksum before dump starts.
The most serious problem of dumping to the disk is that dumping may
overwrite parts of filesystem and destroy user data. This problem
happens when data structures or codes which is used during dumping are
broken.
Diskdump solves this problem by the following methods.
a) Using module checksum, check whether codes (and data) are broken.
b) Confirm whether the target device is really dump device, by checking
that target device is rightly formatted.
Therefore, checking the checksum is necessary before dumping. scsi_dump
always needs to be compiled as module. So I said it is difficult to
merge scsi_dump with scsi_mod.
Best Regards,
Takao Indoh