[MIPS] Dyntick support for SMTC:
The kernel currently only supports broadcasting of the timer interrupt from a single timer, not multicasting into two multicast groups of processors. So the implemented mechanism for SMTC works by broadcasting the cp0 compare interrupt on VPE 0 and ignoring it on any additional VPEs. Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
Родитель
7bcf7717b6
Коммит
ea5804015c
|
@ -1368,6 +1368,7 @@ config MIPS_MT_SMTC
|
|||
depends on CPU_MIPS32_R2
|
||||
#depends on CPU_MIPS64_R2 # once there is hardware ...
|
||||
depends on SYS_SUPPORTS_MULTITHREADING
|
||||
select GENERIC_CLOCKEVENTS_BROADCAST
|
||||
select CPU_MIPSR2_IRQ_VI
|
||||
select CPU_MIPSR2_IRQ_EI
|
||||
select CPU_MIPSR2_SRS
|
||||
|
@ -1537,6 +1538,9 @@ config CPU_HAS_SYNC
|
|||
depends on !CPU_R3000
|
||||
default y
|
||||
|
||||
config GENERIC_CLOCKEVENTS_BROADCAST
|
||||
bool
|
||||
|
||||
#
|
||||
# Use the generic interrupt handling code in kernel/irq/:
|
||||
#
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* Copyright (C) 2004 Mips Technologies, Inc */
|
||||
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/cpumask.h>
|
||||
|
@ -62,7 +63,7 @@ asiduse smtc_live_asid[MAX_SMTC_TLBS][MAX_SMTC_ASIDS];
|
|||
* Clock interrupt "latch" buffers, per "CPU"
|
||||
*/
|
||||
|
||||
unsigned int ipi_timer_latch[NR_CPUS];
|
||||
static atomic_t ipi_timer_latch[NR_CPUS];
|
||||
|
||||
/*
|
||||
* Number of InterProcessor Interupt (IPI) message buffers to allocate
|
||||
|
@ -296,8 +297,10 @@ int __init mipsmt_build_cpu_map(int start_cpu_slot)
|
|||
__cpu_number_map[i] = i;
|
||||
__cpu_logical_map[i] = i;
|
||||
}
|
||||
#ifdef CONFIG_MIPS_MT_FPAFF
|
||||
/* Initialize map of CPUs with FPUs */
|
||||
cpus_clear(mt_fpu_cpumask);
|
||||
#endif
|
||||
|
||||
/* One of those TC's is the one booting, and not a secondary... */
|
||||
printk("%i available secondary CPU TC(s)\n", i - 1);
|
||||
|
@ -359,7 +362,7 @@ void mipsmt_prepare_cpus(void)
|
|||
IPIQ[i].head = IPIQ[i].tail = NULL;
|
||||
spin_lock_init(&IPIQ[i].lock);
|
||||
IPIQ[i].depth = 0;
|
||||
ipi_timer_latch[i] = 0;
|
||||
atomic_set(&ipi_timer_latch[i], 0);
|
||||
}
|
||||
|
||||
/* cpu_data index starts at zero */
|
||||
|
@ -482,10 +485,12 @@ void mipsmt_prepare_cpus(void)
|
|||
|
||||
/* Set up coprocessor affinity CPU mask(s) */
|
||||
|
||||
#ifdef CONFIG_MIPS_MT_FPAFF
|
||||
for (tc = 0; tc < ntc; tc++) {
|
||||
if (cpu_data[tc].options & MIPS_CPU_FPU)
|
||||
cpu_set(tc, mt_fpu_cpumask);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* set up ipi interrupts... */
|
||||
|
||||
|
@ -702,7 +707,7 @@ static void smtc_ipi_qdump(void)
|
|||
* be done with the atomic.h primitives). And since this is
|
||||
* MIPS MT, we can assume that we have LL/SC.
|
||||
*/
|
||||
static __inline__ int atomic_postincrement(unsigned int *pv)
|
||||
static inline int atomic_postincrement(atomic_t *v)
|
||||
{
|
||||
unsigned long result;
|
||||
|
||||
|
@ -714,8 +719,8 @@ static __inline__ int atomic_postincrement(unsigned int *pv)
|
|||
" sc %1, %2 \n"
|
||||
" beqz %1, 1b \n"
|
||||
__WEAK_LLSC_MB
|
||||
: "=&r" (result), "=&r" (temp), "=m" (*pv)
|
||||
: "m" (*pv)
|
||||
: "=&r" (result), "=&r" (temp), "=m" (v->counter)
|
||||
: "m" (v->counter)
|
||||
: "memory");
|
||||
|
||||
return result;
|
||||
|
@ -743,6 +748,8 @@ void smtc_send_ipi(int cpu, int type, unsigned int action)
|
|||
pipi->arg = (void *)action;
|
||||
pipi->dest = cpu;
|
||||
if (cpu_data[cpu].vpe_id != cpu_data[smp_processor_id()].vpe_id) {
|
||||
if (type == SMTC_CLOCK_TICK)
|
||||
atomic_inc(&ipi_timer_latch[cpu]);
|
||||
/* If not on same VPE, enqueue and send cross-VPE interupt */
|
||||
smtc_ipi_nq(&IPIQ[cpu], pipi);
|
||||
LOCK_CORE_PRA();
|
||||
|
@ -784,6 +791,8 @@ void smtc_send_ipi(int cpu, int type, unsigned int action)
|
|||
}
|
||||
smtc_ipi_nq(&IPIQ[cpu], pipi);
|
||||
} else {
|
||||
if (type == SMTC_CLOCK_TICK)
|
||||
atomic_inc(&ipi_timer_latch[cpu]);
|
||||
post_direct_ipi(cpu, pipi);
|
||||
write_tc_c0_tchalt(0);
|
||||
UNLOCK_CORE_PRA();
|
||||
|
@ -801,6 +810,7 @@ static void post_direct_ipi(int cpu, struct smtc_ipi *pipi)
|
|||
unsigned long tcrestart;
|
||||
extern u32 kernelsp[NR_CPUS];
|
||||
extern void __smtc_ipi_vector(void);
|
||||
//printk("%s: on %d for %d\n", __func__, smp_processor_id(), cpu);
|
||||
|
||||
/* Extract Status, EPC from halted TC */
|
||||
tcstatus = read_tc_c0_tcstatus();
|
||||
|
@ -851,25 +861,31 @@ static void ipi_call_interrupt(void)
|
|||
smp_call_function_interrupt();
|
||||
}
|
||||
|
||||
DECLARE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
|
||||
|
||||
void ipi_decode(struct smtc_ipi *pipi)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
struct clock_event_device *cd;
|
||||
void *arg_copy = pipi->arg;
|
||||
int type_copy = pipi->type;
|
||||
int dest_copy = pipi->dest;
|
||||
int ticks;
|
||||
|
||||
smtc_ipi_nq(&freeIPIq, pipi);
|
||||
switch (type_copy) {
|
||||
case SMTC_CLOCK_TICK:
|
||||
irq_enter();
|
||||
kstat_this_cpu.irqs[MIPS_CPU_IRQ_BASE + cp0_compare_irq]++;
|
||||
/* Invoke Clock "Interrupt" */
|
||||
ipi_timer_latch[dest_copy] = 0;
|
||||
#ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG
|
||||
clock_hang_reported[dest_copy] = 0;
|
||||
#endif /* CONFIG_SMTC_IDLE_HOOK_DEBUG */
|
||||
local_timer_interrupt(0, NULL);
|
||||
kstat_this_cpu.irqs[MIPS_CPU_IRQ_BASE + 1]++;
|
||||
cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
|
||||
ticks = atomic_read(&ipi_timer_latch[cpu]);
|
||||
atomic_sub(ticks, &ipi_timer_latch[cpu]);
|
||||
while (ticks) {
|
||||
cd->event_handler(cd);
|
||||
ticks--;
|
||||
}
|
||||
irq_exit();
|
||||
break;
|
||||
|
||||
case LINUX_SMP_IPI:
|
||||
switch ((int)arg_copy) {
|
||||
case SMP_RESCHEDULE_YOURSELF:
|
||||
|
@ -920,25 +936,6 @@ void deferred_smtc_ipi(void)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Send clock tick to all TCs except the one executing the funtion
|
||||
*/
|
||||
|
||||
void smtc_timer_broadcast(void)
|
||||
{
|
||||
int cpu;
|
||||
int myTC = cpu_data[smp_processor_id()].tc_id;
|
||||
int myVPE = cpu_data[smp_processor_id()].vpe_id;
|
||||
|
||||
smtc_cpu_stats[smp_processor_id()].timerints++;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
if (cpu_data[cpu].vpe_id == myVPE &&
|
||||
cpu_data[cpu].tc_id != myTC)
|
||||
smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Cross-VPE interrupts in the SMTC prototype use "software interrupts"
|
||||
* set via cross-VPE MTTR manipulation of the Cause register. It would be
|
||||
|
@ -1180,11 +1177,11 @@ void smtc_idle_loop_hook(void)
|
|||
for (tc = 0; tc < NR_CPUS; tc++) {
|
||||
/* Don't check ourself - we'll dequeue IPIs just below */
|
||||
if ((tc != smp_processor_id()) &&
|
||||
ipi_timer_latch[tc] > timerq_limit) {
|
||||
atomic_read(&ipi_timer_latch[tc]) > timerq_limit) {
|
||||
if (clock_hang_reported[tc] == 0) {
|
||||
pdb_msg += sprintf(pdb_msg,
|
||||
"TC %d looks hung with timer latch at %d\n",
|
||||
tc, ipi_timer_latch[tc]);
|
||||
tc, atomic_read(&ipi_timer_latch[tc]));
|
||||
clock_hang_reported[tc]++;
|
||||
}
|
||||
}
|
||||
|
@ -1225,7 +1222,7 @@ void smtc_soft_dump(void)
|
|||
smtc_ipi_qdump();
|
||||
printk("Timer IPI Backlogs:\n");
|
||||
for (i=0; i < NR_CPUS; i++) {
|
||||
printk("%d: %d\n", i, ipi_timer_latch[i]);
|
||||
printk("%d: %d\n", i, atomic_read(&ipi_timer_latch[i]));
|
||||
}
|
||||
printk("%d Recoveries of \"stolen\" FPU\n",
|
||||
atomic_read(&smtc_fpu_recoveries));
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kallsyms.h>
|
||||
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/cache.h>
|
||||
|
@ -33,6 +34,7 @@
|
|||
#include <asm/cpu-features.h>
|
||||
#include <asm/div64.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/smtc_ipi.h>
|
||||
#include <asm/time.h>
|
||||
|
||||
#include <irq.h>
|
||||
|
@ -230,12 +232,24 @@ static int mips_next_event(unsigned long delta,
|
|||
struct clock_event_device *evt)
|
||||
{
|
||||
unsigned int cnt;
|
||||
int res;
|
||||
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
{
|
||||
unsigned long flags, vpflags;
|
||||
local_irq_save(flags);
|
||||
vpflags = dvpe();
|
||||
#endif
|
||||
cnt = read_c0_count();
|
||||
cnt += delta;
|
||||
write_c0_compare(cnt);
|
||||
|
||||
return ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
|
||||
res = ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
evpe(vpflags);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
static void mips_set_mode(enum clock_event_mode mode,
|
||||
|
@ -244,9 +258,7 @@ static void mips_set_mode(enum clock_event_mode mode,
|
|||
/* Nothing to do ... */
|
||||
}
|
||||
|
||||
struct clock_event_device mips_clockevent;
|
||||
|
||||
static struct clock_event_device *global_cd[NR_CPUS];
|
||||
static DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
|
||||
static int cp0_timer_irq_installed;
|
||||
|
||||
static irqreturn_t timer_interrupt(int irq, void *dev_id)
|
||||
|
@ -271,7 +283,12 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id)
|
|||
*/
|
||||
if (!r2 || (read_c0_cause() & (1 << 30))) {
|
||||
c0_timer_ack();
|
||||
cd = global_cd[cpu];
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
if (cpu_data[cpu].vpe_id)
|
||||
goto out;
|
||||
cpu = 0;
|
||||
#endif
|
||||
cd = &per_cpu(mips_clockevent_device, cpu);
|
||||
cd->event_handler(cd);
|
||||
}
|
||||
|
||||
|
@ -281,7 +298,11 @@ out:
|
|||
|
||||
static struct irqaction timer_irqaction = {
|
||||
.handler = timer_interrupt,
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
.flags = IRQF_DISABLED,
|
||||
#else
|
||||
.flags = IRQF_DISABLED | IRQF_PERCPU,
|
||||
#endif
|
||||
.name = "timer",
|
||||
};
|
||||
|
||||
|
@ -316,6 +337,60 @@ void __init __weak plat_timer_setup(struct irqaction *irq)
|
|||
{
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
DEFINE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
|
||||
|
||||
static void smtc_set_mode(enum clock_event_mode mode,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
}
|
||||
|
||||
int dummycnt[NR_CPUS];
|
||||
|
||||
static void mips_broadcast(cpumask_t mask)
|
||||
{
|
||||
unsigned int cpu;
|
||||
|
||||
for_each_cpu_mask(cpu, mask)
|
||||
smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0);
|
||||
}
|
||||
|
||||
static void setup_smtc_dummy_clockevent_device(void)
|
||||
{
|
||||
//uint64_t mips_freq = mips_hpt_^frequency;
|
||||
unsigned int cpu = smp_processor_id();
|
||||
struct clock_event_device *cd;
|
||||
|
||||
cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
|
||||
|
||||
cd->name = "SMTC";
|
||||
cd->features = CLOCK_EVT_FEAT_DUMMY;
|
||||
|
||||
/* Calculate the min / max delta */
|
||||
cd->mult = 0; //div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
|
||||
cd->shift = 0; //32;
|
||||
cd->max_delta_ns = 0; //clockevent_delta2ns(0x7fffffff, cd);
|
||||
cd->min_delta_ns = 0; //clockevent_delta2ns(0x30, cd);
|
||||
|
||||
cd->rating = 200;
|
||||
cd->irq = 17; //-1;
|
||||
// if (cpu)
|
||||
// cd->cpumask = CPU_MASK_ALL; // cpumask_of_cpu(cpu);
|
||||
// else
|
||||
cd->cpumask = cpumask_of_cpu(cpu);
|
||||
|
||||
cd->set_mode = smtc_set_mode;
|
||||
|
||||
cd->broadcast = mips_broadcast;
|
||||
|
||||
clockevents_register_device(cd);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void mips_event_handler(struct clock_event_device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
void __cpuinit mips_clockevent_init(void)
|
||||
{
|
||||
uint64_t mips_freq = mips_hpt_frequency;
|
||||
|
@ -326,12 +401,18 @@ void __cpuinit mips_clockevent_init(void)
|
|||
if (!cpu_has_counter)
|
||||
return;
|
||||
|
||||
if (cpu == 0)
|
||||
cd = &mips_clockevent;
|
||||
else
|
||||
cd = kzalloc(sizeof(*cd), GFP_ATOMIC);
|
||||
if (!cd)
|
||||
return; /* We're probably roadkill ... */
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
setup_smtc_dummy_clockevent_device();
|
||||
|
||||
/*
|
||||
* On SMTC we only register VPE0's compare interrupt as clockevent
|
||||
* device.
|
||||
*/
|
||||
if (cpu)
|
||||
return;
|
||||
#endif
|
||||
|
||||
cd = &per_cpu(mips_clockevent_device, cpu);
|
||||
|
||||
cd->name = "MIPS";
|
||||
cd->features = CLOCK_EVT_FEAT_ONESHOT;
|
||||
|
@ -344,11 +425,15 @@ void __cpuinit mips_clockevent_init(void)
|
|||
|
||||
cd->rating = 300;
|
||||
cd->irq = irq;
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
cd->cpumask = CPU_MASK_ALL;
|
||||
#else
|
||||
cd->cpumask = cpumask_of_cpu(cpu);
|
||||
#endif
|
||||
cd->set_next_event = mips_next_event;
|
||||
cd->set_mode = mips_set_mode;
|
||||
cd->event_handler = mips_event_handler;
|
||||
|
||||
global_cd[cpu] = cd;
|
||||
clockevents_register_device(cd);
|
||||
|
||||
if (!cp0_timer_irq_installed) {
|
||||
|
|
|
@ -55,7 +55,6 @@ unsigned long cpu_khz;
|
|||
|
||||
static int mips_cpu_timer_irq;
|
||||
extern int cp0_perfcount_irq;
|
||||
extern void smtc_timer_broadcast(void);
|
||||
|
||||
static void mips_timer_dispatch(void)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче