diff -upN reference/arch/i386/Kconfig current/arch/i386/Kconfig
--- reference/arch/i386/Kconfig	2004-04-29 10:39:06.000000000 -0700
+++ current/arch/i386/Kconfig	2004-04-29 10:39:07.000000000 -0700
@@ -1497,6 +1497,9 @@ config MAGIC_SYSRQ
 	depends on KGDB_SYSRQ
 	default y
 
+config 4KSTACKS
+	def_bool y
+
 config X86_FIND_SMP_CONFIG
 	bool
 	depends on X86_LOCAL_APIC || X86_VOYAGER
diff -upN reference/arch/i386/Makefile current/arch/i386/Makefile
--- reference/arch/i386/Makefile	2004-04-29 10:39:02.000000000 -0700
+++ current/arch/i386/Makefile	2004-04-29 10:39:07.000000000 -0700
@@ -56,9 +56,9 @@ cflags-$(CONFIG_X86_ELAN)	+= -march=i486
 GCC_VERSION			:= $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CC))
 cflags-$(CONFIG_REGPARM) 	+= $(shell if [ $(GCC_VERSION) -ge 0300 ] ; then echo "-mregparm=3"; fi ;)
 
-# Enable unit-at-a-time mode when possible. It shrinks the
-# kernel considerably.
-CFLAGS += $(call check_gcc,-funit-at-a-time,)
+# Disable unit-at-a-time mode, it makes gcc use a lot more stack
+# due to the lack of sharing of stacklots.
+CFLAGS += $(call check_gcc,-fno-unit-at-a-time,)
 
 CFLAGS += $(cflags-y)
 
diff -upN reference/arch/i386/kernel/i8259.c current/arch/i386/kernel/i8259.c
--- reference/arch/i386/kernel/i8259.c	2004-03-11 14:33:36.000000000 -0800
+++ current/arch/i386/kernel/i8259.c	2004-04-29 10:39:07.000000000 -0700
@@ -444,4 +444,7 @@ void __init init_IRQ(void)
 	 */
 	if (boot_cpu_data.hard_math && !cpu_has_fpu)
 		setup_irq(FPU_IRQ, &fpu_irq);
+
+	current_thread_info()->cpu = 0;
+	irq_ctx_init(0);
 }
diff -upN reference/arch/i386/kernel/irq.c current/arch/i386/kernel/irq.c
--- reference/arch/i386/kernel/irq.c	2004-04-29 10:39:04.000000000 -0700
+++ current/arch/i386/kernel/irq.c	2004-04-29 10:39:07.000000000 -0700
@@ -75,6 +75,14 @@ irq_desc_t irq_desc[NR_IRQS] __cacheline
 static void register_irq_proc (unsigned int irq);
 
 /*
+ * per-CPU IRQ handling stacks
+ */
+#ifdef CONFIG_4KSTACKS
+union irq_ctx *hardirq_ctx[NR_CPUS];
+union irq_ctx *softirq_ctx[NR_CPUS];
+#endif
+
+/*
  * Special irq handlers.
  */
 
