Why does my hrtimer callback return too early after forwarding it?

房东的猫 提交于 2019-12-07 23:05:29

So to answer this myself: This is a problem of wrong expectations.

What we expected here is to set the timer forward during the callback by the amount we set (24us). But if we take a look at the kernel implementation of hrtimer_forward_now()we can see that the time is actually added to the last event/occurrence of the timer (see the calculation of delta):

From Linux/kernel/time/hrtimer.c

833 u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval)
834 {
835         u64 orun = 1;
836         ktime_t delta;
837 
838         delta = ktime_sub(now, hrtimer_get_expires(timer));
839 
840         if (delta.tv64 < 0)
841                 return 0;
842 
843         if (WARN_ON(timer->state & HRTIMER_STATE_ENQUEUED))
844                 return 0;
845 
846         if (interval.tv64 < hrtimer_resolution)
847                 interval.tv64 = hrtimer_resolution;
848 
849         if (unlikely(delta.tv64 >= interval.tv64)) {
850                 s64 incr = ktime_to_ns(interval);
851 
852                 orun = ktime_divns(delta, incr);
853                 hrtimer_add_expires_ns(timer, incr * orun);
854                 if (hrtimer_get_expires_tv64(timer) > now.tv64)
855                         return orun;
856                 /*
857                  * This (and the ktime_add() below) is the
858                  * correction for exact:
859                  */
860                 orun++;
861         }
862         hrtimer_add_expires(timer, interval);
863 
864         return orun;
865 }

That means that the time delay it took between the timer firing and the callback actually executing is not taken into account here. The hrtimers are meant to be precise in interval timing and not be influenced by the usual delays between firing and the callback. Where our expectancy was to include that time into the calculation because we wanted the timer to restart from the moment we executed an action in the timer callback.

I tried to draw this into the following diagram:

Following the red numbered bubbles we get:

  1. timer is started with X time to fire
  2. time X has passed, the timer is triggered
  3. after "delay X" depending on the load of the system and other factors, the callback function for the hrtimer is called
  4. hrtimer_forward_now sets the new timer forward based on the last event plus the new expected time (which might be only 2us in the future instead of 24)
  5. Here is the discrepancy of expectation vs reality. The hrtimer fires 24us after the last event when we expect it to fire 24us after the call to forward_now()

To sum it all up, we completely trashed the above code example and went with a usleep_range() call between triggering the two GPIO pins. The underlying implementation of that function is also done with hrtimer but it is hidden from the user and it acts as we expect in this case.

I also encounter this problem.Thank you for TabascoEye's answer.I just want to add some code as an example for easier understand.

In my application, I have a hardware interrupt(30ms+-3ms interval) to call reactive_hrtimer() and then timer_do() will be called after 10ms. Since the time enter the interrupt is not regular, I need to implement the input for hrtimer_add_expires() myself.

For irregular time interval:

enum hrtimer_restart timer_do(struct hrtimer *timer)
{
    /**something**/
    return HRTIMER_NORESTART;
}

void reactive_hrtimer( struct hrtimer *hr_timer, ktime_t ktime_interval)
{
    ktime_t delta;
    ktime_t now;
    now = hrtimer_cb_get_time(hr_timer);
    delta = ktime_sub(now, hrtimer_get_expires(hr_timer));

    hrtimer_add_expires(hr_timer, ktime_add(ktime_interval, delta));
    hrtimer_restart(hr_timer);
 }

These code can be put in anywhere instead of inside the callback

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!