Whamcloud - gitweb
1)add netconsole-2.4.24
authorwangdi <wangdi>
Thu, 10 Jun 2004 15:45:05 +0000 (15:45 +0000)
committerwangdi <wangdi>
Thu, 10 Jun 2004 15:45:05 +0000 (15:45 +0000)
2)add journal blocks in ext3_ext_in_ea_new_extent, for its reservation is not enough. (tmp fix here)

lustre/kernel_patches/patches/ext3-extents-in-ea-2.4.20.patch
lustre/kernel_patches/patches/netconsole-2.4.24.patch [new file with mode: 0644]

index ce37846..7cdd5da 100644 (file)
@@ -128,7 +128,7 @@ Index: linux-2.4.20/fs/ext3/extents-in-ea.c
 +              return EXT_CONTINUE;
 +
 +      needed = ext3_ext_calc_credits_for_insert(tree, path);
-+      handle = ext3_journal_start(tree->inode, needed);
++      handle = ext3_journal_start(tree->inode, needed + 10);
 +      if (IS_ERR(handle))
 +              return PTR_ERR(handle);
 +
diff --git a/lustre/kernel_patches/patches/netconsole-2.4.24.patch b/lustre/kernel_patches/patches/netconsole-2.4.24.patch
new file mode 100644 (file)
index 0000000..06efea6
--- /dev/null
@@ -0,0 +1,1676 @@
+Index: linux-2.4.24/drivers/net/netconsole.c
+===================================================================
+--- linux-2.4.24.orig/drivers/net/netconsole.c 2003-01-30 18:24:37.000000000 +0800
++++ linux-2.4.24/drivers/net/netconsole.c      2004-06-09 18:53:37.000000000 +0800
+@@ -0,0 +1,1376 @@
++/*
++ *  linux/drivers/net/netconsole.c
++ *
++ *  Copyright (C) 2001  Ingo Molnar <mingo@redhat.com>
++ *  Copyright (C) 2002  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
++ * 2003-10-30    Add sysrq command processing by Wangdi <wangdi@clusterfs.com>
++ * 
++ */
++
++/****************************************************************
++ *      This program is free software; you can redistribute it and/or modify
++ *      it under the terms of the GNU General Public License as published by
++ *      the Free Software Foundation; either version 2, or (at your option)
++ *      any later version.
++ *
++ *      This program is distributed in the hope that it will be useful,
++ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *      GNU General Public License for more details.
++ *
++ *      You should have received a copy of the GNU General Public License
++ *      along with this program; if not, write to the Free Software
++ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ ****************************************************************/
++
++#include <net/tcp.h>
++#include <net/udp.h>
++#include <linux/mm.h>
++#include <linux/tty.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/random.h>
++#include <linux/reboot.h>
++#include <linux/module.h>
++#include <asm/unaligned.h>
++#include <asm/pgtable.h>
++#if CONFIG_X86_LOCAL_APIC
++#include <asm/apic.h>
++#endif
++#include <linux/console.h>
++#include <linux/smp_lock.h>
++#include <linux/netdevice.h>
++#include <linux/tty_driver.h>
++#include <linux/etherdevice.h>
++#include <linux/elf.h>
++#include <linux/sysrq.h>
++#include "netconsole.h"
++
++static struct net_device *netconsole_dev;
++static u16 source_port, netdump_target_port, netlog_target_port, syslog_target_port;
++static u32 source_ip, netdump_target_ip, netlog_target_ip, syslog_target_ip;
++static unsigned char netdump_daddr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} ;
++static unsigned char netlog_daddr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} ;
++static unsigned char syslog_daddr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} ;
++
++static unsigned int mhz = 500, idle_timeout;
++static unsigned long long mhz_cycles, jiffy_cycles;
++
++
++#define MAX_UDP_CHUNK 1460
++#define MAX_PRINT_CHUNK (MAX_UDP_CHUNK-HEADER_LEN)
++
++#define DEBUG 0 
++#if DEBUG
++# define Dprintk(x...) printk(KERN_INFO x)
++#else
++# define Dprintk(x...)
++#endif
++/*
++ * We maintain a small pool of fully-sized skbs,
++ * to make sure the message gets out even in
++ * extreme OOM situations.
++ */
++#define MAX_NETCONSOLE_SKBS 128
++
++static spinlock_t netconsole_lock = SPIN_LOCK_UNLOCKED;
++static int nr_netconsole_skbs;
++static struct sk_buff *netconsole_skbs;
++
++#define MAX_SKB_SIZE \
++              (MAX_UDP_CHUNK + sizeof(struct udphdr) + \
++                              sizeof(struct iphdr) + sizeof(struct ethhdr))
++
++static int new_arp = 0;
++static unsigned char arp_sha[ETH_ALEN], arp_tha[ETH_ALEN];
++static u32 arp_sip, arp_tip;
++
++static void send_netconsole_arp(struct net_device *dev);
++
++static void __refill_netconsole_skbs(void)
++{
++      struct sk_buff *skb;
++      unsigned long flags;
++
++      spin_lock_irqsave(&netconsole_lock, flags);
++      while (nr_netconsole_skbs < MAX_NETCONSOLE_SKBS) {
++              skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC);
++              if (!skb)
++                      break;
++              if (netconsole_skbs)
++                      skb->next = netconsole_skbs;
++              else
++                      skb->next = NULL;
++              netconsole_skbs = skb;
++              nr_netconsole_skbs++;
++      }
++      spin_unlock_irqrestore(&netconsole_lock, flags);
++}
++
++static struct sk_buff * get_netconsole_skb(void)
++{
++      struct sk_buff *skb;
++
++      unsigned long flags;
++
++      spin_lock_irqsave(&netconsole_lock, flags);
++      skb = netconsole_skbs;
++      if (skb) {
++              netconsole_skbs = skb->next;
++              skb->next = NULL;
++              nr_netconsole_skbs--;
++      }
++      spin_unlock_irqrestore(&netconsole_lock, flags);
++
++      return skb;
++}
++
++static unsigned long long t0;
++
++/*
++ * Do cleanups:
++ * - zap completed output skbs.
++ * - send ARPs if requested
++ * - reboot the box if inactive for more than N seconds.
++ */
++static void zap_completion_queue(void)
++{
++      unsigned long long t1;
++      int cpu = smp_processor_id();
++
++      if (softnet_data[cpu].completion_queue) {
++              struct sk_buff *clist;
++
++              local_irq_disable();
++              clist = softnet_data[cpu].completion_queue;
++              softnet_data[cpu].completion_queue = NULL;
++              local_irq_enable();
++
++              while (clist != NULL) {
++                      struct sk_buff *skb = clist;
++                      clist = clist->next;
++                      __kfree_skb(skb);
++              }
++      }
++
++      if (new_arp) {
++              Dprintk("got ARP req - sending reply.\n");
++              new_arp = 0;
++              send_netconsole_arp(netconsole_dev);
++      }
++
++      rdtscll(t1);
++      if (idle_timeout) {
++              if (t0) {
++                      if (((t1 - t0) >> 20) > mhz_cycles * (unsigned long long)idle_timeout) {
++                              t0 = t1;
++                              printk("netdump idle timeout - rebooting in 3 seconds.\n");
++                              mdelay(3000);
++                              machine_restart(NULL);
++                      }
++              }
++      }
++      /* maintain jiffies in a polling fashion, based on rdtsc. */
++      {
++              static unsigned long long prev_tick;
++
++              if (t1 - prev_tick >= jiffy_cycles) {
++                      prev_tick += jiffy_cycles;
++                      jiffies++;
++              }
++      }
++}
++void (*irqfunc)(int, void *, struct pt_regs *);
++
++static void netdump_poll(struct net_device *dev)
++{
++      int budget = 1;
++
++      disable_irq(dev->irq);
++      
++      irqfunc(dev->irq, dev, 0);
++      
++      if(dev->poll && test_bit(__LINK_STATE_RX_SCHED, &dev->state))
++              dev->poll(dev, &budget);
++
++      enable_irq(dev->irq);
++
++}
++
++static struct sk_buff * alloc_netconsole_skb(struct net_device *dev, int len, int reserve)
++{
++      int once = 1;
++      int count = 0;
++      struct sk_buff *skb = NULL;
++
++repeat:
++      zap_completion_queue();
++      if (nr_netconsole_skbs < MAX_NETCONSOLE_SKBS)
++              __refill_netconsole_skbs();
++
++      skb = alloc_skb(len, GFP_ATOMIC);
++      if (!skb) {
++              skb = get_netconsole_skb();
++              if (!skb) {
++                      count++;
++                      if (once && (count == 1000000)) {
++                              printk("possibly FATAL: out of netconsole skbs!!! will keep retrying.\n");
++                              once = 0;
++                      }
++                      Dprintk("alloc skb: polling controller ...\n");
++                      netdump_poll(dev);
++                      goto repeat;
++              }
++      }
++
++      atomic_set(&skb->users, 1);
++      skb_reserve(skb, reserve);
++      return skb;
++}
++
++static void transmit_raw_skb(struct sk_buff *skb, struct net_device *dev)
++{
++
++repeat_poll:
++      spin_lock(&dev->xmit_lock);
++      dev->xmit_lock_owner = smp_processor_id();
++
++      if (netif_queue_stopped(dev)) {
++              dev->xmit_lock_owner = -1;
++              spin_unlock(&dev->xmit_lock);
++
++              Dprintk("xmit skb: polling controller ...\n");
++              netdump_poll(dev);
++              zap_completion_queue();
++              goto repeat_poll;
++      }
++
++      dev->hard_start_xmit(skb, dev);
++
++      dev->xmit_lock_owner = -1;
++      spin_unlock(&dev->xmit_lock);
++}
++
++static void transmit_netconsole_skb(struct sk_buff *skb, struct net_device *dev,
++      int ip_len, int udp_len,
++      u16 source_port, u16 target_port, u32 source_ip, u32 target_ip,
++      unsigned char * macdaddr)
++{
++      struct udphdr *udph;
++      struct iphdr *iph;
++      struct ethhdr *eth;
++
++      udph = (struct udphdr *) skb_push(skb, sizeof(*udph));
++      udph->source = source_port;
++      udph->dest = target_port;
++      udph->len = htons(udp_len);
++      udph->check = 0;
++
++      iph = (struct iphdr *)skb_push(skb, sizeof(*iph));
++
++      iph->version  = 4;
++      iph->ihl      = 5;
++      iph->tos      = 0;
++      iph->tot_len  = htons(ip_len);
++      iph->id       = 0;
++      iph->frag_off = 0;
++      iph->ttl      = 64;
++      iph->protocol = IPPROTO_UDP;
++      iph->check    = 0;
++      iph->saddr    = source_ip;
++      iph->daddr    = target_ip;
++      iph->check    = ip_fast_csum((unsigned char *)iph, iph->ihl);
++
++      eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
++
++      eth->h_proto = htons(ETH_P_IP);
++      memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
++      memcpy(eth->h_dest, macdaddr, dev->addr_len);
++
++      transmit_raw_skb(skb, dev);
++}
++
++static void send_netconsole_arp(struct net_device *dev)
++{
++      int total_len, arp_len, arp_data_len;
++      struct sk_buff *skb;
++      unsigned char *arp;
++      struct arphdr *arph;
++      struct ethhdr *eth;
++
++      arp_data_len = 2*4 + 2*ETH_ALEN;
++      arp_len = arp_data_len + sizeof(struct arphdr);
++      total_len = arp_len + ETH_HLEN;
++
++      skb = alloc_netconsole_skb(dev, total_len, total_len - arp_data_len);
++
++      arp = skb->data;
++
++      memcpy(arp, dev->dev_addr, ETH_ALEN);
++      arp += ETH_ALEN;
++
++      memcpy(arp, &source_ip, 4);
++      arp += 4;
++
++      memcpy(arp, arp_sha, ETH_ALEN);
++      arp += ETH_ALEN;
++
++      memcpy(arp, &arp_sip, 4);
++      arp += 4;
++
++      skb->len += 2*4 + 2*ETH_ALEN;
++
++      arph = (struct arphdr *)skb_push(skb, sizeof(*arph));
++
++      arph->ar_hrd = htons(dev->type);
++      arph->ar_pro = __constant_htons(ETH_P_IP);
++      arph->ar_hln = ETH_ALEN;
++      arph->ar_pln = 4;
++      arph->ar_op = __constant_htons(ARPOP_REPLY);
++
++      eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
++
++      eth->h_proto = htons(ETH_P_ARP);
++      memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
++      memcpy(eth->h_dest, arp_sha, dev->addr_len);
++
++      transmit_raw_skb(skb, dev);
++}
++
++static void send_netdump_skb(struct net_device *dev, const char *msg, unsigned int msg_len, reply_t *reply)
++{
++      int total_len, ip_len, udp_len;
++      struct sk_buff *skb;
++
++      udp_len = msg_len + HEADER_LEN + sizeof(struct udphdr);
++      ip_len = udp_len + sizeof(struct iphdr);
++      total_len = ip_len + ETH_HLEN;
++
++      skb = alloc_netconsole_skb(dev, total_len, total_len - msg_len - HEADER_LEN);
++
++      skb->data[0] = NETCONSOLE_VERSION;
++      put_unaligned(htonl(reply->nr), (u32 *) (skb->data + 1));
++      put_unaligned(htonl(reply->code), (u32 *) (skb->data + 5));
++      put_unaligned(htonl(reply->info), (u32 *) (skb->data + 9));
++
++      memcpy(skb->data + HEADER_LEN, msg, msg_len);
++      skb->len += msg_len + HEADER_LEN;
++
++      transmit_netconsole_skb(skb, dev, ip_len, udp_len,
++              source_port, netdump_target_port, source_ip, netdump_target_ip, netdump_daddr);
++}
++
++#define SYSLOG_HEADER_LEN 4
++
++static void send_netlog_skb(struct net_device *dev, const char *msg, unsigned int msg_len, reply_t *reply)
++{
++      int total_len, ip_len, udp_len;
++      struct sk_buff *skb;
++
++      udp_len = msg_len + HEADER_LEN + sizeof(struct udphdr);
++      ip_len = udp_len + sizeof(struct iphdr);
++      total_len = ip_len + ETH_HLEN;
++
++      skb = alloc_netconsole_skb(dev, total_len, total_len - msg_len - HEADER_LEN);
++
++      skb->data[0] = NETCONSOLE_VERSION;
++      put_unaligned(htonl(reply->nr), (u32 *) (skb->data + 1));
++      put_unaligned(htonl(reply->code), (u32 *) (skb->data + 5));
++      put_unaligned(htonl(reply->info), (u32 *) (skb->data + 9));
++
++      memcpy(skb->data + HEADER_LEN, msg, msg_len);
++      skb->len += msg_len + HEADER_LEN;
++
++      transmit_netconsole_skb(skb, dev, ip_len, udp_len,
++              source_port, netlog_target_port, source_ip, netlog_target_ip, netlog_daddr);
++}
++
++#define SYSLOG_HEADER_LEN 4
++
++static void send_syslog_skb(struct net_device *dev, const char *msg, unsigned int msg_len, int pri)
++{
++      int total_len, ip_len, udp_len;
++      struct sk_buff *skb;
++
++      udp_len = msg_len + SYSLOG_HEADER_LEN + sizeof(struct udphdr);
++      ip_len = udp_len + sizeof(struct iphdr);
++      total_len = ip_len + ETH_HLEN;
++
++      skb = alloc_netconsole_skb(dev, total_len, total_len - msg_len - SYSLOG_HEADER_LEN);
++
++      skb->data[0] = '<';
++      skb->data[1] = pri + '0';
++      skb->data[2]= '>';
++      skb->data[3]= ' ';
++
++      memcpy(skb->data + SYSLOG_HEADER_LEN, msg, msg_len);
++      skb->len += msg_len + SYSLOG_HEADER_LEN;
++
++      transmit_netconsole_skb(skb, dev, ip_len, udp_len, source_port,
++              syslog_target_port, source_ip, syslog_target_ip, syslog_daddr);
++}
++
++#define MAX_SYSLOG_CHARS 1000
++
++static spinlock_t syslog_lock = SPIN_LOCK_UNLOCKED;
++static int syslog_chars;
++static unsigned char syslog_line [MAX_SYSLOG_CHARS + 10];
++
++/*
++ * 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(struct net_device *dev, const unsigned char c)
++{
++      if (syslog_chars == MAX_SYSLOG_CHARS)
++              syslog_chars--;
++      syslog_line[syslog_chars] = c;
++      syslog_chars++;
++      if (c == '\n') {
++              send_syslog_skb(dev, syslog_line, syslog_chars, 5);
++              syslog_chars = 0;
++      }
++}
++
++static spinlock_t sequence_lock = SPIN_LOCK_UNLOCKED;
++static unsigned int log_offset;
++
++static int thread_stopped = 0;
++/*Interrupt function for netdump */
++static int sysrq_mode = 0;
++static int stop_sysrq_thread = 0;
++#define Set_Sysrq_mode()       (sysrq_mode = 1)
++#define Clear_Sysrq_mode()     (sysrq_mode = 0)
++static char send_cache[MAX_PRINT_CHUNK];
++static unsigned int send_cache_pos = 0;
++wait_queue_head_t sysrq_thread_queue;
++wait_queue_head_t sysrq_thread_waiter_queue;
++
++#define SEND_MSG_BUFFER(buf, len)                     \
++do                                                    \
++{                                                     \
++      reply_t reply;                                  \
++      unsigned int flags;                             \
++      __save_flags(flags);                            \
++      __cli();                                        \
++      reply.code = REPLY_LOG;                         \
++      reply.nr = 0;                                   \
++      reply.info = 0;                                 \
++      spin_lock(&sequence_lock);                      \
++      send_netlog_skb(dev, buf, len, &reply); \
++      spin_unlock(&sequence_lock);                    \
++      __restore_flags(flags);                         \
++}while(0);
++
++void netconsole_do_sysrq(req_t *req)
++{
++        struct pt_regs regs;
++      struct net_device *dev = netconsole_dev;
++
++      if (!dev)
++              return;
++      Set_Sysrq_mode();
++      get_current_regs(&regs);
++      handle_sysrq((int)req->from, &regs, NULL, NULL);
++      Dprintk("Do the command netconsole\n"); 
++      if (send_cache_pos != 0){
++              SEND_MSG_BUFFER(send_cache, send_cache_pos);
++              memset(send_cache, 0, MAX_PRINT_CHUNK);
++              send_cache_pos = 0;
++      } 
++
++      Clear_Sysrq_mode();
++}
++static void write_netconsole_msg(struct console *con, const char *msg0, unsigned int msg_len)
++{
++      int len, left, i;
++      struct net_device *dev;
++      const char *msg = msg0;
++      reply_t reply;
++      
++      dev = netconsole_dev;
++      if (!dev || netdump_mode)
++              return;
++      if (sysrq_mode){
++              unsigned long total_len = send_cache_pos + msg_len;
++              unsigned long left_len = msg_len;
++              while (total_len >=  MAX_PRINT_CHUNK){
++                      unsigned long send_len = MAX_PRINT_CHUNK - send_cache_pos; 
++                      memcpy(send_cache + send_cache_pos, msg, send_len);
++                      SEND_MSG_BUFFER(send_cache, MAX_PRINT_CHUNK);
++                      send_cache_pos = 0;
++                      total_len -= MAX_PRINT_CHUNK;
++                      left_len -= send_len; 
++              }
++              if (left_len > 0){
++                      memcpy(send_cache + send_cache_pos, msg + (msg_len -left_len), left_len);
++                      send_cache_pos += left_len;
++              }
++              return; 
++      }else if (netif_running(dev)) {
++              unsigned long flags;
++
++              __save_flags(flags);
++              __cli();
++              left = msg_len;
++              if (netlog_target_ip) {
++                      while (left) {
++                              if (left > MAX_PRINT_CHUNK)
++                                      len = MAX_PRINT_CHUNK;
++                              else
++                                      len = left;
++                              reply.code = REPLY_LOG;
++                              reply.nr = 0;
++                              spin_lock(&sequence_lock);
++                              reply.info = log_offset;
++                              log_offset += len;
++                              spin_unlock(&sequence_lock);
++                              send_netlog_skb(dev, msg, len, &reply);
++                              msg += len;
++                              left -= len;
++                      }
++              }
++              if (syslog_target_ip) {
++                      spin_lock(&syslog_lock);
++                      for (i = 0; i < msg_len; i++)
++                              feed_syslog_char(dev, msg0[i]);
++                      spin_unlock(&syslog_lock);
++              }
++
++              __restore_flags(flags);
++      }
++}
++
++static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr, unsigned long base)
++{
++      return(csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, base));
++}
++
++static int udp_checksum_init(struct sk_buff *skb, struct udphdr *uh,
++                           unsigned short ulen, u32 saddr, u32 daddr)
++{
++      if (uh->check == 0) {
++              skb->ip_summed = CHECKSUM_UNNECESSARY;
++      } else if (skb->ip_summed == CHECKSUM_HW) {
++              skb->ip_summed = CHECKSUM_UNNECESSARY;
++              if (!udp_check(uh, ulen, saddr, daddr, skb->csum))
++                      return 0;
++              skb->ip_summed = CHECKSUM_NONE;
++      }
++      if (skb->ip_summed != CHECKSUM_UNNECESSARY)
++              skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP,
++0);
++      /* Probably, we should checksum udp header (it should be in cache
++       * in any case) and data in tiny packets (< rx copybreak).
++       */
++      return 0;
++}
++
++static __inline__ int __udp_checksum_complete(struct sk_buff *skb)
++{
++      return (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum));
++}
++
++static __inline__ int udp_checksum_complete(struct sk_buff *skb)
++{
++      return skb->ip_summed != CHECKSUM_UNNECESSARY &&
++              __udp_checksum_complete(skb);
++}
++
++/*
++ * 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 netconsole_magic;
++static u32 magic1, magic2;
++
++static spinlock_t req_lock = SPIN_LOCK_UNLOCKED;
++static int nr_req = 0;
++static LIST_HEAD(request_list);
++
++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);
++
++      rdtscll(t0);
++}
++
++static req_t *get_new_req(void)
++{
++      req_t *req = NULL;
++      unsigned long flags;
++
++      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 int netconsole_rx_hook(struct sk_buff *skb)
++{
++      int proto;
++      struct iphdr *iph;
++      struct udphdr *uh;
++      __u32 len, saddr, daddr, ulen;
++      req_t *__req;
++      req_t *req;
++      struct net_device *dev;
++
++#if DEBUG
++      {
++              static int packet_count;
++              Dprintk("        %d\r", ++packet_count);
++      }
++#endif
++      dev = skb->dev;
++      if (dev->type != ARPHRD_ETHER)
++              goto out;
++      proto = ntohs(skb->mac.ethernet->h_proto);
++      Dprintk("rx got skb %p (len: %d, users: %d), dev %s, h_proto: %04x.\n", skb, skb->len, atomic_read(&skb->users), dev->name, proto);
++      #define D(x) skb->mac.ethernet->h_dest[x]
++      Dprintk("... h_dest:   %02X:%02X:%02X:%02X:%02X:%02X.\n", D(0), D(1), D(2), D(3), D(4), D(5));
++      #undef D
++      #define D(x) skb->mac.ethernet->h_source[x]
++      Dprintk("... h_source: %02X:%02X:%02X:%02X:%02X:%02X.\n", D(0), D(1), D(2), D(3), D(4), D(5));
++      #undef D
++      if (skb->pkt_type == PACKET_OTHERHOST)
++              goto out;
++      if (skb_shared(skb))
++              goto out;
++      if (proto == ETH_P_ARP) {
++              struct arphdr *arp;
++              unsigned char *arp_ptr;
++
++              Dprintk("got arp skb.\n");
++              arp = (struct arphdr *)skb->data;
++              if (!pskb_may_pull(skb, sizeof(struct arphdr) + 2*4 + 2*ETH_ALEN))
++                      goto out;
++              if (htons(dev->type) != arp->ar_hrd)
++                      goto out;
++              if (arp->ar_pro != __constant_htons(ETH_P_IP))
++                      goto out;
++              if (arp->ar_hln != ETH_ALEN)
++                      goto out;
++              if (arp->ar_pln != 4)
++                      goto out;
++              if (arp->ar_op != __constant_htons(ARPOP_REQUEST))
++                      goto out;
++              /*
++               * ARP header looks ok so far, extract fields:
++               */
++              arp_ptr = (unsigned char *)(arp + 1);
++
++              memcpy(arp_sha, arp_ptr, ETH_ALEN);
++              arp_ptr += ETH_ALEN;
++
++              memcpy(&arp_sip, arp_ptr, 4);
++              arp_ptr += 4;
++
++              memcpy(arp_tha, arp_ptr, ETH_ALEN);
++              arp_ptr += ETH_ALEN;
++
++              memcpy(&arp_tip, arp_ptr, 4);
++
++              #define D(x) arp_sha[x]
++              Dprintk("... arp_sha:   %02X:%02X:%02X:%02X:%02X:%02X.\n", D(0), D(1), D(2), D(3), D(4), D(5));
++              #undef D
++              #define D(x) ((unsigned char *)&arp_sip)[x]
++              Dprintk("... arp_sip:   %d.%d.%d.%d.\n", D(0), D(1), D(2), D(3));
++              #undef D
++              #define D(x) arp_tha[x]
++              Dprintk("... arp_tha:   %02X:%02X:%02X:%02X:%02X:%02X.\n", D(0), D(1), D(2), D(3), D(4), D(5));
++              #undef D
++              #define D(x) ((unsigned char *)&arp_tip)[x]
++              Dprintk("... arp_tip:   %d.%d.%d.%d.\n", D(0), D(1), D(2), D(3));
++              #undef D
++              #define D(x) ((unsigned char *)&source_ip)[x]
++              Dprintk("... (source_ip):   %d.%d.%d.%d.\n", D(0), D(1), D(2), D(3));
++              #undef D
++
++              if (LOOPBACK(arp_tip) || MULTICAST(arp_tip))
++                      goto out;
++
++              if (arp_tip != source_ip)
++                      goto out;
++              new_arp = 1;
++              goto out;
++      }
++      if (proto != ETH_P_IP)
++              goto out;
++      /*
++       * IP header correctness testing:
++       */
++      iph = (struct iphdr *)skb->data;
++      if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++              goto out;
++      Dprintk("... IP ihl*4: %d, version: %d.\n", iph->ihl*4, iph->version);
++      if (iph->ihl < 5 || iph->version != 4)
++              goto out;
++      if (!pskb_may_pull(skb, iph->ihl*4))
++              goto out;
++      if (ip_fast_csum((u8 *)iph, iph->ihl) != 0)
++              goto out;
++      len = ntohs(iph->tot_len);
++      Dprintk("... IP len: %d.\n", len);
++      if (skb->len < len || len < iph->ihl*4)
++              goto out;
++      saddr = iph->saddr;
++      daddr = iph->daddr;
++      Dprintk("... IP src: %08x, dst: %08x.\n", saddr, daddr);
++      Dprintk("... IP protocol: %d.\n", iph->protocol);
++      if (iph->protocol != IPPROTO_UDP)
++              goto out;
++      Dprintk("... netdump src: %08x, dst: %08x.\n", source_ip, netlog_target_ip);
++      if (source_ip != daddr)
++              goto out;
++      if (netlog_target_ip != saddr)
++              goto out;
++      len -= iph->ihl*4;
++      uh = (struct udphdr *)(((char *)iph) + iph->ihl*4);
++      ulen = ntohs(uh->len);
++      Dprintk("... UDP len: %d (left %d).\n", ulen, len);
++
++#define MIN_COMM_SIZE (sizeof(*uh) + NETDUMP_REQ_SIZE)
++      if (ulen != len || ulen < MIN_COMM_SIZE) {
++              Dprintk("... UDP, hm, len not ok.\n");
++              goto out;
++      }
++      if (udp_checksum_init(skb, uh, ulen, saddr, daddr) < 0) {
++              Dprintk("... UDP, hm, checksum init not ok.\n");
++              goto out;
++      }
++      if (udp_checksum_complete(skb)) {
++              Dprintk("... UDP, hm, checksum complete not ok.\n");
++              goto out;
++      }
++      Dprintk("... UDP packet OK!\n");
++      Dprintk("... UDP src port: %d, dst port: %d.\n", uh->source, uh->dest);
++      if (source_port != uh->source)
++              goto out;
++      if (netlog_target_port != uh->dest)
++              goto out;
++      __req = (req_t *)(uh + 1);
++      Dprintk("... UDP netdump packet OK!\n");
++
++      req = alloc_req();
++      if (!req) {
++              printk("no more RAM to allocate request - dropping it.\n");
++              goto out;
++      }
++
++      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);
++
++      if (netdump_mode) 
++              add_new_req(req);
++      else if (req->command == COMM_SYSRQ){
++              add_new_req(req);
++              wake_up(&sysrq_thread_queue);   
++              return NET_RX_DROP;
++      }
++out:
++      if (!netdump_mode)
++              return NET_RX_SUCCESS;
++      return NET_RX_DROP;
++}
++
++#define INVALID_PAGE "page is not valid!\n"
++
++static void send_netdump_mem (struct net_device *dev, 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;
++
++      return ;
++      
++      reply.nr = req->nr;
++      reply.info = 0;
++      if (req->from >= max_mapnr) {
++              sprintf(str, "page %08lx is bigger than max page # %08lx!\n", nr, max_mapnr);
++              reply.code = REPLY_ERROR;
++              send_netdump_skb(dev, str, strlen(str), &reply);
++              return;
++      }
++      page = mem_map + nr;
++      if (PageReserved(page))
++              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;
++              send_netdump_skb(dev, kaddr + offset, 1024, &reply);
++      }
++
++//    kunmap_atomic(kaddr, KM_NETDUMP);
++}
++
++
++/*
++ * 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 net_device *dev)
++{
++      char tmp[200];
++      reply_t reply;
++      req_t *req = NULL;
++      int i;
++
++      netdump_mode = 1;
++
++repeat:
++      sprintf(tmp, "NETDUMP start, waiting for start-ACK.\n");
++      reply.code = REPLY_START_NETDUMP;
++      reply.nr = 0;
++      reply.info = 0;
++      send_netdump_skb(dev, tmp, strlen(tmp), &reply);
++
++      for (i = 0; i < 10000; i++) {
++              // wait 1 sec.
++              udelay(100);
++              Dprintk("handshake: polling controller ...\n");
++              netdump_poll(dev);              
++              zap_completion_queue();
++              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");
++}
++
++#if 0
++
++static inline void print_status (req_t *req)
++{
++      static int count = 0;
++
++      switch (++count & 3) {
++              case 0: printk("/\r"); break;
++              case 1: printk("|\r"); break;
++              case 2: printk("\\\r"); break;
++              case 3: printk("-\r"); break;
++      }
++}
++
++#else
++
++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;
++              }
++      }
++}
++
++#endif
++
++#define CLI 1
++
++#if CONFIG_SMP
++static void freeze_cpu (void * dummy)
++{
++      printk("CPU#%d is frozen.\n", smp_processor_id());
++#if CLI
++      for (;;) __cli();
++#else
++      for (;;) __sti();
++#endif
++}
++#endif
++
++static void netconsole_netdump (struct pt_regs *regs)
++{
++      reply_t reply;
++      char tmp[200];
++      unsigned long flags;
++      struct net_device *dev = netconsole_dev;
++      unsigned long esp;
++      unsigned short ss;
++      struct pt_regs myregs;
++      req_t *req;
++
++      __save_flags(flags);
++      __cli();
++#if CONFIG_X86_LOCAL_APIC
++      //nmi_watchdog = 0;
++#endif
++#if CONFIG_SMP
++      smp_call_function(freeze_cpu, NULL, 1, 0);
++#endif
++      mdelay(1000);
++      /*
++       * Just in case we are crashing within the networking code
++       * ... attempt to fix up.
++       */
++      spin_lock_init(&dev->xmit_lock);
++
++      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;
++
++      rdtscll(t0);
++
++      printk("< netdump activated - performing handshake with the client. >\n");
++      netdump_startup_handshake(dev);
++
++      printk("< handshake completed - listening for dump requests. >\n");
++
++      while (netdump_mode) {
++              __cli();
++              Dprintk("main netdump loop: polling controller ...\n");
++              netdump_poll(dev);
++              zap_completion_queue();
++#if !CLI
++              __sti();
++#endif
++              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 ->from ->to.
++                      send_netdump_mem(dev, req);
++                      break;
++
++              case COMM_EXIT:
++                      Dprintk("got EXIT command.\n");
++                      netdump_mode = 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", NETCONSOLE_VERSION);
++                      reply.code = REPLY_HELLO;
++                      reply.nr = req->nr;
++                      reply.info = NETCONSOLE_VERSION;
++                      send_netdump_skb(dev, 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_skb(dev, 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 = max_mapnr;
++                      tmp2 = tmp + sprintf(tmp, "Sending register info.\n");
++                      ELF_CORE_COPY_REGS(elf_regs, regs);
++                      memcpy(tmp2, &elf_regs, sizeof(elf_regs));
++                      send_netdump_skb(dev, tmp, strlen(tmp) + sizeof(elf_regs), &reply);
++                      break;
++              }
++
++              case COMM_GET_NR_PAGES:
++                      reply.code = REPLY_NR_PAGES;
++                      reply.nr = req->nr;
++                      reply.info = max_mapnr;
++                      sprintf(tmp, "Number of pages: %ld\n", max_mapnr);
++                      send_netdump_skb(dev, tmp, strlen(tmp), &reply);
++                      break;
++
++              case COMM_SHOW_STATE:
++                      netdump_mode = 0;
++                      //if (regs)
++                              //show_regs(regs);
++                      //show_state();
++                      //show_mem();
++                      netdump_mode = 1;
++                      reply.code = REPLY_SHOW_STATE;
++                      reply.nr = req->nr;
++                      reply.info = 0;
++                      send_netdump_skb(dev, tmp, strlen(tmp), &reply);
++                      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_skb(dev, 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_skb(dev, tmp, strlen(tmp), &reply);
++      printk("NETDUMP END!\n");
++      __restore_flags(flags);
++}
++static int netconsole_sysrq_schedule(void *arg) 
++{
++      struct task_struct *tsk = current;
++
++      sprintf(tsk->comm, "sysrq_schedule");
++      sigfillset(&tsk->blocked);
++
++      /* main loop */
++      thread_stopped = 0;     
++      for (;;) {
++              wait_event_interruptible(sysrq_thread_queue,
++                                       !list_empty(&request_list) || stop_sysrq_thread);
++              while (!list_empty(&request_list)) {
++                      req_t *req = get_new_req();
++                      Dprintk("get new req %d from req_list\n", (int)req->command);
++                      if (req->command == COMM_SYSRQ) 
++                              netconsole_do_sysrq(req);       
++              }
++              if (stop_sysrq_thread)
++                      break;
++              wake_up(&sysrq_thread_waiter_queue);
++      }
++      thread_stopped = 1;     
++      wake_up(&sysrq_thread_waiter_queue);
++      return 0;
++}
++
++
++static char *dev;
++static int netdump_target_eth_byte0 = 255;
++static int netdump_target_eth_byte1 = 255;
++static int netdump_target_eth_byte2 = 255;
++static int netdump_target_eth_byte3 = 255;
++static int netdump_target_eth_byte4 = 255;
++static int netdump_target_eth_byte5 = 255;
++
++static int netlog_target_eth_byte0 = 255;
++static int netlog_target_eth_byte1 = 255;
++static int netlog_target_eth_byte2 = 255;
++static int netlog_target_eth_byte3 = 255;
++static int netlog_target_eth_byte4 = 255;
++static int netlog_target_eth_byte5 = 255;
++
++static int syslog_target_eth_byte0 = 255;
++static int syslog_target_eth_byte1 = 255;
++static int syslog_target_eth_byte2 = 255;
++static int syslog_target_eth_byte3 = 255;
++static int syslog_target_eth_byte4 = 255;
++static int syslog_target_eth_byte5 = 255;
++
++MODULE_PARM(netdump_target_ip, "i");
++MODULE_PARM_DESC(netdump_target_ip,
++      "remote netdump IP address as a native (not network) endian integer");
++MODULE_PARM(netlog_target_ip, "i");
++MODULE_PARM_DESC(netlog_target_ip,
++      "remote netlog IP address as a native (not network) endian integer");
++MODULE_PARM(syslog_target_ip, "i");
++MODULE_PARM_DESC(syslog_target_ip,
++      "remote syslog IP address as a native (not network) endian integer");
++
++MODULE_PARM(source_port, "h");
++MODULE_PARM_DESC(source_port,
++      "local port from which to send netdump packets");
++
++MODULE_PARM(netdump_target_port, "h");
++MODULE_PARM_DESC(netdump_target_port,
++      "remote port to which to send netdump packets");
++MODULE_PARM(netlog_target_port, "h");
++MODULE_PARM_DESC(netlog_target_port,
++      "remote port to which to send netlog packets");
++MODULE_PARM(syslog_target_port, "h");
++MODULE_PARM_DESC(syslog_target_port,
++      "remote port to which to send syslog packets");
++
++#define ETH_BYTE(name,nr) \
++      MODULE_PARM(name##_target_eth_byte##nr, "i"); \
++      MODULE_PARM_DESC(name##_target_eth_byte##nr, \
++              "byte "#nr" of the netdump server MAC address")
++
++#define ETH_BYTES(name) \
++      ETH_BYTE(name, 0); ETH_BYTE(name, 1); ETH_BYTE(name, 2); \
++      ETH_BYTE(name, 3); ETH_BYTE(name, 4); ETH_BYTE(name, 5);
++
++ETH_BYTES(netdump);
++ETH_BYTES(netlog);
++ETH_BYTES(syslog);
++
++MODULE_PARM(magic1, "i");
++MODULE_PARM_DESC(magic1,
++      "lower 32 bits of magic cookie shared between client and server");
++MODULE_PARM(magic2, "i");
++MODULE_PARM_DESC(magic2,
++      "upper 32 bits of magic cookie shared between client and server");
++MODULE_PARM(dev, "s");
++MODULE_PARM_DESC(dev,
++      "name of the device from which to send netdump and syslog packets");
++MODULE_PARM(mhz, "i");
++MODULE_PARM_DESC(mhz,
++      "one second wall clock time takes this many million CPU cycles");
++MODULE_PARM(idle_timeout, "i");
++MODULE_PARM_DESC(idle_timeout,
++      "reboot system after this many idle seconds");
++
++static struct console netconsole =
++       { flags: CON_ENABLED, write: write_netconsole_msg };
++static int init_netconsole(void)
++{
++      struct net_device *ndev = NULL;
++      struct in_device *in_dev;
++      struct irqaction *action;
++      int rc = 0;
++
++      printk(KERN_INFO "netlog: using network device <%s>\n", dev);
++      // this will be valid once the device goes up.
++      if (dev)
++              ndev = dev_get_by_name(dev);
++      if (!ndev) {
++              printk(KERN_ERR "netlog: network device %s does not exist, aborting.\n", dev);
++              return -1;
++      }
++      in_dev = in_dev_get(ndev);
++      if (!in_dev) {
++              printk(KERN_ERR "netlog: network device %s is not an IP protocol device, aborting.\n", dev);
++              return -1;
++      }
++
++      if (!magic1 || !magic2) {
++              printk(KERN_ERR "netlog: magic cookie (magic1,magic2) not specified.\n");
++              return -1;
++      }
++      netconsole_magic = magic1 + (((u64)magic2)<<32);
++
++      source_ip = ntohl(in_dev->ifa_list->ifa_local);
++      if (!source_ip) {
++              printk(KERN_ERR "netlog: network device %s has no local address, aborting.\n", dev);
++              return -1;
++      }
++#define IP(x) ((unsigned char *)&source_ip)[x]
++      printk(KERN_INFO "netlog: using source IP %u.%u.%u.%u\n",
++              IP(3), IP(2), IP(1), IP(0));
++#undef IP
++      source_ip = htonl(source_ip);
++      if (!source_port) {
++              printk(KERN_ERR "netlog: source_port parameter not specified, aborting.\n");
++              return -1;
++      }
++      printk(KERN_INFO "netlog: using source UDP port: %u\n", source_port);
++      source_port = htons(source_port);
++
++      if (!netdump_target_ip && !netlog_target_ip && !syslog_target_ip) {
++              printk(KERN_ERR "netlog: target_ip parameter not specified, aborting.\n");
++              return -1;
++#define IP(x) ((unsigned char *)&netdump_target_ip)[x]
++              printk(KERN_INFO "netlog: using netdump target IP %u.%u.%u.%u\n",
++                      IP(3), IP(2), IP(1), IP(0));
++#undef IP
++              netdump_target_ip = htonl(netdump_target_ip);
++      }
++      if (netlog_target_ip) {
++#define IP(x) ((unsigned char *)&netlog_target_ip)[x]
++              printk(KERN_INFO "netlog: using netlog target IP %u.%u.%u.%u\n",
++                      IP(3), IP(2), IP(1), IP(0));
++#undef IP
++              netlog_target_ip = htonl(netlog_target_ip);
++      }
++      if (syslog_target_ip) {
++              if (!syslog_target_port)
++                      syslog_target_port = 514;
++#define IP(x) ((unsigned char *)&syslog_target_ip)[x]
++              printk("netlog: using syslog target IP %u.%u.%u.%u, port: %d\n", IP(3), IP(2), IP(1), IP(0), syslog_target_port);
++#undef IP
++              syslog_target_ip = htonl(syslog_target_ip);
++              syslog_target_port = htons(syslog_target_port);
++      }
++      if (!netdump_target_port && !netlog_target_port && !syslog_target_port) {
++              printk(KERN_ERR "netlog: target_port parameter not specified, aborting.\n");
++              return -1;
++      }
++      if (netdump_target_port) {
++              printk(KERN_INFO "netlog: using target UDP port: %u\n", netdump_target_port);
++              netdump_target_port = htons(netdump_target_port);
++      }
++      if (netlog_target_port) {
++              printk(KERN_INFO "netlog: using target UDP port: %u\n", netlog_target_port);
++              netlog_target_port = htons(netlog_target_port);
++      }
++
++      netdump_daddr[0] = netdump_target_eth_byte0;
++      netdump_daddr[1] = netdump_target_eth_byte1;
++      netdump_daddr[2] = netdump_target_eth_byte2;
++      netdump_daddr[3] = netdump_target_eth_byte3;
++      netdump_daddr[4] = netdump_target_eth_byte4;
++      netdump_daddr[5] = netdump_target_eth_byte5;
++
++      if ((netdump_daddr[0] & netdump_daddr[1] & netdump_daddr[2] & netdump_daddr[3] & netdump_daddr[4] & netdump_daddr[5]) == 255)
++              printk(KERN_INFO "netlog: using broadcast ethernet frames to send netdump packets.\n");
++      else
++              printk(KERN_INFO "netlog: using netdump target ethernet address %02x:%02x:%02x:%02x:%02x:%02x.\n",
++                              netdump_daddr[0], netdump_daddr[1], netdump_daddr[2], netdump_daddr[3], netdump_daddr[4], netdump_daddr[5]);
++
++      netlog_daddr[0] = netlog_target_eth_byte0;
++      netlog_daddr[1] = netlog_target_eth_byte1;
++      netlog_daddr[2] = netlog_target_eth_byte2;
++      netlog_daddr[3] = netlog_target_eth_byte3;
++      netlog_daddr[4] = netlog_target_eth_byte4;
++      netlog_daddr[5] = netlog_target_eth_byte5;
++
++      if ((netlog_daddr[0] & netlog_daddr[1] & netlog_daddr[2] & netlog_daddr[3] & netlog_daddr[4] & netlog_daddr[5]) == 255)
++              printk(KERN_INFO "netlog: using broadcast ethernet frames to send netdump packets.\n");
++      else
++              printk(KERN_INFO "netlog: using netdump target ethernet address %02x:%02x:%02x:%02x:%02x:%02x.\n",
++                              netlog_daddr[0], netlog_daddr[1], netlog_daddr[2], netlog_daddr[3], netlog_daddr[4], netlog_daddr[5]);
++      syslog_daddr[0] = syslog_target_eth_byte0;
++      syslog_daddr[1] = syslog_target_eth_byte1;
++      syslog_daddr[2] = syslog_target_eth_byte2;
++      syslog_daddr[3] = syslog_target_eth_byte3;
++      syslog_daddr[4] = syslog_target_eth_byte4;
++      syslog_daddr[5] = syslog_target_eth_byte5;
++
++      if ((syslog_daddr[0] & syslog_daddr[1] & syslog_daddr[2] & syslog_daddr[3] & syslog_daddr[4] & syslog_daddr[5]) == 255)
++              printk(KERN_INFO "netlog: using broadcast ethernet frames to send syslog packets.\n");
++      else
++              printk(KERN_INFO "netlog: using syslog target ethernet address %02x:%02x:%02x:%02x:%02x:%02x.\n",
++                              syslog_daddr[0], syslog_daddr[1], syslog_daddr[2], syslog_daddr[3], syslog_daddr[4], syslog_daddr[5]);
++
++      mhz_cycles = (unsigned long long)mhz * 1000000ULL;
++      jiffy_cycles = (unsigned long long)mhz * (1000000/HZ);
++      
++      ndev->rx_hook = netconsole_rx_hook;
++      netdump_func = netconsole_netdump;
++      netconsole_dev = ndev;
++      /* find irq function of the ndev*/
++      action=find_irq_action(ndev->irq, ndev);
++        if (!action) {
++              printk(KERN_ERR "couldn't find irq handler for <%s>", dev);
++              return -1;
++      }
++      irqfunc = action->handler;
++
++      stop_sysrq_thread = 0;
++      INIT_LIST_HEAD(&request_list);
++      init_waitqueue_head(&sysrq_thread_queue);
++      init_waitqueue_head(&sysrq_thread_waiter_queue);
++      if ((rc = kernel_thread(netconsole_sysrq_schedule, NULL, 0)) < 0 ){
++              printk(KERN_ERR "Can not start netconsole sysrq thread: rc %d\n", rc); 
++            return -1; 
++      }
++
++#define STARTUP_MSG "[...network console startup...]\n"
++      write_netconsole_msg(NULL, STARTUP_MSG, strlen(STARTUP_MSG));
++
++      register_console(&netconsole);
++      printk(KERN_INFO "netlog: network logging started up successfully!\n");
++      return 0;
++}
++
++static void cleanup_netconsole(void)
++{
++      stop_sysrq_thread = 1;
++      
++      wake_up(&sysrq_thread_queue);
++      wait_event(sysrq_thread_waiter_queue, thread_stopped); 
++      printk(KERN_INFO"netlog: network logging shut down.\n");
++      unregister_console(&netconsole);
++
++#define SHUTDOWN_MSG "[...network console shutdown...]\n"
++      write_netconsole_msg(NULL, SHUTDOWN_MSG, strlen(SHUTDOWN_MSG));
++      netconsole_dev->rx_hook = NULL;
++      netconsole_dev = NULL;
++}
++
++module_init(init_netconsole);
++module_exit(cleanup_netconsole);
++
++MODULE_LICENSE("GPL");
++
+Index: linux-2.4.24/drivers/net/netconsole.h
+===================================================================
+--- linux-2.4.24.orig/drivers/net/netconsole.h 2003-01-30 18:24:37.000000000 +0800
++++ linux-2.4.24/drivers/net/netconsole.h      2004-06-09 14:14:54.000000000 +0800
+@@ -0,0 +1,102 @@
++/*
++ *  linux/drivers/net/netconsole.h
++ *
++ *  Copyright (C) 2001  Ingo Molnar <mingo@redhat.com>
++ *
++ *  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 NETCONSOLE_VERSION 0x03
++
++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,
++      COMM_START_WRITE_NETDUMP_ACK = 10,
++        COMM_SYSRQ = 11,
++};
++
++#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,
++      REPLY_SYSRQ = 13,
++};
++
++typedef struct netdump_reply_s {
++      u32 nr;
++      u32 code;
++      u32 info;
++} reply_t;
++
++#define HEADER_LEN (1 + sizeof(reply_t))
++/* for netconsole */
++static inline void get_current_regs(struct pt_regs *regs)
++{
++       __asm__ __volatile__("movl %%ebx,%0" : "=m"(regs->ebx));
++       __asm__ __volatile__("movl %%ecx,%0" : "=m"(regs->ecx));
++       __asm__ __volatile__("movl %%edx,%0" : "=m"(regs->edx));
++       __asm__ __volatile__("movl %%esi,%0" : "=m"(regs->esi));
++       __asm__ __volatile__("movl %%edi,%0" : "=m"(regs->edi));
++       __asm__ __volatile__("movl %%ebp,%0" : "=m"(regs->ebp));
++       __asm__ __volatile__("movl %%eax,%0" : "=m"(regs->eax));
++       __asm__ __volatile__("movl %%esp,%0" : "=m"(regs->esp));
++       __asm__ __volatile__("movw %%ss, %%ax;" :"=a"(regs->xss));
++       __asm__ __volatile__("movw %%cs, %%ax;" :"=a"(regs->xcs));
++       __asm__ __volatile__("movw %%ds, %%ax;" :"=a"(regs->xds));
++       __asm__ __volatile__("movw %%es, %%ax;" :"=a"(regs->xes));
++       __asm__ __volatile__("pushfl; popl %0" :"=m"(regs->eflags));
++       regs->eip = (unsigned long)current_text_addr();
++}
++              
+Index: linux-2.4.24/drivers/net/Makefile
+===================================================================
+--- linux-2.4.24.orig/drivers/net/Makefile     2003-11-29 02:26:20.000000000 +0800
++++ linux-2.4.24/drivers/net/Makefile  2004-06-09 14:16:14.000000000 +0800
+@@ -250,6 +250,8 @@
+ obj-y         += ../acorn/net/acorn-net.o
+ endif
++obj-$(CONFIG_NETCONSOLE) += netconsole.o
++
+ #
+ # HIPPI adapters
+ #
+Index: linux-2.4.24/drivers/net/Config.in
+===================================================================
+--- linux-2.4.24.orig/drivers/net/Config.in    2003-11-29 02:26:20.000000000 +0800
++++ linux-2.4.24/drivers/net/Config.in 2004-06-09 14:17:33.000000000 +0800
+@@ -294,7 +294,7 @@
+    fi
+    dep_tristate '  SysKonnect FDDI PCI support' CONFIG_SKFP $CONFIG_PCI
+ fi
+-
++tristate 'Network logging support' CONFIG_NETCONSOLE
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_INET" = "y" ]; then
+       bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI
+Index: linux-2.4.24/arch/i386/kernel/traps.c
+===================================================================
+--- linux-2.4.24.orig/arch/i386/kernel/traps.c 2004-06-09 14:10:04.000000000 +0800
++++ linux-2.4.24/arch/i386/kernel/traps.c      2004-06-09 14:29:00.000000000 +0800
+@@ -279,6 +279,8 @@
+ bug:
+       printk("Kernel BUG\n");
+ }
++void (*netdump_func) (struct pt_regs *regs) = NULL;
++int netdump_mode = 0;
+ spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
+@@ -290,6 +292,8 @@
+       handle_BUG(regs);
+       printk("%s: %04lx\n", str, err & 0xffff);
+       show_registers(regs);
++        if (netdump_func)
++                netdump_func(regs);
+       bust_spinlocks(0);
+       spin_unlock_irq(&die_lock);
+       do_exit(SIGSEGV);
+@@ -1039,5 +1043,7 @@
+        return -ENOSYS;
+ }
++EXPORT_SYMBOL_GPL(netdump_func);
++EXPORT_SYMBOL_GPL(netdump_mode);
+ EXPORT_SYMBOL_GPL(is_kernel_text_address);
+ EXPORT_SYMBOL_GPL(lookup_symbol);
+Index: linux-2.4.24/arch/i386/kernel/irq.c
+===================================================================
+--- linux-2.4.24.orig/arch/i386/kernel/irq.c   2004-06-09 14:09:59.000000000 +0800
++++ linux-2.4.24/arch/i386/kernel/irq.c        2004-06-09 14:53:34.000000000 +0800
+@@ -1048,6 +1048,21 @@
+       return 0;
+ }
++struct irqaction *find_irq_action(unsigned int irq, void *dev_id)
++{
++       struct irqaction *a, *r=0;
++                                                                                                                                                                                                     
++       spin_lock_irq(&irq_desc[irq].lock);
++       for(a=irq_desc[irq].action; a; a=a->next) {
++               if(a->dev_id == dev_id) {
++                       r=a;
++                       break;
++               }
++       }
++       spin_unlock_irq(&irq_desc[irq].lock);
++       return r;
++}
++
+ static struct proc_dir_entry * root_irq_dir;
+ static struct proc_dir_entry * irq_dir [NR_IRQS];
+Index: linux-2.4.24/arch/i386/kernel/i386_ksyms.c
+===================================================================
+--- linux-2.4.24.orig/arch/i386/kernel/i386_ksyms.c    2003-11-29 02:26:19.000000000 +0800
++++ linux-2.4.24/arch/i386/kernel/i386_ksyms.c 2004-06-09 14:55:11.000000000 +0800
+@@ -66,6 +66,7 @@
+ EXPORT_SYMBOL(iounmap);
+ EXPORT_SYMBOL(enable_irq);
+ EXPORT_SYMBOL(disable_irq);
++EXPORT_SYMBOL(find_irq_action);
+ EXPORT_SYMBOL(disable_irq_nosync);
+ EXPORT_SYMBOL(probe_irq_mask);
+ EXPORT_SYMBOL(kernel_thread);
+Index: linux-2.4.24/arch/x86_64/kernel/traps.c
+===================================================================
+--- linux-2.4.24.orig/arch/x86_64/kernel/traps.c       2003-11-29 02:26:19.000000000 +0800
++++ linux-2.4.24/arch/x86_64/kernel/traps.c    2004-06-09 14:30:02.000000000 +0800
+@@ -80,7 +80,7 @@
+ extern char iret_address[];
+ struct notifier_block *die_chain;
+-
++void (*netdump_func) (struct pt_regs *regs) = NULL;
+ int kstack_depth_to_print = 12;
+ #ifdef CONFIG_KALLSYMS
+Index: linux-2.4.24/arch/x86_64/kernel/x8664_ksyms.c
+===================================================================
+--- linux-2.4.24.orig/arch/x86_64/kernel/x8664_ksyms.c 2003-11-29 02:26:19.000000000 +0800
++++ linux-2.4.24/arch/x86_64/kernel/x8664_ksyms.c      2004-06-09 14:32:14.000000000 +0800
+@@ -40,7 +40,7 @@
+ extern struct drive_info_struct drive_info;
+ EXPORT_SYMBOL(drive_info);
+ #endif
+-
++int netdump_mode = 0;
+ /* platform dependent support */
+ EXPORT_SYMBOL(boot_cpu_data);
+ EXPORT_SYMBOL(dump_fpu);
+@@ -226,6 +226,9 @@
+ extern void int_ret_from_sys_call(void);
+ EXPORT_SYMBOL_NOVERS(int_ret_from_sys_call); 
++EXPORT_SYMBOL_GPL(netdump_func);
++EXPORT_SYMBOL_GPL(netdump_mode);
++
+ EXPORT_SYMBOL(touch_nmi_watchdog);
+ EXPORT_SYMBOL(do_fork);
+Index: linux-2.4.24/include/linux/netdevice.h
+===================================================================
+--- linux-2.4.24.orig/include/linux/netdevice.h        2004-06-09 14:20:33.000000000 +0800
++++ linux-2.4.24/include/linux/netdevice.h     2004-06-09 18:40:24.000000000 +0800
+@@ -435,6 +435,7 @@
+                                                    unsigned char *haddr);
+       int                     (*neigh_setup)(struct net_device *dev, struct neigh_parms *);
+       int                     (*accept_fastpath)(struct net_device *, struct dst_entry*);
++        int                     (*rx_hook)(struct sk_buff *skb);
+       /* open/release and usage marking */
+       struct module *owner;
+Index: linux-2.4.24/include/linux/kernel.h
+===================================================================
+--- linux-2.4.24.orig/include/linux/kernel.h   2004-06-09 14:20:33.000000000 +0800
++++ linux-2.4.24/include/linux/kernel.h        2004-06-09 18:39:18.000000000 +0800
+@@ -105,6 +105,9 @@
+ extern void bust_spinlocks(int yes);
+ extern int oops_in_progress;          /* If set, an oops, panic(), BUG() or die() is in progress */
++extern void (*netdump_func) (struct pt_regs *regs);
++extern int netdump_mode;
++
+ extern int tainted;
+ extern const char *print_tainted(void);
+Index: linux-2.4.24/include/asm-i386/irq.h
+===================================================================
+--- linux-2.4.24.orig/include/asm-i386/irq.h   2004-06-09 14:20:33.000000000 +0800
++++ linux-2.4.24/include/asm-i386/irq.h        2004-06-09 14:54:04.000000000 +0800
+@@ -38,7 +38,7 @@
+ extern void disable_irq_nosync(unsigned int);
+ extern void enable_irq(unsigned int);
+ extern void release_x86_irqs(struct task_struct *);
+-
++extern struct irqaction *find_irq_action(unsigned int irq, void *dev_id);
+ #ifdef CONFIG_X86_LOCAL_APIC
+ #define ARCH_HAS_NMI_WATCHDOG         /* See include/linux/nmi.h */
+ #endif
+Index: linux-2.4.24/net/core/dev.c
+===================================================================
+--- linux-2.4.24.orig/net/core/dev.c   2003-11-29 02:26:21.000000000 +0800
++++ linux-2.4.24/net/core/dev.c        2004-06-09 14:50:03.000000000 +0800
+@@ -1287,7 +1287,13 @@
+       queue = &softnet_data[this_cpu];
+       local_irq_save(flags);
+-
++        if (unlikely(skb->dev->rx_hook != NULL)) {
++                int ret;
++                                                                                                                                                                                                     
++                ret = skb->dev->rx_hook(skb);
++                if (ret == NET_RX_DROP)
++                        goto drop;
++        }
+       netdev_rx_stat[this_cpu].total++;
+       if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
+               if (queue->input_pkt_queue.qlen) {