Commit 5711fe90 authored by Jeremy Kerr's avatar Jeremy Kerr

[POWERPC] cell: Fix lost interrupts due to fasteoi handler

We may currently lose interrupts during SPE context switch, as we alter
the INT_Route register. Because the IIC uses a per-thread priority
status, changing the interrupt routing to a different thread means that
the IRQ is no longer masked by the priority status, so we end up with
two fasteoi IRQ handlers executing for the one irq_desc. The fasteoi
handler doesn't handle multiple IRQs, so drops the second one.

Fix this by using our own flow handler. This is based on
handle_edge_irq, but issues an eoi after IRQs are handled, and doesn't
do any mask/unmasking.
Signed-off-by: default avatarJeremy Kerr <jk@ozlabs.org>
parent 3b575064
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/kernel_stat.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
...@@ -231,6 +232,54 @@ static int iic_host_match(struct irq_host *h, struct device_node *node) ...@@ -231,6 +232,54 @@ static int iic_host_match(struct irq_host *h, struct device_node *node)
"IBM,CBEA-Internal-Interrupt-Controller"); "IBM,CBEA-Internal-Interrupt-Controller");
} }
extern int noirqdebug;
static void handle_iic_irq(unsigned int irq, struct irq_desc *desc)
{
const unsigned int cpu = smp_processor_id();
spin_lock(&desc->lock);
desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
/*
* If we're currently running this IRQ, or its disabled,
* we shouldn't process the IRQ. Mark it pending, handle
* the necessary masking and go out
*/
if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||
!desc->action)) {
desc->status |= IRQ_PENDING;
goto out_eoi;
}
kstat_cpu(cpu).irqs[irq]++;
/* Mark the IRQ currently in progress.*/
desc->status |= IRQ_INPROGRESS;
do {
struct irqaction *action = desc->action;
irqreturn_t action_ret;
if (unlikely(!action))
goto out_eoi;
desc->status &= ~IRQ_PENDING;
spin_unlock(&desc->lock);
action_ret = handle_IRQ_event(irq, action);
if (!noirqdebug)
note_interrupt(irq, desc, action_ret);
spin_lock(&desc->lock);
} while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
desc->status &= ~IRQ_INPROGRESS;
out_eoi:
desc->chip->eoi(irq);
spin_unlock(&desc->lock);
}
static int iic_host_map(struct irq_host *h, unsigned int virq, static int iic_host_map(struct irq_host *h, unsigned int virq,
irq_hw_number_t hw) irq_hw_number_t hw)
{ {
...@@ -240,10 +289,10 @@ static int iic_host_map(struct irq_host *h, unsigned int virq, ...@@ -240,10 +289,10 @@ static int iic_host_map(struct irq_host *h, unsigned int virq,
break; break;
case IIC_IRQ_TYPE_IOEXC: case IIC_IRQ_TYPE_IOEXC:
set_irq_chip_and_handler(virq, &iic_ioexc_chip, set_irq_chip_and_handler(virq, &iic_ioexc_chip,
handle_fasteoi_irq); handle_iic_irq);
break; break;
default: default:
set_irq_chip_and_handler(virq, &iic_chip, handle_fasteoi_irq); set_irq_chip_and_handler(virq, &iic_chip, handle_iic_irq);
} }
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment