hrtimer添加的流程圖以下:node
在添加定時器到紅黑樹時,若是已經存在與紅黑樹上,必須得先刪除定時器,以後使用enqueue_hrtimer函數將hrtimer插入到紅黑樹上。若是當前添加的定時器是最先到期的,則須要從新設定定時器硬件的到期時間,須要將當前定時器的到期時間設置到定時器硬件,使其能夠最先獲得處理。react
高精度定時器系統有3個入口能夠對到期的定時器進行處理,分別是編程
1 沒有切換到高精度模式時,在每一個jiffies的tick事件中斷中進行查詢和處理。app
2 在HRTIMER_SOFTIRQ軟中斷中進行查詢和處理。函數
3 切換到高精度模式之後,在每一個clock_event_device的到期事件的中斷中進行查詢和處理。oop
系統並非一開始就會支持高精度模式,而是在系統啓動後的某個階段,等待全部的條件都知足後,纔會切換到高精度模式。當系統沒有切換到高精度模式時,全部的高精度定時器都運行在低精度模式下,在每一個jiffies的tick事件中斷中進行到期定時器的查詢和處理,顯然此時的精度和低分辨率定時器是同樣的(HZ級別)。低精度模式下,每一個tick事件中斷中,hrtimer_run_queues函數會被調用,由它完成定時器的到期處理。hrtimer_run_queue首先判斷目前高精度模式是否已經啓用,若是已經切換到高精度模式下,直接返回。this
void hrtimer_run_queues(void)spa
{ 調試
struct timerqueue_node *node;code
struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
struct hrtimer_clock_base *base;
int index, gettime = 1, raise = 0;
if (hrtimer_hres_active()) /* 斷定是否啓用高精度模式。若是啓用,則直接退出。 */
return;
for (index = 0; index < HRTIMER_MAX_CLOCK_BASES; index++) { /* 遍歷各個時間基準系統。 */
base = &cpu_base->clock_base[index];
if (!timerqueue_getnext(&base->active))
continue;
if (gettime) {
hrtimer_get_softirq_time(cpu_base);
gettime = 0;
}
raw_spin_lock(&cpu_base->lock);
while ((node = timerqueue_getnext(&base->active))) { /* 獲取base->active紅黑樹中的最先到期節點。*/
struct hrtimer *timer;
timer = container_of(node, struct hrtimer, node);
if (base->softirq_time.tv64 <=
hrtimer_get_expires_tv64(timer)) /* 當前時間小於定時器timer的到期時間,說明此時鐘基準的定時器
未到期,直接退出循環。*/
break;
if (!hrtimer_rt_defer(timer))
__run_hrtimer(timer, &base->softirq_time); /* 到期則使用__run_hrtimer函數進行處理。 */
else
raise = 1;
}
raw_spin_unlock(&cpu_base->lock);
}
if (raise)
raise_softirq_irqoff(HRTIMER_SOFTIRQ);
}
若是hrtimer_hres_active返回false,說明目前處於低精度模式下,則繼續處理。它用一個for循環便利各個時間基準系統,查詢每一個hrtimer_clock_base對應紅黑樹的左下節點,判斷其是否到期。若是到期,則使用__run_hrtimer函數,對到期定時器進行處理。包括,調用定時器回調函數,從紅黑樹中移除定時器,根據回調函數返回值決定是否重啓該定時器等。
函數中,while循環能夠不斷的使用timerqueue_getnext獲取紅黑樹中的左下節點next,是由於__run_hrtimer會在處理過程當中,移除到期的定時器,從而新的最先到期的節點會被更新到next字段中,使得循環能夠一直執行,知道沒有到期的定時器爲止。
在切換到高精度模式後,原來給cpu提供tick時間的tick_device會被高精度定時器系統接管,它的中斷時間回調函數被設置爲hrtimer_interrupt,紅黑樹中最左下節點的定時器的到期時間被編程到該clock_event_device中。這樣,每次clock_event_device的中斷意味着有意個高精度定時器到期。另外,當timerkeeper系統中的時間須要修正,後者clock_event_device的到期事件時間被從新編程時,系統會發出HRTIMER_SOFTIRQ軟中斷,軟中斷的處理函數run_hrtimer_softirq最終會調用hrtimer_interrupt函數對定時器進行處理,所在在這裏,咱們只須要討論hrtimer_interrupt函數便可。
hrtimer_interrupt函數的前半部分和低精度模式下的hrtimer_run_queues函數完成相同的事情。它用一個for循環遍歷各個時間基準系統,查詢每一個hrtimer_clock_base對應的紅黑樹的左下節點,判斷它是否到期,若是到期,經過__run_hrtimer函數,對到期定時器進行處理。高精度定時器在處理完全部到期定時器以後,下一個定到期定時器的到期時間保存在變量expires_next中,接下來的工做就是把這個到期時間編程到tick_device中。
void hrtimer_interrupt(struct clock_event_device *dev)
{
struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
ktime_t expires_next, now, entry_time, delta;
int i, retries = 0, raise = 0;
BUG_ON(!cpu_base->hres_active);
cpu_base->nr_events++;
dev->next_event.tv64 = KTIME_MAX;
entry_time = now = ktime_get();
retry:
expires_next.tv64 = KTIME_MAX;
raw_spin_lock(&cpu_base->lock);
/*
* We set expires_next to KTIME_MAX here with cpu_base->lock
* held to prevent that a timer is enqueued in our queue via
* the migration code. This does not affect enqueueing of
* timers which run their callback and need to be requeued on
* this CPU.
*/
cpu_base->expires_next.tv64 = KTIME_MAX;
for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { /* 與hrtimer_run_queues一致,遍歷各時間基準,查詢到期的定時器並使用
__run_hrtimer進行處理。 */
struct hrtimer_clock_base *base;
struct timerqueue_node *node;
ktime_t basenow;
if (!(cpu_base->active_bases & (1 << i)))
continue;
base = cpu_base->clock_base + i;
basenow = ktime_add(now, base->offset);
while ((node = timerqueue_getnext(&base->active))) {
struct hrtimer *timer;
timer = container_of(node, struct hrtimer, node);
trace_hrtimer_interrupt(raw_smp_processor_id(),
ktime_to_ns(ktime_sub(
hrtimer_get_expires(timer), basenow)),
current,
timer->function == hrtimer_wakeup ?
container_of(timer, struct hrtimer_sleeper,
timer)->task : NULL);
/*
* The immediate goal for using the softexpires is
* minimizing wakeups, not running timers at the
* earliest interrupt after their soft expiration.
* This allows us to avoid using a Priority Search
* Tree, which can answer a stabbing querry for
* overlapping intervals and instead use the simple
* BST we already have.
* We don't add extra wakeups by delaying timers that
* are right-of a not yet expired timer, because that
* timer will have to trigger a wakeup anyway.
*/
if (basenow.tv64 < hrtimer_get_softexpires_tv64(timer)) {
ktime_t expires;
expires = ktime_sub(hrtimer_get_expires(timer),
base->offset);
if (expires.tv64 < expires_next.tv64)
expires_next = expires;
break;
}
if (!hrtimer_rt_defer(timer)) /* 判斷timer->irqsafe是否等於1,若是相等,然會0。*/
__run_hrtimer(timer, &basenow);
else
raise = 1;
}
}
/*
* Store the new expiry value so the migration code can verify
* against it.
*/
cpu_base->expires_next = expires_next; /* 記錄下一個即將到期的定時器的時間。*/
raw_spin_unlock(&cpu_base->lock);
/* Reprogramming necessary ? */
if (expires_next.tv64 == KTIME_MAX ||
!tick_program_event(expires_next, 0)) { /* 若是此時tick_program_event返回非0值,表示過時時間已經在當前時間的前面,
一般可能由如下緣由形成:1 系統正在被調試跟蹤,致使時間在走,程序不走。2
定時器的回調函數花了太長的時間。3 系統運行在虛擬機中,而虛擬機被調度致使
中止運行。默認設置成功,tick_program_event返回0。hrtimer_interrupt
函數執行if中的代碼,程序根據raise的值,判斷是否喚醒HRTIMER_SOFTIRQ後
退出。*/
cpu_base->hang_detected = 0;
if (raise)
raise_softirq_irqoff(HRTIMER_SOFTIRQ);
return;
}
/*
* The next timer was already expired due to:
* - tracing
* - long lasting callbacks
* - being scheduled away when running in a VM
*
* We need to prevent that we loop forever in the hrtimer
* interrupt routine. We give it 3 attempts to avoid
* overreacting on some spurious event.
*/
now = ktime_get();
cpu_base->nr_retries++;
if (++retries < 3) /* 爲了不當前時間已通過了下一個定時器到期時間的發生,系統提供三次機會,從新執行以前的循環
處理到期的定時器。 */
goto retry;
/*
* Give the system a chance to do something else than looping
* here. We stored the entry time, so we know exactly how long
* we spent here. We schedule the next event this amount of
* time away.
*/
cpu_base->nr_hangs++;
cpu_base->hang_detected = 1;
delta = ktime_sub(now, entry_time); /* 計算本次總循環的時間。now爲當前時間,entry_time爲進入hrtimer_interrupt的時間。*/
if (delta.tv64 > cpu_base->max_hang_time.tv64)
cpu_base->max_hang_time = delta;
/*
* Limit it to a sensible value as we enforce a longer
* delay. Give the CPU at least 100ms to catch up.
*/
if (delta.tv64 > 100 * NSEC_PER_MSEC) /* tick_device的到期時間被強制設定在100ms之內。*/
expires_next = ktime_add_ns(now, 100 * NSEC_PER_MSEC);
else
expires_next = ktime_add(now, delta);
tick_program_event(expires_next, 1); /* 設置下一次到期時間。 */
printk_once(KERN_WARNING "hrtimer: interrupt took %llu ns\n",
ktime_to_ns(delta));
}