zoukankan      html  css  js  c++  java
  • ARM Linux 大小核切换 ——cortex-A7 big.LITTLE 大小核 切换代码分析

    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。

  • 相关阅读:
    如何分析redis中的慢查询
    redis订阅关闭异常解决
    异常解决:Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
    linux下postgres的安装
    springboot tomcat配置参数列表
    如何把web.xml中的context-param、Servlet、Listener和Filter定义添加到SpringBoot中
    electron-builder 由于网络原因无法下载问题解决
    Handshake failed due to invalid Upgrade header: null 解决方案
    Linux-006-执行Shell脚本报错 $' ':command not found
    VUE-013-为elementUI 设置 tootip 宽度
  • 原文地址:https://www.cnblogs.com/fozu/p/4552938.html
Copyright © 2011-2022 走看看