ARM big.LITTLE Technology知识点

1. big or little / big and little
"Fortunately, no hardware changes are required to support big.LITTLE MP, so it is possible for a silicon vendor to deploy a platform with CPU migration and upgrade to big.LITTLE MP with a kernel update to deployed platforms." -- <<Advances in big.LITTLE Technology for Power and Energy Savings>>
说明是软件控制的。
2. big.Little in official kernel
首先,要找到哪些代码和big.little相关。
先看一下Kconfig
arch/arm/Kconfig
config BL_SWITCHER
bool "big.LITTLE switcher support (experimental)"
depends on CPU_V7 && EXPERIMENTAL
select CPU_PM
select ARM_CPU_SUSPEND
help
The big.LITTLE "switcher" provides the core functionality to
transparently handle transition between a cluster of A15's
and a cluster of A7's in a big.LITTLE system.
config BL_SWITCHER_DUMMY_IF
bool "Simple big.LITTLE switcher user interface"
depends on BL_SWITCHER
help
This is a simple and dummy char dev interface to control
the big.LITTLE switcher core code. It is meant for
debugging purposes only.
得知了big.LITTLE相关的配置
看一下5420的配置文件
./arch/arm/configs/arndale_octa_android_defconfig:36:CONFIG_BL_SWITCHER=y
./arch/arm/configs/arndale_octa_android_defconfig:37:CONFIG_BL_SWITCHER_DUMMY_IF=y
确实已经配置上。
全搜一把,看下相关代码有哪些。
grep -E " CONFIG_BL_SWITCHER | CONFIG_BL_SWITCHER_DUMMY_IF" -rn ./
...
./drivers/cpufreq/exynos-ikcs-cpufreq.c:388:#ifdef CONFIG_BL_SWITCHER
./drivers/cpufreq/exynos-ikcs-cpufreq.c:459:#ifdef CONFIG_BL_SWITCHER
./drivers/cpufreq/exynos-ikcs-cpufreq.c:550:#ifdef CONFIG_BL_SWITCHER
...
./arch/arm/common/Makefile:18:obj-$(CONFIG_BL_SWITCHER) += bL_head.o bL_entry.o
./arch/arm/common/Makefile:19:obj-$(CONFIG_BL_SWITCHER) += bL_switcher.o
./arch/arm/common/Makefile:20:obj-$(CONFIG_BL_SWITCHER) += bL_vlock.o
./arch/arm/common/gic.c:97:#if defined(CONFIG_BL_SWITCHER)
./arch/arm/common/gic.c:432:#if defined(CONFIG_BL_SWITCHER)
./arch/arm/common/gic.c:542:#if defined(CONFIG_BL_SWITCHER)
./arch/arm/common/gic.c:580:#if defined(CONFIG_BL_SWITCHER)
./arch/arm/common/gic.c:814:#ifdef CONFIG_BL_SWITCHER
...
./arch/arm/mach-exynos/Makefile:24:obj-$(CONFIG_BL_SWITCHER) += sys-if-switcher.o
./arch/arm/mach-exynos/Makefile:185:obj-$(CONFIG_BL_SWITCHER) += bL_control.o bL_setup.o
./arch/arm/mach-exynos/mach-arndale_octa.c:160:#ifdef CONFIG_BL_SWITCHER
./arch/arm/mach-exynos/mach-arndale_octa.c:244:#ifdef CONFIG_BL_SWITCHER
...
./arch/arm/kernel/setup.c:271:#ifdef CONFIG_BL_SWITCHER
./arch/arm/mm/proc-v7.S:130:#ifdef CONFIG_BL_SWITCHER
./kernel/panic.c:76:#ifdef CONFIG_BL_SWITCHER
./kernel/panic.c:117:#ifdef CONFIG_BL_SWITCHER
./kernel/printk.c:47:#ifdef CONFIG_BL_SWITCHER
./kernel/printk.c:1032:#ifdef CONFIG_BL_SWITCHER

