時間子係統5_低分辨率切換高分辨率
// 切換低分辨率動態時鍾模式、或高分辨率模式
// 調用路徑:run_timer_softirq->hrtimer_run_pending
// 函數任務:
// 1.如果高分辨率定時器框架已經激活,則直接返回
// 2.切換到高分辨率模式的條件:
// 2.1 沒有開啟低分辨率動態時鍾
// 2.2 有高分辨率的clocksource
// 2.3 clockevent設備支持單觸發模式
// 3.切換到低分辨率動態時鍾的條件:
// 3.1 啟動時,沒有激活高分辨率率定時框架
// 3.2 clockevent設備支持單觸發模式
1.1 void hrtimer_run_pending(void)
{
if (hrtimer_hres_active())
return;
if (tick_check_oneshot_change(!hrtimer_is_hres_enabled()))
{
//切換到高分辨率模式
hrtimer_switch_to_hres();
}
}
// 切換低分辨率模式的動態時鍾
// 函數任務:
// 1.更新tick device單觸發方式,安裝nohz事件處理函數
// 2.初始化動態時鍾數據結構
// 2.1 標識動態時鍾當前為低分辨率模式
// 2.2 動態時鍾通過單調時鍾基礎管理
// 2.3 更新動態時鍾到期時間
// 3.更新動態時鍾,tick device到期時間
2.1 static void tick_nohz_switch_to_nohz(void)
{
struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
ktime_t next;
local_irq_disable();
//更新tick device單觸發方式,安裝nohz事件處理函數
if (tick_switch_to_oneshot(tick_nohz_handler)) {
local_irq_enable();
return;
}
//低分辨率動態時鍾標識
ts->nohz_mode = NOHZ_MODE_LOWRES;
//低分辨率動態時鍾依賴單調時鍾
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
//下次到期時間
next = tick_init_jiffy_update();
//更新動態時鍾,tick device到期時間
for (;;) {
hrtimer_set_expires(&ts->sched_timer, next);
if (!tick_program_event(next, 0))
break;
next = ktime_add(next, tick_period);
}
local_irq_enable();
}
// 低分辨率動態事件處理函數
// 函數任務:
// 1.選擇cpu負責更新jiffies
// 2.如果距離上次更新jiffies已經有1 jiffy,更新jiffies
// 3.如果動態時鍾已經停止,說明當前在idle狀態,喂狗,防止發生softlockup
// 4.更新當前進程的運行時間
// 6.更新動態時鍾的下個周期時間
// 7.重編程tick device在動態時鍾的下個周期時間到期
// 注:tick_do_timer_cpu 保存負責更新jiffies的cpu
2.2 static void tick_nohz_handler(struct clock_event_device *dev)
{
struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
struct pt_regs *regs = get_irq_regs();
int cpu = smp_processor_id();
ktime_t now = ktime_get();
//保證tick device在最近的時間內不會到期
dev->next_event.tv64 = KTIME_MAX;
//選擇cpu負責更新jiffies
if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE))
tick_do_timer_cpu = cpu;
//更新jiffies
if (tick_do_timer_cpu == cpu)
tick_do_update_jiffies64(now);
//動態時鍾停止,說明當前在idle狀態,喂狗,防止發生softlockup
if (ts->tick_stopped) {
touch_softlockup_watchdog();
ts->idle_jiffies++;
}
//更新進程的運行時間
update_process_times(user_mode(regs));
//重編程clockevent設備
while (tick_nohz_reprogram(ts, now)) {
now = ktime_get();
tick_do_update_jiffies64(now);
}
}
// 切換高分辨率模式
// 函數任務:
// 1.更新clockevent為單觸發模式,安裝高分辨率事件處理程序
// 2.初始化動態時鍾
// 2.1 更新高分辨率激活標誌
// 2.2 更新時鍾基礎為高分辨率標誌
// 3.初始化動態時鍾
// 4.更新tick device到期時間為時鍾基礎中所有hrtimer最短的到期時間
3.1 static int hrtimer_switch_to_hres(void)
{
int cpu = smp_processor_id();
struct hrtimer_cpu_base *base = &per_cpu(hrtimer_bases, cpu);
unsigned long flags;
local_irq_save(flags);
//更新clockevent為單觸發模式,安裝高分辨率事件處理程序
if (tick_init_highres()) {
local_irq_restore(flags);
return 0;
}
//高分辨率激活標誌
base->hres_active = 1;
base->clock_base[CLOCK_REALTIME].resolution = KTIME_HIGH_RES;
base->clock_base[CLOCK_MONOTONIC].resolution = KTIME_HIGH_RES;
//初始化動態時鍾
tick_setup_sched_timer();
//更新tick device到期時間為時鍾基礎中所有hrtimer最短的到期時間
retrigger_next_event(NULL);
local_irq_restore(flags);
return 1;
}
// 更新clockevnet為單觸發模式,安裝事件處理程序
// 調用路徑:hrtimer_switch_to_hres->tick_switch_to_oneshot
3.2 int tick_init_highres(void)
{
return tick_switch_to_oneshot(hrtimer_interrupt);
}
// 更新clockevnet為單觸發模式,安裝事件處理程序
// 調用路徑:hrtimer_switch_to_hres->...->tick_switch_to_oneshot
// 函數任務:
// 1.更新設備為單觸發方式
// 2.安裝事件處理程序
// 3.更新廣播設備為單觸發方式
3.3 int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *))
{
struct tick_device *td = &__get_cpu_var(tick_cpu_device);
struct clock_event_device *dev = td->evtdev;
//更新設備為單觸發方式
td->mode = TICKDEV_MODE_ONESHOT;
//安裝事件處理程序
dev->event_handler = handler;
//編程clockevent設備為單觸發方式
clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
//切換廣播設備為單觸發方式
tick_broadcast_switch_to_oneshot();
return 0;
}
// 初始化動態時鍾
// 調用路徑:hrtimer_switch_to_hres->tick_setup_sched_timer
// 函數任務:
// 1.初始化動態時鍾的hrtimer
// 2.安裝動態時鍾的回調函數
// 3.更新動態時鍾下一個jiffies到期
3.4 void tick_setup_sched_timer(void)
{
struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
ktime_t now = ktime_get();
u64 offset;
//初始化hrtimer
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
//動態時鍾的回調函數
ts->sched_timer.function = tick_sched_timer;
//下一個jiffies到期
hrtimer_set_expires(&ts->sched_timer, tick_init_jiffy_update());
offset = ktime_to_ns(tick_period) >> 1;
do_div(offset, num_possible_cpus());
offset *= smp_processor_id();
hrtimer_add_expires_ns(&ts->sched_timer, offset);
//編程啟動動態時鍾
for (;;) {
hrtimer_forward(&ts->sched_timer, now, tick_period);
hrtimer_start_expires(&ts->sched_timer,
HRTIMER_MODE_ABS_PINNED);
if (hrtimer_active(&ts->sched_timer))
break;
now = ktime_get();
}
....
}
最後更新:2017-04-03 14:54:27