zoukankan      html  css  js  c++  java
  • 一、Linux cpuidle framework(1)_概述和软件架构

    1. 在Linux kernel中,所有进程都不再运行时,即CPU的无所事事,这种状态被称作idle状态,而cpuidle framework,就是为了管理这种状态。

    2. 当idle进程被调度到时,则说明系统的其它进程不再运行了,也即CPU idle了。最终,由idle进程调用idle指令(这里为WFI),让CPU进入idle状态。
    WFI Wakeup events会把CPU从WFI状态唤醒,通常情况下,这些events是一些中断事件,因此CPU唤醒后会执行中断handler,在handler中会wakeup某些进程,在handler返回的时候进行调度,当没有其他进程需要调度执行的时候,调度器会恢复idle进程的执行,当然,idle进程不做什么,继续进入idle状态,等待下一次的wakeup。

    3. 一般情况下,ARM CPU idle时,可以使用WFI指令,把CPU置为Wait for interrupt状态。该状态下,至少(和具体ARM core的实现有关)会把ARM core的clock关闭,以节省功耗。

    4. 很多CPU会从“退出时的延迟”和“idle状态下的功耗”两个方面考虑,设计多种idle级别。对延迟较敏感的场合,可以使用低延迟、高功耗的idle;对延迟不敏感的场合,可以使用高延迟、低功耗的idle。在恰当的时候,选择一个合适的idle状态,这就是cpuidle framework的存在意义。

    5. 软件架构
    Linux kernel中,cpuidle framework位于“drivers/cpuidle”文件夹中,包含cpuidle core、cpuidle governors和cpuidle drivers三个模块,再结合位于kernel sched中的cpuidle entry,共同完成cpu的idle管理。软件架构如下图:

    (1) kernel schedule模块
    位于kernelschedidle.c中,负责实现idle线程的通用入口(cpuidle entry)逻辑,包括idle模式的选择、idle的进入等等。这个文件其实就是初始化了一个
    struct seched_class idle_seched_class 调度类。

    (2) cpuidle core
    cpuidle core负责实现cpuidle framework的整体框架,主要功能包括:

    根据cpuidle的应用场景,抽象出cpuidle device、cpuidle driver、cpuidle governor三个实体;
    以函数调用的形式,向上层sched模块提供接口;
    以sysfs的形式,向用户空间提供接口;
    向下层的cpuidle drivers模块,提供统一的driver注册和管理接口;
    向下层的governors模块,提供统一的governor注册和管理接口。

    cpuidle core的代码主要包括drivers/cpuidle下的:cpuidle.c、driver.c、governor.c、sysfs.c。

    (3) cpuidle drivers
    负责idle机制的实现,即:如何进入idle状态,什么条件下会退出,等等。不同的architecture、不同的CPU core,会有不同的cpuidle driver,平台驱动的开发者,可以在cpuidle core提供的框架之下,开发自己的cpuidle driver。代码主要包括:cpuidle-xxx.c。

    (4) cpuidle governors
    Linux kernel的framework有两种比较固定的抽象模式:
    模式1:provider/consumer模式,interrupt、clock、timer、regulator等大多数的framework是这种模式。它的特点是,这个硬件模块是为其它一个或多个模块服务的,因而framework需要从对上(consumer)和对下(provider)两个角度进行软件抽象;
    模式2:driver/governor模式,本文所描述的cpuidle framework即是这种模式。它的特点是:硬件(或者该硬件所对应的驱动软件)可以提供多种可选“方案”(这里即idle level),“方案”的实现(即机制),由driver负责,但是到底选择哪一种“方案”(即策略),则由另一个模块负责(即这里所说的governor)。

    模式2的cpuidle的场景里面:很多CPU提供了多种idle级别(即上面所说的“方案”),这些idle 级别的主要区别是“idle时的功耗”和“退出时延迟”。cpuidle driver(机制)负责定义这些idle状态(每一个状态的功耗和延迟分别是多少),并实现进入和退出相关的操作。最终,cpuidle driver会把这些信息告诉governor,由governor根据具体的应用场景,决定要选用哪种idle状态(策略)。

    kernel中,cpuidle governor都位于drivers/cpuidle/governors/目录下(Qcom的位于dirvers/cpuidle/lpm_levels.c中,cpuidle driver也是实现在这个文件中了)。

    6. 软件流程
    kernel会在系统启动完成后,在init进程中,处理cpuidle相关的事情。大致的过程是这样的:
    (1) 先启动主CPU,启动过程和传统的单核系统类似:stext --> start_kernel --> rest_init --> cpu_startup_entry
    (2) 启动其它CPU,可以有多种方式,例如CPU hotplug等,启动过程为:secondary_startup --> __secondary_switched --> secondary_start_kernel --> cpu_startup_entry

    cpu_startup_entry接口位于kernel/sched/idle.c中,负责处理CPU idle的事情,流程如下:

    cpu_startup_entry 
            arch_cpu_idle_prepare //进行idle前的准备工作,ARM64中没有实现 
            cpu_idle_loop //进入cpuidle的主循环while(1) do_idle();
                    如果系统当前不需要调度(!need_resched()),执行后续的动作 
                    local_irq_disable //关闭irq中断 
                    arch_cpu_idle_enter //arch相关的cpuidle enter,ARM64中没有实现 
                    cpuidle_idle_call //main idle function 
                            cpuidle_select //通过cpuidle governor->select(),选择一个cpuidle state 
                            cpuidle_enter //通过cpuidle state,进入该idle状态 
                            … 
                            中断产生,idle返回(注意,此时irq是被禁止的,因此CPU不能响应产生中断的事件) 
                            cpuidle_reflect //通知cpuidle governor,更新状态 
                            local_irq_enable //使能中断,响应中断事件,跳转到对应的中断处理函数 
                            …                         
                    arch_cpu_idle_exit //和enter类似,ARM64没有实现 

    7. 使用cpuidle framework进入idle状态时,本地irq是处于关闭的状态,因此从idle返回时,只能接着往下执行,直到irq被打开,才能执行相应的中断handler。这和之前传统的cpuidle不同。同时也间接证实了“Linux cpuidle framework(4)_menu governor”中所提及的,为什么menu governor在reflect接口中只是简单的置一个标志。因为reflect是在关中断时被调用的,需要尽快返回,以便处理中断事件。

    8. 若内核模块参数sleed_disabled为Y时,CPU将无法进入低功耗的idle模式,原理如下:

    Qcom的module_parameter(sleep_disabled)会创建sysfs文件节点
    
    write /sys/module/lpm_levels/parameters/sleep_disabled N  //若是Y的话,CPU不能进入低功耗模式,原理如下:
    
    # cat /sys/devices/system/cpu/cpuidle/current_driver
    msm_idle
    # cat /sys/devices/system/cpu/cpuidle/current_governor_ro
    qcom
    
    在系统进入idle时调用governor->select(),即下面这个governor
    
    static struct cpuidle_governor lpm_governor = {
     .name =  "qcom",
     .rating = 30,
     .select = lpm_cpuidle_select,
    };
    
    cpu_startup_entry 
        while (1) do_idle(); //init线程处理完后就在这死循环一直尝试进入idle状态
            cpuidle_idle_call //kernelschedidle.c
                next_state = cpuidle_select(drv, dev, &stop_tick); //调用governor->select()
                    lpm_cpuidle_select
                        cpu_power_select(dev, cpu); //driverscpuidlelpm-levels.c
                            if (lpm_disallowed(sleep_us, dev->cpu, cpu)) //这个函数中:if (sleep_disabled) return true;
                                goto done_select; //此时返回的best_level为0,也即是next_state为0
            call_cpuidle(drv, dev, next_state); //kernelschedidle.c
                cpuidle_enter(drv, dev, next_state); //driverscpuidlecpuidle.c
                struct cpuidle_state *target_state = &drv->states[index]; //选择的index就一直是0,应该就是state0,即wfi.
    
    # cat /sys/devices/system/cpu/cpu0/cpuidle/state0/desc
    wfi

    参考:http://www.wowotech.net/pm_subsystem/cpuidle_overview.html

  • 相关阅读:
    【Leetcode_easy】961. N-Repeated Element in Size 2N Array
    【Leetcode_easy】953. Verifying an Alien Dictionary
    【Leetcode_easy】949. Largest Time for Given Digits
    【Leetcode_easy】944. Delete Columns to Make Sorted
    【Leetcode_easy】942. DI String Match
    【Leetcode_easy】941. Valid Mountain Array
    【Leetcode_easy】938. Range Sum of BST
    【Leetcode_easy】937. Reorder Log Files
    【Leetcode_easy】933. Number of Recent Calls
    【Leetcode_easy】929. Unique Email Addresses
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/14131194.html
Copyright © 2011-2022 走看看