Skip to contentSkip to conversation

Diskdump - yet another crash dump function

PATCH In review Takao Indoh 8 participants 38 messages May 27, 2004 – Jun 23, 2004

Subsystems (6)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Diskdump - yet another crash dump function

Although I know about LKCD and netdump, I'm developing yet another crash dump, which is a polling-based disk dump as netdump do. Because it disables any interrupts during doing dump, it can avoid lots of problems LKCD has.

Main Feature
- Reliability
Diskdump disables interrupts, stops other cpus and writes to the disk with polling mode. Therefore, unnecessary functions(like interrupt handler) don't disturb dumping.
- Safety
Before writing to the disk, diskdump checks its disk partition to confirm whether it is a really dump device. Therefore, diskdump doesn't overwrite filesystems by mistake.

Currently, diskdump supports only scsi disk(aic7xxx/aic79xx), but, I'm planning to support IDE disk in the near future.

Diskdump can be downloaded from here.
http://sourceforge.net/projects/lkdump
Please see readme.txt which can be downloaded from this site.

Any comments?

Best Regards,
Takao Indoh

#1 lore raw

PATCH 1/4 Diskdump - yet another crash dump function

Hi,

I forgot to attach patches.
This is a patch for diskcump common layer.

Best Regards,
Takao Indoh

arch/i386/kernel/nmi.c +1 -0
diff -Nur linux-2.6.6.org/arch/i386/kernel/nmi.c linux-2.6.6/arch/i386/kernel/nmi.c
@@ -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);
arch/i386/kernel/reboot.c +2 -1
diff -Nur linux-2.6.6.org/arch/i386/kernel/reboot.c linux-2.6.6/arch/i386/kernel/reboot.c
@@ -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();
arch/i386/kernel/traps.c +3 -1
diff -Nur linux-2.6.6.org/arch/i386/kernel/traps.c linux-2.6.6/arch/i386/kernel/traps.c
@@ -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())
arch/i386/mm/init.c +2 -1
diff -Nur linux-2.6.6.org/arch/i386/mm/init.c linux-2.6.6/arch/i386/mm/init.c
@@ -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;
drivers/block/Kconfig +5 -0
diff -Nur linux-2.6.6.org/drivers/block/Kconfig linux-2.6.6/drivers/block/Kconfig
@@ -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
drivers/block/Makefile +1 -0
diff -Nur linux-2.6.6.org/drivers/block/Makefile linux-2.6.6/drivers/block/Makefile
@@ -43,3 +43,4 @@
 obj-$(CONFIG_VIODASD)		+= viodasd.o
 obj-$(CONFIG_BLK_DEV_CARMEL)	+= carmel.o
 
+obj-$(CONFIG_DISKDUMP)		+= diskdump.o diskdumplib.o
drivers/block/diskdump.c +971 -0

971 lines not on this page · raw message

drivers/block/diskdumplib.c +203 -0

203 lines not on this page · raw message

drivers/block/genhd.c +27 -0
diff -Nur linux-2.6.6.org/drivers/block/genhd.c linux-2.6.6/drivers/block/genhd.c
@@ -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);
drivers/char/sysrq.c +14 -1
diff -Nur linux-2.6.6.org/drivers/char/sysrq.c linux-2.6.6/drivers/char/sysrq.c
@@ -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,
include/asm-i386/diskdump.h +73 -0

73 lines not on this page · raw message

include/asm-i386/kmap_types.h +2 -1
diff -Nur linux-2.6.6.org/include/asm-i386/kmap_types.h linux-2.6.6/include/asm-i386/kmap_types.h
@@ -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
include/linux/diskdump.h +150 -0

150 lines not on this page · raw message

include/linux/diskdumplib.h +57 -0

57 lines not on this page · raw message

include/linux/kernel.h +4 -0
diff -Nur linux-2.6.6.org/include/linux/kernel.h linux-2.6.6/include/linux/kernel.h
@@ -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
kernel/panic.c +23 -0
diff -Nur linux-2.6.6.org/kernel/panic.c linux-2.6.6/kernel/panic.c
@@ -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");
+}
#2 lore raw

PATCH 2/4 Diskdump - yet another crash dump function

In reply to Takao Indoh

Hi,

This is a patch for scsi common layer.

Best Regards,
Takao Indoh

drivers/scsi/Kconfig +6 -0
diff -Nur linux-2.6.6.org/drivers/scsi/Kconfig linux-2.6.6/drivers/scsi/Kconfig
@@ -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
drivers/scsi/Makefile +2 -0
diff -Nur linux-2.6.6.org/drivers/scsi/Makefile linux-2.6.6/drivers/scsi/Makefile
@@ -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 \
drivers/scsi/scsi.c +4 -0
diff -Nur linux-2.6.6.org/drivers/scsi/scsi.c linux-2.6.6/drivers/scsi/scsi.c
@@ -691,6 +691,10 @@
 {
 	unsigned long flags;
 
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+	if (crashdump_mode())
+		return;
+#endif
 	/*
 	 * We don't have to worry about this one timing out any more.
 	 * If we are unable to remove the timer, then the command
drivers/scsi/scsi_dump.c +565 -0

565 lines not on this page · raw message

drivers/scsi/scsi_dump.h +38 -0
diff -Nur linux-2.6.6.org/drivers/scsi/scsi_dump.h linux-2.6.6/drivers/scsi/scsi_dump.h
@@ -0,0 +1,38 @@
+#ifndef _SCSI_DUMP_H
+#define _SCSI_DUMP_H
+
+/*
+ *  linux/drivers/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)(Scsi_Device *);
+	int (*quiesce)(Scsi_Device *);
+	int (*shutdown)(Scsi_Device *);
+	void (*poll)(Scsi_Device *);
+};
+
+/* sd.c function */
+extern Scsi_Device *sd_find_scsi_device(dev_t);
+
+#endif /* _SCSI_DUMP_H */
drivers/scsi/scsi_error.c +4 -0
diff -Nur linux-2.6.6.org/drivers/scsi/scsi_error.c linux-2.6.6/drivers/scsi/scsi_error.c
@@ -402,6 +402,10 @@
  **/
 static void scsi_eh_done(struct scsi_cmnd *scmd)
 {
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+	if (crashdump_mode())
+		return;
+#endif
 	/*
 	 * if the timeout handler is already running, then just set the
 	 * flag which says we finished late, and return.  we have no
drivers/scsi/scsi_syms.c +3 -0
diff -Nur linux-2.6.6.org/drivers/scsi/scsi_syms.c linux-2.6.6/drivers/scsi/scsi_syms.c
@@ -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);
drivers/scsi/sd.c +15 -0
diff -Nur linux-2.6.6.org/drivers/scsi/sd.c linux-2.6.6/drivers/scsi/sd.c
@@ -192,6 +192,21 @@
 	up(&sd_ref_sem);
 }
 
+#if defined(CONFIG_DISKDUMP) || defined(CONFIG_DISKDUMP_MODULE)
+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.
include/scsi/scsi_host.h +5 -0
diff -Nur linux-2.6.6.org/include/scsi/scsi_host.h linux-2.6.6/include/scsi/scsi_host.h
@@ -345,6 +345,11 @@
 	 * module_init/module_exit.
 	 */
 	struct list_head legacy_hosts;
+
+	/*
+	 * operations for dump
+	 */
+	struct scsi_dump_ops *dump_ops;
 };
 
 /*
#3 lore raw
Quoted 4 lines · Takao Indoh

obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o
obj-$(CONFIG_CHR_DEV_SG) += sg.o

+obj-$(CONFIG_SCSI_DUMP) += scsi_dump.o

please use tabs instead of space here.

drivers/scsi/scsi.c line 697

Quoted 8 lines
drivers/scsi/scsi.c +4 -0
@@ -691,6 +691,10 @@
 {
 	unsigned long flags;
 
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+	if (crashdump_mode())
+		return;
+#endif

Please make sure crashdump_mode() is always defined so you don't need the ifdef mess.

drivers/scsi/scsi_dump.c line 26

Quoted 1 line
drivers/scsi/scsi_dump.c +1 -0
+#include <linux/config.h>

not needed.

drivers/scsi/scsi_dump.c line 35

Quoted 1 line
drivers/scsi/scsi_dump.c +1 -0
+#include "scsi.h"

please don't use this in new code, use the include/scsi/*.h includes instead.

Review line 700

Quoted 1 line
(quoted diff) +1 -0
+#include "scsi_priv.h"

this is a private header for scsi_mod.ko as the name might suggest ;) But given the rather strange exports I'd suggest to always build scsi_dump.c into scsi_mod.ko anyway.

Quoted 1 line

+#include "hosts.h"

Please use <scsi/scsi_host.h>

drivers/scsi/scsi_dump.c line 46

Quoted 9 lines
drivers/scsi/scsi_dump.c +9 -0
+#include "scsi_dump.h"
+#include <scsi/scsi_ioctl.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>

please rework the include order, <linux/*.h> first, then <asm/*.h>, then <scsi/*.h>, then private headers.

drivers/scsi/scsi_dump.c line 60

Quoted 10 lines
drivers/scsi/scsi_dump.c +10 -0
+#define DEBUG 0
+#if DEBUG
+# define Dbg(x, ...) printk(KERN_INFO "scsi_dump:" x "\n", ## __VA_ARGS__)
+#else
+# define Dbg(x...)
+#endif
+
+#define Err(x, ...)	printk(KERN_ERR "scsi_dump: " x "\n", ## __VA_ARGS__);
+#define Warn(x, ...)	printk(KERN_WARNING "scsi_dump: " x "\n", ## __VA_ARGS__)
+#define Info(x, ...)	printk(x "\n", ## __VA_ARGS__)

please use the pr_* macros from kernel.h

Quoted 1 line

+static int quiesce_ok = 0;

on need to initialize to 0

drivers/scsi/scsi_dump.c line 70

Quoted 5 lines
drivers/scsi/scsi_dump.c +5 -0
+static Scsi_Cmnd scsi_dump_cmnd;
+static struct request scsi_dump_req;
+static uint32_t module_crc;
+
+static void rw_intr(Scsi_Cmnd * scmd)

Please never use the Scsi_Foo types but the struct scsi_foo versions (goes hand in hand with using <scsi/*.h>

Quoted 1 line

+static void init_scsi_command(Scsi_Device *sdev, Scsi_Cmnd *scmd, void *buf, int len, unsigned char direction, int set_lun)

plese make sure lines aren't longer than 80 characters.

drivers/scsi/scsi_dump.c line 385

Quoted 10 lines
drivers/scsi/scsi_dump.c +10 -0
+	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);
+		if (host->host_lock == &host->default_lock)
+			sanity = 1;
+		else
+			return -EIO;
+	}

This look bogus to me. Why handle the case of the default and per-driver lock differently?

drivers/scsi/scsi_dump.c line 522

Quoted 9 lines
drivers/scsi/scsi_dump.c +9 -0
+static int scsi_dump_add_device(struct disk_dump_device *dump_device)
+{
+	Scsi_Device *sdev;
+
+	sdev = dump_device->device;
+	if (!sdev->host->hostt->dump_ops)
+		return -ENOTSUPP;
+
+	scsi_device_get(sdev);  /* retval ignored ? */

Please fix this ;-)

