diff -rNu linux/Documentation/Configure.help linux-custom/Documentation/Configure.help
--- linux/Documentation/Configure.help	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/Documentation/Configure.help	2004-09-27 23:55:00.000000000 +0200
@@ -13521,6 +13521,11 @@
   say M here and read <file:Documentation/modules.txt>.  This is
   recommended.  The module will be called skfp.o.
 
+Network console support
+CONFIG_NETCONSOLE
+  If you want to log kernel messages over the network, then say
+  "M" here. See Documentation/networking/netlogging.txt for details.
+
 HIgh Performance Parallel Interface (HIPPI) support
 CONFIG_HIPPI
   HIgh Performance Parallel Interface (HIPPI) is a 800Mbit/sec and
diff -rNu linux/Documentation/networking/netconsole.txt linux-custom/Documentation/networking/netconsole.txt
--- linux/Documentation/networking/netconsole.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-custom/Documentation/networking/netconsole.txt	2004-09-27 23:55:00.000000000 +0200
@@ -0,0 +1,56 @@
+
+started by Ingo Molnar <mingo@redhat.com>, 2001.09.17
+updated by Herbert Pötzl <herbert@13thfloor.at>, 2004.01.17
+to the new config syntax from Matt Mackall <mpm@selenic.com>
+
+This logs kernel printk messages over UDP allowing debugging of
+issues where disk logging fails and serial consoles are impractical.
+
+It can be used either built-in or as a module. As a built-in,
+netconsole initializes immediately after NIC cards and will bring up
+the specified interface as soon as possible. While this doesn't allow
+capture of early kernel panics, it does capture most of the boot
+process.
+
+It takes a string configuration parameter "netconsole" in the
+following format:
+
+ netconsole=[src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr]
+
+   where
+        src-port      source for UDP packets (defaults to 6665)
+        src-ip        source IP to use (interface address)
+        dev           network interface (eth0)
+        tgt-port      port for logging agent (6666)
+        tgt-ip        IP address for logging agent
+        tgt-macaddr   ethernet MAC address for logging agent (broadcast)
+
+Examples:
+
+ linux netconsole=4444@10.0.0.1/eth1,9353@10.0.0.2/12:34:56:78:9a:bc
+
+  or
+
+ insmod netconsole netconsole=@/,@10.0.0.2/
+
+Built-in netconsole starts immediately after the TCP stack is
+initialized and attempts to bring up the supplied dev at the supplied
+address.
+
+The remote host can run either 'netcat -u -l -p <port>' or syslogd.
+
+WARNING: the default target ethernet setting uses the broadcast
+ethernet address to send packets, which can cause increased load on
+other systems on the same ethernet segment.
+
+NOTE: the network device (eth1 in the above case) can run any kind
+of other network traffic, netconsole is not intrusive. Netconsole
+might cause slight delays in other traffic if the volume of kernel
+messages is high, but should have no other impact.
+
+Netconsole was designed to be as instantaneous as possible, to
+enable the logging of even the most critical kernel bugs. It works
+from IRQ contexts as well, and does not enable interrupts while
+sending packets. Due to these unique needs, configuration can not
+be more automatic, and some fundamental limitations will remain:
+only IP networks, UDP packets and ethernet devices are supported.
diff -rNu linux/drivers/net/3c503.c linux-custom/drivers/net/3c503.c
--- linux/drivers/net/3c503.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/3c503.c	2004-09-27 23:55:00.000000000 +0200
@@ -311,6 +311,9 @@
     dev->open = &el2_open;
     dev->stop = &el2_close;
     dev->ethtool_ops = &netdev_ethtool_ops;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+    dev->poll_controller = ei_poll;
