From: Jake Moilanen <moilanen@austin.ibm.com>

In xics_get_irq(), for a real-to-virt irq lookup, go down the 
slowpath by looking through the entire virt_irq_to_real_map array
if take a miss on the radix tree.  This is possible, when an 
interrupt is taken before the driver has called request_irq() (eg IDE).


---

 25-akpm/arch/ppc64/kernel/irq.c  |   33 +++++++++++++++++++++++++++++++++
 25-akpm/arch/ppc64/kernel/xics.c |    4 ++++
 2 files changed, 37 insertions(+)

diff -puN arch/ppc64/kernel/irq.c~ppc64-ide_request_irq arch/ppc64/kernel/irq.c
--- 25/arch/ppc64/kernel/irq.c~ppc64-ide_request_irq	2004-03-14 15:35:09.508493072 -0800
+++ 25-akpm/arch/ppc64/kernel/irq.c	2004-03-14 15:35:09.511492616 -0800
@@ -910,4 +910,37 @@ int virt_irq_create_mapping(unsigned int
 	return NO_IRQ;
 }
 
+/*
+ * In most cases will get a hit on the very first slot checked in the
+ * virt_irq_to_real_map.  Only when there are a large number of
+ * IRQs will this be expensive.
+ */
+unsigned int real_irq_to_virt_slowpath(unsigned int real_irq)
+{
+	unsigned int virq;
+	unsigned int first_virq;
+
+	virq = real_irq;
+
+	if (virq > MAX_VIRT_IRQ)
+		virq = (virq % NR_VIRT_IRQS) + MIN_VIRT_IRQ;
+
+	first_virq = virq;
+
+	do {
+		if (virt_irq_to_real_map[virq] == real_irq)
+			return virq;
+
+		virq++;
+
+		if (virq >= MAX_VIRT_IRQ)
+			virq = 0;
+
+	} while (first_virq != virq);
+
+	return NO_IRQ;
+
+}
+
+
 #endif
diff -puN arch/ppc64/kernel/xics.c~ppc64-ide_request_irq arch/ppc64/kernel/xics.c
--- 25/arch/ppc64/kernel/xics.c~ppc64-ide_request_irq	2004-03-14 15:35:09.509492920 -0800
+++ 25-akpm/arch/ppc64/kernel/xics.c	2004-03-14 15:35:09.512492464 -0800
@@ -327,6 +327,8 @@ static void xics_mask_and_ack_irq(unsign
 	}
 }
 
+extern unsigned int real_irq_to_virt_slowpath(unsigned int real_irq);
+
 int xics_get_irq(struct pt_regs *regs)
 {
 	unsigned int cpu = smp_processor_id();
@@ -349,6 +351,8 @@ int xics_get_irq(struct pt_regs *regs)
 		irq = -1;
 	} else {
 		irq = real_irq_to_virt(vec);
+		if (irq == NO_IRQ)
+			irq = real_irq_to_virt_slowpath(vec);
 		if (irq == NO_IRQ) {
 			printk(KERN_ERR "Interrupt 0x%x (real) is invalid,"
 			       " disabling it.\n", vec);

_