drivers/scsi/scsi_dump.h line 2

Quoted 6 lines

diff -Nur linux-2.6.6.org/drivers/scsi/scsi_dump.h linux-2.6.6/drivers/scsi/scsi_dump.h

drivers/scsi/scsi_dump.h +2 -0
@@ -0,0 +1,38 @@
+#ifndef _SCSI_DUMP_H
+#define _SCSI_DUMP_H

This file should go into include/scsi/.

drivers/scsi/scsi_dump.h line 33

Quoted 6 lines
drivers/scsi/scsi_dump.h +6 -0
+struct scsi_dump_ops {
+	int (*sanity_check)(Scsi_Device *);
+	int (*quiesce)(Scsi_Device *);
+	int (*shutdown)(Scsi_Device *);
+	void (*poll)(Scsi_Device *);
+};

But I'm not sure we need it at all. These should just go into the scsi_host_template, imho.

drivers/scsi/scsi_dump.h

Quoted 6 lines
drivers/scsi/scsi_dump.h +4 -0
 static void scsi_eh_done(struct scsi_cmnd *scmd)
 {
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+	if (crashdump_mode())
+		return;
+#endif

Same comments as above, please avoid ifdefs.

drivers/scsi/scsi_dump.h line 112

Quoted 10 lines
drivers/scsi/scsi_dump.h +3 -0
+#include "scsi_priv.h"
 
 
 /*
@@ -107,3 +108,5 @@
  */
 EXPORT_SYMBOL(scsi_add_timer);
 EXPORT_SYMBOL(scsi_delete_timer);
+
+EXPORT_SYMBOL(scsi_decide_disposition);

prototype in scsi_priv.h == not exported

Quoted 10 lines
drivers/scsi/sd.c +4 -0
@@ -192,6 +192,21 @@
 	up(&sd_ref_sem);
 }
 
+#if defined(CONFIG_DISKDUMP) || defined(CONFIG_DISKDUMP_MODULE)
+Scsi_Device *sd_find_scsi_device(dev_t dev)
+{
+	struct gendisk *disk;

drivers/scsi/sd.c line 208

Quoted 10 lines
drivers/scsi/sd.c +10 -0
+	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

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.

#4 lore raw

Thanks for many comments.
I am updating files now...

Best Regards,
Takao Indoh

Quoted 83 lines · Takao Indoh
Quoted 4 lines

obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o
obj-$(CONFIG_CHR_DEV_SG) += sg.o

+obj-$(CONFIG_SCSI_DUMP) += scsi_dump.o

please use tabs instead of space here.

drivers/scsi/scsi.c line 697

Quoted 8 lines
drivers/scsi/scsi.c +4 -0
@@ -691,6 +691,10 @@
 {
 	unsigned long flags;
 
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+	if (crashdump_mode())
+		return;
+#endif

Please make sure crashdump_mode() is always defined so you don't need the ifdef mess.

drivers/scsi/scsi_dump.c line 26

Quoted 1 line
drivers/scsi/scsi_dump.c +1 -0
+#include <linux/config.h>

not needed.

drivers/scsi/scsi_dump.c line 35

Quoted 1 line
drivers/scsi/scsi_dump.c +1 -0
+#include "scsi.h"

please don't use this in new code, use the include/scsi/*.h includes instead.

Review line 700

Quoted 1 line
(quoted diff) +1 -0
+#include "scsi_priv.h"

this is a private header for scsi_mod.ko as the name might suggest ;) But given the rather strange exports I'd suggest to always build scsi_dump.c into scsi_mod.ko anyway.

Quoted 1 line

+#include "hosts.h"

Please use <scsi/scsi_host.h>

drivers/scsi/scsi_dump.c line 46

Quoted 9 lines
drivers/scsi/scsi_dump.c +9 -0
+#include "scsi_dump.h"
+#include <scsi/scsi_ioctl.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>

please rework the include order, <linux/*.h> first, then <asm/*.h>, then <scsi/*.h>, then private headers.

Quoted 11 lines
drivers/scsi/scsi_dump.c +9 -0
+#define DEBUG 0
+#if DEBUG
+# define Dbg(x, ...) printk(KERN_INFO "scsi_dump:" x "\n", ## __VA_ARGS__)
+#else
+# define Dbg(x...)
+#endif
+
+#define Err(x, ...)	printk(KERN_ERR "scsi_dump: " x "\n", ## __VA_ARGS__);
+#define Warn(x, ...)	printk(KERN_WARNING "scsi_dump: " x "\n", ## 

__VA_ARGS__)
+#define Info(x, ...) printk(x "\n", ## __VA_ARGS__)

please use the pr_* macros from kernel.h

Quoted 1 line

+static int quiesce_ok = 0;

on need to initialize to 0

drivers/scsi/scsi_dump.c line 70

Quoted 5 lines
drivers/scsi/scsi_dump.c +5 -0
+static Scsi_Cmnd scsi_dump_cmnd;
+static struct request scsi_dump_req;
+static uint32_t module_crc;
+
+static void rw_intr(Scsi_Cmnd * scmd)

Please never use the Scsi_Foo types but the struct scsi_foo versions (goes hand in hand with using <scsi/*.h>

Quoted 2 lines

+static void init_scsi_command(Scsi_Device *sdev, Scsi_Cmnd *scmd, void * buf, int len, unsigned char direction, int set_lun)

Quoted 7 lines

plese make sure lines aren't longer than 80 characters.

drivers/scsi/scsi_dump.c

Quoted 5 lines
drivers/scsi/scsi_dump.c +5 -0
+	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);

Quoted 56 lines

drivers/scsi/scsi_dump.c line 385

Quoted 5 lines
drivers/scsi/scsi_dump.c +5 -0
+		if (host->host_lock == &host->default_lock)
+			sanity = 1;
+		else
+			return -EIO;
+	}

This look bogus to me. Why handle the case of the default and per-driver lock differently?

drivers/scsi/scsi_dump.c line 522

Quoted 9 lines
drivers/scsi/scsi_dump.c +9 -0
+static int scsi_dump_add_device(struct disk_dump_device *dump_device)
+{
+	Scsi_Device *sdev;
+
+	sdev = dump_device->device;
+	if (!sdev->host->hostt->dump_ops)
+		return -ENOTSUPP;
+
+	scsi_device_get(sdev);  /* retval ignored ? */

Please fix this ;-)

drivers/scsi/scsi_dump.h line 2

Quoted 10 lines

diff -Nur linux-2.6.6.org/drivers/scsi/scsi_dump.h linux-2.6.6/drivers/ scsi/scsi_dump.h
--- linux-2.6.6.org/drivers/scsi/scsi_dump.h 1970-01-01 09:00:00.
000000000 +0900
+++ linux-2.6.6/drivers/scsi/scsi_dump.h 2004-05-27 09:31:07.000000000 +

0900

drivers/scsi/scsi_dump.h +2 -0
@@ -0,0 +1,38 @@
+#ifndef _SCSI_DUMP_H
+#define _SCSI_DUMP_H

This file should go into include/scsi/.

drivers/scsi/scsi_dump.h line 33