+#endif
 
     if (dev->mem_start)
 	printk("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
diff -rNu linux/drivers/net/3c59x.c linux-custom/drivers/net/3c59x.c
--- linux/drivers/net/3c59x.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/3c59x.c	2004-09-27 23:55:00.000000000 +0200
@@ -874,6 +874,7 @@
 static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static void vortex_tx_timeout(struct net_device *dev);
 static void acpi_set_WOL(struct net_device *dev);
+static void vorboom_poll(struct net_device *dev);
 static struct ethtool_ops vortex_ethtool_ops;
 
 /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
@@ -1343,6 +1344,9 @@
 	dev->set_multicast_list = set_rx_mode;
 	dev->tx_timeout = vortex_tx_timeout;
 	dev->watchdog_timeo = (watchdog * HZ) / 1000;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = &vorboom_poll;
+#endif
 	if (pdev && vp->enable_wol) {
 		vp->pm_state_valid = 1;
  		pci_save_state(vp->pdev, vp->power_state);
@@ -2322,6 +2326,27 @@
 	spin_unlock(&vp->lock);
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+
+/*
+ * Polling 'interrupt' - used by netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+
+static void vorboom_poll (struct net_device *dev)
+{
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+
+	disable_irq(dev->irq);
+	if (vp->full_bus_master_tx)
+		boomerang_interrupt(dev->irq, dev, NULL);
+	else
+		vortex_interrupt(dev->irq, dev, NULL);
+	enable_irq(dev->irq);
+}
+#endif
+
 static int vortex_rx(struct net_device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
diff -rNu linux/drivers/net/8139too.c linux-custom/drivers/net/8139too.c
--- linux/drivers/net/8139too.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/8139too.c	2004-09-27 23:55:00.000000000 +0200
@@ -628,6 +628,7 @@
 static void __set_rx_mode (struct net_device *dev);
 static void rtl8139_hw_start (struct net_device *dev);
 static struct ethtool_ops rtl8139_ethtool_ops;
+static void rtl8139_poll (struct net_device *dev);
 
 #ifdef USE_IO_OPS
 
@@ -978,6 +979,9 @@
 	dev->ethtool_ops = &rtl8139_ethtool_ops;
 	dev->tx_timeout = rtl8139_tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = rtl8139_poll;
+#endif
 
 	/* note: the hardware is not capable of sg/csum/highdma, however
 	 * through the use of skb_copy_and_csum_dev we enable these
@@ -2412,6 +2416,22 @@
 	return &tp->stats;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+
+/*
+ * Polling 'interrupt' - used by netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+
+static void rtl8139_poll (struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	rtl8139_interrupt(dev->irq, dev, NULL);
+	enable_irq(dev->irq);
+}
+#endif
+
 /* Set or clear the multicast filter for this adaptor.
    This routine is not state sensitive and need not be SMP locked. */
 
diff -rNu linux/drivers/net/8390.c linux-custom/drivers/net/8390.c
--- linux/drivers/net/8390.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/8390.c	2004-09-27 23:55:00.000000000 +0200
@@ -526,6 +526,22 @@
 	return;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+
+/*
+ * Polling 'interrupt' - used by netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+
+void ei_poll(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	ei_interrupt(dev->irq, dev, NULL);
+	enable_irq(dev->irq);
+}
+#endif
+
 /**
  * ei_tx_err - handle transmitter error
  * @dev: network device which threw the exception
@@ -1158,6 +1174,9 @@
 EXPORT_SYMBOL(ei_open);
 EXPORT_SYMBOL(ei_close);
 EXPORT_SYMBOL(ei_interrupt);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+EXPORT_SYMBOL(ei_poll);
+#endif
 EXPORT_SYMBOL(ei_tx_timeout);
 EXPORT_SYMBOL(ethdev_init);
 EXPORT_SYMBOL(NS8390_init);
diff -rNu linux/drivers/net/8390.h linux-custom/drivers/net/8390.h
--- linux/drivers/net/8390.h	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/8390.h	2004-09-27 23:55:00.000000000 +0200
@@ -45,6 +45,10 @@
 extern unsigned long autoirq_report(int waittime);
 #endif
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+extern void ei_poll(struct net_device *dev);
+#endif
+
 extern int ethdev_init(struct net_device *dev);
 extern void NS8390_init(struct net_device *dev, int startp);
 extern int ei_open(struct net_device *dev);
diff -rNu linux/drivers/net/Config.in linux-custom/drivers/net/Config.in
--- linux/drivers/net/Config.in	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/Config.in	2004-09-27 23:55:00.000000000 +0200
@@ -299,6 +299,8 @@
    dep_tristate '  SysKonnect FDDI PCI support' CONFIG_SKFP $CONFIG_PCI
 fi
 
+dep_tristate 'Network console support' CONFIG_NETCONSOLE
+
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
    if [ "$CONFIG_INET" = "y" ]; then
       bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI
diff -rNu linux/drivers/net/Makefile linux-custom/drivers/net/Makefile
--- linux/drivers/net/Makefile	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/Makefile	2004-09-27 23:55:00.000000000 +0200
@@ -253,6 +253,8 @@
 obj-y		+= ../acorn/net/acorn-net.o
 endif
 
+obj-$(CONFIG_NETCONSOLE) += netconsole.o
+
 #
 # HIPPI adapters
 #
diff -rNu linux/drivers/net/ac3200.c linux-custom/drivers/net/ac3200.c
--- linux/drivers/net/ac3200.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/ac3200.c	2004-09-27 23:55:00.000000000 +0200
@@ -246,6 +246,9 @@
 
 	dev->open = &ac_open;
 	dev->stop = &ac_close_card;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 	return 0;
 out2:
diff -rNu linux/drivers/net/apne.c linux-custom/drivers/net/apne.c
--- linux/drivers/net/apne.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/apne.c	2004-09-27 23:55:00.000000000 +0200
@@ -309,6 +309,9 @@
     ei_status.get_8390_hdr = &apne_get_8390_hdr;
     dev->open = &apne_open;
     dev->stop = &apne_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+    dev->poll_controller = ei_poll;
+#endif
     NS8390_init(dev, 0);
 
     pcmcia_ack_int(pcmcia_get_intreq());		/* ack PCMCIA int req */
diff -rNu linux/drivers/net/e2100.c linux-custom/drivers/net/e2100.c
--- linux/drivers/net/e2100.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/e2100.c	2004-09-27 23:55:00.000000000 +0200
@@ -247,6 +247,9 @@
 	ei_status.get_8390_hdr = &e21_get_8390_hdr;
 	dev->open = &e21_open;
 	dev->stop = &e21_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 
 	return 0;
diff -rNu linux/drivers/net/eepro100.c linux-custom/drivers/net/eepro100.c
--- linux/drivers/net/eepro100.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/eepro100.c	2004-09-27 23:55:00.000000000 +0200
@@ -543,6 +543,7 @@
 static int speedo_rx(struct net_device *dev);
 static void speedo_tx_buffer_gc(struct net_device *dev);
 static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static void speedo_poll (struct net_device *dev);
 static int speedo_close(struct net_device *dev);
 static struct net_device_stats *speedo_get_stats(struct net_device *dev);
 static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
@@ -879,6 +880,9 @@
 	dev->get_stats = &speedo_get_stats;
 	dev->set_multicast_list = &set_rx_mode;
 	dev->do_ioctl = &speedo_ioctl;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = &speedo_poll;
+#endif
 
 	return 0;
 }
@@ -1661,6 +1665,23 @@
 	return;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+
+/*
+ * Polling 'interrupt' - used by netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+
+static void speedo_poll (struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	speedo_interrupt (dev->irq, dev, NULL);
+	enable_irq(dev->irq);
+}
+
+#endif
+
 static inline struct RxFD *speedo_rx_alloc(struct net_device *dev, int entry)
 {
 	struct speedo_private *sp = (struct speedo_private *)dev->priv;
diff -rNu linux/drivers/net/es3210.c linux-custom/drivers/net/es3210.c
--- linux/drivers/net/es3210.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/es3210.c	2004-09-27 23:55:00.000000000 +0200
@@ -269,6 +269,9 @@
 
 	dev->open = &es_open;
 	dev->stop = &es_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 	return 0;
 out1:
diff -rNu linux/drivers/net/hp-plus.c linux-custom/drivers/net/hp-plus.c
--- linux/drivers/net/hp-plus.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/hp-plus.c	2004-09-27 23:55:00.000000000 +0200
@@ -212,6 +212,9 @@
 
 	dev->open = &hpp_open;
 	dev->stop = &hpp_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 
 	ei_status.name = name;
 	ei_status.word16 = 0;		/* Agggghhhhh! Debug time: 2 days! */
diff -rNu linux/drivers/net/hp.c linux-custom/drivers/net/hp.c
--- linux/drivers/net/hp.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/hp.c	2004-09-27 23:55:00.000000000 +0200
@@ -183,6 +183,9 @@
 	dev->base_addr = ioaddr + NIC_OFFSET;
 	dev->open = &hp_open;
 	dev->stop = &hp_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 
 	ei_status.name = name;
 	ei_status.word16 = wordmode;
diff -rNu linux/drivers/net/hydra.c linux-custom/drivers/net/hydra.c
--- linux/drivers/net/hydra.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/hydra.c	2004-09-27 23:55:00.000000000 +0200
@@ -160,6 +160,9 @@
     ei_status.reg_offset = hydra_offsets;
     dev->open = &hydra_open;
     dev->stop = &hydra_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+    dev->poll_controller = ei_poll;
+#endif
 #ifdef MODULE
     ei_status.priv = (unsigned long)root_hydra_dev;
     root_hydra_dev = dev;
diff -rNu linux/drivers/net/lne390.c linux-custom/drivers/net/lne390.c
--- linux/drivers/net/lne390.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/lne390.c	2004-09-27 23:55:00.000000000 +0200
@@ -270,6 +270,9 @@
 
 	dev->open = &lne390_open;
 	dev->stop = &lne390_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 	return 0;
 cleanup:
diff -rNu linux/drivers/net/mac8390.c linux-custom/drivers/net/mac8390.c
--- linux/drivers/net/mac8390.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/mac8390.c	2004-09-27 23:55:00.000000000 +0200
@@ -435,6 +435,9 @@
 	/* Now fill in our stuff */
 	dev->open = &mac8390_open;
 	dev->stop = &mac8390_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 
 	/* GAR, ei_status is actually a macro even though it looks global */
 	ei_status.name = cardname[type];
diff -rNu linux/drivers/net/ne.c linux-custom/drivers/net/ne.c
--- linux/drivers/net/ne.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/ne.c	2004-09-27 23:55:00.000000000 +0200
@@ -461,6 +461,9 @@
 	ei_status.priv = 0;
 	dev->open = &ne_open;
 	dev->stop = &ne_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 	return 0;
 
diff -rNu linux/drivers/net/ne2.c linux-custom/drivers/net/ne2.c
--- linux/drivers/net/ne2.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/ne2.c	2004-09-27 23:55:00.000000000 +0200
@@ -486,6 +486,9 @@
 	
 	dev->open = &ne_open;
 	dev->stop = &ne_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 	return 0;
 out:
diff -rNu linux/drivers/net/ne2k-pci.c linux-custom/drivers/net/ne2k-pci.c
--- linux/drivers/net/ne2k-pci.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/ne2k-pci.c	2004-09-27 23:55:00.000000000 +0200
@@ -359,6 +359,10 @@
 	dev->open = &ne2k_pci_open;
 	dev->stop = &ne2k_pci_close;
 	dev->ethtool_ops = &ne2k_pci_ethtool_ops;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
+
 	NS8390_init(dev, 0);
 
 	i = register_netdev(dev);
diff -rNu linux/drivers/net/ne3210.c linux-custom/drivers/net/ne3210.c
--- linux/drivers/net/ne3210.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/ne3210.c	2004-09-27 23:55:00.000000000 +0200
@@ -257,6 +257,9 @@
 
 	dev->open = &ne3210_open;
 	dev->stop = &ne3210_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 	return 0;
 out2:
diff -rNu linux/drivers/net/netconsole.c linux-custom/drivers/net/netconsole.c
--- linux/drivers/net/netconsole.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-custom/drivers/net/netconsole.c	2004-09-27 23:56:29.000000000 +0200
@@ -0,0 +1,489 @@
+/*
+ *  linux/drivers/net/netconsole.c
+ *
+ *  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.
+ * 2004-01-17	updated for recent kernel 2.4.24 and
+ *		synced with Matt Mackall's options by
+ *		Herbert Pötzl <herbert@13thfloor.at>
+ *
+ */
+
+/****************************************************************
+ * 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/module.h>
+#include <asm/unaligned.h>
+#include <linux/console.h>
+#include <linux/smp_lock.h>
+#include <linux/netdevice.h>
+#include <linux/tty_driver.h>
+#include <linux/etherdevice.h>
+#include <linux/string.h>
+#include <linux/inetdevice.h>
+#include <linux/inet.h>
+
+static char *netconsole[2]={"",""};
+
+MODULE_AUTHOR("Maintainer: Herbert Pötzl <herbert@13thfloor.at>");
+MODULE_DESCRIPTION("Network console driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(netconsole, "2s");
+MODULE_PARM_DESC(netconsole, " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr]\n");
+
+struct netpoll {
+	struct net_device *dev;
+	char dev_name[16], *name;
+	u32 local_ip, remote_ip;
+	u16 local_port, remote_port;
+	unsigned char local_mac[6];
+	unsigned char remote_mac[6];
+};
+
+static struct netpoll np = {
+	.name =	"netconsole",
+	.dev_name = "eth0",
+	.local_port = 6665,
+	.remote_port = 6666,
+	.remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+};
+
+
+#define MAX_PRINT_CHUNK 1460
+
+/*
+ * 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 32
+
+static spinlock_t netconsole_lock = SPIN_LOCK_UNLOCKED;
+static int nr_netconsole_skbs;
+static struct sk_buff *netconsole_skbs;
+
+#define MAX_SKB_SIZE \
+		(MAX_PRINT_CHUNK + sizeof(struct udphdr) + \
+				sizeof(struct iphdr) + sizeof(struct ethhdr))
+
+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 void send_netconsole_skb(struct net_device *dev,
+	const char *msg, unsigned int msg_len)
+{
+	int total_len, eth_len, ip_len, udp_len;
+	int status, retries;
+	struct sk_buff *skb;
+	struct udphdr *udph;
+	struct iphdr *iph;
+	struct ethhdr *eth;
+
+	if (!netif_running(dev))
+		return;
+
+	udp_len = msg_len + sizeof(*udph);
+	ip_len = eth_len = udp_len + sizeof(*iph);
+	total_len = eth_len + ETH_HLEN;
+
+	if (nr_netconsole_skbs < MAX_NETCONSOLE_SKBS)
+		__refill_netconsole_skbs();
+
+	skb = alloc_skb(total_len, GFP_ATOMIC);
+	if (!skb) {
+		skb = get_netconsole_skb();
+		if (!skb)
+			/* tough! */
+			return;
+	}
+
+	atomic_set(&skb->users, 1);
+	skb_reserve(skb, total_len - msg_len);
+
+	memcpy(skb->data, msg, msg_len);
+	skb->len += msg_len;
+
+	udph = (struct udphdr *) skb_push(skb, sizeof(*udph));
+	udph->source = htons(np.local_port);
+	udph->dest = htons(np.remote_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    = htonl(np.local_ip);
+	iph->daddr    = htonl(np.remote_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, np.local_mac, 6);
+	memcpy(eth->h_dest, np.remote_mac, 6);
+
+	retries=1000000;
+repeat:
+	if(!--retries) {
+		__kfree_skb(skb);
+		return;
+	}
+
+	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);
+
+		dev->poll_controller(dev);
+		goto repeat;
+	}
+
+	status = dev->hard_start_xmit(skb, dev);
+
+	dev->xmit_lock_owner = -1;
+	spin_unlock(&dev->xmit_lock);
+
+	if (status)
+		goto repeat;
+}
+
+static void write_netconsole_msg(struct console *con, const char *msg, unsigned int msg_len)
+{
+	int len, left;
+	struct net_device *dev;
+
+	dev = np.dev;
+	if (!dev)
+		return;
+
+	if (dev->poll_controller && netif_running(dev)) {
+		unsigned long flags;
+
+		__save_flags(flags);
+		__cli();
+		left = msg_len;
+repeat:
+		if (left > MAX_PRINT_CHUNK)
+			len = MAX_PRINT_CHUNK;
+		else
+			len = left;
+		send_netconsole_skb(dev, msg, len);
+		msg += len;
+		left -= len;
+		if (left)
+			goto repeat;
+		__restore_flags(flags);
+	}
+}
+
+
+
+# define strlcpy(dest,src,len) strncpy(dest,src,(len)-1)
+
+static int netpoll_parse_options(struct netpoll *np)
+{
+	char *cur=netconsole[0], *delim;
+
+	if(!netconsole[0] || !netconsole[1])
+		goto parse_failed;
+
+	if(*cur != '@') {
+		if ((delim = strchr(cur, '@')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np->local_port=simple_strtol(cur, 0, 10);
+		cur=delim;
+	}
+	cur++;
+	printk(KERN_INFO "%s: local port %d\n", np->name, np->local_port);
+
+	if(*cur != '/') {
+		if ((delim = strchr(cur, '/')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np->local_ip=ntohl(in_aton(cur));
+		cur=delim;
+
+		printk(KERN_INFO "%s: local IP %d.%d.%d.%d\n",
+		       np->name, HIPQUAD(np->local_ip));
+	}
+	cur++;
+
+	if (*cur)
+		strlcpy(np->dev_name, cur, sizeof(np->dev_name));
+
+	printk(KERN_INFO "%s: interface %s\n", np->name, np->dev_name);
+
+	cur=netconsole[1];
+
+	if ( *cur != '@' ) {
+		/* dst port */
+		if ((delim = strchr(cur, '@')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np->remote_port=simple_strtol(cur, 0, 10);
+		cur=delim;
+	}
+	cur++;
+	printk(KERN_INFO "%s: remote port %d\n", np->name, np->remote_port);
+
+	/* dst ip */
+	if ((delim = strchr(cur, '/')) == NULL)
+		goto parse_failed;
+	*delim=0;
+	np->remote_ip=ntohl(in_aton(cur));
+	cur=delim+1;
+
+	printk(KERN_INFO "%s: remote IP %d.%d.%d.%d\n",
+		       np->name, HIPQUAD(np->remote_ip));
+
+	if( *cur != 0 )
+	{
+		/* MAC address */
+		if ((delim = strchr(cur, ':')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np->remote_mac[0]=simple_strtol(cur, 0, 16);
+		cur=delim+1;
+		if ((delim = strchr(cur, ':')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np->remote_mac[1]=simple_strtol(cur, 0, 16);
+		cur=delim+1;
+		if ((delim = strchr(cur, ':')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np->remote_mac[2]=simple_strtol(cur, 0, 16);
+		cur=delim+1;
+		if ((delim = strchr(cur, ':')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np->remote_mac[3]=simple_strtol(cur, 0, 16);
+		cur=delim+1;
+		if ((delim = strchr(cur, ':')) == NULL)
+			goto parse_failed;
+		*delim=0;
+		np->remote_mac[4]=simple_strtol(cur, 0, 16);
+		cur=delim+1;
+		np->remote_mac[5]=simple_strtol(cur, 0, 16);
+	}
+
+	printk(KERN_INFO "%s: remote ethernet address "
+	       "%02x:%02x:%02x:%02x:%02x:%02x\n",
+	       np->name,
+	       np->remote_mac[0],
+	       np->remote_mac[1],
+	       np->remote_mac[2],
+	       np->remote_mac[3],
+	       np->remote_mac[4],
+	       np->remote_mac[5]);
+
+	return 0;
+
+parse_failed:
+	printk(KERN_INFO "%s: couldn't parse config at %s!\n",
+	       np->name, cur);
+	return -1;
+}
+
+static int netpoll_setup(struct netpoll *np)
+{
+	struct net_device *ndev = NULL;
+	struct in_device *in_dev;
+
+	if (np->dev_name)
+		ndev = dev_get_by_name(np->dev_name);
+	if (!ndev) {
+		printk(KERN_ERR "%s: %s doesn't exist, aborting.\n",
+		       np->name, np->dev_name);
+		goto release;
+	}
+	if (!ndev->poll_controller) {
+		printk(KERN_ERR "%s: %s doesn't support polling, aborting.\n",
+		       np->name, np->dev_name);
+		goto release;
+	}
+
+	if (!(ndev->flags & IFF_UP)) {
+		unsigned short oflags;
+		unsigned long jiff;
+
+		printk(KERN_INFO "%s: device %s not up yet, forcing it\n",
+		       np->name, np->dev_name);
+
+		oflags = ndev->flags;
+
+		rtnl_shlock();
+		if (dev_change_flags(ndev, oflags | IFF_UP) < 0) {
+			printk(KERN_ERR "%s: failed to open %s\n",
+			       np->name, np->dev_name);
+			rtnl_shunlock();
+			goto release;
+		}
+		rtnl_shunlock();
+
+		jiff = jiffies + 6*HZ;
+		while(!netif_carrier_ok(ndev)) {
+			if (!time_before(jiffies, jiff)) {
+				printk(KERN_NOTICE
+				       "%s: timeout waiting for carrier\n",
+				       np->name);
+				break;
+			}
+			cond_resched();
+		}
+
+	}
+
+	if (!memcmp(np->local_mac, "\0\0\0\0\0\0", 6) && ndev->dev_addr)
+		memcpy(np->local_mac, ndev->dev_addr, 6);
+
+	if (!np->local_ip) {
+		in_dev = in_dev_get(ndev);
+
+		if (!in_dev) {
+			printk(KERN_ERR "%s: no IP address for %s, aborting\n",
+			       np->name, np->dev_name);
+			goto release;
+		}
+
+		np->local_ip = ntohl(in_dev->ifa_list->ifa_local);
+		in_dev_put(in_dev);
+		printk(KERN_INFO "%s: local IP %d.%d.%d.%d\n",
+		       np->name, HIPQUAD(np->local_ip));
+	}
+
+	np->dev = ndev;
+	return 0;
+release:
+	dev_put(ndev);
+	return -1;
+}
+
+static void netpoll_cleanup(struct netpoll *np)
+{
+	dev_put(np->dev);
+	np->dev = 0;
+}
+
+
+static struct console net_console = {
+	flags: CON_ENABLED,
+	write: write_netconsole_msg
+};
+
+
+static int __init option_setup(char *opt)
+{
+	char *delim;
+
+	netconsole[0]=opt;
+	delim=strchr(opt, ',');
+	if(delim)
+		*delim++=0;
+	netconsole[1]=delim;
+	return netpoll_parse_options(&np);
+}
+
+__setup("netconsole=", option_setup);
+
+
+static int __init init_netconsole(void)
+{
+#ifdef MODULE
+	if(netpoll_parse_options(&np))
+		return 1;
+#endif
+	if(!np.remote_ip || netpoll_setup(&np))
+		return 1;
+
+#define STARTUP_MSG "[...network console startup...]\n"
+	write_netconsole_msg(NULL, STARTUP_MSG, strlen(STARTUP_MSG));
+	register_console(&net_console);
+	printk(KERN_INFO "netconsole: network logging started\n");
+	return 0;
+}
+
+static void __exit cleanup_netconsole(void)
+{
+	printk(KERN_INFO "netconsole: network logging shut down.\n");
+	unregister_console(&net_console);
+
+#define SHUTDOWN_MSG "[...network console shutdown...]\n"
+	write_netconsole_msg(NULL, SHUTDOWN_MSG, strlen(SHUTDOWN_MSG));
+	netpoll_cleanup(&np);
+}
+
+module_init(init_netconsole);
+module_exit(cleanup_netconsole);
diff -rNu linux/drivers/net/oaknet.c linux-custom/drivers/net/oaknet.c
--- linux/drivers/net/oaknet.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/oaknet.c	2004-09-27 23:55:00.000000000 +0200
@@ -213,6 +213,9 @@
 
 	dev->open = oaknet_open;
 	dev->stop = oaknet_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 
 	NS8390_init(dev, FALSE);
 
diff -rNu linux/drivers/net/smc-mca.c linux-custom/drivers/net/smc-mca.c
--- linux/drivers/net/smc-mca.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/smc-mca.c	2004-09-27 23:55:00.000000000 +0200
@@ -315,6 +315,9 @@
 
 	dev->open = &ultramca_open;
 	dev->stop = &ultramca_close_card;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 
 	return 0;
diff -rNu linux/drivers/net/smc-ultra.c linux-custom/drivers/net/smc-ultra.c
--- linux/drivers/net/smc-ultra.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/smc-ultra.c	2004-09-27 23:55:00.000000000 +0200
@@ -271,6 +271,9 @@
 	ei_status.reset_8390 = &ultra_reset_8390;
 	dev->open = &ultra_open;
 	dev->stop = &ultra_close_card;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 
 	return 0;
diff -rNu linux/drivers/net/smc-ultra32.c linux-custom/drivers/net/smc-ultra32.c
--- linux/drivers/net/smc-ultra32.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/smc-ultra32.c	2004-09-27 23:55:00.000000000 +0200
@@ -242,6 +242,9 @@
 	ei_status.reset_8390 = &ultra32_reset_8390;
 	dev->open = &ultra32_open;
 	dev->stop = &ultra32_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 
 	return 0;
diff -rNu linux/drivers/net/stnic.c linux-custom/drivers/net/stnic.c
--- linux/drivers/net/stnic.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/stnic.c	2004-09-27 23:55:00.000000000 +0200
@@ -132,6 +132,9 @@
   dev->irq = IRQ_STNIC;
   dev->open = &stnic_open;
   dev->stop = &stnic_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+  dev->poll_controller = ei_poll;
+#endif
 
   /* Snarf the interrupt now.  There's no point in waiting since we cannot
      share and the board will usually be enabled. */
diff -rNu linux/drivers/net/tg3.c linux-custom/drivers/net/tg3.c
--- linux/drivers/net/tg3.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/tg3.c	2004-09-27 23:55:00.000000000 +0200
@@ -2716,7 +2716,9 @@
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void tg3_poll_controller(struct net_device *dev)
 {
+	disable_irq(dev->irq);
 	tg3_interrupt(dev->irq, dev, NULL);
+	enable_irq(dev->irq);
 }
 #endif
 
diff -rNu linux/drivers/net/tlan.c linux-custom/drivers/net/tlan.c
--- linux/drivers/net/tlan.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/tlan.c	2004-09-27 23:55:00.000000000 +0200
@@ -296,6 +296,7 @@
 static int      TLan_probe1( struct pci_dev *pdev, long ioaddr, int irq, int rev, const struct pci_device_id *ent);
 static void	TLan_tx_timeout( struct net_device *dev);
 static int 	tlan_init_one( struct pci_dev *pdev, const struct pci_device_id *ent);
+static void	TLan_poll(struct net_device *dev);
 
 static u32	TLan_HandleInvalid( struct net_device *, u16 );
 static u32	TLan_HandleTxEOF( struct net_device *, u16 );
@@ -452,6 +453,23 @@
 	pci_set_drvdata( pdev, NULL );
 } 
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+
+/*
+ * Polling 'interrupt' - used by netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+
+static void TLan_poll (struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	TLan_HandleInterrupt(dev->irq, dev, NULL);
+	enable_irq(dev->irq);
+}
+
+#endif
+
 static struct pci_driver tlan_driver = {
 	.name		= "tlan",
 	.id_table	= tlan_pci_tbl,
@@ -893,6 +911,9 @@
 	dev->do_ioctl = &TLan_ioctl;
 	dev->tx_timeout = &TLan_tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = &TLan_poll;
+#endif
 
 	return 0;
 
diff -rNu linux/drivers/net/tulip/tulip_core.c linux-custom/drivers/net/tulip/tulip_core.c
--- linux/drivers/net/tulip/tulip_core.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/tulip/tulip_core.c	2004-09-27 23:55:00.000000000 +0200
@@ -267,6 +267,7 @@
 static struct net_device_stats *tulip_get_stats(struct net_device *dev);
 static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static void set_rx_mode(struct net_device *dev);
+static void tulip_poll(struct net_device *dev);
 
 
 
@@ -1729,6 +1730,9 @@
 	dev->get_stats = tulip_get_stats;
 	dev->do_ioctl = private_ioctl;
 	dev->set_multicast_list = set_rx_mode;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = &tulip_poll;
+#endif
 
 	if (register_netdev(dev))
 		goto err_out_free_ring;
@@ -1902,6 +1906,21 @@
 	/* pci_power_off (pdev, -1); */
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+
+/*
+ * Polling 'interrupt' - used by netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+
+static void tulip_poll (struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	tulip_interrupt (dev->irq, dev, NULL);
+	enable_irq(dev->irq);
+}
+#endif
 
 static struct pci_driver tulip_driver = {
 	name:		DRV_NAME,
diff -rNu linux/drivers/net/wd.c linux-custom/drivers/net/wd.c
--- linux/drivers/net/wd.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/wd.c	2004-09-27 23:55:00.000000000 +0200
@@ -301,6 +301,9 @@
 	ei_status.get_8390_hdr = &wd_get_8390_hdr;
 	dev->open = &wd_open;
 	dev->stop = &wd_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = ei_poll;
+#endif
 	NS8390_init(dev, 0);
 
 #if 1
diff -rNu linux/drivers/net/zorro8390.c linux-custom/drivers/net/zorro8390.c
--- linux/drivers/net/zorro8390.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/drivers/net/zorro8390.c	2004-09-27 23:55:00.000000000 +0200
@@ -229,6 +229,9 @@
     ei_status.reg_offset = zorro8390_offsets;
     dev->open = &zorro8390_open;
     dev->stop = &zorro8390_close;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+    dev->poll_controller = ei_poll;
+#endif
 #ifdef MODULE
     ei_status.priv = (unsigned long)root_zorro8390_dev;
     root_zorro8390_dev = dev;
diff -rNu linux/include/linux/netdevice.h linux-custom/include/linux/netdevice.h
--- linux/include/linux/netdevice.h	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/include/linux/netdevice.h	2004-09-27 23:55:00.000000000 +0200
@@ -436,6 +436,10 @@
 						     unsigned char *haddr);
 	int			(*neigh_setup)(struct net_device *dev, struct neigh_parms *);
 	int			(*accept_fastpath)(struct net_device *, struct dst_entry*);
+#if defined(CONFIG_NETCONSOLE) || defined(CONFIG_NETCONSOLE_MODULE)
+#define CONFIG_NET_POLL_CONTROLLER
+	void			(*poll_controller)(struct net_device *dev);
+#endif
 
 	/* open/release and usage marking */
 	struct module *owner;
diff -rNu linux/net/netsyms.c linux-custom/net/netsyms.c
--- linux/net/netsyms.c	2004-09-27 23:56:03.000000000 +0200
+++ linux-custom/net/netsyms.c	2004-09-27 23:55:00.000000000 +0200
@@ -223,9 +223,13 @@
 EXPORT_SYMBOL(destroy_EII_client);
 #endif
 
+/* for 801q VLAN support and netconsole */
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) || defined(CONFIG_NETCONSOLE) || defined(CONFIG_NETCONSOLE_MODULE)
+EXPORT_SYMBOL(dev_change_flags);
+#endif
+
 /* for 801q VLAN support */
 #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
-EXPORT_SYMBOL(dev_change_flags);
 EXPORT_SYMBOL(vlan_ioctl_hook);
 #endif
 
