在freerun_clocksource_init函数中向系统注册新的时钟源,但没有切换时钟源。
freerun_clocksource_init
clocksource_mmio_init
clocksource_register_hz
__clocksource_register_scale
clocksource_select // finished_booting没有被设置为1,在这里是直接返回
在clocksource_done_booting函数中将finished_booting置1,接着调用clocksource_select切换系统的时钟源。
clocksource_done_booting
clocksource_select
timekeeping_notify(curr_clocksource);
stop_machine(change_clocksource, clock, NULL);
__stop_machine(fn, data, cpus);
当调用clocksource_done_booting时,系统会通过clocksource_select选择系统中最好的时钟源作为系统的时钟源。下面分析一下clocksource_select的代码:
static void clocksource_select(void)
{
struct clocksource *best, *cs;
if (!finished_booting || list_empty(&clocksource_list))
return;
/* First clocksource on the list has the best rating. */
best = list_first_entry(&clocksource_list, struct clocksource, list);
/* Check for the override clocksource. */
list_for_each_entry(cs, &clocksource_list, list) {
if (strcmp(cs->name, override_name) != 0)
continue;
/*
* Check to make sure we don't switch to a non-highres
* capable clocksource if the tick code is in oneshot
* mode (highres or nohz)
*/
if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) &&
tick_oneshot_mode_active()) {
/* Override clocksource cannot be used. */
printk(KERN_WARNING "Override clocksource %s is not "
"HRT compatible. Cannot switch while in "
"HRT/NOHZ mode\n", cs->name);
override_name[0] = 0;
} else
/* Override clocksource can be used. */
best = cs;
break;
}
if (curr_clocksource != best) {
printk(KERN_INFO "Switching to clocksource %s\n", best->name);
curr_clocksource = best;
timekeeping_notify(curr_clocksource);
}
}
/*
* clocksource_done_booting - Called near the end of core bootup
*
* Hack to avoid lots of clocksource churn at boot time.
* We use fs_initcall because we want this to start before
* device_initcall but after subsys_initcall.
*/
static int __init clocksource_done_booting(void)
{
mutex_lock(&clocksource_mutex);
curr_clocksource = clocksource_default_clock();
mutex_unlock(&clocksource_mutex);
finished_booting = 1;
/*
* Run the watchdog first to eliminate unstable clock sources
*/
clocksource_watchdog_kthread(NULL);
mutex_lock(&clocksource_mutex);
clocksource_select();
mutex_unlock(&clocksource_mutex);
return 0;
}
fs_initcall(clocksource_done_booting);
__stop_machine实际上调用change_clocksource完成了对时钟源的切换,具体的代码如下:
static inline int __stop_machine(int (*fn)(void *), void *data,
const struct cpumask *cpus)
{
int ret;
local_irq_disable();
//调用的函数是实际是change_clocksource(clock);
ret = fn(data);
local_irq_enable();
return ret;
}
从而切换到我们注册的时钟上。