[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
@ -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 = {
|
|||||||
.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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
#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)
|
|||||||
*/
|
*/
|
||||||
#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)
|
} 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
|
#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 { \
|
|||||||
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 {
|
|||||||
|
|
||||||
#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
|
||||||
|
Loading…
Reference in New Issue
Block a user