diff -uprN linux-2.6.6/Documentation/sysrq.txt linux-2.6.6-netdump/Documentation/sysrq.txt --- linux-2.6.6/Documentation/sysrq.txt 2004-05-09 22:32:36.000000000 -0400 +++ linux-2.6.6-netdump/Documentation/sysrq.txt 2004-06-30 10:48:52.000000000 -0400 @@ -53,6 +53,10 @@ On all - write a character to /proc/sys 'b' - Will immediately reboot the system without syncing or unmounting your disks. +'c' - Intentionally crash the system without syncing or unmounting + your disks. This is most useful if the NETDUMP client package + has been installed. + 'o' - Will shut your system off (if configured and supported). 's' - Will attempt to sync all mounted filesystems. @@ -101,6 +105,10 @@ useful when you want to exit a program t re'B'oot is good when you're unable to shut down. But you should also 'S'ync and 'U'mount first. +'C'rash immediately crashes your system. This is most useful if the machine +has been configured as a NETDUMP client because an OOPS report is generated +and a kernel crash dump is sent to the NETDUMP server. + 'S'ync is great when your system is locked up, it allows you to sync your disks and will certainly lessen the chance of data loss and fscking. Note that the sync hasn't taken place until you see the "OK" and "Done" appear diff -uprN linux-2.6.6/arch/i386/kernel/i386_ksyms.c linux-2.6.6-netdump/arch/i386/kernel/i386_ksyms.c --- linux-2.6.6/arch/i386/kernel/i386_ksyms.c 2004-06-21 14:06:53.000000000 -0400 +++ linux-2.6.6-netdump/arch/i386/kernel/i386_ksyms.c 2004-06-30 10:48:52.000000000 -0400 @@ -209,3 +209,5 @@ EXPORT_SYMBOL(ist_info); #endif EXPORT_SYMBOL(csum_partial); + +EXPORT_SYMBOL_GPL(empty_zero_page); diff -uprN linux-2.6.6/arch/i386/kernel/nmi.c linux-2.6.6-netdump/arch/i386/kernel/nmi.c --- linux-2.6.6/arch/i386/kernel/nmi.c 2004-06-21 14:06:54.000000000 -0400 +++ linux-2.6.6-netdump/arch/i386/kernel/nmi.c 2004-06-30 10:48:52.000000000 -0400 @@ -524,3 +524,4 @@ EXPORT_SYMBOL(reserve_lapic_nmi); EXPORT_SYMBOL(release_lapic_nmi); EXPORT_SYMBOL(disable_timer_nmi_watchdog); EXPORT_SYMBOL(enable_timer_nmi_watchdog); +EXPORT_SYMBOL_GPL(touch_nmi_watchdog); diff -uprN linux-2.6.6/arch/i386/kernel/process.c linux-2.6.6-netdump/arch/i386/kernel/process.c --- linux-2.6.6/arch/i386/kernel/process.c 2004-06-21 14:06:53.000000000 -0400 +++ linux-2.6.6-netdump/arch/i386/kernel/process.c 2004-06-30 10:48:52.000000000 -0400 @@ -252,6 +252,8 @@ void show_regs(struct pt_regs * regs) show_trace(NULL, ®s->esp); } +EXPORT_SYMBOL_GPL(show_regs); + /* * This gets run with %ebx containing the * function to call, and %edx containing diff -uprN linux-2.6.6/arch/i386/kernel/reboot.c linux-2.6.6-netdump/arch/i386/kernel/reboot.c --- linux-2.6.6/arch/i386/kernel/reboot.c 2004-06-21 14:06:53.000000000 -0400 +++ linux-2.6.6-netdump/arch/i386/kernel/reboot.c 2004-06-30 10:48:52.000000000 -0400 @@ -251,7 +251,8 @@ void machine_restart(char * __unused) * Stop all CPUs and turn off local APICs and the IO-APIC, so * other OSs see a clean IRQ state. */ - smp_send_stop(); + if (!netdump_mode) + smp_send_stop(); #elif defined(CONFIG_X86_LOCAL_APIC) if (cpu_has_apic) { local_irq_disable(); diff -uprN linux-2.6.6/arch/i386/kernel/smp.c linux-2.6.6-netdump/arch/i386/kernel/smp.c --- linux-2.6.6/arch/i386/kernel/smp.c 2004-06-21 14:06:53.000000000 -0400 +++ linux-2.6.6-netdump/arch/i386/kernel/smp.c 2004-07-01 08:38:24.000000000 -0400 @@ -495,7 +495,10 @@ int smp_call_function (void (*func) (voi * The function to run. This must be fast and non-blocking. * An arbitrary pointer to pass to the function. * currently unused. - * If true, wait (atomically) until function has completed on other CPUs. + * If 1, wait (atomically) until function has completed on other CPUs. + * If 0, wait for the IPI to be received by other CPUs, but do not wait + * for the completion of the function on each CPU. + * If -1, do not wait for other CPUs to receive IPI. * [RETURNS] 0 on success, else a negative status code. Does not return until * remote CPUs are nearly ready to execute <> or are or have executed. * @@ -510,13 +513,14 @@ int smp_call_function (void (*func) (voi return 0; /* Can deadlock when called with interrupts disabled */ - WARN_ON(irqs_disabled()); + /* Only if we are waiting for other CPU to ack */ + WARN_ON(irqs_disabled() && wait >= 0); data.func = func; data.info = info; atomic_set(&data.started, 0); - data.wait = wait; - if (wait) + data.wait = wait > 0 ? wait : 0; + if (wait > 0) atomic_set(&data.finished, 0); spin_lock(&call_lock); @@ -527,10 +531,11 @@ int smp_call_function (void (*func) (voi send_IPI_allbutself(CALL_FUNCTION_VECTOR); /* Wait for response */ - while (atomic_read(&data.started) != cpus) - barrier(); + if (wait >= 0) + while (atomic_read(&data.started) != cpus) + barrier(); - if (wait) + if (wait > 0) while (atomic_read(&data.finished) != cpus) barrier(); spin_unlock(&call_lock); diff -uprN linux-2.6.6/arch/i386/kernel/traps.c linux-2.6.6-netdump/arch/i386/kernel/traps.c --- linux-2.6.6/arch/i386/kernel/traps.c 2004-06-21 14:06:54.000000000 -0400 +++ linux-2.6.6-netdump/arch/i386/kernel/traps.c 2004-06-30 10:48:52.000000000 -0400 @@ -290,6 +290,7 @@ bug: } spinlock_t die_lock = SPIN_LOCK_UNLOCKED; +static int die_owner = -1; void die(const char * str, struct pt_regs * regs, long err) { @@ -297,7 +298,13 @@ void die(const char * str, struct pt_reg int nl = 0; console_verbose(); - spin_lock_irq(&die_lock); + local_irq_disable(); + if (!spin_trylock(&die_lock)) { + if (smp_processor_id() != die_owner) + spin_lock(&die_lock); + /* allow recursive die to fall through */ + } + die_owner = smp_processor_id(); bust_spinlocks(1); handle_BUG(regs); printk(KERN_ALERT "%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); @@ -316,12 +323,17 @@ void die(const char * str, struct pt_reg if (nl) printk("\n"); show_registers(regs); + if (netdump_func) + netdump_func(regs); bust_spinlocks(0); + die_owner = -1; spin_unlock_irq(&die_lock); if (in_interrupt()) panic("Fatal exception in interrupt"); if (panic_on_oops) { + if (netdump_func) + netdump_func = NULL; printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n"); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(5 * HZ); diff -uprN linux-2.6.6/arch/i386/mm/pgtable.c linux-2.6.6-netdump/arch/i386/mm/pgtable.c --- linux-2.6.6/arch/i386/mm/pgtable.c 2004-06-21 14:06:53.000000000 -0400 +++ linux-2.6.6-netdump/arch/i386/mm/pgtable.c 2004-06-30 10:48:52.000000000 -0400 @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,8 @@ void show_mem(void) printk("%d pages swap cached\n",cached); } +EXPORT_SYMBOL_GPL(show_mem); + /* * Associate a virtual page frame with a given physical page frame * and protection flags for that frame. diff -uprN linux-2.6.6/arch/ia64/kernel/traps.c linux-2.6.6-netdump/arch/ia64/kernel/traps.c --- linux-2.6.6/arch/ia64/kernel/traps.c 2004-06-21 14:06:32.000000000 -0400 +++ linux-2.6.6-netdump/arch/ia64/kernel/traps.c 2004-06-30 10:48:52.000000000 -0400 @@ -92,6 +92,13 @@ die (const char *str, struct pt_regs *re } else printk(KERN_ERR "Recursive die() failure, output suppressed\n"); + if (netdump_func) + netdump_func(regs); + if (panic_on_oops) { + if (netdump_func) + netdump_func = NULL; + panic("Fatal exception"); + } bust_spinlocks(0); die.lock_owner = -1; spin_unlock_irq(&die.lock); diff -uprN linux-2.6.6/arch/ppc64/kernel/traps.c linux-2.6.6-netdump/arch/ppc64/kernel/traps.c --- linux-2.6.6/arch/ppc64/kernel/traps.c 2004-06-21 14:06:53.000000000 -0400 +++ linux-2.6.6-netdump/arch/ppc64/kernel/traps.c 2004-06-30 10:48:52.000000000 -0400 @@ -115,6 +115,8 @@ int die(const char *str, struct pt_regs if (nl) printk("\n"); show_regs(regs); + if (netdump_func) + netdump_func(regs); bust_spinlocks(0); spin_unlock_irq(&die_lock); @@ -122,6 +124,8 @@ int die(const char *str, struct pt_regs panic("Fatal exception in interrupt"); if (panic_on_oops) { + if (netdump_func) + netdump_func = NULL; printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n"); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(5 * HZ); diff -uprN linux-2.6.6/arch/s390/kernel/traps.c linux-2.6.6-netdump/arch/s390/kernel/traps.c --- linux-2.6.6/arch/s390/kernel/traps.c 2004-05-09 22:32:27.000000000 -0400 +++ linux-2.6.6-netdump/arch/s390/kernel/traps.c 2004-06-30 10:48:52.000000000 -0400 @@ -247,12 +247,17 @@ void die(const char * str, struct pt_reg bust_spinlocks(1); printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); show_regs(regs); + if (netdump_func) + netdump_func(regs); bust_spinlocks(0); spin_unlock_irq(&die_lock); if (in_interrupt()) panic("Fatal exception in interrupt"); - if (panic_on_oops) + if (panic_on_oops) { + if (netdump_func) + netdump_func = NULL; panic("Fatal exception: panic_on_oops"); + } do_exit(SIGSEGV); } diff -uprN linux-2.6.6/arch/x86_64/kernel/traps.c linux-2.6.6-netdump/arch/x86_64/kernel/traps.c --- linux-2.6.6/arch/x86_64/kernel/traps.c 2004-06-21 14:06:33.000000000 -0400 +++ linux-2.6.6-netdump/arch/x86_64/kernel/traps.c 2004-06-30 10:48:52.000000000 -0400 @@ -335,8 +335,11 @@ void oops_end(void) bust_spinlocks(0); spin_unlock(&die_lock); local_irq_enable(); /* make sure back scroll still works */ - if (panic_on_oops) + if (panic_on_oops) { + if (netdump_func) + netdump_func = NULL; panic("Oops"); + } } void __die(const char * str, struct pt_regs * regs, long err) @@ -366,6 +369,8 @@ void die(const char * str, struct pt_reg oops_begin(); handle_BUG(regs); __die(str, regs, err); + if (netdump_func) + netdump_func(regs); oops_end(); do_exit(SIGSEGV); } diff -uprN linux-2.6.6/drivers/char/sysrq.c linux-2.6.6-netdump/drivers/char/sysrq.c --- linux-2.6.6/drivers/char/sysrq.c 2004-06-21 14:06:33.000000000 -0400 +++ linux-2.6.6-netdump/drivers/char/sysrq.c 2004-06-30 10:48:52.000000000 -0400 @@ -107,6 +107,17 @@ static struct sysrq_key_op sysrq_reboot_ .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 +246,7 @@ static struct sysrq_key_op *sysrq_key_ta 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 -uprN linux-2.6.6/drivers/net/Kconfig linux-2.6.6-netdump/drivers/net/Kconfig --- linux-2.6.6/drivers/net/Kconfig 2004-06-21 14:06:54.000000000 -0400 +++ linux-2.6.6-netdump/drivers/net/Kconfig 2004-06-30 10:48:52.000000000 -0400 @@ -2546,5 +2546,12 @@ config NETCONSOLE ---help--- If you want to log kernel messages over the network, enable this. See Documentation/networking/netconsole.txt for details. + +config NETDUMP + tristate "Network kernel crash dump support" + depends on NETPOLL && NETPOLL_RX && NETPOLL_TRAP + ---help--- + Enable this option if you have a netdump server and you would like + to collect kernel crash dumps. diff -uprN linux-2.6.6/drivers/net/Makefile linux-2.6.6-netdump/drivers/net/Makefile --- linux-2.6.6/drivers/net/Makefile 2004-06-21 14:06:33.000000000 -0400 +++ linux-2.6.6-netdump/drivers/net/Makefile 2004-06-30 10:48:52.000000000 -0400 @@ -189,3 +189,4 @@ obj-$(CONFIG_HAMRADIO) += hamradio/ obj-$(CONFIG_IRDA) += irda/ obj-$(CONFIG_NETCONSOLE) += netconsole.o +obj-$(CONFIG_NETDUMP) += netdump.o diff -uprN linux-2.6.6/drivers/net/netconsole.c linux-2.6.6-netdump/drivers/net/netconsole.c --- linux-2.6.6/drivers/net/netconsole.c 2004-06-21 14:06:34.000000000 -0400 +++ linux-2.6.6-netdump/drivers/net/netconsole.c 2004-07-01 08:19:41.000000000 -0400 @@ -45,6 +45,9 @@ #include #include #include +#include + +#include "netdump.h" MODULE_AUTHOR("Maintainer: Matt Mackall "); MODULE_DESCRIPTION("Console driver for network interfaces"); @@ -58,28 +61,100 @@ static struct netpoll np = { .name = "netconsole", .dev_name = "eth0", .local_port = 6665, - .remote_port = 6666, + .remote_port = 514, .remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }; static int configured = 0; +static char netlog_config[256]; +module_param_string(netlog, netlog_config, 256, 0); +MODULE_PARM_DESC(netlog, " netlog=[src-port]@[src-ip]/[dev],[tgt-port]@/[tgt-macaddr]\n"); +static struct netpoll netlog_np = { + .name = "netlog", + .dev_name = "eth0", + .local_port = 6664, + .remote_port = 6666, + .remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, +}; +static int netlog_configured = 0; + #define MAX_PRINT_CHUNK 1000 +#define SYSLOG_HEADER_LEN 4 + +static int syslog_chars = SYSLOG_HEADER_LEN; +static unsigned char syslog_line [MAX_PRINT_CHUNK + 10] = { + '<', + '5', + '>', + ' ', + [4 ... MAX_PRINT_CHUNK+5] = '\0', +}; +static unsigned char netlog_line[MAX_PRINT_CHUNK + HEADER_LEN]; +static unsigned int log_offset; + +/* + * We feed kernel messages char by char, and send the UDP packet + * one linefeed. We buffer all characters received. + */ +static inline void feed_syslog_char(const unsigned char c) +{ + if (syslog_chars == MAX_PRINT_CHUNK) + syslog_chars--; + syslog_line[syslog_chars] = c; + syslog_chars++; + if (c == '\n') { + netpoll_send_udp(&np, syslog_line, syslog_chars); + syslog_chars = SYSLOG_HEADER_LEN; + } +} static void write_msg(struct console *con, const char *msg, unsigned int len) { - int frag, left; + int left, i; unsigned long flags; + reply_t reply; + char *netlog_buf = &netlog_line[HEADER_LEN]; - if (!np.dev) + if (!np.dev && !netlog_np.dev) + return; + + if (unlikely(netdump_mode)) return; local_irq_save(flags); - for(left = len; left; ) { - frag = min(left, MAX_PRINT_CHUNK); - netpoll_send_udp(&np, msg, frag); - msg += frag; - left -= frag; + if (np.dev) + for (i = 0; i < len; i++) + feed_syslog_char(msg[i]); + + if (netlog_np.dev) { + left = len; + while (left) { + if (left > MAX_PRINT_CHUNK) + len = MAX_PRINT_CHUNK; + else + len = left; + netlog_line[0] = NETDUMP_VERSION; + + reply.nr = 0; + reply.code = REPLY_LOG; + reply.info = log_offset; + + put_unaligned(htonl(reply.nr), + (u32 *)(netlog_line + 1)); + put_unaligned(htonl(reply.code), + (u32 *)(netlog_line + 5)); + put_unaligned(htonl(reply.info), + (u32 *)(netlog_line + 9)); + + log_offset += len; + memcpy(netlog_buf, msg, len); + + netpoll_send_udp(&netlog_np, + netlog_line, len + HEADER_LEN); + msg += len; + left -= len; + } } local_irq_restore(flags); @@ -98,17 +173,29 @@ static int option_setup(char *opt) __setup("netconsole=", option_setup); +static int netlog_option_setup(char *opt) +{ + netlog_configured = !netpoll_parse_options(&netlog_np, opt); + return 0; +} + +__setup("netlog=", netlog_option_setup); + static int init_netconsole(void) { if(strlen(config)) option_setup(config); - if(!configured) { - printk("netconsole: not configured, aborting\n"); - return -EINVAL; - } + if (strlen(netlog_config)) + netlog_option_setup(netlog_config); + + if (configured && netpoll_setup(&np)) + printk("netconsole: failed to configure syslog service\n"); - if(netpoll_setup(&np)) + if (netlog_configured && netpoll_setup(&netlog_np)) + printk("netconsole: failed to configured netlog service.\n"); + + if (!configured && !netlog_configured) return -EINVAL; register_console(&netconsole); @@ -119,7 +206,12 @@ static int init_netconsole(void) static void cleanup_netconsole(void) { unregister_console(&netconsole); - netpoll_cleanup(&np); + + if (configured) + netpoll_cleanup(&np); + + if (netlog_configured) + netpoll_cleanup(&netlog_np); } module_init(init_netconsole); diff -uprN linux-2.6.6/drivers/net/netdump.c linux-2.6.6-netdump/drivers/net/netdump.c --- linux-2.6.6/drivers/net/netdump.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.6-netdump/drivers/net/netdump.c 2004-07-01 08:22:24.000000000 -0400 @@ -0,0 +1,534 @@ +/* + * linux/drivers/net/netdump.c + * + * Copyright (C) 2001 Ingo Molnar + * Copyright (C) 2002 Red Hat, Inc. + * Copyright (C) 2004 Red Hat, Inc. + * + * This file contains the implementation of an IRQ-safe, crash-safe + * kernel console implementation that outputs kernel messages to the + * network. + * + * Modification history: + * + * 2001-09-17 started by Ingo Molnar. + * 2002-03-14 simultaneous syslog packet option by Michael K. Johnson + * 2004-04-07 port to 2.6 netpoll facility by Dave Anderson and Jeff Moyer. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netdump.h" +#include + +/* + * prototypes. + */ +void netdump_rx(struct netpoll *np, short source, char *data, int dlen); +static void send_netdump_msg(struct netpoll *np, const char *msg, unsigned int msg_len, reply_t *reply); +static void send_netdump_mem(struct netpoll *np, req_t *req); +static void netdump_startup_handshake(struct netpoll *np); +static void netpoll_netdump(struct pt_regs *regs); +static void netpoll_start_netdump(struct pt_regs *regs); + + +#include + + +#undef Dprintk +#define DEBUG 0 +#if DEBUG +# define Dprintk(x...) printk(KERN_INFO x) +#else +# define Dprintk(x...) +#endif + +MODULE_AUTHOR("Maintainer: Dave Anderson "); +MODULE_DESCRIPTION("Network kernel crash dump module"); +MODULE_LICENSE("GPL"); + +static char config[256]; +module_param_string(netdump, config, 256, 0); +MODULE_PARM_DESC(netdump, + " netdump=[src-port]@[src-ip]/[dev],[tgt-port]@/[tgt-macaddr]\n"); + +static u32 magic1, magic2; +module_param(magic1, uint, 000); +module_param(magic2, uint, 000); + +static struct netpoll np = { + .name = "netdump", + .dev_name = "eth0", + .local_port = 6666, + .remote_port = 6666, + .remote_mac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + .rx_hook = netdump_rx, + .dump_func = netpoll_start_netdump, +}; + + +/* + * NOTE: security depends on the trusted path between the netconsole + * server and netconsole client, since none of the packets are + * encrypted. The random magic number protects the protocol + * against spoofing. + */ +static u64 netdump_magic; + +static spinlock_t req_lock = SPIN_LOCK_UNLOCKED; +static int nr_req = 0; +static LIST_HEAD(request_list); + +static unsigned long long t0, jiffy_cycles; +void *netdump_stack; + + +static void update_jiffies(void) +{ + static unsigned long long prev_tick; + platform_timestamp(t0); + + /* maintain jiffies in a polling fashion, based on rdtsc. */ + if (t0 - prev_tick >= jiffy_cycles) { + prev_tick += jiffy_cycles; + jiffies++; + } +} + +static void add_new_req(req_t *req) +{ + unsigned long flags; + + spin_lock_irqsave(&req_lock, flags); + list_add_tail(&req->list, &request_list); + nr_req++; + Dprintk("pending requests: %d.\n", nr_req); + spin_unlock_irqrestore(&req_lock, flags); +} + +static req_t *get_new_req(void) +{ + req_t *req = NULL; + unsigned long flags; + + update_jiffies(); + + spin_lock_irqsave(&req_lock, flags); + if (nr_req) { + req = list_entry(request_list.next, req_t, list); + list_del(&req->list); + nr_req--; + } + spin_unlock_irqrestore(&req_lock, flags); + + return req; +} + +static req_t *alloc_req(void) +{ + req_t *req; + + req = (req_t *) kmalloc(sizeof(*req), GFP_ATOMIC); + return req; +} + +static inline void print_status (req_t *req) +{ + static int count = 0; + static int prev_jiffies = 0; + + if (jiffies/HZ != prev_jiffies/HZ) { + prev_jiffies = jiffies; + count++; + switch (count & 3) { + case 0: printk("%d(%ld)/\r", nr_req, jiffies); break; + case 1: printk("%d(%ld)|\r", nr_req, jiffies); break; + case 2: printk("%d(%ld)\\\r", nr_req, jiffies); break; + case 3: printk("%d(%ld)-\r", nr_req, jiffies); break; + } + } +} + +void netdump_rx(struct netpoll *np, short source, char *data, int dlen) +{ + req_t *req, *__req = (req_t *)data; + + if (!netdump_mode) + return; +#if DEBUG + { + static int packet_count; + Dprintk(" %d\r", ++packet_count); + } +#endif + + if (dlen < NETDUMP_REQ_SIZE) { + Dprintk("... netdump_rx: len not ok.\n"); + return; + } + + req = alloc_req(); + if (!req) { + printk("no more RAM to allocate request - dropping it.\n"); + return; + } + + req->magic = ntohl(__req->magic); + req->command = ntohl(__req->command); + req->from = ntohl(__req->from); + req->to = ntohl(__req->to); + req->nr = ntohl(__req->nr); + + Dprintk("... netdump magic: %08Lx.\n", req->magic); + Dprintk("... netdump command: %08x.\n", req->command); + Dprintk("... netdump from: %08x.\n", req->from); + Dprintk("... netdump to: %08x.\n", req->to); + + add_new_req(req); + return; +} + +#define MAX_MSG_LEN HEADER_LEN + 1024 + +static void send_netdump_msg(struct netpoll *np, const char *msg, unsigned int msg_len, reply_t *reply) +{ + /* max len should be 1024 + HEADER_LEN */ + static unsigned char netpoll_msg[MAX_MSG_LEN + 1]; + + if (msg_len + HEADER_LEN > MAX_MSG_LEN + 1) { + printk("CODER ERROR!!! msg_len %ud too big for send msg\n", + msg_len); + for (;;) local_irq_disable(); + /* NOTREACHED */ + } + + netpoll_msg[0] = NETDUMP_VERSION; + put_unaligned(htonl(reply->nr), (u32 *) (&netpoll_msg[1])); + put_unaligned(htonl(reply->code), (u32 *) (&netpoll_msg[5])); + put_unaligned(htonl(reply->info), (u32 *) (&netpoll_msg[9])); + memcpy(&netpoll_msg[HEADER_LEN], msg, msg_len); + + netpoll_send_udp(np, netpoll_msg, HEADER_LEN + msg_len); +} + +static void send_netdump_mem(struct netpoll *np, req_t *req) +{ + int i; + char *kaddr; + char str[1024]; + struct page *page; + unsigned long nr = req->from; + int nr_chunks = PAGE_SIZE/1024; + reply_t reply; + + Dprintk(" ... send_netdump_mem\n"); + reply.nr = req->nr; + reply.info = 0; + if (req->from >= platform_max_pfn()) { + sprintf(str, "page %08lx is bigger than max page # %08lx!\n", + nr, platform_max_pfn()); + reply.code = REPLY_ERROR; + send_netdump_msg(np, str, strlen(str), &reply); + return; + } + if (page_is_ram(nr)) + page = pfn_to_page(nr); + else + page = ZERO_PAGE(0); + + kaddr = (char *)kmap_atomic(page, KM_NETDUMP); + + for (i = 0; i < nr_chunks; i++) { + unsigned int offset = i*1024; + reply.code = REPLY_MEM; + reply.info = offset; + Dprintk(" ... send_netdump_mem: sending message\n"); + send_netdump_msg(np, kaddr + offset, 1024, &reply); + Dprintk(" ... send_netdump_mem: sent message\n"); + } + + kunmap_atomic(kaddr, KM_NETDUMP); + Dprintk(" ... send_netdump_mem: returning\n"); +} + +/* + * This function waits for the client to acknowledge the receipt + * of the netdump startup reply, with the possibility of packets + * getting lost. We resend the startup packet if no ACK is received, + * after a 1 second delay. + * + * (The client can test the success of the handshake via the HELLO + * command, and send ACKs until we enter netdump mode.) + */ +static void netdump_startup_handshake(struct netpoll *np) +{ + char tmp[200]; + reply_t reply; + req_t *req = NULL; + int i; + +repeat: + sprintf(tmp, "NETDUMP start, waiting for start-ACK.\n"); + reply.code = REPLY_START_NETDUMP; + reply.nr = 0; + reply.info = 0; + + send_netdump_msg(np, tmp, strlen(tmp), &reply); + + for (i = 0; i < 10000; i++) { + // wait 1 sec. + udelay(100); + Dprintk("handshake: polling controller ...\n"); + netpoll_poll(np); + req = get_new_req(); + if (req) + break; + } + if (!req) + goto repeat; + if (req->command != COMM_START_NETDUMP_ACK) { + kfree(req); + goto repeat; + } + kfree(req); + + printk("NETDUMP START!\n"); +} + +static char cpus_frozen[NR_CPUS] = { 0 }; + +static void freeze_cpu (void * dummy) +{ + cpus_frozen[smp_processor_id()] = 1; + for (;;) local_irq_disable(); +} + +static void netpoll_start_netdump(struct pt_regs *regs) +{ + int i; + unsigned long flags; + + /* + * The netdump code is not re-entrant for several reasons. Most + * immediately, we will switch to the base of our stack and + * overwrite all of our call history. + */ + if (netdump_mode) { + printk(KERN_ERR + "netpoll_start_netdump: called recursively. rebooting.\n"); + mdelay(3000); + machine_restart(NULL); + } + netdump_mode = 1; + + local_irq_save(flags); + preempt_disable(); + + smp_call_function(freeze_cpu, NULL, 1, -1); + mdelay(3000); + for (i = 0; i < NR_CPUS; i++) { + if (cpus_frozen[i]) + printk("CPU#%d is frozen.\n", i); + else if (i == smp_processor_id()) + printk("CPU#%d is executing netdump.\n", i); + } + + /* + * Some platforms may want to execute netdump on its own stack. + */ + platform_start_netdump(netdump_stack, regs); + + preempt_enable_no_resched(); + local_irq_restore(flags); + return; +} + +static void netpoll_netdump(struct pt_regs *regs) +{ + reply_t reply; + char tmp[200]; + struct pt_regs myregs; + req_t *req; + + /* + * Just in case we are crashing within the networking code + * ... attempt to fix up. + */ + netpoll_reset_locks(&np); + platform_fix_regs(); + platform_timestamp(t0); + netpoll_set_trap(1); /* bypass networking stack */ + + printk("< netdump activated - performing handshake with the server. >\n"); + netdump_startup_handshake(&np); + + printk("< handshake completed - listening for dump requests. >\n"); + + while (netdump_mode) { + local_irq_disable(); + Dprintk("main netdump loop: polling controller ...\n"); + netpoll_poll(&np); + + req = get_new_req(); + if (!req) + continue; + + Dprintk("got new req, command %d.\n", req->command); + print_status(req); + switch (req->command) { + case COMM_NONE: + Dprintk("got NO command.\n"); + break; + + case COMM_SEND_MEM: + Dprintk("got MEM command.\n"); + send_netdump_mem(&np, req); + break; + + case COMM_EXIT: + Dprintk("got EXIT command.\n"); + netdump_mode = 0; + netpoll_set_trap(0); + break; + + case COMM_REBOOT: + Dprintk("got REBOOT command.\n"); + printk("netdump: rebooting in 3 seconds.\n"); + mdelay(3000); + machine_restart(NULL); + break; + + case COMM_HELLO: + sprintf(tmp, "Hello, this is netdump version 0.%02d\n", + NETDUMP_VERSION); + reply.code = REPLY_HELLO; + reply.nr = req->nr; + reply.info = NETDUMP_VERSION; + send_netdump_msg(&np, tmp, strlen(tmp), &reply); + break; + + case COMM_GET_PAGE_SIZE: + sprintf(tmp, "PAGE_SIZE: %ld\n", PAGE_SIZE); + reply.code = REPLY_PAGE_SIZE; + reply.nr = req->nr; + reply.info = PAGE_SIZE; + send_netdump_msg(&np, tmp, strlen(tmp), &reply); + break; + + case COMM_GET_REGS: + { + char *tmp2 = tmp; + elf_gregset_t elf_regs; + + reply.code = REPLY_REGS; + reply.nr = req->nr; + reply.info = platform_max_pfn(); + tmp2 = tmp + sprintf(tmp, "Sending register info.\n"); + ELF_CORE_COPY_REGS(elf_regs, (&myregs)); + memcpy(tmp2, &elf_regs, sizeof(elf_regs)); + Dprintk("netdump: sending regs\n"); + send_netdump_msg(&np, tmp, + strlen(tmp) + sizeof(elf_regs), &reply); + Dprintk("netdump: sent regs\n"); + break; + } + + case COMM_GET_NR_PAGES: + reply.code = REPLY_NR_PAGES; + reply.nr = req->nr; + reply.info = platform_max_pfn(); + sprintf(tmp, + "Number of pages: %ld\n", platform_max_pfn()); + send_netdump_msg(&np, tmp, strlen(tmp), &reply); + break; + + case COMM_SHOW_STATE: + /* send response first */ + reply.code = REPLY_SHOW_STATE; + reply.nr = req->nr; + reply.info = 0; + + send_netdump_msg(&np, tmp, strlen(tmp), &reply); + + netdump_mode = 0; + if (regs) + show_regs(regs); + show_state(); + show_mem(); + netdump_mode = 1; + break; + + default: + reply.code = REPLY_ERROR; + reply.nr = req->nr; + reply.info = req->command; + Dprintk("got UNKNOWN command!\n"); + sprintf(tmp, "Got unknown command code %d!\n", + req->command); + send_netdump_msg(&np, tmp, strlen(tmp), &reply); + break; + } + kfree(req); + req = NULL; + } + sprintf(tmp, "NETDUMP end.\n"); + reply.code = REPLY_END_NETDUMP; + reply.nr = 0; + reply.info = 0; + send_netdump_msg(&np, tmp, strlen(tmp), &reply); + printk("NETDUMP END!\n"); +} + +static int option_setup(char *opt) +{ + return !netpoll_parse_options(&np, opt); +} + +__setup("netdump=", option_setup); + +static int init_netdump(void) +{ + int configured = 0; + + if (strlen(config)) + configured = option_setup(config); + + if (!configured) { + printk(KERN_ERR "netdump: not configured, aborting\n"); + return -EINVAL; + } + + if (netpoll_setup(&np)) + return -EINVAL; + + if (magic1 || magic2) + netdump_magic = magic1 + (((u64)magic2)<<32); + + /* + * Allocate a separate stack for netdump. + */ + platform_init_stack(&netdump_stack); + + printk(KERN_INFO "netdump: network crash dump enabled\n"); + return 0; +} + +static void cleanup_netdump(void) +{ + netpoll_cleanup(&np); + platform_cleanup_stack(netdump_stack); +} + +module_init(init_netdump); +module_exit(cleanup_netdump); diff -uprN linux-2.6.6/drivers/net/netdump.h linux-2.6.6-netdump/drivers/net/netdump.h --- linux-2.6.6/drivers/net/netdump.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.6-netdump/drivers/net/netdump.h 2004-06-30 10:48:52.000000000 -0400 @@ -0,0 +1,81 @@ +/* + * linux/drivers/net/netdump.h + * + * Copyright (C) 2001 Ingo Molnar + * + * This file contains the implementation of an IRQ-safe, crash-safe + * kernel console implementation that outputs kernel messages to the + * network. + * + * Modification history: + * + * 2001-09-17 started by Ingo Molnar. + */ + +/**************************************************************** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +#define NETDUMP_VERSION 0x04 + +enum netdump_commands { + COMM_NONE = 0, + COMM_SEND_MEM = 1, + COMM_EXIT = 2, + COMM_REBOOT = 3, + COMM_HELLO = 4, + COMM_GET_NR_PAGES = 5, + COMM_GET_PAGE_SIZE = 6, + COMM_START_NETDUMP_ACK = 7, + COMM_GET_REGS = 8, + COMM_SHOW_STATE = 9, +}; + +#define NETDUMP_REQ_SIZE (8+4*4) + +typedef struct netdump_req_s { + u64 magic; + u32 nr; + u32 command; + u32 from; + u32 to; + struct list_head list; +} req_t; + +enum netdump_replies { + REPLY_NONE = 0, + REPLY_ERROR = 1, + REPLY_LOG = 2, + REPLY_MEM = 3, + REPLY_RESERVED = 4, + REPLY_HELLO = 5, + REPLY_NR_PAGES = 6, + REPLY_PAGE_SIZE = 7, + REPLY_START_NETDUMP = 8, + REPLY_END_NETDUMP = 9, + REPLY_REGS = 10, + REPLY_MAGIC = 11, + REPLY_SHOW_STATE = 12, +}; + +typedef struct netdump_reply_s { + u32 nr; + u32 code; + u32 info; +} reply_t; + +#define HEADER_LEN (1 + sizeof(reply_t)) + diff -uprN linux-2.6.6/include/asm-generic/netdump.h linux-2.6.6-netdump/include/asm-generic/netdump.h --- linux-2.6.6/include/asm-generic/netdump.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.6-netdump/include/asm-generic/netdump.h 2004-06-30 10:48:52.000000000 -0400 @@ -0,0 +1,61 @@ +#ifndef _ASM_GENERIC_NETDUMP_H_ +#define _ASM_GENERIC_NETDUMP_H_ + +/* + * linux/include/asm-generic/netdump.h + * + * Copyright (c) 2003, 2004 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__ + +#warning netdump is not supported on this platform +const static int platform_supports_netdump = 0; + +static inline int page_is_ram(unsigned long x) { return 0; } + +#define platform_timestamp(x) do { (x) = 0; } while (0) + +#define platform_fix_regs() do { } while (0) +#define platform_init_stack(stackptr) do { } while (0) +#define platform_cleanup_stack(stackptr) do { } while (0) +#define platform_start_netdump(stackptr,regs) do { } while (0) +#define platform_max_pfn() do { int ret = 0; } while (0) + +#undef ELF_CORE_COPY_REGS +#define ELF_CORE_COPY_REGS(x, y) do { struct pt_regs *z; z = (y); } while (0) + +#define show_mem() do {} while (0) + +#define show_state() do {} while (0) + +#define show_regs(x) do { struct pt_regs *z; z = (x); } while (0) + +#undef ZERO_PAGE +static inline struct page *ZERO_PAGE(void *x) { return NULL; } + +#undef KM_NETDUMP +#define KM_NETDUMP 0 + +#undef kmap_atomic +#undef kunmap_atomic +static inline char *kmap_atomic(void *page, int idx) { return NULL; } +#define kunmap_atomic(addr, idx) do { } while (0) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_GENERIC_NETDUMP_H */ diff -uprN linux-2.6.6/include/asm-i386/kmap_types.h linux-2.6.6-netdump/include/asm-i386/kmap_types.h --- linux-2.6.6/include/asm-i386/kmap_types.h 2004-06-21 14:06:53.000000000 -0400 +++ linux-2.6.6-netdump/include/asm-i386/kmap_types.h 2004-06-30 10:48:52.000000000 -0400 @@ -29,6 +29,8 @@ enum km_type { KM_IRQ1, KM_SOFTIRQ0, KM_SOFTIRQ1, + KM_NETDUMP, + KM_UNUSED, KM_TYPE_NR }; diff -uprN linux-2.6.6/include/asm-i386/netdump.h linux-2.6.6-netdump/include/asm-i386/netdump.h --- linux-2.6.6/include/asm-i386/netdump.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.6-netdump/include/asm-i386/netdump.h 2004-06-30 10:48:52.000000000 -0400 @@ -0,0 +1,100 @@ +#ifndef _ASM_I386_NETDUMP_H +#define _ASM_I386_NETDUMP_H + +/* + * linux/include/asm-i386/netdump.h + * + * Copyright (c) 2003, 2004 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 + +extern int page_is_ram (unsigned long); +const static int platform_supports_netdump = 1; +extern union irq_ctx *netdump_irq_ctx; + +#define platform_timestamp(x) rdtscll(x) + +#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; \ +}; + +static inline void platform_init_stack(void **stackptr) +{ + *stackptr = (void *)kmalloc(sizeof(union irq_ctx), GFP_KERNEL); + if (*stackptr) + memset(*stackptr, 0, sizeof(union irq_ctx)); + else + printk(KERN_WARNING + "netdump: unable to allocate separate stack\n"); +} + +static inline void platform_start_netdump(void *stackptr, struct pt_regs *regs) +{ + u32 *dsp; + union irq_ctx * curctx; + union irq_ctx * dumpctx; + + if (!stackptr) + netpoll_netdump(regs); + else { + curctx = (union irq_ctx *) current_thread_info(); + dumpctx = (union irq_ctx *) stackptr; + + /* build the stack frame on the IRQ stack */ + dsp = (u32*) ((char*)dumpctx + sizeof(*dumpctx)); + dumpctx->tinfo.task = curctx->tinfo.task; + dumpctx->tinfo.real_stack = curctx->tinfo.real_stack; + dumpctx->tinfo.virtual_stack = curctx->tinfo.virtual_stack; + dumpctx->tinfo.previous_esp = current_stack_pointer(); + + *--dsp = (u32) regs; + + asm volatile( + " xchgl %%ebx,%%esp \n" + " call netpoll_netdump \n" + " xchgl %%ebx,%%esp \n" + : : "b"(dsp) : "memory", "cc", "edx", "ecx" + ); + } +} + +#define platform_cleanup_stack(stackptr) \ +do { \ + if (stackptr) \ + kfree(stackptr); \ +} while (0) + +#define platform_max_pfn() (num_physpages) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_I386_NETDUMP_H */ diff -uprN linux-2.6.6/include/asm-ia64/netdump.h linux-2.6.6-netdump/include/asm-ia64/netdump.h --- linux-2.6.6/include/asm-ia64/netdump.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.6-netdump/include/asm-ia64/netdump.h 2004-06-30 10:48:52.000000000 -0400 @@ -0,0 +1,6 @@ +#ifndef _ASM_IA64_NETDUMP_H_ +#define _ASM_IA64_NETDUMP_H_ + +#include + +#endif /* _ASM_IA64_NETDUMP_H_ */ diff -uprN linux-2.6.6/include/asm-ppc64/netdump.h linux-2.6.6-netdump/include/asm-ppc64/netdump.h --- linux-2.6.6/include/asm-ppc64/netdump.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.6-netdump/include/asm-ppc64/netdump.h 2004-06-30 10:48:52.000000000 -0400 @@ -0,0 +1,6 @@ +#ifndef _ASM_PPC64_NETDUMP_H_ +#define _ASM_PPC64_NETDUMP_H_ + +#include + +#endif /* _ASM_PPC64_NETDUMP_H_ */ diff -uprN linux-2.6.6/include/asm-s390/netdump.h linux-2.6.6-netdump/include/asm-s390/netdump.h --- linux-2.6.6/include/asm-s390/netdump.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.6-netdump/include/asm-s390/netdump.h 2004-06-30 10:48:52.000000000 -0400 @@ -0,0 +1,6 @@ +#ifndef _ASM_S390_NETDUMP_H_ +#define _ASM_S390_NETDUMP_H_ + +#include + +#endif /* _ASM_S390_NETDUMP_H_ */ diff -uprN linux-2.6.6/include/asm-x86_64/netdump.h linux-2.6.6-netdump/include/asm-x86_64/netdump.h --- linux-2.6.6/include/asm-x86_64/netdump.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.6-netdump/include/asm-x86_64/netdump.h 2004-06-30 10:48:52.000000000 -0400 @@ -0,0 +1,6 @@ +#ifndef _ASM_X86_64_NETDUMP_H_ +#define _ASM_X86_64_NETDUMP_H_ + +#include + +#endif /* _ASM_X86_64_NETDUMP_H_ */ diff -uprN linux-2.6.6/include/linux/kernel.h linux-2.6.6-netdump/include/linux/kernel.h --- linux-2.6.6/include/linux/kernel.h 2004-06-21 14:06:52.000000000 -0400 +++ linux-2.6.6-netdump/include/linux/kernel.h 2004-06-30 10:48:52.000000000 -0400 @@ -111,6 +111,9 @@ extern int oops_in_progress; /* If set, extern int panic_on_oops; extern int tainted; extern const char *print_tainted(void); +struct pt_regs; +extern void (*netdump_func) (struct pt_regs *regs); +extern int netdump_mode; /* Values used for system_state */ extern enum system_states { diff -uprN linux-2.6.6/include/linux/netdevice.h linux-2.6.6-netdump/include/linux/netdevice.h --- linux-2.6.6/include/linux/netdevice.h 2004-06-21 14:06:52.000000000 -0400 +++ linux-2.6.6-netdump/include/linux/netdevice.h 2004-06-30 10:48:52.000000000 -0400 @@ -459,7 +459,7 @@ struct net_device unsigned char *haddr); int (*neigh_setup)(struct net_device *dev, struct neigh_parms *); int (*accept_fastpath)(struct net_device *, struct dst_entry*); -#ifdef CONFIG_NETPOLL_RX +#ifdef CONFIG_NETPOLL int netpoll_rx; #endif #ifdef CONFIG_NET_POLL_CONTROLLER @@ -687,6 +687,12 @@ extern void dev_init(void); extern int netdev_nit; +/* netconsole rx hook registration */ +extern int netdump_register_hooks(int (*)(struct sk_buff *), + int (*)(struct sk_buff *), + void (*)(struct pt_regs *)); +extern void netdump_unregister_hooks(void); + /* Post buffer to the network code from _non interrupt_ context. * see net/core/dev.c for netif_rx description. */ diff -uprN linux-2.6.6/include/linux/netpoll.h linux-2.6.6-netdump/include/linux/netpoll.h --- linux-2.6.6/include/linux/netpoll.h 2004-05-09 22:32:02.000000000 -0400 +++ linux-2.6.6-netdump/include/linux/netpoll.h 2004-06-30 10:48:52.000000000 -0400 @@ -16,7 +16,8 @@ struct netpoll; struct netpoll { struct net_device *dev; char dev_name[16], *name; - void (*rx_hook)(struct netpoll *, int, char *, int); + void (*rx_hook)(struct netpoll *, short, char *, int); + void (*dump_func)(struct pt_regs *); u32 local_ip, remote_ip; u16 local_port, remote_port; unsigned char local_mac[6], remote_mac[6]; @@ -32,6 +33,7 @@ int netpoll_trap(void); void netpoll_set_trap(int trap); void netpoll_cleanup(struct netpoll *np); int netpoll_rx(struct sk_buff *skb); +void netpoll_reset_locks(struct netpoll *np); #endif diff -uprN linux-2.6.6/kernel/panic.c linux-2.6.6-netdump/kernel/panic.c --- linux-2.6.6/kernel/panic.c 2004-05-09 22:33:20.000000000 -0400 +++ linux-2.6.6-netdump/kernel/panic.c 2004-06-30 10:48:52.000000000 -0400 @@ -37,6 +37,9 @@ static int __init panic_setup(char *str) } __setup("panic=", panic_setup); +int netdump_mode = 0; +EXPORT_SYMBOL_GPL(netdump_mode); + /** * panic - halt the system * @fmt: The text string to print @@ -60,6 +63,8 @@ NORET_TYPE void panic(const char * fmt, vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); printk(KERN_EMERG "Kernel panic: %s\n",buf); + if (netdump_func) + BUG(); if (in_interrupt()) printk(KERN_EMERG "In interrupt handler - not syncing\n"); else if (!current->pid) diff -uprN linux-2.6.6/kernel/printk.c linux-2.6.6-netdump/kernel/printk.c --- linux-2.6.6/kernel/printk.c 2004-06-21 14:06:52.000000000 -0400 +++ linux-2.6.6-netdump/kernel/printk.c 2004-06-30 10:48:52.000000000 -0400 @@ -377,6 +377,20 @@ asmlinkage long sys_syslog(int type, cha } /* + * Netdump special routine. Don't print to global log_buf, just to the + * actual console device(s). + */ +static void netdump_call_console_drivers(const char *buf, unsigned long len) +{ + struct console *con; + + for (con = console_drivers; con; con = con->next) { + if ((con->flags & CON_ENABLED) && con->write) + con->write(con, buf, len); + } +} + +/* * Call the console drivers on a range of log_buf */ static void __call_console_drivers(unsigned long start, unsigned long end) @@ -525,6 +539,12 @@ asmlinkage int printk(const char *fmt, . printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args); va_end(args); + if (unlikely(netdump_mode)) { + netdump_call_console_drivers(printk_buf, printed_len); + spin_unlock_irqrestore(&logbuf_lock, flags); + goto out; + } + /* * Copy the output into log_buf. If the caller didn't provide * appropriate log level tags, we insert them here diff -uprN linux-2.6.6/kernel/sched.c linux-2.6.6-netdump/kernel/sched.c --- linux-2.6.6/kernel/sched.c 2004-06-21 14:06:53.000000000 -0400 +++ linux-2.6.6-netdump/kernel/sched.c 2004-06-30 10:48:52.000000000 -0400 @@ -3254,6 +3254,8 @@ void show_state(void) read_unlock(&tasklist_lock); } +EXPORT_SYMBOL_GPL(show_state); + void __devinit init_idle(task_t *idle, int cpu) { runqueue_t *idle_rq = cpu_rq(cpu), *rq = cpu_rq(task_cpu(idle)); diff -uprN linux-2.6.6/net/core/dev.c linux-2.6.6-netdump/net/core/dev.c --- linux-2.6.6/net/core/dev.c 2004-06-21 14:06:52.000000000 -0400 +++ linux-2.6.6-netdump/net/core/dev.c 2004-07-01 08:51:01.000000000 -0400 @@ -230,6 +230,8 @@ extern void netdev_unregister_sysfs(stru #define netdev_unregister_sysfs(dev) do { } while(0) #endif +/* netdump function */ +void (*netdump_func) (struct pt_regs *regs) = NULL; /******************************************************************************* @@ -1575,7 +1577,7 @@ int netif_rx(struct sk_buff *skb) struct softnet_data *queue; unsigned long flags; -#ifdef CONFIG_NETPOLL_RX +#ifdef CONFIG_NETPOLL if (skb->dev->netpoll_rx && netpoll_rx(skb)) { kfree_skb(skb); return NET_RX_DROP; @@ -1737,7 +1739,7 @@ int netif_receive_skb(struct sk_buff *sk int ret = NET_RX_DROP; unsigned short type; -#ifdef CONFIG_NETPOLL_RX +#ifdef CONFIG_NETPOLL if (skb->dev->netpoll_rx && skb->dev->poll && netpoll_rx(skb)) { kfree_skb(skb); return NET_RX_DROP; diff -uprN linux-2.6.6/net/core/netpoll.c linux-2.6.6-netdump/net/core/netpoll.c --- linux-2.6.6/net/core/netpoll.c 2004-06-21 14:06:52.000000000 -0400 +++ linux-2.6.6-netdump/net/core/netpoll.c 2004-07-01 09:07:15.000000000 -0400 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,9 @@ #define MAX_SKBS 32 #define MAX_UDP_CHUNK 1460 +#define NETPOLL_RX_ENABLED 1 +#define NETPOLL_RX_DROP 2 + static spinlock_t skb_list_lock = SPIN_LOCK_UNLOCKED; static int nr_skbs; static struct sk_buff *skbs; @@ -38,6 +42,8 @@ static LIST_HEAD(rx_list); static int trapped; +extern void (*netdump_func) (struct pt_regs *regs); + #define MAX_SKB_SIZE \ (MAX_UDP_CHUNK + sizeof(struct udphdr) + \ sizeof(struct iphdr) + sizeof(struct ethhdr)) @@ -61,7 +67,7 @@ static int checksum_udp(struct sk_buff * void netpoll_poll(struct netpoll *np) { - int budget = 1; + int budget = netdump_mode ? 64 : 16; if(!np->dev || !netif_running(np->dev) || !np->dev->poll_controller) return; @@ -70,9 +76,19 @@ void netpoll_poll(struct netpoll *np) np->dev->poll_controller(np->dev); /* If scheduling is stopped, tickle NAPI bits */ - if(trapped && np->dev->poll && - test_bit(__LINK_STATE_RX_SCHED, &np->dev->state)) - np->dev->poll(np->dev, &budget); + if (np->dev->poll && + test_bit(__LINK_STATE_RX_SCHED, &np->dev->state)) { + np->dev->netpoll_rx |= NETPOLL_RX_DROP; + if (trapped) { + np->dev->poll(np->dev, &budget); + } else { + trapped = 1; + np->dev->poll(np->dev, &budget); + trapped = 0; + } + np->dev->netpoll_rx &= ~NETPOLL_RX_DROP; + } + zap_completion_queue(); } @@ -115,6 +131,7 @@ static void zap_completion_queue(void) } put_cpu_var(softnet_data); + touch_nmi_watchdog(); } static struct sk_buff * find_skb(struct netpoll *np, int len, int reserve) @@ -333,6 +350,9 @@ int netpoll_rx(struct sk_buff *skb) struct list_head *p; unsigned long flags; + if (!(skb->dev->netpoll_rx & NETPOLL_RX_ENABLED)) + return 1; + if (skb->dev->type != ARPHRD_ETHER) goto out; @@ -591,15 +611,16 @@ int netpoll_setup(struct netpoll *np) if(np->rx_hook) { unsigned long flags; -#ifdef CONFIG_NETPOLL_RX - np->dev->netpoll_rx = 1; -#endif + np->dev->netpoll_rx = NETPOLL_RX_ENABLED; spin_lock_irqsave(&rx_list_lock, flags); list_add(&np->rx_list, &rx_list); spin_unlock_irqrestore(&rx_list_lock, flags); } + if(np->dump_func) + netdump_func = np->dump_func; + return 0; release: dev_put(ndev); @@ -613,9 +634,7 @@ void netpoll_cleanup(struct netpoll *np) spin_lock_irqsave(&rx_list_lock, flags); list_del(&np->rx_list); -#ifdef CONFIG_NETPOLL_RX np->dev->netpoll_rx = 0; -#endif spin_unlock_irqrestore(&rx_list_lock, flags); } @@ -633,6 +652,13 @@ void netpoll_set_trap(int trap) trapped = trap; } +void netpoll_reset_locks(struct netpoll *np) +{ + spin_lock_init(&rx_list_lock); + spin_lock_init(&skb_list_lock); + spin_lock_init(&np->dev->xmit_lock); +} + EXPORT_SYMBOL(netpoll_set_trap); EXPORT_SYMBOL(netpoll_trap); EXPORT_SYMBOL(netpoll_parse_options); @@ -641,3 +667,4 @@ EXPORT_SYMBOL(netpoll_cleanup); EXPORT_SYMBOL(netpoll_send_skb); EXPORT_SYMBOL(netpoll_send_udp); EXPORT_SYMBOL(netpoll_poll); +EXPORT_SYMBOL_GPL(netpoll_reset_locks);