[MIPS] IRQ Affinity Support for SMTC on Malta Platform
Signed-off-by: Kevin D. Kissell <kevink@mips.com> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
parent
bbf25010f1
commit
f571eff0a2
6 changed files with 195 additions and 2 deletions
|
@ -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
|
||||
|
||||
/*
|
||||
|
@ -56,13 +88,27 @@ static inline void smtc_im_ack_irq(unsigned int irq)
|
|||
*/
|
||||
#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)); \
|
||||
~(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
|
||||
|
|
Loading…
Reference in a new issue