Skip to content

Commit

Permalink
[POWERPC] cell: Fix lost interrupts due to fasteoi handler
Browse files Browse the repository at this point in the history
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: Jeremy Kerr <jk@ozlabs.org>
  • Loading branch information
Jeremy Kerr committed May 5, 2008
1 parent 3b57506 commit 5711fe9
Showing 1 changed file with 51 additions and 2 deletions.
53 changes: 51 additions & 2 deletions arch/powerpc/platforms/cell/interrupt.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <linux/percpu.h>
#include <linux/types.h>
#include <linux/ioport.h>
#include <linux/kernel_stat.h>

#include <asm/io.h>
#include <asm/pgtable.h>
Expand Down Expand Up @@ -231,6 +232,54 @@ static int iic_host_match(struct irq_host *h, struct device_node *node)
"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,
irq_hw_number_t hw)
{
Expand All @@ -240,10 +289,10 @@ static int iic_host_map(struct irq_host *h, unsigned int virq,
break;
case IIC_IRQ_TYPE_IOEXC:
set_irq_chip_and_handler(virq, &iic_ioexc_chip,
handle_fasteoi_irq);
handle_iic_irq);
break;
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;
}
Expand Down

0 comments on commit 5711fe9

Please sign in to comment.