[PARISC] Add IRQ affinities
This really only adds them for the machines I can check SMP on, which
is CPU interrupts and IOSAPIC (so not any of the GSC based machines).
With this patch, irqbalanced can be used to maintain irq balancing.
Unfortunately, irqbalanced is a bit x86 centric, so it doesn't do an
incredibly good job, but it does work.
Signed-off-by: James Bottomley <jejb@parisc-linux.org>
Signed-off-by: Kyle McMartin <kyle@parisc-linux.org>
diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c
index 865611c..2626405 100644
--- a/arch/parisc/kernel/irq.c
+++ b/arch/parisc/kernel/irq.c
@@ -30,6 +30,7 @@
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/types.h>
+#include <asm/io.h>
#include <asm/smp.h>
@@ -84,6 +85,35 @@
void no_ack_irq(unsigned int irq) { }
void no_end_irq(unsigned int irq) { }
+#ifdef CONFIG_SMP
+int cpu_check_affinity(unsigned int irq, cpumask_t *dest)
+{
+ int cpu_dest;
+
+ /* timer and ipi have to always be received on all CPUs */
+ if (irq == TIMER_IRQ || irq == IPI_IRQ) {
+ /* Bad linux design decision. The mask has already
+ * been set; we must reset it */
+ irq_affinity[irq] = CPU_MASK_ALL;
+ return -EINVAL;
+ }
+
+ /* whatever mask they set, we just allow one CPU */
+ cpu_dest = first_cpu(*dest);
+ *dest = cpumask_of_cpu(cpu_dest);
+
+ return 0;
+}
+
+static void cpu_set_affinity_irq(unsigned int irq, cpumask_t dest)
+{
+ if (cpu_check_affinity(irq, &dest))
+ return;
+
+ irq_affinity[irq] = dest;
+}
+#endif
+
static struct hw_interrupt_type cpu_interrupt_type = {
.typename = "CPU",
.startup = cpu_startup_irq,
@@ -92,7 +122,9 @@
.disable = cpu_disable_irq,
.ack = no_ack_irq,
.end = no_end_irq,
-// .set_affinity = cpu_set_affinity_irq,
+#ifdef CONFIG_SMP
+ .set_affinity = cpu_set_affinity_irq,
+#endif
};
int show_interrupts(struct seq_file *p, void *v)
@@ -229,6 +261,13 @@
return -1;
}
+unsigned long txn_affinity_addr(unsigned int irq, int cpu)
+{
+ irq_affinity[irq] = cpumask_of_cpu(cpu);
+
+ return cpu_data[cpu].txn_addr;
+}
+
unsigned long txn_alloc_addr(unsigned int virt_irq)
{
static int next_cpu = -1;
@@ -243,7 +282,7 @@
if (next_cpu >= NR_CPUS)
next_cpu = 0; /* nothing else, assign monarch */
- return cpu_data[next_cpu].txn_addr;
+ return txn_affinity_addr(virt_irq, next_cpu);
}
@@ -282,12 +321,29 @@
/* Work our way from MSb to LSb...same order we alloc EIRs */
for (irq = TIMER_IRQ; eirr_val && bit; bit>>=1, irq++) {
+ cpumask_t dest = irq_affinity[irq];
+
if (!(bit & eirr_val))
continue;
/* clear bit in mask - can exit loop sooner */
eirr_val &= ~bit;
+ /* FIXME: because generic set affinity mucks
+ * with the affinity before sending it to us
+ * we can get the situation where the affinity is
+ * wrong for our CPU type interrupts */
+ if (irq != TIMER_IRQ && irq != IPI_IRQ &&
+ !cpu_isset(smp_processor_id(), dest)) {
+ int cpu = first_cpu(dest);
+
+ printk("rethrowing irq %d from %d to %d\n",
+ irq, smp_processor_id(), cpu);
+ gsc_writel(irq + CPU_IRQ_BASE,
+ cpu_data[cpu].hpa);
+ continue;
+ }
+
__do_IRQ(irq, regs);
}
}