ARM Linux 大小核切換
主要代碼分布:
arch/arm/common/bL_switcher.c
arch/arm/include/asm/bL_switcher.h
drivers/cpufreq/Arm_big_little.c
編譯后形成的兩個(gè)驅(qū)動(dòng)模塊:
__initcall_bL_switcher_init6
__initcall_bL_cpufreq_register7
執(zhí)行流程圖如下所示。
上圖的左邊是bL_switcher_init執(zhí)行流程。
右邊是切換線程bL_switcher_thread執(zhí)行流程,這個(gè)線程是核心代碼。代碼的上半部分是一個(gè)CPU在運(yùn)行,然后這個(gè)CPU完成下電后。下半部分藍(lán)色所示,是起來的那個(gè)CPU在運(yùn)行,這樣就完成了大小核的切換操作。
1 bL_switcher_init
bL_switcher_init執(zhí)行流程如下:
如果最大CPU組數(shù)(簇)不為2,則報(bào)錯(cuò),然后返回
bL_running_cluster賦值1,默認(rèn)設(shè)為是第一簇在運(yùn)行
no_bL_switcher初值為FALSE
bL_switcher_enable
bL_switcher_halve_cpus 關(guān)閉不必要的CPU
為每個(gè)online的CPU創(chuàng)建切換線程bL_switcher_thread
bL_switcher_active賦值1
bL_switcher_sysfs_init 創(chuàng)建sys/kernel/bL_switcher
bL_switcher_restore_unpaired_cpus 恢復(fù)不成對(duì)的CPU,給它們上電
創(chuàng)建完sys/kernel/bL_switcher,則可以通過下面的命令,手動(dòng)查看/設(shè)置能否切換、查看/設(shè)置切換那個(gè)簇的CPU。
cat /sys/kernel/bL_switcher/active
echo 0/1 > /sys/kernel/ bL_switcher/active
cat /sys/kernel/bL_switcher/do_switch
echo0/1> /sys/kernel/ bL_switcher/ do_switch
2 切換請(qǐng)求bL_switch_request
這是個(gè)內(nèi)聯(lián)函數(shù),需要兩個(gè)參數(shù),第一個(gè)是CPU的ID,第二個(gè)是簇號(hào),內(nèi)核調(diào)到的有三處。
一是在Arm_big_little.c:bL_cpufreq_set_rate:需要不同的簇切換時(shí);
二、三是在執(zhí)行echo 0/1 > /sys/kernel/ bL_switcher/ do_switch ,調(diào)用到bL_do_switch_store函數(shù),這里面判斷是否需要切換
這個(gè)內(nèi)聯(lián)函數(shù),直接執(zhí)行bL_switch_request_cb,參數(shù)是前面的2個(gè),再加上兩個(gè)NULL。
bL_switch_request_cb:
判斷第一個(gè)參數(shù)CPU的ID是否超出范圍;
獲取當(dāng)前CPU的線程函數(shù)指針
賦值wanted_cluster
喚醒當(dāng)前線程函數(shù)的工作隊(duì)列
3 切換線程bL_switcher_thread
bL_switcher_thread執(zhí)行流程:
等待事件。滿足事件的兩個(gè)可能條件:
一是上面的bL_switch_request_cb函數(shù),喚醒線程,且切換到的CPU簇?cái)?shù)不為-1;
二是bL_switcher_disable函數(shù)調(diào)用kthread_stop,喚醒線程
bL_switch_to
找到成對(duì)的CPU ID ,簇號(hào)
mcpm_cpu_power_up 給CPU上電,跳轉(zhuǎn)到 mcpm_entry_point
gic_send_sgi 給其發(fā)送0號(hào)軟中斷
wait_for_completion_timeout(&inbound_alive 等待它給我發(fā)送軟中斷 IPI_COMPLETION
關(guān)閉IRQ、FIQ
遷移中斷到對(duì)應(yīng)的CPU上
關(guān)閉時(shí)鐘Tick
cpu_pm_enter gic_cpu_save 保存中斷設(shè)置
cpu_suspend這個(gè)跟睡眠的流程很相似
bL_switchpoint
call_with_stack arch/arm/lib/call_with_stack.S 若失敗了,是可以返回的
bL_do_switch
讓剛起來的CPU跳轉(zhuǎn)到cpu_resume,給其發(fā)送sev
若handshake變量為0,則進(jìn)入wfe
等待不為0后,mcpm_cpu_power_down對(duì)自身斷電
call_with_stack:攜帶的三個(gè)參數(shù),第一個(gè)是函數(shù),第二個(gè)是函數(shù)調(diào)用時(shí)用到的參數(shù),第三個(gè)是棧地址
將SP、LR依次放入棧的頂部
SP賦值讓開兩個(gè)寄存器后的地址
R0賦給R2
R1賦給R0
LR賦值下面的標(biāo)號(hào)1處
R2賦給PC,即跳轉(zhuǎn)到R2
若失敗了,則跳轉(zhuǎn)到LR處,就是標(biāo)號(hào)1處
彈出LR
彈出SP
LR賦給PC,跳轉(zhuǎn)到了bL_switchpoint,調(diào)用call_with_stack函數(shù)的地方。
此處設(shè)置真是巧妙,不同簇對(duì)應(yīng)的CPU ID都是0,則剛起來的CPU正好能通過下面的步驟
mcpm_entry_point-> cpu_resume-> cpu_do_resume=cpu_v7_do_resume -> cpu_resume_mmu -> 再次跳轉(zhuǎn)到上面調(diào)用__cpu_suspend處繼續(xù)運(yùn)行了。
即要下電的CPU剛保存后堆棧,又被剛上電的CPU恢復(fù)了。
接下來就是上電的CPU再運(yùn)行了。
cpu_pm_exit gic_cpu_restore 恢復(fù)中斷設(shè)置
時(shí)鐘接著運(yùn)行 Tick,兩個(gè)CPU用到的節(jié)拍Timer是同一個(gè)。
開啟IRQ、FIQ
*handshake_ptr = 1;
dsb_sev 給那個(gè)CPU發(fā)送事件
這樣就完成了CPU的切換,這個(gè)函數(shù)的前一半是一個(gè)CPU在執(zhí)行,后一半變成了另一個(gè)CPU在執(zhí)行。
4 bL_cpufreq_register
獲取bL_switcher_active的值,將這個(gè)值(真或者假)設(shè)置給bL_switching_enabled變量;
初始化互斥鎖cluster_lock;
注冊(cè)cpufreq_driver驅(qū)動(dòng)bL_cpufreq_driver。
如果上面的驅(qū)動(dòng)注冊(cè)成功,則將bL_switcher_notifier 掛在bL_activation_notifier鏈表上;
若掛載失敗,則卸載驅(qū)動(dòng)bL_cpufreq_driver
bL_cpufreq_driver定義如下:
static struct cpufreq_driver bL_cpufreq_driver = {
.name = "arm-big-little",
.flags = CPUFREQ_STICKY,
.verify = bL_cpufreq_verify_policy,
.target = bL_cpufreq_set_target,
.get = bL_cpufreq_get_rate,
.init = bL_cpufreq_init,
.have_governor_per_policy = true,
.attr = bL_cpufreq_attr,
};
若bL_cpufreq_driver注冊(cè)成功,執(zhí)行下面的命令,就可以看到有個(gè)驅(qū)動(dòng)是arm-big-little。
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
conservative ondemand userspace powersaveinteractiveperformance
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governors
interactive
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver
arm-big-little
用bL_cpufreq_driver這種調(diào)頻策略時(shí),就會(huì)執(zhí)行到bL_cpufreq_set_target,然后執(zhí)行bL_cpufreq_set_rate,則有可能調(diào)用到bL_switch_request。
評(píng)論