CPUIDLE

参考:Linux cpuidle framework(1)_概述和软件架构

前言

在Linux系统中,CPU被两类程序占用,一类是进程(或线程),也称进程上下文;一类是各种中断、异常的处理程序,也称中断上下文。当CPU上没有进程需要运行,也没有异常发生,则cpu就处于无所事事的状态,也就是idle状态,Linux kernel抽象了cpuidle framework框架用来管理这种状态。

CPUIDLE Framework

linux kernel中,cpuidle framework位于drivers/cpuidle目录中,包含cpuidle core, cpuidle governorscpuidle driver三个模块,在结合位于kernel/sched目录中的cpuidle entry,共同完成cpuidle管理,软件架构如下图:

 kernel sched模块:位于kernel/sched/idle.c中,负责实现idle线程的通用入口(cpuidle entry)逻辑,包括idle模式的选择,idle的进入等

cpuidle core:位于drivers/cpuidle,负责cpuidle framework的整体框架,主要功能包括:根据cpuidle的应用场景抽象出cpuidle devicecpuidle drivercpuidle governor三个实体;以函数调用的形式,向上层sched模块提供接口;以sysfs的形式,向用户空间提供接口;向下层的cpuidle drivers模块,提供统一的driver注册和管理接口;向下层的governors模块,提供统一的governor注册和管理接口。相关文件cpuidle.c driver.c governor.c sysfs.c

cpuidle drivers:负责idle机制的实现,即如何进入idle状态,什么条件下退出等

cpuidle governors:位于drivers/cpuidle/governors目录,负责提供多种idle levelgovernor选择一种方案执行

重要数据结构

struct cpuidle_state

很多复杂的cpu,有多种不同的idle级别,这些idle级别有不同的功耗和延迟,从而可以在不同的场景下使用,linux kernel使用struct cpuidle_state结构抽象idle level。

参考:Linux kernel5.4 drivers/cpuidle/cpuidle-psci.c

在结构体cpuidle_driver中初始化cpuidle_state结构体如下:

static struct cpuidle_driver psci_idle_driver __initdata = {
    .name = "psci_idle",
    .owner = THIS_MODULE,
/*
 * PSCI idle states relies on architectural WFI to
 * be represented as state index 0.
 */
    .states[0] = {
        .enter                  = psci_enter_idle_state,
        .exit_latency           = 1,
        .target_residency       = 1,
        .power_usage            = UINT_MAX,
        .name                   = "WFI",
        .desc                   = "ARM WFI",
    }
};

 结构体struct cpuidle_state变量state[]

        .enter:进入该idle状态的回调函数。

        .target-residency:进入该idle状态最少停留时间

        .power_usage:在该idle状态下的功耗

        .exit_latency:退出该idle状态的延迟

CPUIDLE driver和device的注册

代码:drivers/cpuidle/cpuidle-psci.c

CPUIDLE DTS 配置

以某arm64平台DTS配置为例,idle-states节点表示支持的cpu idle的具体信息,每个cpu通过cpu-idle-states属性表示可以支持的cpu idle,根据cpu idle的程度可以支持多个,该示例中只支持一个,cpu-idle-states属性支持的cpu idle信息被添加到cpuidle driverstates数组的1号成员或之后的成员中,该数组的0号成员是cpu通过执行wfi指令而达到的禁止状态,states数组成员必须按照功耗由大到小进行排序。

cpus {
	#address-cells = <2>;
	#size-cells = <0>;
	idle-states {
		entry-method = "arm,psci";
		CPU_OFF_LIT: pm-cpu-level0 {
			compatible = "arm,idle-state";
			arm,psci-suspend-param = <0x1010012>;
			local-timer-stop;
			entry-latency-us = <100>;
			exit-latency-us = <100>;
			min-residency-us = <5000>;
		};
		CPU_OFF_BIG: pm-cpu-level1 {
			compatible = "arm,idle-state";
			arm,psci-suspend-param = <0x1010012>;
			local-timer-stop;
			entry-latency-us = <100>;
			exit-latency-us = <100>;
			min-residency-us = <5000>;
		};
	};
	a55_cpu0: cpu@400 {
		device_type = "cpu";
		compatible = "arm,cortex-a55", "arm,armv8";
		reg = <0x0 0x400>;
		enable-method = "psci";
		cpu-idle-states = <&CPU_OFF_BIG>;
	};
};

CPU0的state[1]就对应CPU_OFF_BIG状态,该状态对应的enter函数为psci_enter_idle_state

static const struct of_device_id 
		psci_idle_state_match[] __initconst = {
	{ .compatible = "arm,idle-state",
	  .data = psci_enter_idle_state },
	{ },
};

CPUIDLE enter流程

Boot cpu启动完成后会把init task变为idle thread,然后调用cpu_startup_entry函数让cpu进入到idle

其他cpu在启动之前会为每个cpu创建idle thread,启动完成后调用cpu_startup_entry函数让cpu进入到idle

cpu_startup_entry流程:

 cpuidle_select,通过cpuidle governor,选择一个cpuidle state

target_state->enter(), 进入目标状态的处理函数,在该示例中cpuidle_state结构体初始化完成后,支持两个levelcpuidle state,两个cpuidle_state的进入函数均为psci_enter_idle_state。psci_enter_idle_state流程如下图:

对于states[0], 通过cpu_do_idle函数最终调用wfi执行让cpu进入idle

对于states[1],通过invoke_psci_fn()函数,陷入到bl31,让cpu进入idle。Invoke_psci_fn回调函数在set_conduit()函数中根据dts配置的smc方法,初始化为__invoke_psci_fn_smc()函数;invoke_psci_fn_smc()函数最终调用smc指令,让系统陷入到bl31执行。

psci {
	compatible = "arm,psci-1.0";
	method = "smc";
};

arch/arm64/kernel/smccc-call.S
ENTRY(__arm_smccc_smc)
	SMCCC	smc
ENDPROC(__arm_smccc_smc)
EXPORT_SYMBOL(__arm_smccc_smc)

SMC陷入bl31流程

1. 设置vbar_el3异常向量表入口为runtime_exceptions

 2. 异常发生后跳转到异常向量表runtime_exceptions,在runtime_exceptions中根据同步异常类型跳到PC(x15)指针处执行std_svc_smc_handle()函数处理异常

 3. std_svc_smc_handle()函数处理异常,通过kernel传入的fn0xC4000001进入到PSCI_CPU_SUSPEND_AARCH64分支执行cpu idle操作psci_plat_pm_ops在各个平台的驱动中初始化

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值