The broadcast timer could be passed as parameter to the function instead of using again tick_broadcast_device.evtdev which was previously used in the caller function.
Signed-off-by: Daniel Lezcano daniel.lezcano@linaro.org --- kernel/time/tick-broadcast.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index f113755..baf9b0e7 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -370,10 +370,9 @@ struct cpumask *tick_get_broadcast_oneshot_mask(void) return to_cpumask(tick_broadcast_oneshot_mask); }
-static int tick_broadcast_set_event(ktime_t expires, int force) +static int tick_broadcast_set_event(struct clock_event_device *bc, + ktime_t expires, int force) { - struct clock_event_device *bc = tick_broadcast_device.evtdev; - if (bc->mode != CLOCK_EVT_MODE_ONESHOT) clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT);
@@ -443,7 +442,7 @@ again: * Rearm the broadcast device. If event expired, * repeat the above */ - if (tick_broadcast_set_event(next_event, 0)) + if (tick_broadcast_set_event(dev, next_event, 0)) goto again; } raw_spin_unlock(&tick_broadcast_lock); @@ -486,7 +485,7 @@ void tick_broadcast_oneshot_control(unsigned long reason) cpumask_set_cpu(cpu, tick_get_broadcast_oneshot_mask()); clockevents_set_mode(dev, CLOCK_EVT_MODE_SHUTDOWN); if (dev->next_event.tv64 < bc->next_event.tv64) - tick_broadcast_set_event(dev->next_event, 1); + tick_broadcast_set_event(bc, dev->next_event, 1); } } else { if (cpumask_test_cpu(cpu, tick_get_broadcast_oneshot_mask())) { @@ -555,7 +554,7 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc) clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); tick_broadcast_init_next_event(to_cpumask(tmpmask), tick_next_period); - tick_broadcast_set_event(tick_next_period, 1); + tick_broadcast_set_event(bc, tick_next_period, 1); } else bc->next_event.tv64 = KTIME_MAX; } else {
When a cpu goes to a deep idle state where its local timer is shutdown, it notifies the time frame work to use the broadcast timer instead.
Unfortunately, the broadcast device could wake up any CPU, including an idle one which is not concerned by the wake up at all.
This implies, in the worst case, an idle CPU will wake up to send an IPI to another idle cpu.
This patch solves this by setting the irq affinity to the cpu concerned by the nearest timer event, by this way, the CPU which is wake up is guarantee to be the one concerned by the next event and we are safe with unnecessary wakeup for another idle CPU.
As the irq affinity is not supported by all the archs, a flag is needed to specify which clocksource can handle it.
Signed-off-by: Daniel Lezcano daniel.lezcano@linaro.org --- include/linux/clockchips.h | 1 + kernel/time/tick-broadcast.c | 39 ++++++++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 7 deletions(-)
diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index 8a7096f..5cedb27 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -54,6 +54,7 @@ enum clock_event_nofitiers { */ #define CLOCK_EVT_FEAT_C3STOP 0x000008 #define CLOCK_EVT_FEAT_DUMMY 0x000010 +#define CLOCK_EVT_FEAT_DYNIRQ 0x000020
/** * struct clock_event_device - clock event device descriptor diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index baf9b0e7..cbd6737 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -370,13 +370,36 @@ struct cpumask *tick_get_broadcast_oneshot_mask(void) return to_cpumask(tick_broadcast_oneshot_mask); }
-static int tick_broadcast_set_event(struct clock_event_device *bc, +/* + * Set broadcast interrupt affinity + */ +static void tick_broadcast_set_affinity(struct clock_event_device *bc, int cpu) +{ + struct cpumask cpumask; + + if (!(bc->features & CLOCK_EVT_FEAT_DYNIRQ)) + return; + + cpumask_clear(&cpumask); + cpumask_set_cpu(cpu, &cpumask); + irq_set_affinity(bc->irq, &cpumask); +} + +static int tick_broadcast_set_event(struct clock_event_device *bc, int cpu, ktime_t expires, int force) { + int ret; + if (bc->mode != CLOCK_EVT_MODE_ONESHOT) clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT);
- return clockevents_program_event(bc, expires, force); + ret = clockevents_program_event(bc, expires, force); + if (ret) + return ret; + + tick_broadcast_set_affinity(bc, cpu); + + return 0; }
int tick_resume_broadcast_oneshot(struct clock_event_device *bc) @@ -405,7 +428,7 @@ static void tick_handle_oneshot_broadcast(struct clock_event_device *dev) { struct tick_device *td; ktime_t now, next_event; - int cpu; + int cpu, next_cpu;
raw_spin_lock(&tick_broadcast_lock); again: @@ -418,8 +441,10 @@ again: td = &per_cpu(tick_cpu_device, cpu); if (td->evtdev->next_event.tv64 <= now.tv64) cpumask_set_cpu(cpu, to_cpumask(tmpmask)); - else if (td->evtdev->next_event.tv64 < next_event.tv64) + else if (td->evtdev->next_event.tv64 < next_event.tv64) { next_event.tv64 = td->evtdev->next_event.tv64; + next_cpu = cpu; + } }
/* @@ -442,7 +467,7 @@ again: * Rearm the broadcast device. If event expired, * repeat the above */ - if (tick_broadcast_set_event(dev, next_event, 0)) + if (tick_broadcast_set_event(dev, next_cpu, next_event, 0)) goto again; } raw_spin_unlock(&tick_broadcast_lock); @@ -485,7 +510,7 @@ void tick_broadcast_oneshot_control(unsigned long reason) cpumask_set_cpu(cpu, tick_get_broadcast_oneshot_mask()); clockevents_set_mode(dev, CLOCK_EVT_MODE_SHUTDOWN); if (dev->next_event.tv64 < bc->next_event.tv64) - tick_broadcast_set_event(bc, dev->next_event, 1); + tick_broadcast_set_event(bc, cpu, dev->next_event, 1); } } else { if (cpumask_test_cpu(cpu, tick_get_broadcast_oneshot_mask())) { @@ -554,7 +579,7 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc) clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); tick_broadcast_init_next_event(to_cpumask(tmpmask), tick_next_period); - tick_broadcast_set_event(bc, tick_next_period, 1); + tick_broadcast_set_event(bc, cpu, tick_next_period, 1); } else bc->next_event.tv64 = KTIME_MAX; } else {
On Thu, 21 Feb 2013 23:01:23 +0100 Daniel Lezcano daniel.lezcano@linaro.org wrote:
+/*
- Set broadcast interrupt affinity
- */
+static void tick_broadcast_set_affinity(struct clock_event_device *bc, int cpu) +{
- struct cpumask cpumask;
- if (!(bc->features & CLOCK_EVT_FEAT_DYNIRQ))
return;
- cpumask_clear(&cpumask);
- cpumask_set_cpu(cpu, &cpumask);
- irq_set_affinity(bc->irq, &cpumask);
would it be more efficient to keep track of the current bc->irq affinity via cpumask then set it only if it is different?
On Fri, 22 Feb 2013, Jacob Pan wrote:
On Thu, 21 Feb 2013 23:01:23 +0100 Daniel Lezcano daniel.lezcano@linaro.org wrote:
+/*
- Set broadcast interrupt affinity
- */
+static void tick_broadcast_set_affinity(struct clock_event_device *bc, int cpu) +{
- struct cpumask cpumask;
- if (!(bc->features & CLOCK_EVT_FEAT_DYNIRQ))
return;
- cpumask_clear(&cpumask);
- cpumask_set_cpu(cpu, &cpumask);
- irq_set_affinity(bc->irq, &cpumask);
would it be more efficient to keep track of the current bc->irq affinity via cpumask then set it only if it is different?
You beat me :)
On 02/22/2013 06:55 PM, Jacob Pan wrote:
On Thu, 21 Feb 2013 23:01:23 +0100 Daniel Lezcano daniel.lezcano@linaro.org wrote:
+/*
- Set broadcast interrupt affinity
- */
+static void tick_broadcast_set_affinity(struct clock_event_device *bc, int cpu) +{
- struct cpumask cpumask;
- if (!(bc->features & CLOCK_EVT_FEAT_DYNIRQ))
return;
- cpumask_clear(&cpumask);
- cpumask_set_cpu(cpu, &cpumask);
- irq_set_affinity(bc->irq, &cpumask);
would it be more efficient to keep track of the current bc->irq affinity via cpumask then set it only if it is different?
Do you mean a cpumask static variable ? and something like:
if (!cpumask_test_cpu(cpu, &affinitymask)) { cpumask_set_cpu(cpu, &affinitymask); irq_set_affinity(bc->irq, &affinitymask) }
On Mon, 25 Feb 2013 23:50:23 +0100 Daniel Lezcano daniel.lezcano@linaro.org wrote:
Do you mean a cpumask static variable ? and something like:
if (!cpumask_test_cpu(cpu, &affinitymask)) { cpumask_set_cpu(cpu, &affinitymask); irq_set_affinity(bc->irq, &affinitymask) }
yeah. but i think you can use the cpumask in struct clock_event_device.
On 22 February 2013 03:31, Daniel Lezcano daniel.lezcano@linaro.org wrote:
The broadcast timer could be passed as parameter to the function instead of using again tick_broadcast_device.evtdev which was previously used in the caller function.
Signed-off-by: Daniel Lezcano daniel.lezcano@linaro.org
I know you are going for another round with this patchset and was just trying v1.
I did my tests on ARM Vexpress - TC2, big.LITTLE Arch.
Tested-by: Viresh Kumar viresh.kumar@linaro.org
On 02/26/2013 09:45 AM, Viresh Kumar wrote:
On 22 February 2013 03:31, Daniel Lezcano daniel.lezcano@linaro.org wrote:
The broadcast timer could be passed as parameter to the function instead of using again tick_broadcast_device.evtdev which was previously used in the caller function.
Signed-off-by: Daniel Lezcano daniel.lezcano@linaro.org
I know you are going for another round with this patchset and was just trying v1.
I did my tests on ARM Vexpress - TC2, big.LITTLE Arch.
Tested-by: Viresh Kumar viresh.kumar@linaro.org
Thanks Viresh for testing.
On 02/26/2013 09:45 AM, Viresh Kumar wrote:
On 22 February 2013 03:31, Daniel Lezcano daniel.lezcano@linaro.org wrote:
The broadcast timer could be passed as parameter to the function instead of using again tick_broadcast_device.evtdev which was previously used in the caller function.
Signed-off-by: Daniel Lezcano daniel.lezcano@linaro.org
I know you are going for another round with this patchset and was just trying v1.
I did my tests on ARM Vexpress - TC2, big.LITTLE Arch.
Tested-by: Viresh Kumar viresh.kumar@linaro.org
Oh, by the way, could send me the patch to set the flag to the timer device ? I will include it to the patchset.
Thanks -- Daniel
On 26 February 2013 17:01, Daniel Lezcano daniel.lezcano@linaro.org wrote:
Oh, by the way, could send me the patch to set the flag to the timer device ? I will include it to the patchset.
Sure. Find it attached too as gmail may break it.
commit 14422c760bb5b2485867f3efb7842d296081ad86 Author: Viresh Kumar viresh.kumar@linaro.org Date: Fri Feb 22 12:42:39 2013 +0530
ARM/timer-sp: Set dynamic irq affinity
When a cpu goes to a deep idle state where its local timer is shutdown, it notifies the time frame work to use the broadcast timer instead.
Unfortunately, the broadcast device could wake up any CPU, including an idle one which is not concerned by the wake up at all.
This implies, in the worst case, an idle CPU will wake up to send an IPI to another idle cpu.
This patch fixes this for ARM platforms using timer-sp, by setting CLOCK_EVT_FEAT_DYNIRQ feature.
Signed-off-by: Viresh Kumar viresh.kumar@linaro.org --- arch/arm/common/timer-sp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/arm/common/timer-sp.c b/arch/arm/common/timer-sp.c index 9d2d3ba..ae3c0f9 100644 --- a/arch/arm/common/timer-sp.c +++ b/arch/arm/common/timer-sp.c @@ -158,7 +158,8 @@ static int sp804_set_next_event(unsigned long next, }
static struct clock_event_device sp804_clockevent = { - .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | + CLOCK_EVT_FEAT_DYNIRQ, .set_mode = sp804_set_mode, .set_next_event = sp804_set_next_event, .rating = 300,
linaro-kernel@lists.linaro.org