Quoted 6 lines
drivers/scsi/scsi_dump.h +6 -0
+struct scsi_dump_ops {
+	int (*sanity_check)(Scsi_Device *);
+	int (*quiesce)(Scsi_Device *);
+	int (*shutdown)(Scsi_Device *);
+	void (*poll)(Scsi_Device *);
+};

But I'm not sure we need it at all. These should just go into the scsi_host_template, imho.

drivers/scsi/scsi_error.c line 408

Quoted 6 lines
drivers/scsi/scsi_error.c +4 -0
 static void scsi_eh_done(struct scsi_cmnd *scmd)
 {
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+	if (crashdump_mode())
+		return;
+#endif

Same comments as above, please avoid ifdefs.

Quoted 3 lines

+#include "scsi_priv.h"

Quoted 11 lines

drivers/scsi/scsi_syms.c line 112

Quoted 7 lines

/*

drivers/scsi/scsi_syms.c +2 -0
@@ -107,3 +108,5 @@
  */
 EXPORT_SYMBOL(scsi_add_timer);
 EXPORT_SYMBOL(scsi_delete_timer);
+
+EXPORT_SYMBOL(scsi_decide_disposition);

prototype in scsi_priv.h == not exported

Quoted 1 line

--- linux-2.6.6.org/drivers/scsi/sd.c 2004-05-20 08:58:48.000000000 +

0900

Quoted 23 lines
Quoted 9 lines

+++ linux-2.6.6/drivers/scsi/sd.c 2004-05-27 09:24:46.000000000 +0900

drivers/scsi/sd.c +4 -0
@@ -192,6 +192,21 @@
 	up(&sd_ref_sem);
 }
 