可以开始看代码了。
3. dive into codes
为了找到big.LITTLE的entry点, 先从熟悉的地方找入口。
先看
arch/arm/mach-exynos/sys-if-switcher.c 它提供了sys文件系统的接口,让用户可以手动进行 switch查看switch状态和统计信息
在初始化函数exynos_bL_sys_if_init里看到它做了2件事:
1. subsys_system_register //注册sys文件系统的interface, 以后在板子上跑的时候,在/sys/devices/system/b.L/* 下可以看到user接口(与proc文件系统类似)
这里注册了3个属性:
A) b.L_switcher(写)
通过向/sys/devices/system/b.L/b.L_switcher 写入特定格式字符串 (<cpu#>,<cluster#>),进行切换。这样,就找到了手动切换入口, 也是今后要详细分析的函数:
arch/arm/common/bL_switcher.c
bL_switch_request - Switch to a specific cluster for the given CPU
bL_cluster_switch_request <<<< 切一组里的所有cpu至另一组,当前配置使用这个
搜索 bL_cluster_switch_request,可以看到如下调用关系:
drivers/cpufreq/exynos-ikcs-cpufreq.c
exynos_target -> exynos_switch -> bL_cluster_switch_request -> bL_enter_migration/bL_exit_migration
target方法不知道是什么,待看。这个应该是自动切换入口。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
待答:
1. 它们和cci的关系。
2. cci是什么,为什么当前没有设置上.config:403:# CONFIG_EXYNOS5_CCI is not set。
名词解释:
CCI - Cache Coherent Interconnect support
IKS - SAMSUNG EXYNOS SoCs for Inkernel Switcher
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
B) b.L_core_stat(读)
各核状态
C) b.L_info(读)
打印switch_time统计信息。
2. register_bL_switcher_notifier //统计switch耗时
注册exynos_bL_noti_handler至bL_switcher_notifier_list消息通知链,在bL_enter_migration和bL_exit_migration时(arch/arm/common/bL_switcher.c)会发消息至消息通知链,最终调用handler以统计switch消耗时间。
4. 切换策略
根据当前kernel的配置,使用的是 CPU migration方式。

一、首先,kernel在每一个cpu上(共4个)各启动一个work thread,名字叫做bL_switch_thread, 这个thread在被请求切换cpu时会被激活。

1)启动task

late_initcall(bL_control_init);

bL_control_init (arch/arm/mach-exynos/bL_control.c) -> __raw_writel(virt_to_phys(bl_entry_point), REG_SWITCHING_ADDR); //对于REG_SWITCHING_ADDR这个寄存器有疑问看起来是switchinbound cpu会跳入这里设置的entry,待查spac


bL_control_init (arch/arm/mach-exynos/bL_control.c) -> bL_switcher_init (arch/arm/common/bL_switcher.c) -> schedule_on_each_cpu(switcher_thread_on_each_cpu) ->

-> switcher_thread_on_each_cpu -> bL_switcher_thread_create -> kthread_create_on_node(bL_switcher_thread ...) 然后 wake_up_process(t->task)

2)bL_switch_thread同步策略

do{

...
wait_event_interruptible(t->wq, t->wanted_cluster != -1)

...

bL_switch_to(cluster)

...

}while (!kthread_should_stop())

switch thread正在等待wanted_cluster状态更改请求。wanted_cluster是core所在的组,如A15组或A7组。

二、然后,在需要切换cpu时,调用bL_switch_request/bL_cluster_switch_request 这个接口

这类接口主要做2件事儿:

1)bL_enter_migration

向各模块发送SWITCH_ENTER事件,这样,各个模块就能进行各自的migration处理。目前代码中关心此事件的模块有MobiCoreDriver和sys-if-switcher(之前提到的耗时统计)。

2)唤醒switch thread

t->wanted_cluster = new_cluster_id; //更改状态
wake_up(&t->wq);

三、最后,bL_switch_thread被唤醒,执行bL_switch_to(cluster)

这里的cluster就是new_cluster_id.

bL_switch_to中有几个地方需要分析:

这里将outbound和inbound cpu对应的bL_entry_vectors清除。这样,进入bl_entry_point后发现该vector为空,就会循环等待。

/* Close the gate for our entry vectors */
bL_set_entry_vector(cpuid, ob_cluster, NULL);
bL_set_entry_vector(cpuid, ib_cluster, NULL);
...

//绿色待分析

