• Gaurav Kohli's avatar
    timers: Clear timer_base::must_forward_clk with timer_base::lock held · 363e934d
    Gaurav Kohli authored
    timer_base::must_forward_clock is indicating that the base clock might be
    stale due to a long idle sleep.
    
    The forwarding of the base clock takes place in the timer softirq or when a
    timer is enqueued to a base which is idle. If the enqueue of timer to an
    idle base happens from a remote CPU, then the following race can happen:
    
      CPU0					CPU1
      run_timer_softirq			mod_timer
    
    					base = lock_timer_base(timer);
      base->must_forward_clk = false
    					if (base->must_forward_clk)
    				       	    forward(base); -> skipped
    
    					enqueue_timer(base, timer, idx);
    					-> idx is calculated high due to
    					   stale base
    					unlock_timer_base(timer);
      base = lock_timer_base(timer);
      forward(base);
    
    The root cause is that timer_base::must_forward_clk is cleared outside the
    timer_base::lock held region, so the remote queuing CPU observes it as
    cleared, but the base clock is still stale. This can cause large
    granularity values for timers, i.e. the accuracy of the expiry time
    suffers.
    
    Prevent this by clearing the flag with timer_base::lock held, so that the
    forwarding takes place before the cleared flag is observable by a remote
    CPU.
    Signed-off-by: default avatarGaurav Kohli <gkohli@codeaurora.org>
    Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
    Cc: john.stultz@linaro.org
    Cc: sboyd@kernel.org
    Cc: linux-arm-msm@vger.kernel.org
    Link: https://lkml.kernel.org/r/1533199863-22748-1-git-send-email-gkohli@codeaurora.org
    363e934d
timer.c 56.9 KB