ARM Linux 大小核切换
主要代码分布:
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
echo0/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的值,将这个值
ARMLinux大小核切 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)