+#if defined(CONFIG_DISKDUMP) || defined(CONFIG_DISKDUMP_MODULE)
+Scsi_Device *sd_find_scsi_device(dev_t dev)
+{
+	struct gendisk *disk;

drivers/scsi/sd.c line 208

Quoted 10 lines
drivers/scsi/sd.c +10 -0
+	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

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.

#5 lore raw

PATCH 3/4 Diskdump - yet another crash dump function

In reply to Takao Indoh

This is a patch for aic7xxx driver.

Best Regards,
Takao Indoh

drivers/scsi/aic7xxx/aic7xxx_osm.c +43 -0
diff -Nur linux-2.6.6.org/drivers/scsi/aic7xxx/aic7xxx_osm.c linux-2.6.6/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -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(Scsi_Device *);
+static void	   ahc_linux_poll(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(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(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)
 {
drivers/scsi/aic7xxx/aic7xxx_osm.h +19 -0
diff -Nur linux-2.6.6.org/drivers/scsi/aic7xxx/aic7xxx_osm.h linux-2.6.6/drivers/scsi/aic7xxx/aic7xxx_osm.h
@@ -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>
 
@@ -87,6 +88,8 @@
 #include "scsi.h"
 #include "hosts.h"
 
+#include "scsi_dump.h"
+
 /* Name space conflict with BSD queue macros */
 #ifdef LIST_HEAD
 #undef LIST_HEAD
@@ -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;
 
#6 lore raw

drivers/scsi/aic7xxx/aic7xxx_osm.h line 272

Quoted 13 lines
drivers/scsi/aic7xxx/aic7xxx_osm.h +13 -0
+/******************************** 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.

#7 lore raw
Quoted 8 lines · Christoph Hellwig
Quoted 8 lines
drivers/scsi/aic7xxx/aic7xxx_osm.h +8 -0
+/******************************** 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
Quoted 8 lines

drivers/scsi/aic7xxx/aic7xxx_osm.h line 272

Quoted 5 lines
drivers/scsi/aic7xxx/aic7xxx_osm.h +5 -0
+#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.

yeah, this is arguably the biggest (and i think only) conceptual item that needs to be solved before this can be integrated.

The goal of these defines is to provide a separate (and polling based) timer mechanism that is completely separate from the normal kernel's state.

it would also be easier to enable diskdump in a driver if this was handled in add_timer()/del_timer()/mod_timer()/tasklet_schedule().

Ingo

#8 lore raw

Hi, all

Quoted 24 lines · Ingo Molnar
Quoted 9 lines · Christoph Hellwig
Quoted 9 lines

+/******************************** Disk dump ************************** *********/

(quoted diff) +7 -0
+#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
Quoted 8 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+#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.

yeah, this is arguably the biggest (and i think only) conceptual item that needs to be solved before this can be integrated.

Quoted 6 lines

The goal of these defines is to provide a separate (and polling based) timer mechanism that is completely separate from the normal kernel's state.

it would also be easier to enable diskdump in a driver if this was handled in add_timer()/del_timer()/mod_timer()/tasklet_schedule().

Instead of redefinition, I add hooks into the timer/tasklet routines.

ex.

int mod_timer(struct timer_list *timer, unsigned long expires) {
BUG_ON(!timer->function);

+ if (crashdump_mode()) {
+ return diskdump_mod_timer(timer, expires);
+ }

check_timer(timer);

Please see the following patches for details.
How do you think?

Best Regards,
Takao Indoh

include/linux/diskdumplib.h +19 -0
diff -Nur linux-2.6.6.org/include/linux/diskdumplib.h linux-2.6.6/include/linux/diskdumplib.h
@@ -0,0 +1,19 @@
+#ifndef _LINUX_DISKDUMPLIB_H
+#define _LINUX_DISKDUMPLIB_H
+
+struct timer_list;
+struct tasklet_struct;
+struct work_struct;
+
+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 *);
+long diskdump_schedule_timeout(signed long timeout);
+
+#endif /* _LINUX_DISKDUMPLIB_H */
include/linux/interrupt.h +6 -0
diff -Nur linux-2.6.6.org/include/linux/interrupt.h linux-2.6.6/include/linux/interrupt.h
@@ -7,6 +7,7 @@
 #include <linux/linkage.h>
 #include <linux/bitops.h>
 #include <linux/preempt.h>
+#include <linux/diskdumplib.h>
 #include <asm/atomic.h>
 #include <asm/hardirq.h>
 #include <asm/ptrace.h>
@@ -172,6 +173,11 @@
 
 static inline void tasklet_schedule(struct tasklet_struct *t)
 {
+	if (crashdump_mode()) {
+		diskdump_tasklet_schedule(t);
+		return;
+	}
+
 	if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
 		__tasklet_schedule(t);
 }
include/linux/timer.h +5 -1
diff -Nur linux-2.6.6.org/include/linux/timer.h linux-2.6.6/include/linux/timer.h
@@ -4,6 +4,7 @@
 #include <linux/config.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
+#include <linux/diskdumplib.h>
 
 struct tvec_t_base_s;
 
@@ -83,7 +84,10 @@
  */
 static inline void add_timer(struct timer_list * timer)
 {
-	__mod_timer(timer, timer->expires);
+	if (crashdump_mode())
+		diskdump_add_timer(timer);
+	else
+		__mod_timer(timer, timer->expires);
 }
 
 #ifdef CONFIG_SMP
kernel/Makefile +1 -1
diff -Nur linux-2.6.6.org/kernel/Makefile linux-2.6.6/kernel/Makefile
@@ -7,7 +7,7 @@
 	    sysctl.o capability.o ptrace.o timer.o user.o \
 	    signal.o sys.o kmod.o workqueue.o pid.o \
 	    rcupdate.o intermodule.o extable.o params.o posix-timers.o \
-	    kthread.o
+	    kthread.o diskdumplib.o
 
 obj-$(CONFIG_FUTEX) += futex.o
 obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
kernel/diskdumplib.c +213 -0

213 lines not on this page · raw message

kernel/timer.c +16 -0
diff -Nur linux-2.6.6.org/kernel/timer.c linux-2.6.6/kernel/timer.c
@@ -255,6 +255,10 @@
 {
 	BUG_ON(!timer->function);
 
+	if (crashdump_mode()) {
+		return diskdump_mod_timer(timer, expires);
+	}
+
 	check_timer(timer);
 
 	/*
@@ -286,6 +290,10 @@
 	unsigned long flags;
 	tvec_base_t *base;
 
+	if (crashdump_mode()) {
+		return diskdump_del_timer(timer);
+	}
+
 	check_timer(timer);
 
 repeat:
@@ -327,6 +335,10 @@
 	tvec_base_t *base;
 	int i, ret = 0;
 
+	if (crashdump_mode()) {
+		return diskdump_del_timer(timer);
+	}
+
 	check_timer(timer);
 
 del_again:
@@ -1070,6 +1082,10 @@
 	struct timer_list timer;
 	unsigned long expire;
 
+	if (crashdump_mode()) {
+		return diskdump_schedule_timeout(timeout);
+	}
+
 	switch (timeout)
 	{
 	case MAX_SCHEDULE_TIMEOUT:
kernel/workqueue.c +4 -1
diff -Nur linux-2.6.6.org/kernel/workqueue.c linux-2.6.6/kernel/workqueue.c
@@ -386,7 +386,10 @@
 
 int fastcall schedule_work(struct work_struct *work)
 {
-	return queue_work(keventd_wq, work);
+	if (crashdump_mode())
+		return diskdump_schedule_work(work);
+	else
+		return queue_work(keventd_wq, work);
 }
 
 int fastcall schedule_delayed_work(struct work_struct *work, unsigned long delay)
#9 lore raw
Quoted 14 lines · Takao Indoh

Instead of redefinition, I add hooks into the timer/tasklet routines.

ex.

int mod_timer(struct timer_list *timer, unsigned long expires) {

kernel/timer.c +3 -0
 	BUG_ON(!timer->function);
 
+	if (crashdump_mode()) {
+		return diskdump_mod_timer(timer, expires);
+	}
 
 	 check_timer(timer);
Quoted 2 lines

Please see the following patches for details.
How do you think?

Looks good in principle, some more comments on the actual patch below:

include/linux/diskdumplib.h

Quoted 3 lines

diff -Nur linux-2.6.6.org/include/linux/diskdumplib.h linux-2.6.6/include/linux/diskdumplib.h

include/linux/diskdumplib.h +0 -0

Should probably just go to linux/dump.h or dump_priv.h

Quoted 13 lines

diff -Nur linux-2.6.6.org/include/linux/interrupt.h linux-2.6.6/include/linux/interrupt.h

include/linux/interrupt.h +1 -0
@@ -7,6 +7,7 @@
 #include <linux/linkage.h>
 #include <linux/bitops.h>
 #include <linux/preempt.h>
+#include <linux/diskdumplib.h>
 #include <asm/atomic.h>
 #include <asm/hardirq.h>
 #include <asm/ptrace.h>
@@ -172,6 +173,11 @@
 

include/linux/interrupt.h line 183

Quoted 10 lines
include/linux/interrupt.h +5 -0
 static inline void tasklet_schedule(struct tasklet_struct *t)
 {
+	if (crashdump_mode()) {
+		diskdump_tasklet_schedule(t);
+		return;
+	}
+
 	if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
 		__tasklet_schedule(t);
 }

Please sprinclde unlikely's all over here. Also the above could be shortened to

static inline void tasklet_schedule(struct tasklet_struct *t)

{
	if (unlikely(crashdump_mode()))
		diskdump_tasklet_schedule(t);
	else if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
		__tasklet_schedule(t);
}

include/linux/interrupt.h

Quoted 5 lines
include/linux/interrupt.h +5 -0
+int diskdump_schedule_work(struct work_struct *work)
+{
+	list_add_tail(&work->entry, &diskdump_workq);
+	return 1;
+}

Should probably just inlined, I guess the function call is bigger than all of list_add_tail.

include/linux/interrupt.h

Quoted 5 lines
include/linux/interrupt.h +5 -0
+void diskdump_add_timer(struct timer_list *timer)
+{
+	timer->base = (void *)1;
+	list_add(&timer->entry, &diskdump_timers);
+}

dito.

include/linux/interrupt.h

Quoted 3 lines
include/linux/interrupt.h +3 -0
+	/* run timers */
+	list_for_each_safe(t, n, &diskdump_timers) {
+		timer = list_entry(t, struct timer_list, entry);

list_for_each_entry_safe here please.

include/linux/interrupt.h line 260

Quoted 7 lines
include/linux/interrupt.h +3 -0
@@ -255,6 +255,10 @@
 {
 	BUG_ON(!timer->function);
 
+	if (crashdump_mode()) {
+		return diskdump_mod_timer(timer, expires);
+	}

please remove the superflous braces here.

#10 lore raw

Hi,

Thank you for comment. After I fix these codes, I'll post new version of diskdump.

Best Regards,
Takao Indoh

Quoted 81 lines
Quoted 6 lines

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-17 18:28:01.000000000
+0900

Should probably just go to linux/dump.h or dump_priv.h

Quoted 17 lines

diff -Nur linux-2.6.6.org/include/linux/interrupt.h linux-2.6.6/include/ linux/interrupt.h
--- linux-2.6.6.org/include/linux/interrupt.h 2004-06-04 21:22:09.
000000000 +0900
+++ linux-2.6.6/include/linux/interrupt.h 2004-06-17 18:28:01.000000000 +

0900

include/linux/interrupt.h +1 -0
@@ -7,6 +7,7 @@
 #include <linux/linkage.h>
 #include <linux/bitops.h>
 #include <linux/preempt.h>
+#include <linux/diskdumplib.h>
 #include <asm/atomic.h>
 #include <asm/hardirq.h>
 #include <asm/ptrace.h>
@@ -172,6 +173,11 @@
 

include/linux/interrupt.h line 183

Quoted 10 lines
include/linux/interrupt.h +5 -0
 static inline void tasklet_schedule(struct tasklet_struct *t)
 {
+	if (crashdump_mode()) {
+		diskdump_tasklet_schedule(t);
+		return;
+	}
+
 	if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
 		__tasklet_schedule(t);
 }

Please sprinclde unlikely's all over here. Also the above could be shortened to

static inline void tasklet_schedule(struct tasklet_struct *t)

{
	if (unlikely(crashdump_mode()))
		diskdump_tasklet_schedule(t);
	else if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
		__tasklet_schedule(t);
}

kernel/diskdumplib.c line 95

Quoted 5 lines
kernel/diskdumplib.c +5 -0
+int diskdump_schedule_work(struct work_struct *work)
+{
+	list_add_tail(&work->entry, &diskdump_workq);
+	return 1;
+}

Should probably just inlined, I guess the function call is bigger than all of list_add_tail.

kernel/diskdumplib.c line 108

Quoted 5 lines
kernel/diskdumplib.c +5 -0
+void diskdump_add_timer(struct timer_list *timer)
+{
+	timer->base = (void *)1;
+	list_add(&timer->entry, &diskdump_timers);
+}

dito.

kernel/diskdumplib.c line 156

Quoted 3 lines
kernel/diskdumplib.c +3 -0
+	/* run timers */
+	list_for_each_safe(t, n, &diskdump_timers) {
+		timer = list_entry(t, struct timer_list, entry);

list_for_each_entry_safe here please.

kernel/timer.c line 260

Quoted 7 lines
kernel/timer.c +3 -0
@@ -255,6 +255,10 @@
 {
 	BUG_ON(!timer->function);
 
+	if (crashdump_mode()) {
+		return diskdump_mod_timer(timer, expires);
+	}

please remove the superflous braces here.

#11 lore raw
In reply to Takao Indoh

kernel/timer.c line 260

Quoted 7 lines · Takao Indoh

int mod_timer(struct timer_list *timer, unsigned long expires) {

kernel/timer.c +3 -0
 	BUG_ON(!timer->function);
 
+	if (crashdump_mode()) {
+		return diskdump_mod_timer(timer, expires);
+	}

looks good. Please use the proper coding style:

+ if (crashdump_mode())
+ return diskdump_mod_timer(timer, expires);

but there's another possible method (suggested by Alan Cox) that requires no changes to the timer API hotpaths: 'clear' all timer lists upon a crash [once all CPUs have stopped and irqs are disabled] and just let the drivers use the normal timer APIs. Drive timer execution via a polling method.

this basically approximates your polling based implementation but uses the existing kernel timer data structures and timer mechanism so should be robust and compatible. It doesnt rely on any previous state (because all currently pending timers are discarded) so it's as crash-safe as possible.

what do you think?

Ingo

#12 lore raw

Btw, now that we got you in the loop, any chance to see a forward-port of netdump to 2.6? I think diskdump and netdump could share a lot of infrastructure, and given we already have the net polling hooks adding netdump shouldn't be that much work anymore.

#13 lore raw
Quoted 4 lines · Christoph Hellwig

Btw, now that we got you in the loop, any chance to see a forward-port of netdump to 2.6? I think diskdump and netdump could share a lot of infrastructure, and given we already have the net polling hooks adding netdump shouldn't be that much work anymore.

i think a forward port of netdump might already exist - Jeff, Dave?

i agree that netdump and diskdump should be merged. (Red Hat is involved in the diskdump project too so this is an ultimate goal even though the patches are divergent.) Basically diskdumping is another IO transport - the format, userspace tools and much of the non-IO kernel mechanism is shared. Diskdumping is more complex on the driver level and it also needs to be more careful because it writes to media so it verifies various assumptions by reading on-disk sectors before writing to the area.

Ingo

#14 lore raw

==> Regarding Re: [3/4] [PATCH]Diskdump - yet another crash dump function; Ingo Molnar <[email protected]> adds:

Quoted 4 lines · mingo> * Christoph Hellwig <[email protected]> wrote:
Quoted 4 lines

Btw, now that we got you in the loop, any chance to see a forward-port of netdump to 2.6? I think diskdump and netdump could share a lot of infrastructure, and given we already have the net polling hooks adding netdump shouldn't be that much work anymore.

mingo> i think a forward port of netdump might already exist - Jeff, Dave?

Yes, I ported the code forward to 2.6. The netpoll infrastructure needed a little tweaking to accommodate netdump, but nothing major. Namely, we need to reset some locks, and I added an element to the netpoll data structure for the dump function. I also updated the zap_completion_queue function to touch the nmi watchdog.

mingo> i agree that netdump and diskdump should be merged. (Red Hat is mingo> involved in the diskdump project too so this is an ultimate goal mingo> even though the patches are divergent.) Basically diskdumping is mingo> another IO transport - the format, userspace tools and much of the mingo> non-IO kernel mechanism is shared. Diskdumping is more complex on mingo> the driver level and it also needs to be more careful because it mingo> writes to media so it verifies various assumptions by reading mingo> on-disk sectors before writing to the area.

I'm not quite sure what infrastructure would be shared between the two. Page selection, perhaps? Anything else?

-Jeff

#15 lore raw
Quoted 28 lines · Jeff Moyer

==> Regarding Re: [3/4] [PATCH]Diskdump - yet another crash dump function; Ingo Molnar <[email protected]> adds:

Quoted 4 lines · mingo> * Christoph Hellwig <[email protected]> wrote:
Quoted 4 lines

Btw, now that we got you in the loop, any chance to see a forward-port of netdump to 2.6? I think diskdump and netdump could share a lot of infrastructure, and given we already have the net polling hooks adding netdump shouldn't be that much work anymore.

mingo> i think a forward port of netdump might already exist - Jeff, Dave?

Yes, I ported the code forward to 2.6. The netpoll infrastructure needed a little tweaking to accommodate netdump, but nothing major. Namely, we need to reset some locks, and I added an element to the netpoll data structure for the dump function. I also updated the zap_completion_queue function to touch the nmi watchdog.

mingo> i agree that netdump and diskdump should be merged. (Red Hat is mingo> involved in the diskdump project too so this is an ultimate goal mingo> even though the patches are divergent.) Basically diskdumping is mingo> another IO transport - the format, userspace tools and much of the mingo> non-IO kernel mechanism is shared. Diskdumping is more complex on mingo> the driver level and it also needs to be more careful because it mingo> writes to media so it verifies various assumptions by reading mingo> on-disk sectors before writing to the area.

Quoted 2 lines

I'm not quite sure what infrastructure would be shared between the two. Page selection, perhaps? Anything else?

I think freezing other CPUs code can be shared, but I can't find more.

#16 lore raw
In reply to Ingo Molnar
Quoted 6 lines · Ingo Molnar

but there's another possible method (suggested by Alan Cox) that requires no changes to the timer API hotpaths: 'clear' all timer lists upon a crash [once all CPUs have stopped and irqs are disabled] and just let the drivers use the normal timer APIs. Drive timer execution via a polling method.

Quoted 8 lines

this basically approximates your polling based implementation but uses the existing kernel timer data structures and timer mechanism so should be robust and compatible. It doesnt rely on any previous state (because all currently pending timers are discarded) so it's as crash-safe as possible.

what do you think?

It sounds good because change of timer/tasklet code is not needed. But, I wonder whether this method is safe. For example, if kernel crashes because of problem of timer, clearing lists may be dangerous before dumping. Is it possible to clear all timer lists safely?

Best Regards,
Takao Indoh

#17 lore raw
Quoted 4 lines · Takao Indoh

It sounds good because change of timer/tasklet code is not needed. But, I wonder whether this method is safe. For example, if kernel crashes because of problem of timer, clearing lists may be dangerous before dumping. Is it possible to clear all timer lists safely?

yes it can be done safely - just INIT_LIST_HEAD() all the timer list heads - like init_timers_cpu() does.

Ingo

#18 lore raw
Quoted 9 lines · Ingo Molnar
Quoted 4 lines · Takao Indoh

It sounds good because change of timer/tasklet code is not needed. But, I wonder whether this method is safe. For example, if kernel crashes because of problem of timer, clearing lists may be dangerous before dumping. Is it possible to clear all timer lists safely?

yes it can be done safely - just INIT_LIST_HEAD() all the timer list heads - like init_timers_cpu() does.

obviously this only involves the dumping CPU - no other CPU will run any kernel code. On SMP you should also clear the timer spinlock of the dumping CPU's timer base, if the crash happened within the timer code.

Ingo

#19 lore raw
Quoted 7 lines · Ingo Molnar
Quoted 2 lines

yes it can be done safely - just INIT_LIST_HEAD() all the timer list heads - like init_timers_cpu() does.

obviously this only involves the dumping CPU - no other CPU will run any kernel code. On SMP you should also clear the timer spinlock of the dumping CPU's timer base, if the crash happened within the timer code.

put another way: you can consider 'safe dumping' to involve a simplified bootup and initialization of the kernel's data structures that you need during the dump, to create a barrier for any (possibly corrupted) kernel data state prior the crash.

you need to 'boot up' your dumping data structures, the driver (and hardware) you are going to use for dumping, and the timer code as well.

(total separation is not possible because e.g. the dumping code's pagetable entries are a data structure too that could get corrupted - on the other hand complete separation is not necessary because crash statistics show that this is not a likely event - it is much more likely to get failure due to hw errors.)

Ingo

#20 lore raw
In reply to Ingo Molnar
Quoted 10 lines · Ingo Molnar
Quoted 7 lines · Ingo Molnar
Quoted 4 lines · Takao Indoh

It sounds good because change of timer/tasklet code is not needed. But, I wonder whether this method is safe. For example, if kernel crashes because of problem of timer, clearing lists may be dangerous before dumping. Is it possible to clear all timer lists safely?

Quoted 8 lines
Quoted 2 lines

yes it can be done safely - just INIT_LIST_HEAD() all the timer list heads - like init_timers_cpu() does.

obviously this only involves the dumping CPU - no other CPU will run any kernel code. On SMP you should also clear the timer spinlock of the dumping CPU's timer base, if the crash happened within the timer code.

Ingo

How about this?

void clear_timers(void)

{
	int j;
	tvec_base_t *base;
       
	base = &per_cpu(tvec_bases, smp_processor_id());
	spin_lock_init(&base->lock);
	for (j = 0; j < TVN_SIZE; j++) {
		INIT_LIST_HEAD(base->tv5.vec + j);
		INIT_LIST_HEAD(base->tv4.vec + j);
		INIT_LIST_HEAD(base->tv3.vec + j);
		INIT_LIST_HEAD(base->tv2.vec + j);
	}
	for (j = 0; j < TVR_SIZE; j++)
		INIT_LIST_HEAD(base->tv1.vec + j);

	base->timer_jiffies = jiffies;
}

And new function which runs timer during dumping is needed, like __run_timers() does...

Best Regards,
Takao Indoh

#21 lore raw

Hello,

Quoted 37 lines · Takao Indoh
Quoted 11 lines · Ingo Molnar
Quoted 8 lines · Ingo Molnar
Quoted 4 lines · Takao Indoh

It sounds good because change of timer/tasklet code is not needed. But, I wonder whether this method is safe. For example, if kernel crashes because of problem of timer, clearing lists may be dangerous before dumping. Is it possible to clear all timer lists safely?

Quoted 8 lines
Quoted 2 lines

yes it can be done safely - just INIT_LIST_HEAD() all the timer list heads - like init_timers_cpu() does.

obviously this only involves the dumping CPU - no other CPU will run any kernel code. On SMP you should also clear the timer spinlock of the dumping CPU's timer base, if the crash happened within the timer code.

Ingo

How about this?

void clear_timers(void)

{
	int j;
	tvec_base_t *base;
       
	base = &per_cpu(tvec_bases, smp_processor_id());
	spin_lock_init(&base->lock);
	for (j = 0; j < TVN_SIZE; j++) {
		INIT_LIST_HEAD(base->tv5.vec + j);
		INIT_LIST_HEAD(base->tv4.vec + j);
Quoted 8 lines
		INIT_LIST_HEAD(base->tv3.vec + j);
		INIT_LIST_HEAD(base->tv2.vec + j);
	}
	for (j = 0; j < TVR_SIZE; j++)
		INIT_LIST_HEAD(base->tv1.vec + j);

	base->timer_jiffies = jiffies;
}

I think you should save original values to somewhere else, so you can refer these values from vmcore if you want.

#22 lore raw
Quoted 2 lines · Nobuhiro Tachino

I think you should save original values to somewhere else, so you can refer these values from vmcore if you want.

Yes, I should. It is necessary if kernel crashed because of timer problem. Thanks!

Best Regards,
Takao Indoh

#23 lore raw
In reply to Ingo Molnar
Quoted 11 lines · Ingo Molnar

but there's another possible method (suggested by Alan Cox) that requires no changes to the timer API hotpaths: 'clear' all timer lists upon a crash [once all CPUs have stopped and irqs are disabled] and just let the drivers use the normal timer APIs. Drive timer execution via a polling method.

this basically approximates your polling based implementation but uses the existing kernel timer data structures and timer mechanism so should be robust and compatible. It doesnt rely on any previous state (because all currently pending timers are discarded) so it's as crash-safe as possible.

This is a test patch for clearing timer/tasklet and executing it during dumping.
This patch does not work yet. I tried dumping using this patch, but aic7xxx is unstable...

include/linux/interrupt.h +4 -0
diff -Nur linux-2.6.6.org/include/linux/interrupt.h linux-2.6.6/include/linux/interrupt.h
@@ -246,4 +246,8 @@
 extern int probe_irq_off(unsigned long);	/* returns 0 or negative on failure */
 extern unsigned int probe_irq_mask(unsigned long);	/* returns mask of ISA interrupts */
 
+
+extern void diskdump_clear_tasklet(void);
+extern void diskdump_run_tasklet(void);
+
 #endif
include/linux/timer.h +3 -0
diff -Nur linux-2.6.6.org/include/linux/timer.h linux-2.6.6/include/linux/timer.h
@@ -96,4 +96,7 @@
 extern void run_local_timers(void);
 extern void it_real_fn(unsigned long);
 
+extern void diskdump_clear_timers(void);
+extern void diskdump_run_timers(void);
+
 #endif
include/linux/workqueue.h +3 -0
diff -Nur linux-2.6.6.org/include/linux/workqueue.h linux-2.6.6/include/linux/workqueue.h
@@ -84,4 +84,7 @@
 	return ret;
 }
 
+extern void diskdump_clear_workqueue(void);
+extern void diskdump_run_workqueue(void);
+
 #endif
kernel/softirq.c +31 -0
diff -Nur linux-2.6.6.org/kernel/softirq.c linux-2.6.6/kernel/softirq.c
@@ -314,6 +314,37 @@
 
 EXPORT_SYMBOL(tasklet_kill);
 
+struct tasklet_head saved_tasklet;
+void diskdump_clear_tasklet(void)
+{
+	saved_tasklet.list = __get_cpu_var(tasklet_vec).list;
+	__get_cpu_var(tasklet_vec).list = NULL;
+}
+
+EXPORT_SYMBOL(diskdump_clear_tasklet);
+
+void diskdump_run_tasklet(void)
+{
+	struct tasklet_struct *list;
+
+	list = __get_cpu_var(tasklet_vec).list;
+	__get_cpu_var(tasklet_vec).list = NULL;
+
+	while (list) {
+		struct tasklet_struct *t = list;
+		list = list->next;
+
+		if (!atomic_read(&t->count) &&
+		    (test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)))
+				t->func(t->data);
+
+		t->next = __get_cpu_var(tasklet_vec).list;
+		__get_cpu_var(tasklet_vec).list = t;
+	}
+}
+
+EXPORT_SYMBOL(diskdump_run_tasklet);
+
 void __init softirq_init(void)
 {
 	open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
kernel/timer.c +26 -0
diff -Nur linux-2.6.6.org/kernel/timer.c linux-2.6.6/kernel/timer.c
@@ -31,6 +31,7 @@
 #include <linux/time.h>
 #include <linux/jiffies.h>
 #include <linux/cpu.h>
+#include <linux/delay.h>
 
 #include <asm/uaccess.h>
 #include <asm/div64.h>
@@ -1070,6 +1071,12 @@
 	struct timer_list timer;
 	unsigned long expire;
 
+	if (unlikely(crashdump_mode())) {
+		mdelay(timeout);
+		set_current_state(TASK_RUNNING);
+		return timeout;
+	}
+
 	switch (timeout)
 	{
 	case MAX_SCHEDULE_TIMEOUT:
@@ -1292,6 +1299,25 @@
 	base->timer_jiffies = jiffies;
 }
 
+tvec_base_t saved_tvec_base;
+void diskdump_clear_timers(void)
+{
+	tvec_base_t *base = &per_cpu(tvec_bases, smp_processor_id());
+
+	memcpy(&saved_tvec_base, base, sizeof(saved_tvec_base));
+	init_timers_cpu(smp_processor_id());
+}
+
+EXPORT_SYMBOL(diskdump_clear_timers);
+
+void diskdump_run_timers(void)
+{
+	tvec_base_t *base = &__get_cpu_var(tvec_bases);
+	__run_timers(base);
+}
+
+EXPORT_SYMBOL(diskdump_run_timers);
+
 #ifdef CONFIG_HOTPLUG_CPU
 static int migrate_timer_list(tvec_base_t *new_base, struct list_head *head)
 {
kernel/workqueue.c +33 -0
diff -Nur linux-2.6.6.org/kernel/workqueue.c linux-2.6.6/kernel/workqueue.c
@@ -420,6 +420,36 @@
 
 }
 
+struct cpu_workqueue_struct saved_cwq;
+void diskdump_clear_workqueue(void)
+{
+	int cpu = smp_processor_id();
+	struct cpu_workqueue_struct *cwq = keventd_wq->cpu_wq + cpu;
+
+	memcpy(&saved_cwq, cwq, sizeof(saved_cwq));
+	spin_lock_init(&cwq->lock);
+	INIT_LIST_HEAD(&cwq->worklist);
+	init_waitqueue_head(&cwq->more_work);
+	init_waitqueue_head(&cwq->work_done);
+}
+
+void diskdump_run_workqueue(void)
+{
+	struct cpu_workqueue_struct *cwq;
+
+	cwq = keventd_wq->cpu_wq + smp_processor_id();
+	while (!list_empty(&cwq->worklist)) {
+		struct work_struct *work = list_entry(cwq->worklist.next,
+						struct work_struct, entry);
+		void (*f) (void *) = work->func;
+		void *data = work->data;
+
+		list_del_init(cwq->worklist.next);
+		clear_bit(0, &work->pending);
+		f(data);
+	}
+}
+
 #ifdef CONFIG_HOTPLUG_CPU
 /* Take the work from this (downed) CPU. */
 static void take_over_work(struct workqueue_struct *wq, unsigned int cpu)
@@ -503,3 +533,6 @@
 EXPORT_SYMBOL(schedule_delayed_work);
 EXPORT_SYMBOL(flush_scheduled_work);
 
+EXPORT_SYMBOL(diskdump_clear_workqueue);
+EXPORT_SYMBOL(diskdump_run_workqueue);
+
#24 lore raw
Quoted 16 lines · Takao Indoh
Quoted 11 lines · Ingo Molnar

but there's another possible method (suggested by Alan Cox) that requires no changes to the timer API hotpaths: 'clear' all timer lists upon a crash [once all CPUs have stopped and irqs are disabled] and just let the drivers use the normal timer APIs. Drive timer execution via a polling method.

this basically approximates your polling based implementation but uses the existing kernel timer data structures and timer mechanism so should be robust and compatible. It doesnt rely on any previous state (because all currently pending timers are discarded) so it's as crash-safe as possible.

Quoted 4 lines

This is a test patch for clearing timer/tasklet and executing it during dumping.
This patch does not work yet. I tried dumping using this patch, but aic7xxx is unstable...

Your new dump_run_timers() calls __run_timers() directly. I think that's the reason of unstability. __run_timers() calls spin_unlock_irq() and enables IRQ, but diskdump expects everything runs with IRQ disabled.

#25 lore raw
Quoted 4 lines · Nobuhiro Tachino

Your new dump_run_timers() calls __run_timers() directly. I think that's the reason of unstability. __run_timers() calls spin_unlock_irq() and enables IRQ, but diskdump expects everything runs with IRQ disabled.

indeed!

luckily we can solve this in the upstream kernel without too much fuss, see the patch below. All callers of __run_timers() run with irqs enabled.

(NOTE: we unconditionally disable interrupts after having run the timer fn - this solves the problem of a timer fn keeping irqs disabled.)

does this patch stabilize diskdump?

Ingo

kernel/timer.c renamed from kernel/timer.c.orig +4 -3
@@ -423,8 +423,9 @@ static int cascade(tvec_base_t *base, tv
 static inline void __run_timers(tvec_base_t *base)
 {
 	struct timer_list *timer;
+	unsigned long flags;
 
-	spin_lock_irq(&base->lock);
+	spin_lock_irqsave(&base->lock, flags);
 	while (time_after_eq(jiffies, base->timer_jiffies)) {
 		struct list_head work_list = LIST_HEAD_INIT(work_list);
 		struct list_head *head = &work_list;
@@ -453,14 +454,14 @@ repeat:
 			set_running_timer(base, timer);
 			smp_wmb();
 			timer->base = NULL;
-			spin_unlock_irq(&base->lock);
+			spin_unlock_irqrestore(&base->lock, flags);
 			fn(data);
 			spin_lock_irq(&base->lock);
 			goto repeat;
 		}
 	}
 	set_running_timer(base, NULL);
-	spin_unlock_irq(&base->lock);
+	spin_unlock_irqrestore(&base->lock, flags);
 }
 
 #ifdef CONFIG_NO_IDLE_HZ
#26 lore raw

Hi,

Quoted 8 lines · Ingo Molnar

indeed!

luckily we can solve this in the upstream kernel without too much fuss, see the patch below. All callers of __run_timers() run with irqs enabled.

(NOTE: we unconditionally disable interrupts after having run the timer fn - this solves the problem of a timer fn keeping irqs disabled.)

My patch posted on 22 Jun had a bug!
http://marc.theaimsgroup.com/?l=linux-kernel&m=108791355225806&w=2
In the __run_timers, I missed spin_unlock_irq before fn(data). Your patch correct rightly. Thanks!

Quoted 1 line

does this patch stabilize diskdump?

No. There is one thing I need correct. I need replace proc interface with sysfs, as Christoph and Arjan commented.
After fix this, I'll release stable version 1.0.

Best Regards,
Takao Indoh

#27 lore raw
Quoted 5 lines · Takao Indoh
Quoted 1 line

does this patch stabilize diskdump?

No. There is one thing I need correct. I need replace proc interface with sysfs, as Christoph and Arjan commented.
After fix this, I'll release stable version 1.0.

Sorry, I misread. Yes, diskdump is stable with your patch. Thanks.

Best Regards,
Takao Indoh

#28 lore raw
In reply to Ingo Molnar
Quoted 11 lines · Ingo Molnar

but there's another possible method (suggested by Alan Cox) that requires no changes to the timer API hotpaths: 'clear' all timer lists upon a crash [once all CPUs have stopped and irqs are disabled] and just let the drivers use the normal timer APIs. Drive timer execution via a polling method.

this basically approximates your polling based implementation but uses the existing kernel timer data structures and timer mechanism so should be robust and compatible. It doesnt rely on any previous state (because all currently pending timers are discarded) so it's as crash-safe as possible.

Don't forget live crash dumping. The system is running and is behaving strangely so you want to take a dump for investigation, but you do not want to kill the system afterwards. Live crash dumping is very useful for problem diagnosis.

It is a little more complex than dumping after an oops because you must not destroy any kernel data, including timer lists.

#29 lore raw

Hi,

Quoted 14 lines · Keith Owens
Quoted 11 lines · Ingo Molnar

but there's another possible method (suggested by Alan Cox) that requires no changes to the timer API hotpaths: 'clear' all timer lists upon a crash [once all CPUs have stopped and irqs are disabled] and just let the drivers use the normal timer APIs. Drive timer execution via a polling method.

this basically approximates your polling based implementation but uses the existing kernel timer data structures and timer mechanism so should be robust and compatible. It doesnt rely on any previous state (because all currently pending timers are discarded) so it's as crash-safe as possible.

Quoted 6 lines

Don't forget live crash dumping. The system is running and is behaving strangely so you want to take a dump for investigation, but you do not want to kill the system afterwards. Live crash dumping is very useful for problem diagnosis.
It is a little more complex than dumping after an oops because you must not destroy any kernel data, including timer lists.

The method he proposed is used only when die/panic happens. Live dump is a function crash command provides throuth /dev/mem.

Best Regards,
Takao Indoh

#30 lore raw
In reply to Keith Owens
Quoted 6 lines · Keith Owens
Quoted 5 lines

this basically approximates your polling based implementation but uses the existing kernel timer data structures and timer mechanism so should be robust and compatible. It doesnt rely on any previous state (because all currently pending timers are discarded) so it's as crash-safe as possible.

Quoted 7 lines

Don't forget live crash dumping. The system is running and is behaving strangely so you want to take a dump for investigation, but you do not want to kill the system afterwards. Live crash dumping is very useful for problem diagnosis.

It is a little more complex than dumping after an oops because you must not destroy any kernel data, including timer lists.

How much does this differ from the proposed software suspend stuff based on some of the crash dumping code?

If you've got software suspend and LVM snapshots, the combination of the two could theoretically be pretty useful in an enterprise environment...

Rob

Signature · 2 lines
www.linucon.org: Linux Expo and Science Fiction Convention
October 8-10, 2004 in Austin Texas.  (I'm the con chair.)
#31 lore raw
In reply to Keith Owens
Quoted 1 line · Keith Owens

Don't forget live crash dumping. [...]

it's still possible, you'll have to save/restore the timer table, instead of overwriting it.

Ingo

#32 lore raw
In reply to Christoph Hellwig
Quoted 9 lines · Christoph Hellwig
Quoted 9 lines

+/******************************** Disk dump **************************** *******/

drivers/scsi/aic7xxx/aic7xxx_osm.h +7 -0
+#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
Quoted 8 lines

drivers/scsi/aic7xxx/aic7xxx_osm.h line 272

Quoted 5 lines
drivers/scsi/aic7xxx/aic7xxx_osm.h +5 -0
+#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 method is to add codes for diskdump into add_timer/del_timer/etc itself, but I don't think it is good method. Do you have any good idea?

Best Regards,
Takao Indoh

#33 lore raw

PATCH 4/4 Diskdump - yet another crash dump function

In reply to Takao Indoh

This is a patch for aic79xx driver.

drivers/scsi/aic7xxx/aic79xx_osm.c +49 -0
diff -Nur linux-2.6.6.org/drivers/scsi/aic7xxx/aic79xx_osm.c linux-2.6.6/drivers/scsi/aic7xxx/aic79xx_osm.c
@@ -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(Scsi_Device *);
+static void	   ahd_linux_poll(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(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(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)
 {
drivers/scsi/aic7xxx/aic79xx_osm.h +21 -0
diff -Nur linux-2.6.6.org/drivers/scsi/aic7xxx/aic79xx_osm.h linux-2.6.6/drivers/scsi/aic7xxx/aic79xx_osm.h
@@ -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>
 
@@ -70,6 +73,10 @@
 #include "scsi.h"
 #include "hosts.h"
 
+#if defined(CONFIG_SCSI_DUMP) || defined(CONFIG_SCSI_DUMP_MODULE)
+#include "scsi_dump.h"
+#endif
+
 /* Name space conflict with BSD queue macros */
 #ifdef LIST_HEAD
 #undef LIST_HEAD
@@ -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)
#34 lore raw

PATCH 1/1 Diskdump - yet another crash dump function

In reply to Takao Indoh

This is a document for diskdump including overview, installation, and so on.

Best Regards,
Takao Indoh

Introduction
------------
Diskdump offers a function to preserve so-called crash dump. When "panic" or "oops" happens, diskdump automatically saves system memory to the disk. We can investigate the cause of panic using this saved memory image which we call as crash dump.

Overview
-------
- How it works

This is 2-stage dump which is similar to traditional UNIX dump. The 1st stage starts when panic occurs, at which time the register state and other dump-related data is stored in a header, followed by the full contents of memory, in a dedicated dump partition. The dump partition will have been pre-formatted with per-block signatures. The 2nd stage is executed by rc script after the next system reboot, at which time the savecore command will create the vmcore file from the contents of the dump partition. After that, the per-block signatures will be re-written over the dump partition, in preparation for the next panic.

The handling of panic is essentially the same as netdump. It inhibits interrupts, freezes all other CPUs, and then for each page of data, issues the I/O command to the host adapter driver, followed by calling the interrupt handler of the adapter driver iteratively until the I/O has completed. The difference compared to netdump is that diskdump saves memory to the dump partition in its own loop, and does not wait for instructions from an external entity.

- Safety

When diskdump is executed, of course the system is in serious trouble. Therefore, there is a possibility that user resources on the disk containing the dump partition could be corrupted. To avoid this danger, signatures are written over the complete dump partition. When a panic occurs, the diskdump module reads the whole dump partition, and checks if the signatures are written correctly. If the signatures match, the diskdump presumes that it will be writing to the correct device, and that there is a high possibility that it will be able to write the dump correctly. The signatures should be the ones which have low possibility to exist on the disk.

We decided that the following format will be written in each one block (page-size) unit on the partition. The signatures are created by a simple formulas based on the block number, making it a low possibility that the created signature would ever be the same as a user resource:

32-bit word 0: "disk"
32-bit word 1: "dump"
32-bit word 2: block number
32-bit word 3: (block number+3)*11
32-bit word 4: ((word 3)+3)*11
32-bit work 5: ((word 4)+3)*11
32-bit work 6: ((word 5)+3)*11
...
32-bit work 1023: ((word 1022)+3)*11

The diskdump module also verifies that its code and data contents have not been corrupted. The dump module computes CRC value of its module at the point that dump device is registered, and saves it. When panic occurs, the dump module re-computes the CRC value at that point and compares with the saved value. If the values aren't the same, the dump knows that it has been damaged and aborts the dump process.

- Reliability

After panic occurs, I/O is executed by the diskdump module calling the queuecommand() function of the host adapter driver, and by polling the interrupt handler directly. The dump is executed by diskdump module and host adapter driver only. It is executed without depending on other components of kernel, so it works even when panic occurs in interrupt context. (XXX To be exact, a couple of drivers are not finished completely, because they calls kmalloc() as an extension of queuecommand())

In SCSI, a host reset is executed first, so it is possible to dump with a stable bus condition. In a couple of drivers, especially in the host reset process, timers and tasklets may be used. For these drivers, I created a helper library to emulate these functions. The helper library executes timer and tasklet functionality, which helps to minimize the modification required to support diskdump. The size of initrd increases slightly because the driver depends upon the helper library.

Multiple dump devices can be registered. When a panic occurs, the diskdump module checks each dump device condition and selects the first sane device that it finds.

Diskdump and netdump can co-exist. When both of modules are enabled, the diskdump works in preference to the netdump. If the signature checking fails, if a disk I/O error occurs, or if a double panic occurs in the diskdump process, it falls back to netdump.

- The architectures and drivers to be supported

IA32 only is supported. Regarding drivers, aic7xxx, aic79xx and qla1280 are supported. I will support some qlogic drivers later.

The modification of supported drivers is needed, but the changes are very small if they are SCSI drivers.

- The consistency with the netdump

The format of the saved vmcore file is completely the same as the one which is created by the netdump-server. The vmcore file created by the savecore command can be read by the existing crash utility.

The saved directory is /var/crash/127.0.0.1-<DATE> which is consistent with the netdump. 127.0.0.1 is an IP address which the netdump can never use, so there is no conflict. Our savecore command also calls /var/crash/scripts/netdump-nospace script as does the netdump-server daemon.

- Binary compatibility

The binary compatibility is assured because of no change of the size of the existing structures and the arguments of existing functions.

- Impact to kernel

The host adapter driver needs to be modified to support diskdump, but the required steps are small. For example, the modification patch for the aic7xxx/aic9xxx drivers contains 100 lines for each. At a minimum, a poll handler needs to be added, which is called from diskdump to handle I/O completion. If the adapter driver does not use timers or tasklets, that's all that is required. Even if timers or tasklets are used, it only requires a small amount of code from the emulation library.

Similar to netdump, the variable diskdump_mode, the diskdump_func hook, and the diskdump_register_hook() function has been created to register diskdump functionality.

The function sd_find_scsi_device() which gets Scsi_Device structure from kdev which is the selected device is added to the sd module, and is exported.

To check the result code of Scsi_Cmnd, scsi_decide_disposition() is also exported.

scsi_done() and scsi_eh_done() discards Scsi_Cmnds when diskdump_mode is set. With this implementation, extra processing can be avoided in the the extension of outstanding completion of Scsi_Cmd, which is completed in the extension of host reset process. This is the only overhead to be added to the main route. It's simply an addition of "if unlikely(diskdump_mode) return", so the overhead is negligible.

Internal structure
------------------
- The interface between disdkdump.o and scsi_dump.o

scsi_dump.o is the diskdump driver for SCSI, and it registers itself to diskdump.o. (The diskdump drivers for IDE or SATA, if and when they are created, would also register themselves to diskdump.o.)

scsi_mod.o defines the following structures:

struct disk_dump_type {
    request_queue_t *(*probe)(kdev_t);
    int (*add_device)(struct disk_dump_device *);
    void (*remove_device)(struct disk_dump_device *);
    struct module *owner;
    list_t list;
};

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,
};

scsi_dump registers them by register_disk_dump_type().

The probe() handler is called from diskdump.o to determine whether the selected kdev_t belongs to scsi_mod.o. If it returns 0 to probe(), diskdump.o creates a disk_dump_device structure and calls add_device(). The add_device() handler of scsi_dump.o populates the disk_dump_device_ops of the disk_dump_device. disk_dump_device_ops is the set of handlers which are called from diskdump.o when panic occurs:

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);
};

The handler functions are only called when a panic occurs.

sanity_check() checks if the selected device works normally. A device which returns an error status will not be selected as the dump device.

quiesce() is called after the device is selected as the dump device. If it is SCSI, host reset is executed and Write Cache Enable bit of the disk device is temporarily set for the dump operation.

shutdown() is called after dump is completed. If it is SCSI, "SYNCHRONIZE CACHE" command is issued to the disk.

rw_block() executes I/O in one block unit. The length of data is a page size, and is guaranteed to be physically contiguous. In scsi_dump.o, it issues I/O by calling the queuecommand() handler from the rw_block() handler. The poll handler of adapter driver is called until the I/O has completed.

- The interface between scsi_dump.o and the adapter driver

The SCSI adapter which supports the diskdump prepares the following structure:

     struct scsi_dump_ops {
         int (*sanity_check)(Scsi_Device *);
         int (*quiesce)(Scsi_Device *);
         int (*shutdown)(Scsi_Device *);
         void (*poll)(Scsi_Device *);
     };

The poll function should call the interrupt handler. It is called repeatedly after queuecommand() is issued, and until the command is completed.

The other handlers are called by the handlers in scsi_dump.o which have the same names.

The adapter driver should set its own scsi_dump_ops to dump_ops field in the scsi_host_template.

     struct scsi_host_template {
(snipped)
             /*
              * operations for dump
              */
             struct scsi_dump_ops *dump_ops;
     };

Supported Hardware
------------------
Currently, diskdump supports only scsi disk(aic7xxx/aic79xx). Please see README in diskdumputils-0.1.5.tar.bz2 for detail.

Installation
------------
1) Download software

1. Linux kernel version 2.6.6
linux-2.6.6.tar.bz2 can be downloaded from
ftp://ftp.kernel.org/pub/linux/kernel/v2.6/

2. diskdump kernel patch
diskdump-0.1.tar.gz can be downloaded from the project page.
http://sourceforge.net/projects/lkdump

3. diskdumputils
diskdumputils-0.1.5.tar.bz2 can be downloaded from the project page.

4. crash command
Download from here: ftp://people.redhat.com/anderson/

2) Build and Install Kernel

1. Untar Linux kernel source

     tar -xjvf  linux-2.6.6.tar.bz2
 2. Apply all patches in the diskdump-0.1.tar.gz
 3. Kernel Configuration
     a. make menuconfig
     b. Under "Device Drivers"-> "Block devices", select the following:
         i. Select "m" for "Disk dump support".
     c. Under "Device Drivers"-> "SCSI device support", select the
        following:
         i. Select "m" for "SCSI dump support".
     d. Under "Kernel hacking", select the following:
         i. Select "y" for "Kernel debugging".
        ii. Select "y" for "Magic SysRq key". (optional)
       iii. Select "y" for "Compile the kernel with debug info".
     e. Configure other kernel config settings as needed.
 4. make
 5. make modules_install
 6. Build initrd if you need
 7. Copy the kernel image to the boot directory
     ex. cp arch/i386/boot/bzImage /boot/vmlinuz-2.6.6-diskdump

8. Reboot

3) Build and Install diskdumputils

1. Untar diskdumputils package

     tar -xjvf diskdumputils-0.1.5.tar.bz2

2. make
3. make install

4) Setup

The setup procedure is as follows. First a dump device must be selected. Either whole device or a partition is fine. The dump device is wholly formatted for dump, it cannot be shared with a file system or as a swap partition. The size of dump device should be big enough to save the whole dump. The size to be written by the dump is the size of whole memory plus a header field. To determine the exact size, refer to the output kernel message after the diskdump module is loaded:

  # modprobe diskdump
  # dmesg | tail
  header blocks: 3
  bitmap blocks: 8
  total number of memory blocks: 261999
  total blocks written: 262010

The last number is the data size in pagesize units that will be written by the diskdump function.

select the dump partition in /etc/sysconfig/diskdump, as in the following example:

-------------------
DEVICE=/dev/sde1
-------------------

Next, Format the dump partition. The administrator needs to execute this once.

  # service diskdump initialformat

Lastly, enable the service:

  # chkconfig diskdump on
  # service diskdump start

If /proc/diskdump exists, and it shows the registered dump device, the diskdump has been activated:

  # cat /proc/diskdump
  /dev/sde1 514080 1012095

To test the diskdump, use Alt-SysRq-C or "echo c > /proc/sysrq-trigger". After completing the dump, a vmcore file will created during the next reboot sequence, and saved in a directory of the name format:

/var/crash/127.0.0.1-<date>

The dump format is same as the netdump one, so we can use crash command to analyse. Crash command can be downloaded from ftp://people.redhat.com/anderson/.

  # crash vmlinux vmcore
#35 lore raw
In reply to Takao Indoh

Hi!

Quoted 10 lines

Although I know about LKCD and netdump, I'm developing yet another crash dump, which is a polling-based disk dump as netdump do. Because it disables any interrupts during doing dump, it can avoid lots of problems LKCD has.

Main Feature
- Reliability
Diskdump disables interrupts, stops other cpus and writes to the disk with polling mode. Therefore, unnecessary functions(like interrupt handler) don't disturb dumping.

Hmm... with this, better design of swsusp mifht be feasible.

Pavel

Signature · 1 line
64 bytes from 195.113.31.123: icmp_seq=28 ttl=51 time=448769.1 ms         
#36 lore raw

Hello,

Quoted 5 lines · Pavel Machek
Quoted 5 lines

Although I know about LKCD and netdump, I'm developing yet another crash dump, which is a polling-based disk dump as netdump do. Because it disables any interrupts during doing dump, it can avoid lots of problems LKCD has.

Quoted 7 lines
Quoted 5 lines

Main Feature
- Reliability
Diskdump disables interrupts, stops other cpus and writes to the disk with polling mode. Therefore, unnecessary functions(like interrupt handler) don't disturb dumping.

Hmm... with this, better design of swsusp mifht be feasible.

I don't know mechanism of swsusp well.
Do you mean diskdump is useful when saving system memory to the disk?

Regards,
Takao Indoh

#37 lore raw

Hi!

Quoted 11 lines
Quoted 11 lines
Quoted 10 lines

Although I know about LKCD and netdump, I'm developing yet another crash dump, which is a polling-based disk dump as netdump do. Because it disables any interrupts during doing dump, it can avoid lots of problems LKCD has.

Main Feature
- Reliability
Diskdump disables interrupts, stops other cpus and writes to the disk with polling mode. Therefore, unnecessary functions(like interrupt handler) don't disturb dumping.

Quoted 4 lines
Quoted 1 line

Hmm... with this, better design of swsusp mifht be feasible.

I don't know mechanism of swsusp well.
Do you mean diskdump is useful when saving system memory to the disk?

Yes. Self-contained code accessing disk would be usefull both when saving system memory and when reading it back. (This is probably 2.8 material, through).
Pavel

Signature · 1 line
934a471f20d6580d5aad759bf0d97ddc