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
impact on interrupt service overhead. Disable it only if you know
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
bool "Load VPE program into memory hidden from linux"
depends on MIPS_VPE_LOADER
......
......@@ -39,6 +39,9 @@ static struct irq_chip i8259A_chip = {
.disable = disable_8259A_irq,
.unmask = enable_8259A_irq,
.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,
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.
* Within a VPE one TC can interrupt another by different approaches.
......@@ -830,6 +884,15 @@ void ipi_decode(struct smtc_ipi *pipi)
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:
printk("Impossible SMTC IPI Type 0x%x\n", type_copy);
break;
......
......@@ -88,3 +88,53 @@ void __cpuinit prom_smp_finish(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)
#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
/*
......@@ -55,14 +87,28 @@ static inline void smtc_im_ack_irq(unsigned int irq)
* Otherwise it will be done on return from exception.
*/
#define __DO_IRQ_SMTC_HOOK(irq) \
do { \
IRQ_AFFINITY_HOOK(irq); \
if (irq_hwmask[irq] & 0x0000ff00) \
write_c0_tccontext(read_c0_tccontext() & \
~(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)
#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
/*
......@@ -81,6 +127,23 @@ do { \
irq_exit(); \
} 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 spurious_interrupt(void);
......
......@@ -34,6 +34,7 @@ struct smtc_ipi {
#define LINUX_SMP_IPI 1
#define SMTC_CLOCK_TICK 2
#define IRQ_AFFINITY_IPI 3
/*
* 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