標籤:
ARM Linux 大小核切換——cortex-A7 big.LITTLE 大小切換程式碼分析
8核CPU或者是更多核的處理器,這些CPU有可能不完全對稱。有的是4個A15和4個A7,或者是4個A57和4個A53,甚至像海思麒麟935處理器(4核A53 2.2 GHz + 4核A53 1.5 GHz),這8個核的頻率可能不一樣,則使用過程中需要大小核切換(頻率高的是大核,頻率低的是小核)。本文以ARM cortex-A7為例,分析大小核切換的代碼,著重於分析實現切換的代碼,對於為什麼要這樣切換、以及什麼時候切換,不做過多探討。
主要代碼分布:
arch/arm/common/bL_switcher.c
arch/arm/include/asm/bL_switcher.h
drivers/cpufreq/Arm_big_little.c
編譯後形成的兩個驅動模組:
__initcall_bL_switcher_init6
__initcall_bL_cpufreq_register7
執行流程圖如下所示。
的左邊是bL_switcher_init執行流程。
右邊是切換線程bL_switcher_thread執行流程,這個線程是核心代碼。代碼的上半部分是一個CPU在運行,然後這個CPU完成下電後。下半部分藍色所示,是起來的那個CPU在運行,這樣就完成了大小核的切換操作。
1 bL_switcher_init
bL_switcher_init執行流程如下:
如果最大CPU組數(簇)不為2,則報錯,然後返回
bL_running_cluster賦值1,預設設為是第一簇在運行
no_bL_switcher初值為FALSE
bL_switcher_enable
bL_switcher_halve_cpus 關閉不必要的CPU
為每個online的CPU建立切換線程bL_switcher_thread
bL_switcher_active賦值1
bL_switcher_sysfs_init 建立sys/kernel/bL_switcher
bL_switcher_restore_unpaired_cpus 恢複不成對的CPU,給它們上電
建立完sys/kernel/bL_switcher,則可以通過下面的命令,手動查看/設定能否切換、查看/設定切換那個簇的CPU。
cat /sys/kernel/bL_switcher/active
echo 0/1 > /sys/kernel/ bL_switcher/active
cat /sys/kernel/bL_switcher/do_switch
echo 0/1 > /sys/kernel/ bL_switcher/ do_switch
2 切換請求bL_switch_request
這是個內嵌函式,需要兩個參數,第一個是CPU的ID,第二個是簇號,核心調到的有三處。
一是在Arm_big_little.c:bL_cpufreq_set_rate:需要不同的簇切換時;
二、三是在執行echo 0/1 > /sys/kernel/ bL_switcher/ do_switch ,調用到bL_do_switch_store函數,這裡面判斷是否需要切換
這個內嵌函式,直接執行bL_switch_request_cb,參數是前面的2個,再加上兩個NULL。
bL_switch_request_cb:
判斷第一個參數CPU的ID是否超出範圍;
擷取當前CPU的線程函數指標
賦值wanted_cluster
喚醒當前線程函數的工作隊列
3 切換線程bL_switcher_thread
bL_switcher_thread執行流程:
等待事件。滿足事件的兩個可能條件:
一是上面的bL_switch_request_cb函數,喚醒線程,且切換到的CPU簇數不為-1;
二是bL_switcher_disable函數調用kthread_stop,喚醒線程
bL_switch_to
找到成對的CPU ID ,簇號
mcpm_cpu_power_up 給CPU上電,跳轉到 mcpm_entry_point
gic_send_sgi 給其發送0號非強制中斷
wait_for_completion_timeout(&inbound_alive 等待它給我發送非強制中斷 IPI_COMPLETION
關閉IRQ、FIQ
遷移中斷到對應的CPU上
關閉時鐘Tick
cpu_pm_enter gic_cpu_save 儲存中斷設定
cpu_suspend 這個跟睡眠的流程很相似
bL_switchpoint
call_with_stack arch/arm/lib/call_with_stack.S 若失敗了,是可以返回的
bL_do_switch
讓剛起來的CPU跳轉到cpu_resume,給其發送sev
若handshake變數為0,則進入wfe
等待不為0後,mcpm_cpu_power_down對自身斷電
call_with_stack: 攜帶的三個參數,第一個是函數,第二個是函數調用時用到的參數,第三個是棧地址
將SP、LR依次放入棧的頂部
SP賦值讓開兩個寄存器後的地址
R0賦給R2
R1賦給R0
LR賦值下面的標號1處
R2賦給PC,即跳轉到R2
若失敗了,則跳轉到LR處,就是標號1處
彈出LR
彈出SP
LR賦給PC,跳轉到了bL_switchpoint,調用call_with_stack函數的地方。
此處設定真是巧妙,不同簇對應的CPU ID都是0,則剛起來的CPU正好能通過下面的步驟
mcpm_entry_point -> cpu_resume -> cpu_do_resume=cpu_v7_do_resume -> cpu_resume_mmu -> 再次跳轉到上面調用__cpu_suspend處繼續運行了。
即要下電的CPU剛儲存後堆棧,又被剛上電的CPU恢複了。
接下來就是上電的CPU再運行了。
cpu_pm_exit gic_cpu_restore 恢複中斷設定
時鐘接著運行 Tick,兩個CPU用到的節拍Timer是同一個。
開啟IRQ、FIQ
*handshake_ptr = 1;
dsb_sev 給那個CPU發送事件
這樣就完成了CPU的切換,這個函數的前一半是一個CPU在執行,後一半變成了另一個CPU在執行。
4 bL_cpufreq_register
擷取bL_switcher_active的值,將這個值(真或者假)設定給bL_switching_enabled變數;
初始化互斥鎖cluster_lock;
註冊cpufreq_driver驅動bL_cpufreq_driver。
如果上面的驅動註冊成功,則將bL_switcher_notifier 掛在bL_activation_notifier鏈表上;
若掛載失敗,則卸載驅動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註冊成功,執行下面的命令,就可以看到有個驅動是arm-big-little。
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
conservative ondemand userspace powersave interactive performance
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這種調頻策略時,就會執行到bL_cpufreq_set_target,然後執行bL_cpufreq_set_rate,則有可能調用到bL_switch_request。
ARM Linux 大小核切換 ——cortex-A7 big.LITTLE 大小核 切換程式碼分析