Commit f571eff0 authored by Kevin D. Kissell's avatar Kevin D. Kissell Committed by Ralf Baechle

[MIPS] IRQ Affinity Support for SMTC on Malta Platform

Signed-off-by: default avatarKevin D. Kissell <kevink@mips.com>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent bbf25010
...@@ -1378,6 +1378,19 @@ config MIPS_MT_SMTC_IM_BACKSTOP ...@@ -1378,6 +1378,19 @@ config MIPS_MT_SMTC_IM_BACKSTOP
impact on interrupt service overhead. Disable it only if you know impact on interrupt service overhead. Disable it only if you know
what you are doing. what you are doing.
config MIPS_MT_SMTC_IRQAFF
bool "Support IRQ affinity API"
depends on MIPS_MT_SMTC
default n
help
Enables SMP IRQ affinity API (/proc/irq/*/smp_affinity, etc.)
for SMTC Linux kernel. Requires platform support, of which
an example can be found in the MIPS kernel i8259 and Malta
platform code. It is recommended that MIPS_MT_SMTC_INSTANT_REPLAY
be enabled if MIPS_MT_SMTC_IRQAFF is used. Adds overhead to
interrupt dispatch, and should be used only if you know what
you are doing.
config MIPS_VPE_LOADER_TOM config MIPS_VPE_LOADER_TOM
bool "Load VPE program into memory hidden from linux" bool "Load VPE program into memory hidden from linux"
depends on MIPS_VPE_LOADER depends on MIPS_VPE_LOADER
......
...@@ -39,6 +39,9 @@ static struct irq_chip i8259A_chip = { ...@@ -39,6 +39,9 @@ static struct irq_chip i8259A_chip = {
.disable = disable_8259A_irq, .disable = disable_8259A_irq,
.unmask = enable_8259A_irq, .unmask = enable_8259A_irq,
.mask_ack = mask_and_ack_8259A, .mask_ack = mask_and_ack_8259A,
#ifdef CONFIG_MIPS_MT_SMTC_IRQAFF
.set_affinity = plat_set_irq_affinity,
#endif /* CONFIG_MIPS_MT_SMTC_IRQAFF */
}; };
/* /*
......
...@@ -606,6 +606,60 @@ int setup_irq_smtc(unsigned int irq, struct irqaction * new, ...@@ -606,6 +606,60 @@ int setup_irq_smtc(unsigned int irq, struct irqaction * new,
return setup_irq(irq, new); return setup_irq(irq, new);
} }
#ifdef CONFIG_MIPS_MT_SMTC_IRQAFF
/*
* Support for IRQ affinity to TCs
*/
void smtc_set_irq_affinity(unsigned int irq, cpumask_t affinity)
{
/*
* If a "fast path" cache of quickly decodable affinity state
* is maintained, this is where it gets done, on a call up
* from the platform affinity code.
*/
}
void smtc_forward_irq(unsigned int irq)
{
int target;
/*
* OK wise guy, now figure out how to get the IRQ
* to be serviced on an authorized "CPU".
*
* Ideally, to handle the situation where an IRQ has multiple
* eligible CPUS, we would maintain state per IRQ that would
* allow a fair distribution of service requests. Since the
* expected use model is any-or-only-one, for simplicity
* and efficiency, we just pick the easiest one to find.
*/
target = first_cpu(irq_desc[irq].affinity);
/*
* We depend on the platform code to have correctly processed
* IRQ affinity change requests to ensure that the IRQ affinity
* mask has been purged of bits corresponding to nonexistent and
* offline "CPUs", and to TCs bound to VPEs other than the VPE
* connected to the physical interrupt input for the interrupt
* in question. Otherwise we have a nasty problem with interrupt
* mask management. This is best handled in non-performance-critical
* platform IRQ affinity setting code, to minimize interrupt-time
* checks.
*/
/* If no one is eligible, service locally */
if (target >= NR_CPUS) {
do_IRQ_no_affinity(irq);
return;
}
smtc_send_ipi(target, IRQ_AFFINITY_IPI, irq);
}
#endif /* CONFIG_MIPS_MT_SMTC_IRQAFF */
/* /*
* IPI model for SMTC is tricky, because interrupts aren't TC-specific. * IPI model for SMTC is tricky, because interrupts aren't TC-specific.
* Within a VPE one TC can interrupt another by different approaches. * Within a VPE one TC can interrupt another by different approaches.
...@@ -830,6 +884,15 @@ void ipi_decode(struct smtc_ipi *pipi) ...@@ -830,6 +884,15 @@ void ipi_decode(struct smtc_ipi *pipi)
break; break;
} }
break; break;
#ifdef CONFIG_MIPS_MT_SMTC_IRQAFF
case IRQ_AFFINITY_IPI:
/*
* Accept a "forwarded" interrupt that was initially
* taken by a TC who doesn't have affinity for the IRQ.
*/
do_IRQ_no_affinity((int)arg_copy);
break;
#endif /* CONFIG_MIPS_MT_SMTC_IRQAFF */
default: default:
printk("Impossible SMTC IPI Type 0x%x\n", type_copy); printk("Impossible SMTC IPI Type 0x%x\n", type_copy);
break; break;
......
...@@ -88,3 +88,53 @@ void __cpuinit prom_smp_finish(void) ...@@ -88,3 +88,53 @@ void __cpuinit prom_smp_finish(void)
void prom_cpus_done(void) void prom_cpus_done(void)
{ {
} }
#ifdef CONFIG_MIPS_MT_SMTC_IRQAFF
/*
* IRQ affinity hook
*/
void plat_set_irq_affinity(unsigned int irq, cpumask_t affinity)
{
cpumask_t tmask = affinity;
int cpu = 0;
void smtc_set_irq_affinity(unsigned int irq, cpumask_t aff);
/*
* On the legacy Malta development board, all I/O interrupts
* are routed through the 8259 and combined in a single signal
* to the CPU daughterboard, and on the CoreFPGA2/3 34K models,
* that signal is brought to IP2 of both VPEs. To avoid racing
* concurrent interrupt service events, IP2 is enabled only on
* one VPE, by convention VPE0. So long as no bits are ever
* cleared in the affinity mask, there will never be any
* interrupt forwarding. But as soon as a program or operator
* sets affinity for one of the related IRQs, we need to make
* sure that we don't ever try to forward across the VPE boundry,
* at least not until we engineer a system where the interrupt
* _ack() or _end() function can somehow know that it corresponds
* to an interrupt taken on another VPE, and perform the appropriate
* restoration of Status.IM state using MFTR/MTTR instead of the
* normal local behavior. We also ensure that no attempt will
* be made to forward to an offline "CPU".
*/
for_each_cpu_mask(cpu, affinity) {
if ((cpu_data[cpu].vpe_id != 0) || !cpu_online(cpu))
cpu_clear(cpu, tmask);
}
irq_desc[irq].affinity = tmask;
if (cpus_empty(tmask))
/*
* We could restore a default mask here, but the
* runtime code can anyway deal with the null set
*/
printk(KERN_WARNING
"IRQ affinity leaves no legal CPU for IRQ %d\n", irq);
/* Do any generic SMTC IRQ affinity setup */
smtc_set_irq_affinity(irq, tmask);
}
#endif /* CONFIG_MIPS_MT_SMTC_IRQAFF */
...@@ -46,6 +46,38 @@ static inline void smtc_im_ack_irq(unsigned int irq) ...@@ -46,6 +46,38 @@ static inline void smtc_im_ack_irq(unsigned int irq)
#endif /* CONFIG_MIPS_MT_SMTC */ #endif /* CONFIG_MIPS_MT_SMTC */
#ifdef CONFIG_MIPS_MT_SMTC_IRQAFF
#include <linux/cpumask.h>
extern void plat_set_irq_affinity(unsigned int irq, cpumask_t affinity);
extern void smtc_forward_irq(unsigned int irq);
/*
* IRQ affinity hook invoked at the beginning of interrupt dispatch
* if option is enabled.
*
* Up through Linux 2.6.22 (at least) cpumask operations are very
* inefficient on MIPS. Initial prototypes of SMTC IRQ affinity
* used a "fast path" per-IRQ-descriptor cache of affinity information
* to reduce latency. As there is a project afoot to optimize the
* cpumask implementations, this version is optimistically assuming
* that cpumask.h macro overhead is reasonable during interrupt dispatch.
*/
#define IRQ_AFFINITY_HOOK(irq) \
do { \
if (!cpu_isset(smp_processor_id(), irq_desc[irq].affinity)) { \
smtc_forward_irq(irq); \
irq_exit(); \
return; \
} \
} while (0)
#else /* Not doing SMTC affinity */
#define IRQ_AFFINITY_HOOK(irq) do { } while (0)
#endif /* CONFIG_MIPS_MT_SMTC_IRQAFF */
#ifdef CONFIG_MIPS_MT_SMTC_IM_BACKSTOP #ifdef CONFIG_MIPS_MT_SMTC_IM_BACKSTOP
/* /*
...@@ -56,13 +88,27 @@ static inline void smtc_im_ack_irq(unsigned int irq) ...@@ -56,13 +88,27 @@ static inline void smtc_im_ack_irq(unsigned int irq)
*/ */
#define __DO_IRQ_SMTC_HOOK(irq) \ #define __DO_IRQ_SMTC_HOOK(irq) \
do { \ do { \
IRQ_AFFINITY_HOOK(irq); \
if (irq_hwmask[irq] & 0x0000ff00) \ if (irq_hwmask[irq] & 0x0000ff00) \
write_c0_tccontext(read_c0_tccontext() & \ write_c0_tccontext(read_c0_tccontext() & \
~(irq_hwmask[irq] & 0x0000ff00)); \ ~(irq_hwmask[irq] & 0x0000ff00)); \
} while (0)
#define __NO_AFFINITY_IRQ_SMTC_HOOK(irq) \
do { \
if (irq_hwmask[irq] & 0x0000ff00) \
write_c0_tccontext(read_c0_tccontext() & \
~(irq_hwmask[irq] & 0x0000ff00)); \
} while (0) } while (0)
#else #else
#define __DO_IRQ_SMTC_HOOK(irq) do { } while (0) #define __DO_IRQ_SMTC_HOOK(irq) \
do { \
IRQ_AFFINITY_HOOK(irq); \
} while (0)
#define __NO_AFFINITY_IRQ_SMTC_HOOK(irq) do { } while (0)
#endif #endif
/* /*
...@@ -81,6 +127,23 @@ do { \ ...@@ -81,6 +127,23 @@ do { \
irq_exit(); \ irq_exit(); \
} while (0) } while (0)
#ifdef CONFIG_MIPS_MT_SMTC_IRQAFF
/*
* To avoid inefficient and in some cases pathological re-checking of
* IRQ affinity, we have this variant that skips the affinity check.
*/
#define do_IRQ_no_affinity(irq) \
do { \
irq_enter(); \
__NO_AFFINITY_IRQ_SMTC_HOOK(irq); \
generic_handle_irq(irq); \
irq_exit(); \
} while (0)
#endif /* CONFIG_MIPS_MT_SMTC_IRQAFF */
extern void arch_init_irq(void); extern void arch_init_irq(void);
extern void spurious_interrupt(void); extern void spurious_interrupt(void);
......
...@@ -34,6 +34,7 @@ struct smtc_ipi { ...@@ -34,6 +34,7 @@ struct smtc_ipi {
#define LINUX_SMP_IPI 1 #define LINUX_SMP_IPI 1
#define SMTC_CLOCK_TICK 2 #define SMTC_CLOCK_TICK 2
#define IRQ_AFFINITY_IPI 3
/* /*
* A queue of IPI messages * A queue of IPI messages
......
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