bL_platform_ops->power_up(cpuid, ib_cluster);
gic_migrate_target(cpuid + ib_cluster*4); 将中断target切换到另一组paircpu
arm_send_ping_ipi(smp_processor_id()); // 此时,inbound cpu被ipi中断唤醒,进入bl_entry_point
cpu_suspend((unsigned long)NULL, bL_switchpoint);

1)cpu_suspend

cpu_suspend((unsigned long)NULL, bL_switchpoint) (arch/arm/common/bL_switcher.c) -> __cpu_suspend (arch/arm/kernel/sleep.S) -> __cpu_suspend_save (arch/arm/kernel/suspend.c) -> cpu_do_suspend -> cpu_v7_do_suspend (#define ____glue(name,fn) name##fn, arch/arm/mm/proc-v7.S)

其中__cpu_suspend

stmfd sp!, {r4 - r11, lr} @r4~r11和返回地址入栈

...

mov r5, sp

...

mov r2, r5 @栈地址作为参数,准备存储

...

stmfd sp!, {r0, r1} @ save suspend func arg and pointer

...

bl __cpu_suspend_save

/* This must correspond to the LDM in cpu_resume() assembly */
@*ptr++ = virt_to_phys(idmap_pgd);
@ *ptr++ = sp; 将sp存入sleep_save_sp,顺带说下,这里stack内存储的是调用__cpu_suspend之后的{r4 - r11, lr}。

@ *ptr++ = virt_to_phys(cpu_do_resume); 在ptr++的位置存储cpu_do_resume入口地址。之后resume时会使用。

...

ldmfd sp!, {r0, pc} @ call suspend fn,此时save工作已经结束。

...

即最后调用bL_switchpoint -> call_with_stack(bL_do_switch, NULL, stack) -> bL_do_switch

附:sleep_save_sp存储的内容

sleep_save_sp (cpu n)
idmap_pgdoutbound cpu 进入__cpu_suspend时的记录
sp------->r4
cpu_do_resumer5
cp15的内容FCSE/PIDr6
User r/o thread IDr7
Domain IDr8
TTB 1r9
Control registerr10
Auxiliary control registerr11
Co-processor access controllr
TTB control register

2)bL_do_switch

bL_do_switch -> bL_set_entry_vector(cpuid, ib_cluster, cpu_resume)

在bL_set_entry_vector函数中,将inbound cpu的bL_entry_vectors设置为cpu_resume。

此时inbound cpu已经开始平行运行。执行cpu_resume相关操作,后面会分析。

outbound cpu运行arch/arm/mach-exynos/bL_control.c内注册的power_down函数。

bL_platform_ops->power_down(cpuid, ob_cluster);

phys_reset = (phys_reset_t)(unsigned long)virt_to_phys(cpu_reset);
phys_reset(virt_to_phys(bl_entry_point));

其中cpu_reset -> cpu_v7_reset (arch/arm/mm/proc-v7.S) 有如下代码:

...

disable mmu,

mov pc, r0

所以最后调用bl_entry_point //这里有疑问为什么outbound cpu也要reset,并且进入bl_entry_point。

3)bl_entry_point (arch/arm/common/bL_head.S)

...

add r5, r5, r6 @ r5 = bL_entry_vectors

bL_entry_gated:
ldr r6, [r5, r4, lsl #2]
cmp r6, #0
/* wfeeq */
beq bL_entry_gated
...

bx r6

在bL_entry_vectors被填写之后,最后会跳入相应的vectors,也就是cpu_resume

4) cpu_resume

cpu_resume ( arch/arm/kernel/sleep.S ) -> cpu_v7_do_resume (arch/arm/mm/proc-v7.S) ->cpu_resume_mmu ( arch/arm/kernel/sleep.S ) -> cpu_resume_after_mmu

cpu_resume调用

adr r0, sleep_save_sp @将之前存储的sp和pc等取出,

ARM( ldmia r0!, {r1, sp, pc} @ 这时的pc = cpu_v7_do_resume

cpu_v7_do_resume

会进行cp15的恢复,且不会对sp进行任何修改和操作。

最后调用b cpu_resume_mmu

cpu_resume_after_mmu

会将r0设置为0 (成功返回)

最后调用ldmfd sp!, {r4 - r11, pc} ,这样,inbound在resume完,就回到oubound cpu之前suspend结束的点,继续运行。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值