From dca86b7f360e14537366e40a5e7cd3624cd3a3de Mon Sep 17 00:00:00 2001 From: Channagoud Kadabi Date: Fri, 18 Aug 2017 13:22:34 -0700 Subject: [PATCH] kernel: time: Fix accuracy for low resolution timer timer wheel calculates the index for any timer based on the expiry value and level granularity of the timer. Due to the level granularity timer will not fire at the exact time instead expire at a time value expires + granularity. This is done in the timer code when the index for each timer is calculated based on the expiry and granularity at each level: expires = (expires + LVL_GRAN(lvl)) >> LVL_SHIFT(lvl); For devfreq drivers the requirement is to fire the timer at the exact time. If the timer does not expire at the exact time then it'll take much longer to react and increase the device frequency. Devfreq driver registers timer for 10ms expiry and due to slack in timer code the expirty happens at 20 ms. For eg: Frame rendering time is 16ms. If devfreq driver reacts after 20ms instead of 10ms, that's way past a frame rendering time. Timers with 10ms to 630ms expiry fall under level 0, to overcome the granularity issue for level 0 with low expirty values do not add the granularity by introducing a new function calc_index_min_granularity. Change-Id: I60f6f4394de4b5f409829de9734645e1a0d7659e Signed-off-by: Channagoud Kadabi Signed-off-by: Rishabh Bhatnagar [eberman@codeaurora.org: simplify to one function] Signed-off-by: Elliot Berman --- kernel/time/timer.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 57722f670bf0..39307fc34bb1 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -492,7 +492,11 @@ static inline void timer_set_idx(struct timer_list *timer, unsigned int idx) */ static inline unsigned calc_index(unsigned expires, unsigned lvl) { - expires = (expires + LVL_GRAN(lvl)) >> LVL_SHIFT(lvl); + if (lvl != 0 && (expires & ~(UINT_MAX << LVL_SHIFT(lvl)))) + expires = (expires + LVL_GRAN(lvl)) >> LVL_SHIFT(lvl); + else + expires = expires >> LVL_SHIFT(lvl); + return LVL_OFFS(lvl) + (expires & LVL_MASK); }