Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1480315 > unrolled thread
| Started by | Nicolai Stange <nicstange@gmail.com> |
|---|---|
| First post | 2016-09-09 22:10 +0200 |
| Last post | 2016-09-09 22:30 +0200 |
| Articles | 11 — 1 participant |
Back to article view | Back to linux.kernel
[RFC v6 00/23] adapt clockevents frequencies to mono clock Nicolai Stange <nicstange@gmail.com> - 2016-09-09 22:10 +0200
[RFC v6 07/23] many clockevent drivers: set ->min_delta_ticks and ->max_delta_ticks Nicolai Stange <nicstange@gmail.com> - 2016-09-09 22:10 +0200
[RFC v6 04/23] clocksource: em_sti: compute rate before registration Nicolai Stange <nicstange@gmail.com> - 2016-09-09 22:10 +0200
[RFC v6 19/23] timer_list: print_tickdevice(): calculate ->min_delta_ns dynamically Nicolai Stange <nicstange@gmail.com> - 2016-09-09 22:20 +0200
[RFC v6 22/23] clockevents: make setting of ->mult and ->mult_adjusted atomic Nicolai Stange <nicstange@gmail.com> - 2016-09-09 22:20 +0200
[RFC v6 18/23] clockevents: min delta increment: calculate min_delta_ns from ticks Nicolai Stange <nicstange@gmail.com> - 2016-09-09 22:20 +0200
[RFC v6 21/23] clockevents: initial support for mono to raw time conversion Nicolai Stange <nicstange@gmail.com> - 2016-09-09 22:20 +0200
[RFC v6 20/23] clockevents: purge ->min_delta_ns Nicolai Stange <nicstange@gmail.com> - 2016-09-09 22:20 +0200
[RFC v6 23/23] timekeeping: inform clockevents about freq adjustments Nicolai Stange <nicstange@gmail.com> - 2016-09-09 22:20 +0200
[RFC v6 16/23] clockevents: clockevents_program_min_delta(): don't set ->next_event Nicolai Stange <nicstange@gmail.com> - 2016-09-09 22:20 +0200
[RFC v6 17/23] clockevents: use ->min_delta_ticks_adjusted to program minimum delta Nicolai Stange <nicstange@gmail.com> - 2016-09-09 22:30 +0200
| From | Nicolai Stange <nicstange@gmail.com> |
|---|---|
| Date | 2016-09-09 22:10 +0200 |
| Subject | [RFC v6 00/23] adapt clockevents frequencies to mono clock |
| Message-ID | <sfAhA-7rj-3@gated-at.bofh.it> |
Goal: avoid programming ced devices too early for large deltas, for
details, c.f. the description of [21/23].
This v6 fixes two issues I realized after sending v5 out.
The latter was meant to incorporate your feedback to v4.
Previous v4 of this series can be found here:
http://lkml.kernel.org/r/20160822233320.4548-1-nicstange@gmail.com
and v5 here:
http://lkml.kernel.org/r/20160904010200.17200-1-nicstange@gmail.com
Concerns raised to v4 were
1.) There should be a flag available which when enabled, should
exclude a ced from getting its ->mult adjusted.
2.) The ns2cyc multiplication in clockevents_program_event() can
overflow with the adjusted ->mult.
The first item is addressed by the new
[13/23] ("clockevents: introduce CLOCK_EVT_FEAT_NO_ADJUST flag"),
the second by
[14/23] ("clockevents: decouple ->max_delta_ns from ->max_delta_ticks")
A note regarding the CLOCK_EVT_FEAT_NO_ADJUST flag: we probably want
to set it for the ->mult = 1, ->shift = 0 devices. However, since the
frequency adjustments are small (and bounded), they actually translate
to noops for those ceds. Thus, setting it there would be a matter of
style and optimization and I left this out for now.
This series can be divided into logical subseries as follows:
[1-6/23] Don't modify ced rate after registrations through mechanisms
other than clockevents_update_freq().
[7-12/23] Let all ced devices set their ->*_delta_ticks values and let
the clockevent core do the rest.
[13/23] Introduce the CLOCK_EVT_FEAT_NO_ADJUST flag
[14-20/23] Fiddle around with the bound checking code in order to
allow for non-atomic frequency updates from a CPU different
than where the ced is programmed.
[21-23/23] Actually do the frequency adjustments.
Tested on x86_64 and next-20160825.
Changes to v5:
[21/23] ("clockevents: initial support for mono to raw time conversion")
Replace the max_t() in
adj = max_t(u64, adj, mult_ce_raw / 8);
by min_t(): mult_ce_raw / 8 actually sets an upper bound on the
mult adjustments.
[23/23] ("timekeeping: inform clockevents about freq adjustments")
Move the clockevents_adjust_all_freqs() invocation from
timekeeping_apply_adjustment() to timekeeping_freqadjust().
Reason is given in the patch description.
Changes to v4:
[1-12/23] Unchanged
[13/23] ("clockevents: introduce CLOCK_EVT_FEAT_NO_ADJUST flag")
New.
[14/23] ("clockevents: decouple ->max_delta_ns from ->max_delta_ticks")
New. Solves the overflow problem the former
[13/22] ("clockevents: check a programmed delta's bounds in terms of cycles")
from v4 introduced.
(Note that the former
[14/22] ("clockevents: clockevents_program_event(): turn clc into unsigned long")
from v4 has been purged.)
[15/23] ("clockevents: do comparison of delta against minimum in terms of cycles")
This is the former
[13/22] ("clockevents: check a programmed delta's bounds in terms of cycles"),
but only for the ->min_delta_* -- the ->max_delta_* are handled by [14/23] now.
[16/23] ("clockevents: clockevents_program_min_delta(): don't set ->next_event")
Former [15/22] unchanged.
[17/23] ("clockevents: use ->min_delta_ticks_adjusted to program minimum delta")
Former [16/22]. Trivially fix compilation error with
CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=n.
[18/22] ("clockevents: min delta increment: calculate min_delta_ns from ticks")
Former [17/22] unchanged.
[19/23] ("timer_list: print_tickdevice(): calculate ->min_delta_ns dynamically")
Corresponds to former
[18/22] ("timer_list: print_tickdevice(): calculate ->*_delta_ns dynamically")
from v4, but only for ->min_delta_ns. The changes required for the display of
->max_delta_ns are now being made in [14/23] already.
[20/23] ("clockevents: purge ->min_delta_ns")
Corresponds to former
[19/22] ("clockevents: purge ->min_delta_ns and ->max_delta_ns"),
but with ->max_delta_ns being kept.
[21/23] ("clockevents: initial support for mono to raw time conversion")
Former [20/22] with the following changes:
- Don't adjust mult for those ced's that have CLOCK_EVT_FEAT_NO_ADJUST set.
- Don't meld __clockevents_update_bounds() into __clockevents_adjust_freq()
anymore: the bounds for those devices having CLOCK_EVT_FEAT_NO_ADJUST set
must have got their bounds set as well.
- In __clockevents_calc_adjust_freq(), make sure that the adjusted mult
doesn't exceed the original by more than 12.5%. C.f. [14/23].
- In timekeeping, define timekeeping_get_mono_mult() only for
CONFIG_GENERIC_CLOCKEVENTS=y.
[22/23] ("clockevents: make setting of ->mult and ->mult_adjusted atomic")
Former [12/22], but with the description updated: previously, it said that
this patch would introduce a new locking dependency. This is not true.
[23/23] ("timekeeping: inform clockevents about freq adjustments")
Former [22/22] with the following changes:
- Don't adjust mult for those ced's that have CLOCK_EVT_FEAT_NO_ADJUST set.
- In clockevents_adjust_all_freqs(), reuse the adjusted cached mult only
if the associated ->shift also matches.
- Introduce noop clockevents_adjust_all_freqs() in order to fix a
compilation error with CONFIG_GENERIC_CLOCKEVENTS=n.
Nicolai Stange (23):
clocksource: sh_cmt: compute rate before registration again
clocksource: sh_tmu: compute rate before registration again
clocksource: em_sti: split clock prepare and enable steps
clocksource: em_sti: compute rate before registration
clocksource: h8300_timer8: don't reset rate in ->set_state_oneshot()
clockevents: make clockevents_config() static
many clockevent drivers: set ->min_delta_ticks and ->max_delta_ticks
arch/s390/kernel/time: set ->min_delta_ticks and ->max_delta_ticks
arch/x86/platform/uv/uv_time: set ->min_delta_ticks and
->max_delta_ticks
arch/tile/kernel/time: set ->min_delta_ticks and ->max_delta_ticks
clockevents: always initialize ->min_delta_ns and ->max_delta_ns
many clockevent drivers: don't set ->min_delta_ns and ->max_delta_ns
clockevents: introduce CLOCK_EVT_FEAT_NO_ADJUST flag
clockevents: decouple ->max_delta_ns from ->max_delta_ticks
clockevents: do comparison of delta against minimum in terms of cycles
clockevents: clockevents_program_min_delta(): don't set ->next_event
clockevents: use ->min_delta_ticks_adjusted to program minimum delta
clockevents: min delta increment: calculate min_delta_ns from ticks
timer_list: print_tickdevice(): calculate ->min_delta_ns dynamically
clockevents: purge ->min_delta_ns
clockevents: initial support for mono to raw time conversion
clockevents: make setting of ->mult and ->mult_adjusted atomic
timekeeping: inform clockevents about freq adjustments
arch/avr32/kernel/time.c | 4 +-
arch/blackfin/kernel/time-ts.c | 8 +-
arch/c6x/platforms/timer64.c | 4 +-
arch/hexagon/kernel/time.c | 4 +-
arch/m68k/coldfire/pit.c | 6 +-
arch/microblaze/kernel/timer.c | 6 +-
arch/mips/alchemy/common/time.c | 4 +-
arch/mips/jz4740/time.c | 4 +-
arch/mips/kernel/cevt-bcm1480.c | 4 +-
arch/mips/kernel/cevt-ds1287.c | 4 +-
arch/mips/kernel/cevt-gt641xx.c | 4 +-
arch/mips/kernel/cevt-sb1250.c | 4 +-
arch/mips/kernel/cevt-txx9.c | 5 +-
arch/mips/loongson32/common/time.c | 4 +-
arch/mips/loongson64/common/cs5536/cs5536_mfgpt.c | 4 +-
arch/mips/loongson64/loongson-3/hpet.c | 4 +-
arch/mips/ralink/cevt-rt3352.c | 4 +-
arch/mips/sgi-ip27/ip27-timer.c | 4 +-
arch/mn10300/kernel/cevt-mn10300.c | 4 +-
arch/powerpc/kernel/time.c | 6 +-
arch/s390/kernel/time.c | 4 +-
arch/score/kernel/time.c | 6 +-
arch/sparc/kernel/time_32.c | 4 +-
arch/sparc/kernel/time_64.c | 6 +-
arch/tile/kernel/time.c | 4 +-
arch/um/kernel/time.c | 4 +-
arch/unicore32/kernel/time.c | 6 +-
arch/x86/kernel/apic/apic.c | 12 +-
arch/x86/lguest/boot.c | 4 +-
arch/x86/platform/uv/uv_time.c | 6 +-
arch/x86/xen/time.c | 8 +-
drivers/clocksource/dw_apb_timer.c | 5 +-
drivers/clocksource/em_sti.c | 49 +++--
drivers/clocksource/h8300_timer8.c | 8 -
drivers/clocksource/metag_generic.c | 4 +-
drivers/clocksource/numachip.c | 4 +-
drivers/clocksource/sh_cmt.c | 50 ++---
drivers/clocksource/sh_tmu.c | 26 +--
drivers/clocksource/timer-atlas7.c | 4 +-
include/linux/clockchips.h | 21 +-
kernel/time/clockevents.c | 221 ++++++++++++++++++----
kernel/time/tick-broadcast-hrtimer.c | 2 -
kernel/time/tick-internal.h | 6 +
kernel/time/timekeeping.c | 16 ++
kernel/time/timer_list.c | 9 +-
45 files changed, 376 insertions(+), 204 deletions(-)
--
2.9.3
[toc] | [next] | [standalone]
| From | Nicolai Stange <nicstange@gmail.com> |
|---|---|
| Date | 2016-09-09 22:10 +0200 |
| Subject | [RFC v6 07/23] many clockevent drivers: set ->min_delta_ticks and ->max_delta_ticks |
| Message-ID | <sfAhB-7rj-55@gated-at.bofh.it> |
| In reply to | #1480315 |
The struct clock_event_device has got the ->min_delta_ns, ->max_delta_ns,
->min_delta_ticks and ->max_delta_ticks members for setting the bounds
of valid deltas to be programmed.
During operation, the clockevent core uses the ->*_delta_ns versions only.
OTOH, the ->*_delta_ticks are currently used solely for the calculations of
the ->*_delta_ns values in clockevents_config().
From the hardware's point of view, giving the valid ranges in terms of
cycles, i.e. the ->*_delta_ticks values, is the natural choice.
Right now, as the ratio of ns/cycles is fixed, it doesn't matter much which
unit of measurement is actually used by the clockevents core. (Well, apart
from the fact that nearly all drivers which set the ->*_delta_ns manually
don't round towards the correct direction).
This situation is going to change though: as NTP time correction awareness
will be introduced to the clockevents core, the ratio ns/cycles will cease
to stay fixed at all times.
In conclusion, every driver should hand its valid range in terms of an
invariant unit, i.e. cycles, to the clockevent core.
Those drivers which configure their clockevent devices via
clockevents_config_and_register() already do this.
This patch makes the vast majority of the remaining drivers do that, too.
For the sake of bisectability, the manual settings of the ->*delta_ns
by drivers will be removed by a later patch.
This patch was created with the help of the Coccinelle script below. It is
noteworthy that for constructs like
ced.min_delta_ns = clockevent_delta2ns(<cycles>, &ced) + 1;
it is assumed that the +1 addition is done in order to compensate for
any rounding introduced by the conversion of cycles to ns and thus,
is irrelevant for ->min_delta_cycles.
@@
expression ced, cycles;
@@
ced->min_delta_ns = clockevent_delta2ns(cycles, ced);
+ ced->min_delta_ticks = cycles;
@min@
expression ced, cycles;
@@
ced.min_delta_ns = clockevent_delta2ns(cycles, &ced);
+ ced.min_delta_ticks = cycles;
@min_round_up depends on !min@
expression ced, cycles;
@@
ced.min_delta_ns = clockevent_delta2ns(cycles, &ced) + 1;
+ ced.min_delta_ticks = cycles;
@@
identifier ced;
expression ns;
@@
struct clock_event_device ced = {
.mult = 1,
.shift = 0,
.min_delta_ns = ns,
+ .min_delta_ticks = ns,
};
@p_max_neg_const@
expression ced;
constant int cycles;
@@
ced->max_delta_ns = clockevent_delta2ns(-cycles, ced);
+ ced->max_delta_ticks = (unsigned long)-cycles;
@p_max depends on !p_max_neg_const@
expression ced, cycles;
@@
ced->max_delta_ns = clockevent_delta2ns(cycles, ced);
+ ced->max_delta_ticks = cycles;
@@
expression ced, cycles;
@@
ced.max_delta_ns = clockevent_delta2ns(cycles, &ced);
+ ced.max_delta_ticks = cycles;
@@
identifier ced;
expression ns;
@@
struct clock_event_device ced = {
.mult = 1,
.shift = 0,
.max_delta_ns = ns,
+ .max_delta_ticks = ns,
};
This patch differs from what spatch created only in some whitespace
adjustments and an explicit cast added in arch/sparc/kernel/time_32.c.
Signed-off-by: Nicolai Stange <nicstange@gmail.com>
---
arch/avr32/kernel/time.c | 2 ++
arch/blackfin/kernel/time-ts.c | 4 ++++
arch/c6x/platforms/timer64.c | 2 ++
arch/hexagon/kernel/time.c | 2 ++
arch/m68k/coldfire/pit.c | 2 ++
arch/microblaze/kernel/timer.c | 2 ++
arch/mips/alchemy/common/time.c | 4 +++-
arch/mips/jz4740/time.c | 2 ++
arch/mips/kernel/cevt-bcm1480.c | 2 ++
arch/mips/kernel/cevt-ds1287.c | 2 ++
arch/mips/kernel/cevt-gt641xx.c | 2 ++
arch/mips/kernel/cevt-sb1250.c | 2 ++
arch/mips/kernel/cevt-txx9.c | 2 ++
arch/mips/loongson32/common/time.c | 2 ++
arch/mips/loongson64/common/cs5536/cs5536_mfgpt.c | 2 ++
arch/mips/loongson64/loongson-3/hpet.c | 2 ++
arch/mips/ralink/cevt-rt3352.c | 2 ++
arch/mips/sgi-ip27/ip27-timer.c | 2 ++
arch/mn10300/kernel/cevt-mn10300.c | 2 ++
arch/powerpc/kernel/time.c | 2 ++
arch/score/kernel/time.c | 2 ++
arch/sparc/kernel/time_32.c | 2 ++
arch/sparc/kernel/time_64.c | 2 ++
arch/um/kernel/time.c | 4 +++-
arch/unicore32/kernel/time.c | 2 ++
arch/x86/kernel/apic/apic.c | 4 ++++
arch/x86/lguest/boot.c | 2 ++
arch/x86/xen/time.c | 4 ++++
drivers/clocksource/dw_apb_timer.c | 2 ++
drivers/clocksource/metag_generic.c | 2 ++
drivers/clocksource/numachip.c | 2 ++
drivers/clocksource/sh_cmt.c | 2 ++
drivers/clocksource/timer-atlas7.c | 2 ++
33 files changed, 74 insertions(+), 2 deletions(-)
diff --git a/arch/avr32/kernel/time.c b/arch/avr32/kernel/time.c
index a124c55..3fff4c9 100644
--- a/arch/avr32/kernel/time.c
+++ b/arch/avr32/kernel/time.c
@@ -142,7 +142,9 @@ void __init time_init(void)
/* setup COMPARE clockevent */
comparator.mult = div_sc(counter_hz, NSEC_PER_SEC, comparator.shift);
comparator.max_delta_ns = clockevent_delta2ns((u32)~0, &comparator);
+ comparator.max_delta_ticks = (u32)~0;
comparator.min_delta_ns = clockevent_delta2ns(50, &comparator) + 1;
+ comparator.min_delta_ticks = 50;
comparator.cpumask = cpumask_of(0);
sysreg_write(COMPARE, 0);
diff --git a/arch/blackfin/kernel/time-ts.c b/arch/blackfin/kernel/time-ts.c
index fb9e95f..4c93b6f 100644
--- a/arch/blackfin/kernel/time-ts.c
+++ b/arch/blackfin/kernel/time-ts.c
@@ -230,7 +230,9 @@ static void __init bfin_gptmr0_clockevent_init(struct clock_event_device *evt)
clock_tick = get_sclk();
evt->mult = div_sc(clock_tick, NSEC_PER_SEC, evt->shift);
evt->max_delta_ns = clockevent_delta2ns(-1, evt);
+ evt->max_delta_ticks = (unsigned long)-1;
evt->min_delta_ns = clockevent_delta2ns(100, evt);
+ evt->min_delta_ticks = 100;
evt->cpumask = cpumask_of(0);
@@ -344,7 +346,9 @@ void bfin_coretmr_clockevent_init(void)
clock_tick = get_cclk() / TIME_SCALE;
evt->mult = div_sc(clock_tick, NSEC_PER_SEC, evt->shift);
evt->max_delta_ns = clockevent_delta2ns(-1, evt);
+ evt->max_delta_ticks = (unsigned long)-1;
evt->min_delta_ns = clockevent_delta2ns(100, evt);
+ evt->min_delta_ticks = 100;
evt->cpumask = cpumask_of(cpu);
diff --git a/arch/c6x/platforms/timer64.c b/arch/c6x/platforms/timer64.c
index c19901e..0bd0452 100644
--- a/arch/c6x/platforms/timer64.c
+++ b/arch/c6x/platforms/timer64.c
@@ -234,7 +234,9 @@ void __init timer64_init(void)
clockevents_calc_mult_shift(cd, c6x_core_freq / TIMER_DIVISOR, 5);
cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
+ cd->max_delta_ticks = 0x7fffffff;
cd->min_delta_ns = clockevent_delta2ns(250, cd);
+ cd->min_delta_ticks = 250;
cd->cpumask = cpumask_of(smp_processor_id());
diff --git a/arch/hexagon/kernel/time.c b/arch/hexagon/kernel/time.c
index a6a1d1f..fbdeac1 100644
--- a/arch/hexagon/kernel/time.c
+++ b/arch/hexagon/kernel/time.c
@@ -199,7 +199,9 @@ void __init time_init_deferred(void)
clockevents_calc_mult_shift(ce_dev, sleep_clk_freq, 4);
ce_dev->max_delta_ns = clockevent_delta2ns(0x7fffffff, ce_dev);
+ ce_dev->max_delta_ticks = 0x7fffffff;
ce_dev->min_delta_ns = clockevent_delta2ns(0xf, ce_dev);
+ ce_dev->min_delta_ticks = 0xf;
#ifdef CONFIG_SMP
setup_percpu_clockdev();
diff --git a/arch/m68k/coldfire/pit.c b/arch/m68k/coldfire/pit.c
index d86a9ff..91850e7 100644
--- a/arch/m68k/coldfire/pit.c
+++ b/arch/m68k/coldfire/pit.c
@@ -149,8 +149,10 @@ void hw_timer_init(irq_handler_t handler)
cf_pit_clockevent.mult = div_sc(FREQ, NSEC_PER_SEC, 32);
cf_pit_clockevent.max_delta_ns =
clockevent_delta2ns(0xFFFF, &cf_pit_clockevent);
+ cf_pit_clockevent.max_delta_ticks = 0xFFFF;
cf_pit_clockevent.min_delta_ns =
clockevent_delta2ns(0x3f, &cf_pit_clockevent);
+ cf_pit_clockevent.min_delta_ticks = 0x3f;
clockevents_register_device(&cf_pit_clockevent);
setup_irq(MCF_IRQ_PIT1, &pit_irq);
diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c
index 5bbf38b..7ba2ff6 100644
--- a/arch/microblaze/kernel/timer.c
+++ b/arch/microblaze/kernel/timer.c
@@ -177,8 +177,10 @@ static __init int xilinx_clockevent_init(void)
clockevent_xilinx_timer.shift);
clockevent_xilinx_timer.max_delta_ns =
clockevent_delta2ns((u32)~0, &clockevent_xilinx_timer);
+ clockevent_xilinx_timer.max_delta_ticks = (u32)~0;
clockevent_xilinx_timer.min_delta_ns =
clockevent_delta2ns(1, &clockevent_xilinx_timer);
+ clockevent_xilinx_timer.min_delta_ticks = 1;
clockevent_xilinx_timer.cpumask = cpumask_of(0);
clockevents_register_device(&clockevent_xilinx_timer);
diff --git a/arch/mips/alchemy/common/time.c b/arch/mips/alchemy/common/time.c
index f99d3ec..4afdb70 100644
--- a/arch/mips/alchemy/common/time.c
+++ b/arch/mips/alchemy/common/time.c
@@ -138,7 +138,9 @@ static int __init alchemy_time_init(unsigned int m2int)
cd->shift = 32;
cd->mult = div_sc(32768, NSEC_PER_SEC, cd->shift);
cd->max_delta_ns = clockevent_delta2ns(0xffffffff, cd);
- cd->min_delta_ns = clockevent_delta2ns(9, cd); /* ~0.28ms */
+ cd->max_delta_ticks = 0xffffffff;
+ cd->min_delta_ns = clockevent_delta2ns(9, cd);
+ cd->min_delta_ticks = 9; /* ~0.28ms */
clockevents_register_device(cd);
setup_irq(m2int, &au1x_rtcmatch2_irqaction);
diff --git a/arch/mips/jz4740/time.c b/arch/mips/jz4740/time.c
index 1f7ca2c..8cb992f 100644
--- a/arch/mips/jz4740/time.c
+++ b/arch/mips/jz4740/time.c
@@ -145,7 +145,9 @@ void __init plat_time_init(void)
clockevent_set_clock(&jz4740_clockevent, clk_rate);
jz4740_clockevent.min_delta_ns = clockevent_delta2ns(100, &jz4740_clockevent);
+ jz4740_clockevent.min_delta_ticks = 100;
jz4740_clockevent.max_delta_ns = clockevent_delta2ns(0xffff, &jz4740_clockevent);
+ jz4740_clockevent.max_delta_ticks = 0xffff;
jz4740_clockevent.cpumask = cpumask_of(0);
clockevents_register_device(&jz4740_clockevent);
diff --git a/arch/mips/kernel/cevt-bcm1480.c b/arch/mips/kernel/cevt-bcm1480.c
index 940ac00..8f9f2da 100644
--- a/arch/mips/kernel/cevt-bcm1480.c
+++ b/arch/mips/kernel/cevt-bcm1480.c
@@ -123,7 +123,9 @@ void sb1480_clockevent_init(void)
CLOCK_EVT_FEAT_ONESHOT;
clockevent_set_clock(cd, V_SCD_TIMER_FREQ);
cd->max_delta_ns = clockevent_delta2ns(0x7fffff, cd);
+ cd->max_delta_ticks = 0x7fffff;
cd->min_delta_ns = clockevent_delta2ns(2, cd);
+ cd->min_delta_ticks = 2;
cd->rating = 200;
cd->irq = irq;
cd->cpumask = cpumask_of(cpu);
diff --git a/arch/mips/kernel/cevt-ds1287.c b/arch/mips/kernel/cevt-ds1287.c
index 77a5ddf..61ad907 100644
--- a/arch/mips/kernel/cevt-ds1287.c
+++ b/arch/mips/kernel/cevt-ds1287.c
@@ -128,7 +128,9 @@ int __init ds1287_clockevent_init(int irq)
cd->irq = irq;
clockevent_set_clock(cd, 32768);
cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
+ cd->max_delta_ticks = 0x7fffffff;
cd->min_delta_ns = clockevent_delta2ns(0x300, cd);
+ cd->min_delta_ticks = 0x300;
cd->cpumask = cpumask_of(0);
clockevents_register_device(&ds1287_clockevent);
diff --git a/arch/mips/kernel/cevt-gt641xx.c b/arch/mips/kernel/cevt-gt641xx.c
index 6604005..fd90c82 100644
--- a/arch/mips/kernel/cevt-gt641xx.c
+++ b/arch/mips/kernel/cevt-gt641xx.c
@@ -152,7 +152,9 @@ static int __init gt641xx_timer0_clockevent_init(void)
cd->rating = 200 + gt641xx_base_clock / 10000000;
clockevent_set_clock(cd, gt641xx_base_clock);
cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
+ cd->max_delta_ticks = 0x7fffffff;
cd->min_delta_ns = clockevent_delta2ns(0x300, cd);
+ cd->min_delta_ticks = 0x300;
cd->cpumask = cpumask_of(0);
clockevents_register_device(>641xx_timer0_clockevent);
diff --git a/arch/mips/kernel/cevt-sb1250.c b/arch/mips/kernel/cevt-sb1250.c
index 3d860ef..9d1edb5 100644
--- a/arch/mips/kernel/cevt-sb1250.c
+++ b/arch/mips/kernel/cevt-sb1250.c
@@ -123,7 +123,9 @@ void sb1250_clockevent_init(void)
CLOCK_EVT_FEAT_ONESHOT;
clockevent_set_clock(cd, V_SCD_TIMER_FREQ);
cd->max_delta_ns = clockevent_delta2ns(0x7fffff, cd);
+ cd->max_delta_ticks = 0x7fffff;
cd->min_delta_ns = clockevent_delta2ns(2, cd);
+ cd->min_delta_ticks = 2;
cd->rating = 200;
cd->irq = irq;
cd->cpumask = cpumask_of(cpu);
diff --git a/arch/mips/kernel/cevt-txx9.c b/arch/mips/kernel/cevt-txx9.c
index 537eefd..e651a03 100644
--- a/arch/mips/kernel/cevt-txx9.c
+++ b/arch/mips/kernel/cevt-txx9.c
@@ -196,7 +196,9 @@ void __init txx9_clockevent_init(unsigned long baseaddr, int irq,
clockevent_set_clock(cd, TIMER_CLK(imbusclk));
cd->max_delta_ns =
clockevent_delta2ns(0xffffffff >> (32 - TXX9_TIMER_BITS), cd);
+ cd->max_delta_ticks = 0xffffffff >> (32 - TXX9_TIMER_BITS);
cd->min_delta_ns = clockevent_delta2ns(0xf, cd);
+ cd->min_delta_ticks = 0xf;
cd->irq = irq;
cd->cpumask = cpumask_of(0),
clockevents_register_device(cd);
diff --git a/arch/mips/loongson32/common/time.c b/arch/mips/loongson32/common/time.c
index ff224f0..bf42507 100644
--- a/arch/mips/loongson32/common/time.c
+++ b/arch/mips/loongson32/common/time.c
@@ -199,7 +199,9 @@ static void __init ls1x_time_init(void)
clockevent_set_clock(cd, mips_hpt_frequency);
cd->max_delta_ns = clockevent_delta2ns(0xffffff, cd);
+ cd->max_delta_ticks = 0xffffff;
cd->min_delta_ns = clockevent_delta2ns(0x000300, cd);
+ cd->min_delta_ticks = 0x000300;
cd->cpumask = cpumask_of(smp_processor_id());
clockevents_register_device(cd);
diff --git a/arch/mips/loongson64/common/cs5536/cs5536_mfgpt.c b/arch/mips/loongson64/common/cs5536/cs5536_mfgpt.c
index da77d41..fd9a9f2 100644
--- a/arch/mips/loongson64/common/cs5536/cs5536_mfgpt.c
+++ b/arch/mips/loongson64/common/cs5536/cs5536_mfgpt.c
@@ -123,7 +123,9 @@ void __init setup_mfgpt0_timer(void)
cd->cpumask = cpumask_of(cpu);
clockevent_set_clock(cd, MFGPT_TICK_RATE);
cd->max_delta_ns = clockevent_delta2ns(0xffff, cd);
+ cd->max_delta_ticks = 0xffff;
cd->min_delta_ns = clockevent_delta2ns(0xf, cd);
+ cd->min_delta_ticks = 0xf;
/* Enable MFGPT0 Comparator 2 Output to the Interrupt Mapper */
_wrmsr(DIVIL_MSR_REG(MFGPT_IRQ), 0, 0x100);
diff --git a/arch/mips/loongson64/loongson-3/hpet.c b/arch/mips/loongson64/loongson-3/hpet.c
index 4788bea..94f5410 100644
--- a/arch/mips/loongson64/loongson-3/hpet.c
+++ b/arch/mips/loongson64/loongson-3/hpet.c
@@ -241,7 +241,9 @@ void __init setup_hpet_timer(void)
cd->cpumask = cpumask_of(cpu);
clockevent_set_clock(cd, HPET_FREQ);
cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
+ cd->max_delta_ticks = 0x7fffffff;
cd->min_delta_ns = clockevent_delta2ns(HPET_MIN_PROG_DELTA, cd);
+ cd->min_delta_ticks = HPET_MIN_PROG_DELTA;
clockevents_register_device(cd);
setup_irq(HPET_T0_IRQ, &hpet_irq);
diff --git a/arch/mips/ralink/cevt-rt3352.c b/arch/mips/ralink/cevt-rt3352.c
index f24eee0..b8a1376 100644
--- a/arch/mips/ralink/cevt-rt3352.c
+++ b/arch/mips/ralink/cevt-rt3352.c
@@ -129,7 +129,9 @@ static int __init ralink_systick_init(struct device_node *np)
systick.dev.name = np->name;
clockevents_calc_mult_shift(&systick.dev, SYSTICK_FREQ, 60);
systick.dev.max_delta_ns = clockevent_delta2ns(0x7fff, &systick.dev);
+ systick.dev.max_delta_ticks = 0x7fff;
systick.dev.min_delta_ns = clockevent_delta2ns(0x3, &systick.dev);
+ systick.dev.min_delta_ticks = 0x3;
systick.dev.irq = irq_of_parse_and_map(np, 0);
if (!systick.dev.irq) {
pr_err("%s: request_irq failed", np->name);
diff --git a/arch/mips/sgi-ip27/ip27-timer.c b/arch/mips/sgi-ip27/ip27-timer.c
index 42d6cb9..c1e0c84 100644
--- a/arch/mips/sgi-ip27/ip27-timer.c
+++ b/arch/mips/sgi-ip27/ip27-timer.c
@@ -113,7 +113,9 @@ void hub_rt_clock_event_init(void)
cd->features = CLOCK_EVT_FEAT_ONESHOT;
clockevent_set_clock(cd, CYCLES_PER_SEC);
cd->max_delta_ns = clockevent_delta2ns(0xfffffffffffff, cd);
+ cd->max_delta_ticks = 0xfffffffffffff;
cd->min_delta_ns = clockevent_delta2ns(0x300, cd);
+ cd->min_delta_ticks = 0x300;
cd->rating = 200;
cd->irq = irq;
cd->cpumask = cpumask_of(cpu);
diff --git a/arch/mn10300/kernel/cevt-mn10300.c b/arch/mn10300/kernel/cevt-mn10300.c
index d9b34dd..2b21bbc 100644
--- a/arch/mn10300/kernel/cevt-mn10300.c
+++ b/arch/mn10300/kernel/cevt-mn10300.c
@@ -98,7 +98,9 @@ int __init init_clockevents(void)
/* Calculate the min / max delta */
cd->max_delta_ns = clockevent_delta2ns(TMJCBR_MAX, cd);
+ cd->max_delta_ticks = TMJCBR_MAX;
cd->min_delta_ns = clockevent_delta2ns(100, cd);
+ cd->min_delta_ticks = 100;
cd->rating = 200;
cd->cpumask = cpumask_of(smp_processor_id());
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index 3efbede..fd2c36f 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -972,8 +972,10 @@ static void __init init_decrementer_clockevent(void)
decrementer_clockevent.max_delta_ns =
clockevent_delta2ns(decrementer_max, &decrementer_clockevent);
+ decrementer_clockevent.max_delta_ticks = decrementer_max;
decrementer_clockevent.min_delta_ns =
clockevent_delta2ns(2, &decrementer_clockevent);
+ decrementer_clockevent.min_delta_ticks = 2;
register_decrementer_clockevent(cpu);
}
diff --git a/arch/score/kernel/time.c b/arch/score/kernel/time.c
index 679b8d7..29aafc7 100644
--- a/arch/score/kernel/time.c
+++ b/arch/score/kernel/time.c
@@ -81,8 +81,10 @@ void __init time_init(void)
score_clockevent.shift);
score_clockevent.max_delta_ns = clockevent_delta2ns((u32)~0,
&score_clockevent);
+ score_clockevent.max_delta_ticks = (u32)~0;
score_clockevent.min_delta_ns = clockevent_delta2ns(50,
&score_clockevent) + 1;
+ score_clockevent.min_delta_ticks = 50;
score_clockevent.cpumask = cpumask_of(0);
clockevents_register_device(&score_clockevent);
}
diff --git a/arch/sparc/kernel/time_32.c b/arch/sparc/kernel/time_32.c
index 1affabc..1203c7c 100644
--- a/arch/sparc/kernel/time_32.c
+++ b/arch/sparc/kernel/time_32.c
@@ -228,7 +228,9 @@ void register_percpu_ce(int cpu)
ce->mult = div_sc(sparc_config.clock_rate, NSEC_PER_SEC,
ce->shift);
ce->max_delta_ns = clockevent_delta2ns(sparc_config.clock_rate, ce);
+ ce->max_delta_ticks = (unsigned long)sparc_config.clock_rate;
ce->min_delta_ns = clockevent_delta2ns(100, ce);
+ ce->min_delta_ticks = 100;
clockevents_register_device(ce);
}
diff --git a/arch/sparc/kernel/time_64.c b/arch/sparc/kernel/time_64.c
index c69b21e..83f0f99 100644
--- a/arch/sparc/kernel/time_64.c
+++ b/arch/sparc/kernel/time_64.c
@@ -796,8 +796,10 @@ void __init time_init(void)
sparc64_clockevent.max_delta_ns =
clockevent_delta2ns(0x7fffffffffffffffUL, &sparc64_clockevent);
+ sparc64_clockevent.max_delta_ticks = 0x7fffffffffffffffUL;
sparc64_clockevent.min_delta_ns =
clockevent_delta2ns(0xF, &sparc64_clockevent);
+ sparc64_clockevent.min_delta_ticks = 0xF;
printk("clockevent: mult[%x] shift[%d]\n",
sparc64_clockevent.mult, sparc64_clockevent.shift);
diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c
index 25c2366..11b96ed 100644
--- a/arch/um/kernel/time.c
+++ b/arch/um/kernel/time.c
@@ -65,7 +65,9 @@ static struct clock_event_device timer_clockevent = {
.set_next_event = itimer_next_event,
.shift = 0,
.max_delta_ns = 0xffffffff,
- .min_delta_ns = TIMER_MIN_DELTA, //microsecond resolution should be enough for anyone, same as 640K RAM
+ .max_delta_ticks = 0xffffffff,
+ .min_delta_ns = TIMER_MIN_DELTA,
+ .min_delta_ticks = TIMER_MIN_DELTA, // microsecond resolution should be enough for anyone, same as 640K RAM
.irq = 0,
.mult = 1,
};
diff --git a/arch/unicore32/kernel/time.c b/arch/unicore32/kernel/time.c
index ac4c544..29c91a9 100644
--- a/arch/unicore32/kernel/time.c
+++ b/arch/unicore32/kernel/time.c
@@ -91,8 +91,10 @@ void __init time_init(void)
ckevt_puv3_osmr0.max_delta_ns =
clockevent_delta2ns(0x7fffffff, &ckevt_puv3_osmr0);
+ ckevt_puv3_osmr0.max_delta_ticks = 0x7fffffff;
ckevt_puv3_osmr0.min_delta_ns =
clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_puv3_osmr0) + 1;
+ ckevt_puv3_osmr0.min_delta_ticks = MIN_OSCR_DELTA * 2;
ckevt_puv3_osmr0.cpumask = cpumask_of(0);
setup_irq(IRQ_TIMER0, &puv3_timer_irq);
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 8b7c33d..5d0f80c 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -729,8 +729,10 @@ static int __init calibrate_APIC_clock(void)
TICK_NSEC, lapic_clockevent.shift);
lapic_clockevent.max_delta_ns =
clockevent_delta2ns(0x7FFFFF, &lapic_clockevent);
+ lapic_clockevent.max_delta_ticks = 0x7FFFFF;
lapic_clockevent.min_delta_ns =
clockevent_delta2ns(0xF, &lapic_clockevent);
+ lapic_clockevent.min_delta_ticks = 0xF;
lapic_clockevent.features &= ~CLOCK_EVT_FEAT_DUMMY;
return 0;
}
@@ -776,8 +778,10 @@ static int __init calibrate_APIC_clock(void)
lapic_clockevent.shift);
lapic_clockevent.max_delta_ns =
clockevent_delta2ns(0x7FFFFFFF, &lapic_clockevent);
+ lapic_clockevent.max_delta_ticks = 0x7FFFFFFF;
lapic_clockevent.min_delta_ns =
clockevent_delta2ns(0xF, &lapic_clockevent);
+ lapic_clockevent.min_delta_ticks = 0xF;
lapic_timer_frequency = (delta * APIC_DIVISOR) / LAPIC_CAL_LOOPS;
diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c
index 25da5bc8..cafef00 100644
--- a/arch/x86/lguest/boot.c
+++ b/arch/x86/lguest/boot.c
@@ -1008,7 +1008,9 @@ static struct clock_event_device lguest_clockevent = {
.mult = 1,
.shift = 0,
.min_delta_ns = LG_CLOCK_MIN_DELTA,
+ .min_delta_ticks = LG_CLOCK_MIN_DELTA,
.max_delta_ns = LG_CLOCK_MAX_DELTA,
+ .max_delta_ticks = LG_CLOCK_MAX_DELTA,
};
/*
diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c
index 67356d2..449a627 100644
--- a/arch/x86/xen/time.c
+++ b/arch/x86/xen/time.c
@@ -209,7 +209,9 @@ static const struct clock_event_device xen_timerop_clockevent = {
.features = CLOCK_EVT_FEAT_ONESHOT,
.max_delta_ns = 0xffffffff,
+ .max_delta_ticks = 0xffffffff,
.min_delta_ns = TIMER_SLOP,
+ .min_delta_ticks = TIMER_SLOP,
.mult = 1,
.shift = 0,
@@ -268,7 +270,9 @@ static const struct clock_event_device xen_vcpuop_clockevent = {
.features = CLOCK_EVT_FEAT_ONESHOT,
.max_delta_ns = 0xffffffff,
+ .max_delta_ticks = 0xffffffff,
.min_delta_ns = TIMER_SLOP,
+ .min_delta_ticks = TIMER_SLOP,
.mult = 1,
.shift = 0,
diff --git a/drivers/clocksource/dw_apb_timer.c b/drivers/clocksource/dw_apb_timer.c
index 797505a..2d68204 100644
--- a/drivers/clocksource/dw_apb_timer.c
+++ b/drivers/clocksource/dw_apb_timer.c
@@ -257,7 +257,9 @@ dw_apb_clockevent_init(int cpu, const char *name, unsigned rating,
clockevents_calc_mult_shift(&dw_ced->ced, freq, APBT_MIN_PERIOD);
dw_ced->ced.max_delta_ns = clockevent_delta2ns(0x7fffffff,
&dw_ced->ced);
+ dw_ced->ced.max_delta_ticks = 0x7fffffff;
dw_ced->ced.min_delta_ns = clockevent_delta2ns(5000, &dw_ced->ced);
+ dw_ced->ced.min_delta_ticks = 5000;
dw_ced->ced.cpumask = cpumask_of(cpu);
dw_ced->ced.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ;
diff --git a/drivers/clocksource/metag_generic.c b/drivers/clocksource/metag_generic.c
index a80ab3e..2b08024 100644
--- a/drivers/clocksource/metag_generic.c
+++ b/drivers/clocksource/metag_generic.c
@@ -114,7 +114,9 @@ static int arch_timer_starting_cpu(unsigned int cpu)
clk->mult = div_sc(hwtimer_freq, NSEC_PER_SEC, clk->shift);
clk->max_delta_ns = clockevent_delta2ns(0x7fffffff, clk);
+ clk->max_delta_ticks = 0x7fffffff;
clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
+ clk->min_delta_ticks = 0xf;
clk->cpumask = cpumask_of(cpu);
clockevents_register_device(clk);
diff --git a/drivers/clocksource/numachip.c b/drivers/clocksource/numachip.c
index 4e0f11f..6a20dc8 100644
--- a/drivers/clocksource/numachip.c
+++ b/drivers/clocksource/numachip.c
@@ -51,7 +51,9 @@ static struct clock_event_device numachip2_clockevent = {
.mult = 1,
.shift = 0,
.min_delta_ns = 1250,
+ .min_delta_ticks = 1250,
.max_delta_ns = LONG_MAX,
+ .max_delta_ticks = LONG_MAX,
};
static void numachip_timer_interrupt(void)
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index fd9caa7..8daef3d 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -814,7 +814,9 @@ static int sh_cmt_register_clockevent(struct sh_cmt_channel *ch,
ced->shift = 32;
ced->mult = div_sc(ch->cmt->rate, NSEC_PER_SEC, ced->shift);
ced->max_delta_ns = clockevent_delta2ns(ch->max_match_value, ced);
+ ced->max_delta_ticks = ch->max_match_value;
ced->min_delta_ns = clockevent_delta2ns(0x1f, ced);
+ ced->min_delta_ticks = 0x1f;
dev_info(&ch->cmt->pdev->dev, "ch%u: used for clock events\n",
ch->index);
diff --git a/drivers/clocksource/timer-atlas7.c b/drivers/clocksource/timer-atlas7.c
index 4334e03..c95f209 100644
--- a/drivers/clocksource/timer-atlas7.c
+++ b/drivers/clocksource/timer-atlas7.c
@@ -192,7 +192,9 @@ static int sirfsoc_local_timer_starting_cpu(unsigned int cpu)
ce->set_next_event = sirfsoc_timer_set_next_event;
clockevents_calc_mult_shift(ce, atlas7_timer_rate, 60);
ce->max_delta_ns = clockevent_delta2ns(-2, ce);
+ ce->max_delta_ticks = (unsigned long)-2;
ce->min_delta_ns = clockevent_delta2ns(2, ce);
+ ce->min_delta_ticks = 2;
ce->cpumask = cpumask_of(cpu);
action->dev_id = ce;
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Nicolai Stange <nicstange@gmail.com> |
|---|---|
| Date | 2016-09-09 22:10 +0200 |
| Subject | [RFC v6 04/23] clocksource: em_sti: compute rate before registration |
| Message-ID | <sfAhB-7rj-57@gated-at.bofh.it> |
| In reply to | #1480315 |
With the upcoming NTP correction related rate adjustments to be implemented
in the clockevents core, the latter needs to get informed about every rate
change of a clockevent device made after its registration.
Currently, em_sti violates this requirement in that it registers its
clockevent device with a dummy rate and sets its final rate through
clockevents_config() called from its ->set_state_oneshot().
This patch moves the setting of the clockevent device's rate to its
registration.
I checked all current em_sti users in arch/arm/mach-shmobile and right now,
none of them changes any rate in any clock tree relevant to em_sti after
their respective time_init(). Since all em_sti instances are created after
time_init(), none of them should ever observe any clock rate changes.
- Determine the ->rate value in em_sti_probe() at device probing rather
than at first usage.
- Set the clockevent device's rate at its registration.
- Although not strictly necessary for the upcoming clockevent core changes,
set the clocksource's rate at its registration for consistency.
Signed-off-by: Nicolai Stange <nicstange@gmail.com>
---
Notes:
For a detailed analysis of the current em_sti users, please see
https://nicst.de/ced-clk-rate-change-analysis/em_sti-cgitted.html
Compile-only tested on ARCH=arm.
drivers/clocksource/em_sti.c | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/drivers/clocksource/em_sti.c b/drivers/clocksource/em_sti.c
index 46750c0..6168d18 100644
--- a/drivers/clocksource/em_sti.c
+++ b/drivers/clocksource/em_sti.c
@@ -205,13 +205,9 @@ static cycle_t em_sti_clocksource_read(struct clocksource *cs)
static int em_sti_clocksource_enable(struct clocksource *cs)
{
- int ret;
struct em_sti_priv *p = cs_to_em_sti(cs);
- ret = em_sti_start(p, USER_CLOCKSOURCE);
- if (!ret)
- __clocksource_update_freq_hz(cs, p->rate);
- return ret;
+ return em_sti_start(p, USER_CLOCKSOURCE);
}
static void em_sti_clocksource_disable(struct clocksource *cs)
@@ -240,8 +236,7 @@ static int em_sti_register_clocksource(struct em_sti_priv *p)
dev_info(&p->pdev->dev, "used as clock source\n");
- /* Register with dummy 1 Hz value, gets updated in ->enable() */
- clocksource_register_hz(cs, 1);
+ clocksource_register_hz(cs, p->rate);
return 0;
}
@@ -263,7 +258,6 @@ static int em_sti_clock_event_set_oneshot(struct clock_event_device *ced)
dev_info(&p->pdev->dev, "used for oneshot clock events\n");
em_sti_start(p, USER_CLOCKEVENT);
- clockevents_config(&p->ced, p->rate);
return 0;
}
@@ -294,8 +288,7 @@ static void em_sti_register_clockevent(struct em_sti_priv *p)
dev_info(&p->pdev->dev, "used for clock events\n");
- /* Register with dummy 1 Hz value, gets updated in ->set_state_oneshot() */
- clockevents_config_and_register(ced, 1, 2, 0xffffffff);
+ clockevents_config_and_register(ced, p->rate, 2, 0xffffffff);
}
static int em_sti_probe(struct platform_device *pdev)
@@ -344,11 +337,22 @@ static int em_sti_probe(struct platform_device *pdev)
goto err_clk_put;
}
+ ret = clk_enable(p->clk);
+ if (ret < 0) {
+ dev_err(&p->pdev->dev, "cannot enable clock\n");
+ goto err_clk_unprepare;
+ }
+ p->rate = clk_get_rate(p->clk);
+ clk_disable(p->clk);
+
raw_spin_lock_init(&p->lock);
em_sti_register_clockevent(p);
em_sti_register_clocksource(p);
return 0;
+err_clk_unprepare:
+ clk_unprepare(p->clk);
+
err_clk_put:
clk_put(p->clk);
return ret;
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Nicolai Stange <nicstange@gmail.com> |
|---|---|
| Date | 2016-09-09 22:20 +0200 |
| Subject | [RFC v6 19/23] timer_list: print_tickdevice(): calculate ->min_delta_ns dynamically |
| Message-ID | <sfArf-7zf-1@gated-at.bofh.it> |
| In reply to | #1480315 |
print_tickdevice(), assembling the per-tick device sections in
/proc/timer_list, is the last user of struct clock_event_device's
->min_delta_ns member.
In order to make this one fully obsolete while retaining userspace ABI,
calculate the displayed value of 'min_delta_ns' on the fly from
->min_delta_ticks_adjusted.
Signed-off-by: Nicolai Stange <nicstange@gmail.com>
---
kernel/time/timer_list.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c
index ac20d4c..9067760 100644
--- a/kernel/time/timer_list.c
+++ b/kernel/time/timer_list.c
@@ -206,10 +206,11 @@ static void
print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu)
{
struct clock_event_device *dev = td->evtdev;
- u64 max_delta_ns;
+ u64 max_delta_ns, min_delta_ns;
max_delta_ns = clockevent_delta2ns(dev->max_delta_ticks, dev);
max_delta_ns = min(max_delta_ns, dev->max_delta_ns);
+ min_delta_ns = clockevent_delta2ns(dev->min_delta_ticks_adjusted, dev);
SEQ_printf(m, "Tick Device: mode: %d\n", td->mode);
if (cpu < 0)
@@ -226,7 +227,7 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu)
SEQ_printf(m, " max_delta_ns: %llu\n",
(unsigned long long) max_delta_ns);
SEQ_printf(m, " min_delta_ns: %llu\n",
- (unsigned long long) dev->min_delta_ns);
+ (unsigned long long) min_delta_ns);
SEQ_printf(m, " mult: %u\n", dev->mult);
SEQ_printf(m, " shift: %u\n", dev->shift);
SEQ_printf(m, " mode: %d\n", clockevent_get_state(dev));
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Nicolai Stange <nicstange@gmail.com> |
|---|---|
| Date | 2016-09-09 22:20 +0200 |
| Subject | [RFC v6 22/23] clockevents: make setting of ->mult and ->mult_adjusted atomic |
| Message-ID | <sfArg-7zf-15@gated-at.bofh.it> |
| In reply to | #1480315 |
In order to avoid races between setting a struct clock_event_device's ->mult_adjusted in clockevents_update_freq() and yet to be implemented updates triggered from the timekeeping core, the setting of ->mult and ->mult_adjusted should be made atomic. Protect the update in clockevents_update_freq() by locking the clockevents_lock spinlock. Frequency updates are expected to be done seldomly and thus, taking this subsystem lock should not have any impact on performance. Note that the call to tick_broadcast_update_freq() is also protected by this clockevents_lock and thus, this patch can take the tick_broadcast_lock with the clockevents_lock being held. However, this is supposed to be safe since the sequence __clockevents_unbind() -> clockevents_replace() -> tick_install_replacement() -> tick_setup_device() -> tick_device_uses_broadcast() already does this, too. Signed-off-by: Nicolai Stange <nicstange@gmail.com> --- kernel/time/clockevents.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 67d572e..b121932 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -658,11 +658,11 @@ int clockevents_update_freq(struct clock_event_device *dev, u32 freq) unsigned long flags; int ret; - local_irq_save(flags); + raw_spin_lock_irqsave(&clockevents_lock, flags); ret = tick_broadcast_update_freq(dev, freq); if (ret == -ENODEV) ret = __clockevents_update_freq(dev, freq); - local_irq_restore(flags); + raw_spin_unlock_irqrestore(&clockevents_lock, flags); return ret; } -- 2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Nicolai Stange <nicstange@gmail.com> |
|---|---|
| Date | 2016-09-09 22:20 +0200 |
| Subject | [RFC v6 18/23] clockevents: min delta increment: calculate min_delta_ns from ticks |
| Message-ID | <sfArg-7zf-17@gated-at.bofh.it> |
| In reply to | #1480315 |
The use of a clockevent device's ->min_delta_ns in the event programming
path hinders upcoming changes to the clockevent core making it NTP
correction aware: both, ->mult and ->min_delta_ns would need to get
updated as well as consumed atomically and we'd rather like to avoid any
locking here.
We already have got ->min_delta_ticks_adjusted which
- resembles the value of ->min_delta_ns
- and is guaranteed to be always >= the hardware's hard limit
->min_delta_ticks and thus, can be used w/o locking as we don't care
for small deviations.
In clockevents_increase_min_delta(), don't use ->min_delta_ns but
calculate it dynamically from ->min_delta_ticks_adjusted.
As clockevents_increase_min_delta() gets invoked only rarely, the
additional division should not be an issue from a performance standpoint.
Signed-off-by: Nicolai Stange <nicstange@gmail.com>
---
kernel/time/clockevents.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index aa7b325..86d9f97 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -210,23 +210,26 @@ int clockevents_tick_resume(struct clock_event_device *dev)
*/
static int clockevents_increase_min_delta(struct clock_event_device *dev)
{
+ u64 min_delta_ns = cev_delta2ns(dev->min_delta_ticks_adjusted, dev,
+ false);
+
/* Nothing to do if we already reached the limit */
- if (dev->min_delta_ns >= MIN_DELTA_LIMIT) {
+ if (min_delta_ns >= MIN_DELTA_LIMIT) {
printk_deferred(KERN_WARNING
"CE: Reprogramming failure. Giving up\n");
dev->next_event.tv64 = KTIME_MAX;
return -ETIME;
}
- if (dev->min_delta_ns < 5000)
- dev->min_delta_ns = 5000;
+ if (min_delta_ns < 5000)
+ min_delta_ns = 5000;
else
- dev->min_delta_ns += dev->min_delta_ns >> 1;
+ min_delta_ns += min_delta_ns >> 1;
- if (dev->min_delta_ns > MIN_DELTA_LIMIT)
- dev->min_delta_ns = MIN_DELTA_LIMIT;
+ if (min_delta_ns > MIN_DELTA_LIMIT)
+ min_delta_ns = MIN_DELTA_LIMIT;
- dev->min_delta_ticks_adjusted = (unsigned long)((dev->min_delta_ns *
+ dev->min_delta_ticks_adjusted = (unsigned long)((min_delta_ns *
dev->mult) >> dev->shift);
dev->min_delta_ticks_adjusted = max(dev->min_delta_ticks_adjusted,
dev->min_delta_ticks);
@@ -234,7 +237,7 @@ static int clockevents_increase_min_delta(struct clock_event_device *dev)
printk_deferred(KERN_WARNING
"CE: %s increased min_delta_ns to %llu nsec\n",
dev->name ? dev->name : "?",
- (unsigned long long) dev->min_delta_ns);
+ (unsigned long long) min_delta_ns);
return 0;
}
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Nicolai Stange <nicstange@gmail.com> |
|---|---|
| Date | 2016-09-09 22:20 +0200 |
| Subject | [RFC v6 21/23] clockevents: initial support for mono to raw time conversion |
| Message-ID | <sfArg-7zf-19@gated-at.bofh.it> |
| In reply to | #1480315 |
With NOHZ_FULL and one single well-isolated, CPU consumptive task, one
would expect approximately one clockevent interrupt per second. However, on
my Intel Haswell where the monotonic clock is the TSC monotonic clock and
the clockevent device is the TSC deadline device, it turns out that every
second, there are two such interrupts: the first one arrives always
approximately ~50us before the scheduled deadline as programmed by
tick_nohz_stop_sched_tick() through the hrtimer API. The
__hrtimer_run_queues() called in this interrupt detects that the queued
tick_sched_timer hasn't expired yet and simply does nothing except
reprogramming the clock event device to fire shortly after again.
These too early programmed deadlines are explained as follows:
clockevents_program_event() programs the clockevent device to fire
after
f_event * delta_t_progr
clockevent device cycles where f_event is the clockevent device's hardware
frequency and delta_t_progr is the requested time interval. After that many
clockevent device cycles have elapsed, the device underlying the monotonic
clock, that is the monotonic raw clock has seen f_raw / f_event as many
cycles.
The ktime_get() called from __hrtimer_run_queues() interprets those
cycles to run at the frequency of the monotonic clock. Summarizing:
delta_t_perc = 1/f_mono * f_raw/f_event * f_event * delta_t_progr
= f_raw / f_mono * delta_t_progr
with f_mono being the monotonic clock's frequency and delta_t_perc being
the elapsed time interval as perceived by __hrtimer_run_queues().
Now, f_mono is not a constant, but is dynamically adjusted in
timekeeping_adjust() in order to compensate for the NTP error. With the
large values of delta_t_progr of 10^9ns with NOHZ_FULL, the error made
becomes significant and results in the double timer interrupts described
above.
Compensate for this error by multiplying the clockevent device's f_event
by f_mono/f_raw.
Namely:
- Introduce a ->mult_adjusted member to the struct clock_event_device. Its
value is supposed to be equal to ->mult * f_mono/f_raw for devices
which don't have the CLOCK_EVT_FEAT_NO_ADJUST flag set, equal to ->mult
otherwise.
- Introduce the timekeeping_get_mono_mult() helper which provides
the clockevent core with access to the timekeeping's current f_mono
and f_raw.
- Introduce the helper __clockevents_adjust_freq() which
sets a clockevent device's ->mult_adjusted member as appropriate. It is
implemented with the help of the new __clockevents_calc_adjust_freq().
- Call __clockevents_adjust_freq() at clockevent device registration time
as well as at frequency updates through clockevents_update_freq().
- Use the ->mult_adjusted rather than ->mult in the ns to cycle
conversion made in clockevents_program_event() as well as in the
cycle to ns conversion in cev_delta2ns().
- Finally, move ->mult out of struct clock_event_device's first cacheline.
Note that future adjustments of the monotonic clock are not taken into
account yet. Furthemore, this patch assumes that after a clockevent
device's registration, its ->mult changes only through calls to
clockevents_update_freq().
Signed-off-by: Nicolai Stange <nicstange@gmail.com>
---
include/linux/clockchips.h | 6 ++--
kernel/time/clockevents.c | 79 ++++++++++++++++++++++++++++++++++++++-------
kernel/time/tick-internal.h | 1 +
kernel/time/timekeeping.c | 14 ++++++++
4 files changed, 87 insertions(+), 13 deletions(-)
diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h
index 2ff15f03..28f9263 100644
--- a/include/linux/clockchips.h
+++ b/include/linux/clockchips.h
@@ -82,7 +82,7 @@ enum clock_event_state {
* @max_delta_ns: maximum delta value in ns
* @max_delta_ticks: maximum delta value in ticks
* @min_delta_ticks_adjusted: minimum delta value, increased as needed
- * @mult: nanosecond to cycles multiplier
+ * @mult_adjusted: adjusted multiplier compensating for NTP adjustments
* @shift: nanoseconds to cycles divisor (power of two)
* @state_use_accessors:current state of the device, assigned by the core code
* @features: features
@@ -94,6 +94,7 @@ enum clock_event_state {
* @tick_resume: resume clkevt device
* @broadcast: function to broadcast events
* @min_delta_ticks: minimum delta value in ticks stored for reconfiguration
+ * @mult: ns to cycles multiplier stored for reconfiguration
* @name: ptr to clock event name
* @rating: variable to rate clock event devices
* @irq: IRQ number (only for non CPU local devices)
@@ -110,7 +111,7 @@ struct clock_event_device {
u64 max_delta_ns;
unsigned long max_delta_ticks;
unsigned long min_delta_ticks_adjusted;
- u32 mult;
+ u32 mult_adjusted;
u32 shift;
enum clock_event_state state_use_accessors;
unsigned int features;
@@ -126,6 +127,7 @@ struct clock_event_device {
void (*suspend)(struct clock_event_device *);
void (*resume)(struct clock_event_device *);
unsigned long min_delta_ticks;
+ u32 mult;
const char *name;
int rating;
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index 394b8dc..67d572e 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -34,17 +34,19 @@ struct ce_unbind {
int res;
};
+static void __clockevents_adjust_freq(struct clock_event_device *dev);
+
static u64 cev_delta2ns(unsigned long latch, struct clock_event_device *evt,
bool ismax)
{
u64 clc = (u64) latch << evt->shift;
u64 rnd;
- if (unlikely(!evt->mult)) {
- evt->mult = 1;
+ if (unlikely(!evt->mult_adjusted)) {
+ evt->mult_adjusted = 1;
WARN_ON(1);
}
- rnd = (u64) evt->mult - 1;
+ rnd = (u64) evt->mult_adjusted - 1;
/*
* Upper bound sanity check. If the backwards conversion is
@@ -73,10 +75,10 @@ static u64 cev_delta2ns(unsigned long latch, struct clock_event_device *evt,
* Also omit the add if it would overflow the u64 boundary.
*/
if ((~0ULL - clc > rnd) &&
- (!ismax || evt->mult <= (1ULL << evt->shift)))
+ (!ismax || evt->mult_adjusted <= (1ULL << evt->shift)))
clc += rnd;
- do_div(clc, evt->mult);
+ do_div(clc, evt->mult_adjusted);
/* Deltas less than 1usec are pointless noise */
return clc > 1000 ? clc : 1000;
@@ -165,8 +167,8 @@ void clockevents_switch_state(struct clock_event_device *dev,
* on it, so fix it up and emit a warning:
*/
if (clockevent_state_oneshot(dev)) {
- if (unlikely(!dev->mult)) {
- dev->mult = 1;
+ if (unlikely(!dev->mult_adjusted)) {
+ dev->mult_adjusted = 1;
WARN_ON(1);
}
}
@@ -229,8 +231,9 @@ static int clockevents_increase_min_delta(struct clock_event_device *dev)
if (min_delta_ns > MIN_DELTA_LIMIT)
min_delta_ns = MIN_DELTA_LIMIT;
- dev->min_delta_ticks_adjusted = (unsigned long)((min_delta_ns *
- dev->mult) >> dev->shift);
+ dev->min_delta_ticks_adjusted =
+ (unsigned long)((min_delta_ns * dev->mult_adjusted) >>
+ dev->shift);
dev->min_delta_ticks_adjusted = max(dev->min_delta_ticks_adjusted,
dev->min_delta_ticks);
@@ -330,7 +333,7 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,
delta = min(delta, (int64_t) dev->max_delta_ns);
- clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
+ clc = ((unsigned long long) delta * dev->mult_adjusted) >> dev->shift;
clc = min_t(unsigned long, clc, dev->max_delta_ticks);
clc = max_t(unsigned long, clc, dev->min_delta_ticks_adjusted);
@@ -497,7 +500,8 @@ static void __clockevents_update_bounds(struct clock_event_device *dev)
*/
dev->min_delta_ticks_adjusted =
max(dev->min_delta_ticks,
- (unsigned long)((1000ULL * dev->mult) >> dev->shift));
+ (unsigned long)((1000ULL * dev->mult_adjusted) >>
+ dev->shift));
}
/**
@@ -516,6 +520,7 @@ void clockevents_register_device(struct clock_event_device *dev)
dev->cpumask = cpumask_of(smp_processor_id());
}
+ __clockevents_adjust_freq(dev);
__clockevents_update_bounds(dev);
raw_spin_lock_irqsave(&clockevents_lock, flags);
@@ -570,9 +575,61 @@ void clockevents_config_and_register(struct clock_event_device *dev,
}
EXPORT_SYMBOL_GPL(clockevents_config_and_register);
+static u32 __clockevents_calc_adjust_freq(u32 mult_ce_raw, u32 mult_cs_mono,
+ u32 mult_cs_raw)
+{
+ u64 adj;
+ int sign;
+
+ if (mult_cs_raw >= mult_cs_mono) {
+ sign = 0;
+ adj = mult_cs_raw - mult_cs_mono;
+ } else {
+ sign = 1;
+ adj = mult_cs_mono - mult_cs_raw;
+ }
+
+ adj *= mult_ce_raw;
+ adj += mult_cs_mono / 2;
+ do_div(adj, mult_cs_mono);
+
+ if (!sign) {
+ /*
+ * Never increase mult by more than 12.5%,
+ * c.f. __clockevents_update_bounds().
+ */
+ adj = min_t(u64, adj, mult_ce_raw / 8);
+ if (U32_MAX - mult_ce_raw < adj)
+ return U32_MAX;
+ return mult_ce_raw + (u32)adj;
+ }
+ if (adj >= mult_ce_raw)
+ return 1;
+ return mult_ce_raw - (u32)adj;
+}
+
+void __clockevents_adjust_freq(struct clock_event_device *dev)
+{
+ u32 mult_cs_mono, mult_cs_raw;
+
+ if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT))
+ return;
+
+ if (dev->features & CLOCK_EVT_FEAT_NO_ADJUST) {
+ dev->mult_adjusted = dev->mult;
+ return;
+ }
+
+ timekeeping_get_mono_mult(&mult_cs_mono, &mult_cs_raw);
+ dev->mult_adjusted = __clockevents_calc_adjust_freq(dev->mult,
+ mult_cs_mono,
+ mult_cs_raw);
+}
+
int __clockevents_update_freq(struct clock_event_device *dev, u32 freq)
{
clockevents_config(dev, freq);
+ __clockevents_adjust_freq(dev);
__clockevents_update_bounds(dev);
if (clockevent_state_oneshot(dev))
diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h
index f738251..0b29d23 100644
--- a/kernel/time/tick-internal.h
+++ b/kernel/time/tick-internal.h
@@ -56,6 +56,7 @@ extern int clockevents_program_event(struct clock_event_device *dev,
ktime_t expires, bool force);
extern void clockevents_handle_noop(struct clock_event_device *dev);
extern int __clockevents_update_freq(struct clock_event_device *dev, u32 freq);
+extern void timekeeping_get_mono_mult(u32 *mult_cs_mono, u32 *mult_cs_raw);
extern ssize_t sysfs_get_uname(const char *buf, char *dst, size_t cnt);
/* Broadcasting support */
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index e07fb09..7ddca9e 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -329,6 +329,20 @@ static inline s64 timekeeping_cycles_to_ns(struct tk_read_base *tkr,
return timekeeping_delta_to_ns(tkr, delta);
}
+void timekeeping_get_mono_mult(u32 *mult_cs_mono, u32 *mult_cs_raw)
+{
+ unsigned int seq;
+ struct tk_read_base *tkr_mono = &tk_core.timekeeper.tkr_mono;
+
+ /* The seqlock protects us from a racing change_clocksource(). */
+ do {
+ seq = read_seqcount_begin(&tk_core.seq);
+
+ *mult_cs_mono = tkr_mono->mult;
+ *mult_cs_raw = tkr_mono->clock->mult;
+ } while (read_seqcount_retry(&tk_core.seq, seq));
+}
+
/**
* update_fast_timekeeper - Update the fast and NMI safe monotonic timekeeper.
* @tkr: Timekeeping readout base from which we take the update
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Nicolai Stange <nicstange@gmail.com> |
|---|---|
| Date | 2016-09-09 22:20 +0200 |
| Subject | [RFC v6 20/23] clockevents: purge ->min_delta_ns |
| Message-ID | <sfArg-7zf-27@gated-at.bofh.it> |
| In reply to | #1480315 |
The struct clock_event_device's ->min_delta_ns member isn't used anymore.
Purge it.
In __clockevents_update_bounds(), shortcut the
->min_delta_ticks => ->min_delta_ns => ->min_delta_ticks_adjusted
calculation detour -- it had been made solely for the purpose of ensuring
that ->min_delta_ticks_adjusted corresponds to something >= 1us.
Signed-off-by: Nicolai Stange <nicstange@gmail.com>
---
include/linux/clockchips.h | 2 --
kernel/time/clockevents.c | 12 +++++-------
2 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h
index 7c3a193..2ff15f03 100644
--- a/include/linux/clockchips.h
+++ b/include/linux/clockchips.h
@@ -94,7 +94,6 @@ enum clock_event_state {
* @tick_resume: resume clkevt device
* @broadcast: function to broadcast events
* @min_delta_ticks: minimum delta value in ticks stored for reconfiguration
- * @min_delta_ns: minimum delta value in ns
* @name: ptr to clock event name
* @rating: variable to rate clock event devices
* @irq: IRQ number (only for non CPU local devices)
@@ -127,7 +126,6 @@ struct clock_event_device {
void (*suspend)(struct clock_event_device *);
void (*resume)(struct clock_event_device *);
unsigned long min_delta_ticks;
- u64 min_delta_ns;
const char *name;
int rating;
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index 86d9f97..394b8dc 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -492,14 +492,12 @@ static void __clockevents_update_bounds(struct clock_event_device *dev)
}
/*
- * cev_delta2ns() never returns values less than 1us and thus,
- * we'll never program any ced with anything less.
+ * Enforce ->min_delta_ticks_adjusted to correspond to a value
+ * >= 1us.
*/
- dev->min_delta_ns = cev_delta2ns(dev->min_delta_ticks, dev, false);
- dev->min_delta_ticks_adjusted = (unsigned long)((dev->min_delta_ns *
- dev->mult) >> dev->shift);
- dev->min_delta_ticks_adjusted = max(dev->min_delta_ticks_adjusted,
- dev->min_delta_ticks);
+ dev->min_delta_ticks_adjusted =
+ max(dev->min_delta_ticks,
+ (unsigned long)((1000ULL * dev->mult) >> dev->shift));
}
/**
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Nicolai Stange <nicstange@gmail.com> |
|---|---|
| Date | 2016-09-09 22:20 +0200 |
| Subject | [RFC v6 23/23] timekeeping: inform clockevents about freq adjustments |
| Message-ID | <sfArg-7zf-29@gated-at.bofh.it> |
| In reply to | #1480315 |
Upon adjustments of the monotonic clock's frequencies from the
timekeeping core, the clockevents devices' ->mult_adjusted should be
changed accordingly, too.
Introduce clockevents_adjust_all_freqs() which traverses all registered
clockevent devices and, if the CLOCK_EVT_FEAT_NO_ADJUST flag is not set,
recalculates their ->mult_adjusted based on the monotonic clock's current
frequency.
Call clockevents_adjust_all_freqs() from timekeeping_freqadjust().
Note that it might look like as if timekeeping_apply_adjustment() was the
more natural candidate to trigger the clockevent devices' frequency updates
from: it's the single place where the mono clock's ->mult is changed.
However, timekeeping_apply_adjustment() is also invoked for the
on-off-controlled adjustments made to the mono clock's ->mult from
timekeeping_adjust(). These adjustments are very small in magnitude and,
more importantly, exhibit some oscillatory behaviour once the NTP error
becomes small. We don't want the clockevent devices' ->mult values to
follow these oscillations because they're negligible and because the
process of updating them would periodically destroy what
clockevents_increase_min_delta() might have built up.
Signed-off-by: Nicolai Stange <nicstange@gmail.com>
---
kernel/time/clockevents.c | 33 +++++++++++++++++++++++++++++++++
kernel/time/tick-internal.h | 5 +++++
kernel/time/timekeeping.c | 2 ++
3 files changed, 40 insertions(+)
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index b121932..e009add 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -626,6 +626,39 @@ void __clockevents_adjust_freq(struct clock_event_device *dev)
mult_cs_raw);
}
+void clockevents_adjust_all_freqs(u32 mult_cs_mono, u32 mult_cs_raw)
+{
+ u32 last_mult_raw = 0, last_shift = 0, last_mult_adjusted = 0;
+ u32 mult_raw, shift;
+ unsigned long flags;
+ struct clock_event_device *dev;
+
+ raw_spin_lock_irqsave(&clockevents_lock, flags);
+ list_for_each_entry(dev, &clockevent_devices, list) {
+ if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT) ||
+ (dev->features & CLOCK_EVT_FEAT_NO_ADJUST))
+ continue;
+
+ /*
+ * The cached last_mult_adjusted is only valid if
+ * shift == last_shift. Otherwise, it could exceed
+ * what is allowed by ->max_delta_ns.
+ */
+ mult_raw = dev->mult;
+ shift = dev->shift;
+ if (mult_raw != last_mult_raw || shift != last_shift) {
+ last_mult_raw = mult_raw;
+ last_shift = shift;
+ last_mult_adjusted =
+ __clockevents_calc_adjust_freq(mult_raw,
+ mult_cs_mono,
+ mult_cs_raw);
+ }
+ dev->mult_adjusted = last_mult_adjusted;
+ }
+ raw_spin_unlock_irqrestore(&clockevents_lock, flags);
+}
+
int __clockevents_update_freq(struct clock_event_device *dev, u32 freq)
{
clockevents_config(dev, freq);
diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h
index 0b29d23..2d97c42 100644
--- a/kernel/time/tick-internal.h
+++ b/kernel/time/tick-internal.h
@@ -56,6 +56,7 @@ extern int clockevents_program_event(struct clock_event_device *dev,
ktime_t expires, bool force);
extern void clockevents_handle_noop(struct clock_event_device *dev);
extern int __clockevents_update_freq(struct clock_event_device *dev, u32 freq);
+extern void clockevents_adjust_all_freqs(u32 mult_cs_mono, u32 mult_cs_raw);
extern void timekeeping_get_mono_mult(u32 *mult_cs_mono, u32 *mult_cs_raw);
extern ssize_t sysfs_get_uname(const char *buf, char *dst, size_t cnt);
@@ -95,6 +96,10 @@ static inline void tick_set_periodic_handler(struct clock_event_device *dev, int
#else /* !GENERIC_CLOCKEVENTS: */
static inline void tick_suspend(void) { }
static inline void tick_resume(void) { }
+
+static inline void clockevents_adjust_all_freqs(u32 mult_cs_mono,
+ u32 mult_cs_raw)
+{}
#endif /* !GENERIC_CLOCKEVENTS */
/* Oneshot related functions */
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 7ddca9e..f55926b 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1910,6 +1910,8 @@ static __always_inline void timekeeping_freqadjust(struct timekeeper *tk,
/* scale the corrections */
timekeeping_apply_adjustment(tk, offset, negative, adj_scale);
+ clockevents_adjust_all_freqs(tk->tkr_mono.mult,
+ tk->tkr_mono.clock->mult);
}
/*
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Nicolai Stange <nicstange@gmail.com> |
|---|---|
| Date | 2016-09-09 22:20 +0200 |
| Subject | [RFC v6 16/23] clockevents: clockevents_program_min_delta(): don't set ->next_event |
| Message-ID | <sfArg-7zf-31@gated-at.bofh.it> |
| In reply to | #1480315 |
Currently, clockevents_program_min_delta() sets a clockevent device's
->next_event to the point in time where the minimum delta would actually
expire:
delta = dev->min_delta_ns;
dev->next_event = ktime_add_ns(ktime_get(), delta);
For your reference, this is so since the initial advent of
clockevents_program_min_delta() with
commit d1748302f70b ("clockevents: Make minimum delay adjustments
configurable").
clockevents_program_min_delta() is called from clockevents_program_event()
only. More specifically, it is called if the latter's force argument is set
and, neglecting the case of device programming failure for the moment, if
the requested expiry is in the past.
On the contrary, if the expiry requested from clockevents_program_event()
is in the future, but less than ->min_delta_ns behind, then
- ->next_event gets set to that expiry verbatim
- but the clockevent device gets silently programmed to fire after
->min_delta_ns only.
Thus, in the extreme cases of expires == ktime_get() and
expires == ktime_get() + 1, the respective values of ->next_event would
differ by ->min_delta_ns while the clockevent device would actually get
programmed to fire at (almost) the same times (with force being set,
of course).
While this discontinuity of ->next_event at expires == ktime_get() is not
a problem by itself, the mere use of ->min_delta_ns in the event
programming path hinders upcoming changes making the clockevent core
NTP correction aware: both, ->mult and ->min_delta_ns would need to get
updated as well as consumed atomically and we'd rather like to avoid any
locking here.
Thus, let clockevents_program_event() unconditionally set ->next_event to
the expiry time actually requested by its caller, i.e. don't set
->next_event from clockevents_program_min_delta().
A few notes on why this change is safe with the current consumers of
->next_event:
1.
Note that a clockevents_program_event() with a requested expiry in the
past and force being set basically means: "fire ASAP". Now, consider this
so programmed event getting handed once again to
clockevents_program_event(), i.e. that a
clockevents_program_event(dev, dev->next_event, false)
as in __clockevents_update_freq() is done.
With this change applied, clockevents_program_event() would now properly
detect the expiry being in the past and, due to the force argument being
unset, wouldn't actually do anything.
Before this change OTOH, there would be the (very unlikely) possibility
that the requested event is still somewhere in the future and
clockevents_program_event() would silently delay the event expiration by
another ->min_delta_ns.
2.
The periodic tick handlers on oneshot-only devices use ->next_event
to calculate the followup expiry time.
tick_handle_periodic() spins on reprogramming the clockevent device
until some expiry in the future has been reached:
ktime_t next = dev->next_event;
...
for(;;) {
next = ktime_add(next, tick_period);
if (!clockevents_program_event(dev, next, false))
return;
...
}
Thus, tick_handle_periodic() isn't affected by this change.
For tick_handle_periodic_broadcast(), the situation is different since
commit 2951d5c031a3 ("tick: broadcast: Prevent livelock from event
handler")
though: a loop similar to the one from tick_handle_periodic() above got
replaced by a single
ktime_t next = ktime_add(dev->next_event, tick_period);
clockevents_program_event(dev, next, true);
In the case that dev->next_event + tick_period happens to be less than
ktime_get() + ->min_delta_ns, without this change applied, ->next_event
would get recovered to some point in the future after a single
tick_handle_periodic_broadcast() event.
On the contrary, with this patch applied, it could potentially take some
number of tick_handle_periodic_broadcast() events, each separated by
->min_delta_ns only, until ->next_event is able to catch up with the
current ktime_get(). However, if this turns out to become a problem,
the reprogramming loop in tick_handle_periodic_broadcast() can probably
be restored easily.
3.
In kernel/time/tick-broadcast.c, the broadcast receiving clockevent
devices' ->next_event is read multiple times in order to determine who's
next or who must be pinged. These uses all continue to work. Moreover,
clockevent devices getting programmed to something less than
ktime_get() + ->min_delta_ns
might not be the best candidates for a transition into C3 anyway.
4.
Finally, a "sleep length" is calculated at the very end of
tick_nohz_stop_sched_tick() as follows:
ts->sleep_length = ktime_sub(dev->next_event, now);
AFAICS, this can happen to be negative w/o this change applied already: in
NOHZ_MODE_HIGHRES mode there can be some overdue hrtimers whose removal is
blocked because tick_nohz_stop_sched_tick() gets called with interrupts
disabled. Unfortunately, the only user, the menu cpuidle governor,
can't cope with negative sleep lengths as it casts the return value
of the tick_nohz_get_sleep_length() getter to an unsigned int.
This change can very well make things worse here. A followup patch
will force this ->sleep_length to >= 0.
Signed-off-by: Nicolai Stange <nicstange@gmail.com>
---
kernel/time/clockevents.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index f41f584..8983fee 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -252,7 +252,6 @@ static int clockevents_program_min_delta(struct clock_event_device *dev)
for (i = 0;;) {
delta = dev->min_delta_ns;
- dev->next_event = ktime_add_ns(ktime_get(), delta);
if (clockevent_state_shutdown(dev))
return 0;
@@ -289,7 +288,6 @@ static int clockevents_program_min_delta(struct clock_event_device *dev)
int64_t delta;
delta = dev->min_delta_ns;
- dev->next_event = ktime_add_ns(ktime_get(), delta);
if (clockevent_state_shutdown(dev))
return 0;
--
2.9.3
[toc] | [prev] | [next] | [standalone]
| From | Nicolai Stange <nicstange@gmail.com> |
|---|---|
| Date | 2016-09-09 22:30 +0200 |
| Subject | [RFC v6 17/23] clockevents: use ->min_delta_ticks_adjusted to program minimum delta |
| Message-ID | <sfAAV-7Cd-3@gated-at.bofh.it> |
| In reply to | #1480315 |
The use of a clockevent device's ->min_delta_ns in the event programming
path hinders upcoming changes to the clockevent core making it NTP
correction aware: both, ->mult and ->min_delta_ns would need to get
updated as well as consumed atomically and we'd rather like to avoid any
locking here.
We already have got ->min_delta_ticks_adjusted which
- resembles the value of ->min_delta_ns
- and is guaranteed to be always >= the hardware's hard limit
->min_delta_ticks and thus, can be used w/o locking as we don't care
for small deviations.
In both implementations of clockevents_program_min_delta(),
don't calculate the event's deadline from ->min_delta_ns, but use its
drop-in replacement ->min_delta_ticks_adjusted.
Signed-off-by: Nicolai Stange <nicstange@gmail.com>
---
kernel/time/clockevents.c | 15 ++-------------
1 file changed, 2 insertions(+), 13 deletions(-)
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index 8983fee..aa7b325 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -246,19 +246,14 @@ static int clockevents_increase_min_delta(struct clock_event_device *dev)
*/
static int clockevents_program_min_delta(struct clock_event_device *dev)
{
- unsigned long long clc;
- int64_t delta;
int i;
for (i = 0;;) {
- delta = dev->min_delta_ns;
-
if (clockevent_state_shutdown(dev))
return 0;
dev->retries++;
- clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
- if (dev->set_next_event((unsigned long) clc, dev) == 0)
+ if (!dev->set_next_event(dev->min_delta_ticks_adjusted, dev))
return 0;
if (++i > 2) {
@@ -284,17 +279,11 @@ static int clockevents_program_min_delta(struct clock_event_device *dev)
*/
static int clockevents_program_min_delta(struct clock_event_device *dev)
{
- unsigned long long clc;
- int64_t delta;
-
- delta = dev->min_delta_ns;
-
if (clockevent_state_shutdown(dev))
return 0;
dev->retries++;
- clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
- return dev->set_next_event((unsigned long) clc, dev);
+ return dev->set_next_event(dev->min_delta_ticks_adjusted, dev);
}
#endif /* CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST */
--
2.9.3
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web