@@ -213,7 +221,7 @@ inline void synchronize_irq(unsigned int
  * waste of time and is not what some drivers would
  * prefer.
  */
-int handle_IRQ_event(unsigned int irq,
+asmlinkage int handle_IRQ_event(unsigned int irq,
 		struct pt_regs *regs, struct irqaction *action)
 {
 	int status = 1;	/* Force the "do bottom halves" bit */
@@ -436,7 +444,7 @@ asmlinkage unsigned int do_IRQ(struct pt
 
 		__asm__ __volatile__("andl %%esp,%0" :
 					"=r" (esp) : "0" (THREAD_SIZE - 1));
-		if (unlikely(esp < (sizeof(struct thread_info) + 1024))) {
+		if (unlikely(esp < (sizeof(struct thread_info) + STACK_WARN))) {
 			printk("do_IRQ: stack overflow: %ld\n",
 				esp - sizeof(struct thread_info));
 			dump_stack();
@@ -484,11 +492,68 @@ asmlinkage unsigned int do_IRQ(struct pt
 	 * useful for irq hardware that does not mask cleanly in an
 	 * SMP environment.
 	 */
+#ifdef CONFIG_4KSTACKS
+
+	for (;;) {
+		irqreturn_t action_ret;
+		u32 *isp;
+		union irq_ctx * curctx;
+		union irq_ctx * irqctx;
+
+		curctx = (union irq_ctx *) current_thread_info();
+		irqctx = hardirq_ctx[smp_processor_id()];
+
+		spin_unlock(&desc->lock);
+
+		/*
+		 * this is where we switch to the IRQ stack. However, if we are already using
+		 * the IRQ stack (because we interrupted a hardirq handler) we can't do that
+		 * and just have to keep using the current stack (which is the irq stack already
+		 * after all)
+		 */
+
+		if (curctx == irqctx)
+			action_ret = handle_IRQ_event(irq, &regs, action);
+		else {
+			/* build the stack frame on the IRQ stack */
+			isp = (u32*) ((char*)irqctx + sizeof(*irqctx));
+			irqctx->tinfo.task = curctx->tinfo.task;
+			irqctx->tinfo.previous_esp = current_stack_pointer();
+
+			*--isp = (u32) action;
+			*--isp = (u32) &regs;
+			*--isp = (u32) irq;
+
+			asm volatile(
+				"       xchgl   %%ebx,%%esp     \n"
+				"       call    handle_IRQ_event \n"
+				"       xchgl   %%ebx,%%esp     \n"
+				: "=a"(action_ret)
+				: "b"(isp)
+				: "memory", "cc", "edx", "ecx"
+			);
+
+
+		}
+		spin_lock(&desc->lock);
+		if (!noirqdebug)
+			note_interrupt(irq, desc, action_ret);
+		if (curctx != irqctx)
+			irqctx->tinfo.task = NULL;
+		if (likely(!(desc->status & IRQ_PENDING)))
+			break;
+		desc->status &= ~IRQ_PENDING;
+	}
+
+#else
+
 	for (;;) {
 		irqreturn_t action_ret;
 
 		spin_unlock(&desc->lock);
+
 		action_ret = handle_IRQ_event(irq, &regs, action);
+
 		spin_lock(&desc->lock);
 		if (!noirqdebug)
 			note_interrupt(irq, desc, action_ret);
@@ -496,6 +561,7 @@ asmlinkage unsigned int do_IRQ(struct pt
 			break;
 		desc->status &= ~IRQ_PENDING;
 	}
+#endif
 	desc->status &= ~IRQ_INPROGRESS;
 
 out:
@@ -1055,3 +1121,79 @@ void init_irq_proc (void)
 		register_irq_proc(i);
 }
 
+
+#ifdef CONFIG_4KSTACKS
+static char softirq_stack[NR_CPUS * THREAD_SIZE]  __attribute__((__aligned__(THREAD_SIZE)));
+static char hardirq_stack[NR_CPUS * THREAD_SIZE]  __attribute__((__aligned__(THREAD_SIZE)));
+
+/*
+ * allocate per-cpu stacks for hardirq and for softirq processing
+ */
+void irq_ctx_init(int cpu)
+{
+	union irq_ctx *irqctx;
+
+	if (hardirq_ctx[cpu])
+		return;
+
+	irqctx = (union irq_ctx*) &hardirq_stack[cpu*THREAD_SIZE];
+	irqctx->tinfo.task              = NULL;
+	irqctx->tinfo.exec_domain       = NULL;
+	irqctx->tinfo.cpu               = cpu;
+	irqctx->tinfo.preempt_count     = HARDIRQ_OFFSET;
+	irqctx->tinfo.addr_limit        = MAKE_MM_SEG(0);
+
+	hardirq_ctx[cpu] = irqctx;
+
+	irqctx = (union irq_ctx*) &softirq_stack[cpu*THREAD_SIZE];
+	irqctx->tinfo.task              = NULL;
+	irqctx->tinfo.exec_domain       = NULL;
+	irqctx->tinfo.cpu               = cpu;
+	irqctx->tinfo.preempt_count     = SOFTIRQ_OFFSET;
+	irqctx->tinfo.addr_limit        = MAKE_MM_SEG(0);
+
+	softirq_ctx[cpu] = irqctx;
+
+	printk("CPU %u irqstacks, hard=%p soft=%p\n",
+		cpu,hardirq_ctx[cpu],softirq_ctx[cpu]);
+}
+
+extern asmlinkage void __do_softirq(void);
+
+asmlinkage void do_softirq(void)
+{
+	unsigned long flags;
+	struct thread_info *curctx;
+	union irq_ctx *irqctx;
+	u32 *isp;
+
+	if (in_interrupt())
+		return;
+
+	local_irq_save(flags);
+
+	if (local_softirq_pending()) {
+		curctx = current_thread_info();
+		irqctx = softirq_ctx[smp_processor_id()];
+		irqctx->tinfo.task = curctx->task;
+		irqctx->tinfo.previous_esp = current_stack_pointer();
+
+		/* build the stack frame on the softirq stack */
+		isp = (u32*) ((char*)irqctx + sizeof(*irqctx));
+
+
+		asm volatile(
+			"       xchgl   %%ebx,%%esp     \n"
+			"       call    __do_softirq    \n"
+			"       movl    %%ebx,%%esp     \n"
+			: "=b"(isp)
+			: "0"(isp)
+			: "memory", "cc", "edx", "ecx", "eax"
+		);
+	}
+
+	local_irq_restore(flags);
+}
+
+EXPORT_SYMBOL(do_softirq);
+#endif
diff -upN reference/arch/i386/kernel/smpboot.c current/arch/i386/kernel/smpboot.c
--- reference/arch/i386/kernel/smpboot.c	2004-04-29 10:39:06.000000000 -0700
+++ current/arch/i386/kernel/smpboot.c	2004-04-29 10:39:07.000000000 -0700
@@ -816,6 +816,8 @@ static int __init do_boot_cpu(int apicid
 	/* Stack for startup_32 can be just as for start_secondary onwards */
 	stack_start.esp = (void *) idle->thread.esp;
 
+	irq_ctx_init(cpu);
+
 	/*
 	 * This grunge runs the startup process for
 	 * the targeted processor.
diff -upN reference/arch/i386/kernel/traps.c current/arch/i386/kernel/traps.c
--- reference/arch/i386/kernel/traps.c	2004-04-29 10:39:06.000000000 -0700
+++ current/arch/i386/kernel/traps.c	2004-04-29 10:39:07.000000000 -0700
@@ -141,12 +141,20 @@ void show_trace(struct task_struct *task
 #ifdef CONFIG_KALLSYMS
 	printk("\n");
 #endif
-	while (!kstack_end(stack)) {
-		addr = *stack++;
-		if (kernel_text_address(addr)) {
-			printk(" [<%08lx>] ", addr);
-			print_symbol("%s\n", addr);
+	while (1) {
+		struct thread_info *context;
+		context = (struct thread_info*) ((unsigned long)stack & (~(THREAD_SIZE - 1)));
+		while (!kstack_end(stack)) {
+			addr = *stack++;
+			if (kernel_text_address(addr)) {
+				printk(" [<%08lx>] ", addr);
+				print_symbol("%s\n", addr);
+			}
 		}
+		stack = (unsigned long*)context->previous_esp;
+		if (!stack)
+			break;
+		printk(" =======================\n");
 	}
 	printk("\n");
 }
diff -upN reference/include/asm-alpha/irq.h current/include/asm-alpha/irq.h
--- reference/include/asm-alpha/irq.h	2003-10-01 11:35:30.000000000 -0700
+++ current/include/asm-alpha/irq.h	2004-04-29 10:39:07.000000000 -0700
@@ -93,5 +93,8 @@ extern void enable_irq(unsigned int);
 struct pt_regs;
 extern void (*perf_irq)(unsigned long, struct pt_regs *);
 
+struct irqaction;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 
 #endif /* _ALPHA_IRQ_H */
diff -upN reference/include/asm-arm/irq.h current/include/asm-arm/irq.h
--- reference/include/asm-arm/irq.h	2003-06-05 14:39:14.000000000 -0700
+++ current/include/asm-arm/irq.h	2004-04-29 10:39:07.000000000 -0700
@@ -44,5 +44,9 @@ void disable_irq_wake(unsigned int irq);
 void enable_irq_wake(unsigned int irq);
 int setup_irq(unsigned int, struct irqaction *);
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif
 
diff -upN reference/include/asm-arm26/irq.h current/include/asm-arm26/irq.h
--- reference/include/asm-arm26/irq.h	2003-06-19 14:41:50.000000000 -0700
+++ current/include/asm-arm26/irq.h	2004-04-29 10:39:07.000000000 -0700
@@ -45,6 +45,8 @@ extern void enable_irq(unsigned int);
 int set_irq_type(unsigned int irq, unsigned int type);
 
 int setup_irq(unsigned int, struct irqaction *);
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
 
 #endif
 
diff -upN reference/include/asm-cris/irq.h current/include/asm-cris/irq.h
--- reference/include/asm-cris/irq.h	2003-07-28 15:31:10.000000000 -0700
+++ current/include/asm-cris/irq.h	2004-04-29 10:39:07.000000000 -0700
@@ -14,6 +14,10 @@ extern void enable_irq(unsigned int);
 #define disable_irq_nosync      disable_irq
 #define enable_irq_nosync       enable_irq
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif  /* _ASM_IRQ_H */
 
 
diff -upN reference/include/asm-h8300/irq.h current/include/asm-h8300/irq.h
--- reference/include/asm-h8300/irq.h	2004-04-07 14:54:32.000000000 -0700
+++ current/include/asm-h8300/irq.h	2004-04-29 10:39:07.000000000 -0700
@@ -68,4 +68,8 @@ extern void disable_irq(unsigned int);
 #define enable_irq_nosync(x)	enable_irq(x)
 #define disable_irq_nosync(x)	disable_irq(x)
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif /* _H8300_IRQ_H_ */
diff -upN reference/include/asm-i386/irq.h current/include/asm-i386/irq.h
--- reference/include/asm-i386/irq.h	2004-01-15 10:41:17.000000000 -0800
+++ current/include/asm-i386/irq.h	2004-04-29 10:39:07.000000000 -0700
@@ -14,6 +14,7 @@
 #include <linux/sched.h>
 /* include comes from machine specific directory */
 #include "irq_vectors.h"
+#include <asm/thread_info.h>
 
 static __inline__ int irq_canonicalize(int irq)
 {
@@ -30,4 +31,28 @@ extern int can_request_irq(unsigned int,
 #define ARCH_HAS_NMI_WATCHDOG		/* See include/linux/nmi.h */
 #endif
 
+#ifdef CONFIG_4KSTACKS
+/*
+ * per-CPU IRQ handling contexts (thread information and stack)
+ */
+union irq_ctx {
+	struct thread_info      tinfo;
+	u32                     stack[THREAD_SIZE/sizeof(u32)];
+};
+
+extern union irq_ctx *hardirq_ctx[NR_CPUS];
+extern union irq_ctx *softirq_ctx[NR_CPUS];
+
+extern void irq_ctx_init(int cpu);
+
+#define __ARCH_HAS_DO_SOFTIRQ
+#else
+#define irq_ctx_init(cpu) do { ; } while (0)
+#endif
+
+struct irqaction;
+struct pt_regs;
+asmlinkage int handle_IRQ_event(unsigned int, struct pt_regs *,
+				struct irqaction *);
+
 #endif /* _ASM_IRQ_H */
diff -upN reference/include/asm-i386/module.h current/include/asm-i386/module.h
--- reference/include/asm-i386/module.h	2004-04-07 14:54:32.000000000 -0700
+++ current/include/asm-i386/module.h	2004-04-29 10:39:07.000000000 -0700
@@ -60,6 +60,12 @@ struct mod_arch_specific
 #define MODULE_REGPARM ""
 #endif
 
-#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY MODULE_REGPARM
+#ifdef CONFIG_4KSTACKS
+#define MODULE_STACKSIZE "4KSTACKS "
+#else
+#define MODULE_STACKSIZE ""
+#endif
+
+#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY MODULE_REGPARM MODULE_STACKSIZE
 
 #endif /* _ASM_I386_MODULE_H */
diff -upN reference/include/asm-i386/thread_info.h current/include/asm-i386/thread_info.h
--- reference/include/asm-i386/thread_info.h	2004-03-11 14:35:15.000000000 -0800
+++ current/include/asm-i386/thread_info.h	2004-04-29 10:39:07.000000000 -0700
@@ -9,6 +9,9 @@
 
 #ifdef __KERNEL__
 
+#include <linux/config.h>
+#include <asm/page.h>
+
 #ifndef __ASSEMBLY__
 #include <asm/processor.h>
 #endif
@@ -29,12 +32,16 @@ struct thread_info {
 	__u32			cpu;		/* current CPU */
 	__s32			preempt_count; /* 0 => preemptable, <0 => BUG */
 
+
 	mm_segment_t		addr_limit;	/* thread address space:
 					 	   0-0xBFFFFFFF for user-thead
 						   0-0xFFFFFFFF for kernel-thread
 						*/
 	struct restart_block    restart_block;
 
+	unsigned long           previous_esp;   /* ESP of the previous stack in case
+						   of nested (IRQ) stacks
+						*/
 	__u8			supervisor_stack[0];
 };
 
@@ -53,7 +60,13 @@ struct thread_info {
 #endif
 
 #define PREEMPT_ACTIVE		0x4000000
+#ifdef CONFIG_4KSTACKS
+#define THREAD_SIZE            (4096)
+#else
+#define THREAD_SIZE		(8192)
+#endif
 
+#define STACK_WARN             (THREAD_SIZE/8)
 /*
  * macros/functions for gaining access to the thread information structure
  *
@@ -77,7 +90,6 @@ struct thread_info {
 #define init_thread_info	(init_thread_union.thread_info)
 #define init_stack		(init_thread_union.stack)
 
-#define THREAD_SIZE (2*PAGE_SIZE)
 
 /* how to get the thread information struct from C */
 static inline struct thread_info *current_thread_info(void)
@@ -87,6 +99,14 @@ static inline struct thread_info *curren
 	return ti;
 }
 
+/* how to get the current stack pointer from C */
+static inline unsigned long current_stack_pointer(void)
+{
+	unsigned long ti;
+	__asm__("movl %%esp,%0; ":"=r" (ti) : );
+	return ti;
+}
+
 /* thread information allocation */
 #ifdef CONFIG_DEBUG_STACK_USAGE
 #define alloc_thread_info(tsk)					\
@@ -108,8 +128,6 @@ static inline struct thread_info *curren
 
 #else /* !__ASSEMBLY__ */
 
-#define THREAD_SIZE	8192
-
 /* how to get the thread information struct from ASM */
 #define GET_THREAD_INFO(reg) \
 	movl $-THREAD_SIZE, reg; \
diff -upN reference/include/asm-ia64/irq.h current/include/asm-ia64/irq.h
--- reference/include/asm-ia64/irq.h	2003-06-05 14:39:17.000000000 -0700
+++ current/include/asm-ia64/irq.h	2004-04-29 10:39:07.000000000 -0700
@@ -29,4 +29,8 @@ extern void disable_irq_nosync (unsigned
 extern void enable_irq (unsigned int);
 extern void set_irq_affinity_info (unsigned int irq, int dest, int redir);
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif /* _ASM_IA64_IRQ_H */
diff -upN reference/include/asm-m68k/irq.h current/include/asm-m68k/irq.h
--- reference/include/asm-m68k/irq.h	2004-04-07 14:54:33.000000000 -0700
+++ current/include/asm-m68k/irq.h	2004-04-29 10:39:07.000000000 -0700
@@ -124,4 +124,8 @@ extern volatile unsigned int num_spuriou
  */
 extern irq_node_t *new_irq_node(void);
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif /* _M68K_IRQ_H_ */
diff -upN reference/include/asm-m68knommu/irq.h current/include/asm-m68knommu/irq.h
--- reference/include/asm-m68knommu/irq.h	2004-03-11 14:35:16.000000000 -0800
+++ current/include/asm-m68knommu/irq.h	2004-04-29 10:39:07.000000000 -0700
@@ -121,4 +121,8 @@ extern irq_node_t *new_irq_node(void);
 #define enable_irq_nosync(x)	enable_irq(x)
 #define disable_irq_nosync(x)	disable_irq(x)
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif /* _M68K_IRQ_H_ */
diff -upN reference/include/asm-mips/irq.h current/include/asm-mips/irq.h
--- reference/include/asm-mips/irq.h	2004-03-11 14:35:18.000000000 -0800
+++ current/include/asm-mips/irq.h	2004-04-29 10:39:07.000000000 -0700
@@ -31,4 +31,7 @@ extern asmlinkage unsigned int do_IRQ(in
 
 extern void init_generic_irq(void);
 
+struct irqaction;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif /* _ASM_IRQ_H */
diff -upN reference/include/asm-parisc/irq.h current/include/asm-parisc/irq.h
--- reference/include/asm-parisc/irq.h	2003-10-14 15:50:33.000000000 -0700
+++ current/include/asm-parisc/irq.h	2004-04-29 10:39:07.000000000 -0700
@@ -96,4 +96,7 @@ extern unsigned long txn_alloc_addr(int)
 /* soft power switch support (power.c) */
 extern struct tasklet_struct power_tasklet;
 
+struct irqaction;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif	/* _ASM_PARISC_IRQ_H */
diff -upN reference/include/asm-ppc/irq.h current/include/asm-ppc/irq.h
--- reference/include/asm-ppc/irq.h	2003-10-01 11:48:23.000000000 -0700
+++ current/include/asm-ppc/irq.h	2004-04-29 10:39:07.000000000 -0700
@@ -211,5 +211,9 @@ extern unsigned long ppc_cached_irq_mask
 extern unsigned long ppc_lost_interrupts[NR_MASK_WORDS];
 extern atomic_t ppc_n_lost_interrupts;
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif /* _ASM_IRQ_H */
 #endif /* __KERNEL__ */
diff -upN reference/include/asm-ppc64/irq.h current/include/asm-ppc64/irq.h
--- reference/include/asm-ppc64/irq.h	2004-03-11 14:35:23.000000000 -0800
+++ current/include/asm-ppc64/irq.h	2004-04-29 10:39:08.000000000 -0700
@@ -48,5 +48,9 @@ static __inline__ int irq_canonicalize(i
 
 #define NR_MASK_WORDS	((NR_IRQS + 63) / 64)
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif /* _ASM_IRQ_H */
 #endif /* __KERNEL__ */
diff -upN reference/include/asm-s390/irq.h current/include/asm-s390/irq.h
--- reference/include/asm-s390/irq.h	2003-07-28 15:33:24.000000000 -0700
+++ current/include/asm-s390/irq.h	2004-04-29 10:39:08.000000000 -0700
@@ -21,6 +21,10 @@ enum interruption_class {
 
 #define touch_nmi_watchdog() do { } while(0)
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif /* __KERNEL__ */
 #endif
 
diff -upN reference/include/asm-sh/irq.h current/include/asm-sh/irq.h
--- reference/include/asm-sh/irq.h	2004-04-07 14:54:34.000000000 -0700
+++ current/include/asm-sh/irq.h	2004-04-29 10:39:08.000000000 -0700
@@ -329,4 +329,8 @@ static inline int generic_irq_demux(int 
 #define irq_canonicalize(irq)	(irq)
 #define irq_demux(irq)		__irq_demux(sh_mv.mv_irq_demux(irq))
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif /* __ASM_SH_IRQ_H */
diff -upN reference/include/asm-sparc/irq.h current/include/asm-sparc/irq.h
--- reference/include/asm-sparc/irq.h	2003-10-21 11:16:12.000000000 -0700
+++ current/include/asm-sparc/irq.h	2004-04-29 10:39:08.000000000 -0700
@@ -184,4 +184,8 @@ extern struct sun4m_intregs *sun4m_inter
 #define SUN4M_INT_SBUS(x)	(1 << (x+7))
 #define SUN4M_INT_VME(x)	(1 << (x))
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif
diff -upN reference/include/asm-sparc64/irq.h current/include/asm-sparc64/irq.h
--- reference/include/asm-sparc64/irq.h	2003-10-01 11:41:16.000000000 -0700
+++ current/include/asm-sparc64/irq.h	2004-04-29 10:39:08.000000000 -0700
@@ -150,4 +150,8 @@ static __inline__ unsigned long get_soft
 	return retval;
 }
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif
diff -upN reference/include/asm-um/irq.h current/include/asm-um/irq.h
--- reference/include/asm-um/irq.h	2002-12-09 18:46:25.000000000 -0800
+++ current/include/asm-um/irq.h	2004-04-29 10:39:08.000000000 -0700
@@ -32,4 +32,9 @@ extern int um_request_irq(unsigned int i
 			  void (*handler)(int, void *, struct pt_regs *),
 			  unsigned long irqflags,  const char * devname,
 			  void *dev_id);
+
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif
diff -upN reference/include/asm-v850/irq.h current/include/asm-v850/irq.h
--- reference/include/asm-v850/irq.h	2003-06-05 14:39:21.000000000 -0700
+++ current/include/asm-v850/irq.h	2004-04-29 10:39:08.000000000 -0700
@@ -65,4 +65,8 @@ extern void disable_irq_nosync (unsigned
 
 #endif /* !__ASSEMBLY__ */
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif /* __V850_IRQ_H__ */
diff -upN reference/include/asm-x86_64/irq.h current/include/asm-x86_64/irq.h
--- reference/include/asm-x86_64/irq.h	2004-01-15 10:41:18.000000000 -0800
+++ current/include/asm-x86_64/irq.h	2004-04-29 10:39:08.000000000 -0700
@@ -53,4 +53,8 @@ extern int can_request_irq(unsigned int,
 #define ARCH_HAS_NMI_WATCHDOG		/* See include/linux/nmi.h */
 #endif
 
+struct irqaction;
+struct pt_regs;
+int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
+
 #endif /* _ASM_IRQ_H */
diff -upN reference/include/linux/compiler-gcc3.h current/include/linux/compiler-gcc3.h
--- reference/include/linux/compiler-gcc3.h	2004-03-11 14:35:28.000000000 -0800
+++ current/include/linux/compiler-gcc3.h	2004-04-29 10:39:08.000000000 -0700
@@ -3,7 +3,7 @@
 /* These definitions are for GCC v3.x.  */
 #include <linux/compiler-gcc.h>
 
-#if __GNUC_MINOR__ >= 1
+#if __GNUC_MINOR__ >= 1  && __GNUC_MINOR__ < 4
 # define inline		__inline__ __attribute__((always_inline))
 # define __inline__	__inline__ __attribute__((always_inline))
 # define __inline	__inline__ __attribute__((always_inline))
diff -upN reference/include/linux/irq.h current/include/linux/irq.h
--- reference/include/linux/irq.h	2003-10-01 11:41:17.000000000 -0700
+++ current/include/linux/irq.h	2004-04-29 10:39:08.000000000 -0700
@@ -71,7 +71,6 @@ extern irq_desc_t irq_desc [NR_IRQS];
 
 #include <asm/hw_irq.h> /* the arch dependent stuff */
 
-extern int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
 extern int setup_irq(unsigned int , struct irqaction * );
 
 extern hw_irq_controller no_irq_type;  /* needed in every arch ? */
diff -upN reference/kernel/softirq.c current/kernel/softirq.c
--- reference/kernel/softirq.c	2004-04-07 14:54:37.000000000 -0700
+++ current/kernel/softirq.c	2004-04-29 10:39:08.000000000 -0700
@@ -16,6 +16,7 @@
 #include <linux/cpu.h>
 #include <linux/kthread.h>
 
+#include <asm/irq.h>
 /*
    - No shared variables, all the data are CPU local.
    - If a softirq needs serialization, let it serialize itself
@@ -69,53 +70,66 @@ static inline void wakeup_softirqd(void)
  */
 #define MAX_SOFTIRQ_RESTART 10
 
-asmlinkage void do_softirq(void)
+asmlinkage void __do_softirq(void)
 {
-	int max_restart = MAX_SOFTIRQ_RESTART;
+	struct softirq_action *h;
 	__u32 pending;
-	unsigned long flags;
+	int max_restart = MAX_SOFTIRQ_RESTART;
 
-	if (in_interrupt())
-		return;
+	pending = local_softirq_pending();
 
-	local_irq_save(flags);
+	local_bh_disable();
+restart:
+	/* Reset the pending bitmask before enabling irqs */
+	local_softirq_pending() = 0;
+
+	local_irq_enable();
+
+	h = softirq_vec;
+
+	do {
+		if (pending & 1)
+			h->action(h);
+		h++;
+		pending >>= 1;
+	} while (pending);
+
+	local_irq_disable();
 
 	pending = local_softirq_pending();
+	if (pending && --max_restart)
+		goto restart;
 
-	if (pending) {
-		struct softirq_action *h;
+	if (pending)
+		wakeup_softirqd();
 
-		local_bh_disable();
-restart:
-		/* Reset the pending bitmask before enabling irqs */
-		local_softirq_pending() = 0;
+	__local_bh_enable();
+}
 
-		local_irq_enable();
+#ifndef __ARCH_HAS_DO_SOFTIRQ
+
+asmlinkage void do_softirq(void)
+{
+	__u32 pending;
+	unsigned long flags;
 
-		h = softirq_vec;
+	if (in_interrupt())
+		return;
 
-		do {
-			if (pending & 1)
-				h->action(h);
-			h++;
-			pending >>= 1;
-		} while (pending);
+	local_irq_save(flags);
 
-		local_irq_disable();
+	pending = local_softirq_pending();
 
-		pending = local_softirq_pending();
-		if (pending && --max_restart)
-			goto restart;
-		if (pending)
-			wakeup_softirqd();
-		__local_bh_enable();
-	}
+	if (pending)
+		__do_softirq();
 
 	local_irq_restore(flags);
 }
 
 EXPORT_SYMBOL(do_softirq);
 
+#endif
+
 void local_bh_enable(void)
 {
 	__local_bh_enable();