Linux cpufreq子系统

Linux cpufreq子系统全面解析
本文详细介绍Linux cpufreq子系统,其功能是动态电压/频率调整,可在功耗和性能间平衡。阐述了软件架构、各模块功能,如cpufreq core、cpufreq drivers等,还介绍了调频策略及常见问题解答,如查看频率表、切换模式等。

Linux cpufreq子系统

目录

前言

修订记录

日期 作者 版本 修改说明
2023.08.15 枫潇潇 V1.0.0 初始版本

1 Linux cpufreq概述

Cpufreq framework的功能称作动态电压/频率调整(Dynamic Voltage/Frequency Scaling, DVFS)。通过调整CPU的电压和频率,可以在功耗和性能之间找到一个平衡点。在不需要高性能时,降低电压和频率,以降低功耗;在需要高性能时,提高电压和频率,以提高性能。要达到此目的,有两个关键点:

  • 1)如何控制CPU core的电压和频率;
  • 2)何时改变CPU core的电压和频率。

针对这两个关键点,CPU core有两种实现。

  • 实现1:CPU core根据自身的负荷,自动调整电压和频率,不需要OS级别的软件参与。
    这种实现,软件复杂度非常低,通常情况下,只需要告诉CPU core电压和频率的调整范围(通过频率表示,scaling_min_freq和scaling_max_freq,也称作policy),CPU core即可自行调整。因此:
    关键点1,由CPU core自行处理;
    关键点2,OS需要根据大致的应用场景(例如,是高性能场景,还是低性能场景),设定一个频率范围,改变时机,由CPU core自行决定。
    注1:由于软件参与度小,该实现的省电效率可能较低

  • 实现2:CPU core不参与任何的逻辑动作,由OS软件根据系统运行情况,调整电压和频率。
    这种实现,几乎完全由软件掌控DVFS行为:
    关键点1,基于clock framework和regulator framework提供的接口,控制CPU core的频率和电压;
    关键点2,根据应用场景,手动(用户发起,例如省电模式)或者自动(软件自动调整,例如HMP)的调整。
    注2:对关键点2来说,如果调整比较频繁,则需要CPU core在不同频率之间转换的速度足够快,后面会详细介绍。

为了实现上述功能需求,cpufreq framework抽象出cpufreq driver、cpufreq policy(策略)、cpufreq governor等多个软件实体。

2 软件架构

在这里插入图片描述

对下,cpufreq framework基于cpu subsystem driver、OPP、clock framework、regulator framework等模块,提供对CPU core频率和电压的控制。这一部分主要由cpufreq driver实现。

对上,cpufreq framework会通过cpufreq core、cpufreq governors、cpufreq stats等模块,以sysfs的形式,向用户空间提供cpu frequency的查询、控制等接口。同时,在频率改变的时候,通过notifier通知关心的driver。

内部,cpufreq framework包括cpufreq core、cpufreq driver、cpufreq governors、cpufreq stats等模块。

3 软件模块的功能

3.1 Cpufreq core

cpufreq core是cpufreq framework的核心模块,和kernel其它framework类似,它主要实现三类功能:

对上,以sysfs的形式向用户空间提供统一的接口,以notifier的形式向其它driver提供频率变化的通知;

对下,提供CPU core频率和电压控制的驱动框架,方便底层driver的开发;同时,提供governor框架,用于实现不同的频率调整机制;

内部,封装各种逻辑,实现所需功能。这些逻辑主要围绕struct cpufreq_driver、struct cpufreq_policy和struct cpufreq_governor三个数据结构进行,下面会详细分析。

3.1.1 struct cpufreq_drive
struct cpufreq_driver {
	struct module *owner;
	char			name[CPUFREQ_NAME_LEN];	//名字唯一
	u8			flags;

	/* needed by all drivers */
	int	(*init)		(struct cpufreq_policy *policy);	//必须实现,填充policy内容
	int	(*verify)	(struct cpufreq_policy *policy);	//必须实现,验证policy的内容是否符合硬件要求
	
	/* define one out of two */
	用于设置CPU core动态频率调整的范围(即policy)
	int	(*setpolicy)	(struct cpufreq_policy *policy);
	//设定CPU指定频率,接口为旧接口
	int	(*target)	(struct cpufreq_policy *policy,
				 unsigned int target_freq,
				 unsigned int relation);
	
	/* should be defined, if possible */
	unsigned int	(*get)	(unsigned int cpu);
	
	/* optional */
	unsigned int (*getavg)	(struct cpufreq_policy *policy,
				 unsigned int cpu);
	int	(*bios_limit)	(int cpu, unsigned int *limit);
	
	int	(*exit)		(struct cpufreq_policy *policy);
	int	(*suspend)	(struct cpufreq_policy *policy);
	int	(*resume)	(struct cpufreq_policy *policy);
	struct freq_attr	**attr;
};

相关API:

int cpufreq_register_driver(struct cpufreq_driver *driver_data);
int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);

const char *cpufreq_get_current_driver(void);
void *cpufreq_get_driver_data(void);
3.1.2 struct cpufreq_cpuinfo
struct cpufreq_cpuinfo {
	unsigned int		max_freq;
	unsigned int		min_freq;

	/* in 10^(-9) s = nanoseconds */
	unsigned int		transition_latency;
};

cpuinfo,该cpu调频相关的固定信息,包括最大频率、最小频率、切换延迟,其中最大频率、最小频率可以通过frequency table推导得出

3.1.3 struct cpufreq_policy
struct cpufreq_policy {
	cpumask_var_t		cpus;	/* CPUs requiring sw coordination */
	cpumask_var_t		related_cpus; /* CPUs with any coordination */
	unsigned int		    shared_type; /* ANY or ALL affected CPUs
						should set cpufreq */
	unsigned int		    cpu;    /* cpu nr of registered CPU */
	struct cpufreq_cpuinfo	cpuinfo;/* see above */

	unsigned int		min;    /* in kHz */
	unsigned int		max;    /* in kHz */
	unsigned int		cur;    /* in kHz, only needed if cpufreq * governors are used */
	unsigned int		policy; /* see above */
	
	//不能自动调频的CPU,需要governor设置具体的频率
	struct cpufreq_governor	*governor; /* see below */
	
	struct work_struct	update; /* if update_policy() needs to be * called, but you're in IRQ context */
	
	struct cpufreq_real_policy	user_policy;
	
	struct kobject		kobj;
	struct completion	kobj_unregister;
};
3.1.4 struct cpufreq_governor
struct cpufreq_governor {
	char	name[CPUFREQ_NAME_LEN];
	//用于governor状态切换的回调函数
	int	(*governor)	(struct cpufreq_policy *policy,  unsigned int event);

	//用于提供 sysfs setspeed attribute文件的回调函数
	ssize_t	(*show_setspeed)	(struct cpufreq_policy *policy, char *buf);
	int	(*store_setspeed)	(struct cpufreq_policy *policy, unsigned int freq);
	//该governor所能容忍的最大频率切换延迟
	unsigned int max_transition_latency; /* HW must be able to switch to
			next freq faster than this value in nano secs or we
			will fallback to performance governor */
	struct list_head	governor_list;
	struct module		*owner;
};

3.2 cpufreq drivers

3.2.1 cpufreq driver编写步骤

Cpufreq driver主要完成平台的相关的CPU频率/电压的控制,他在cpufreq framework中是非常简单的模块,编写步骤如下:

  • 1)平台相关的初始化动作,包括CPU core的clock/regulator获取、初始化等。
  • 2)生成frequency table,即CPU core所支持的频率/电压列表。并在初始化时将该table保存在policy中。
  • 3)定义一个struct cpufreq_driver变量,填充必要的字段,并根据平台的特性,实现其中的回调函数。
  • 4)调用cpufreq_register_driver将driver注册到cpufreq framework中。
  • 5)cpufreq core会在CPU设备添加时,调用driver的init接口。driver需要在该接口中初始化struct cpufreq_policy变量。
  • 6)系统运行过程中,cpufreq core会根据实际情况,调用driver的setpolicy或者target/target_index等接口,设置CPU的调频策略或者频率值。
  • 7)系统suspend的时中,会将CPU的频率设置为指定的值,或者调用driver的suspend回调函数;系统resume时,调用driver的resume回调函数。
3.2.2 cpufreq driver有关的API即功能分析
3.2.2.1 frequency table

frequency table是CPU core可以正确运行的一组频率/电压组合,一般情况下,会在项目启动的初期,通过“try频点”的方法,确定出稳定性、通用性都符合要求的频点。

frequency table之所以存在的一个思考点是:table是频率和电压之间的一个一一对应的组合,因此cpufreq framework只需要关心频率,所有的策略都称做“调频”策略。而cpufreq driver可以在“调频”的同时,通过table取出和频率对应的电压,进行修改CPU core电压,实现“调压”的功能。

/* Special Values of .frequency field */
#define CPUFREQ_ENTRY_INVALID   ~0u
#define CPUFREQ_TABLE_END       ~1u
/* Special Values of .flags field */
#define CPUFREQ_BOOST_FREQ      (1 << 0)
 
struct cpufreq_frequency_table {
	unsigned int    flags;
	unsigned int    driver_data; /* driver specific data, not used by core */
	unsigned int    frequency;   /* kHz - doesn't need to be in ascending order */
};

flags,现在只有一个----CPUFREQ_BOOST_FREQ,表示这个频率值是一个boost频率。

3.2.2.2 struct cpufreq_drive
struct cpufreq_driver {
	char		name[CPUFREQ_NAME_LEN];
	u8		flags;
	void		*driver_data;

	/* needed by all drivers */
	int		(*init)(struct cpufreq_policy *policy);
	int		(*verify)(struct cpufreq_policy *policy);

	/* define one out of two */
	int		(*setpolicy)(struct cpufreq_policy *policy);

	/*
	 * On failure, should always restore frequency to policy->restore_freq
	 * (i.e. old freq).
	 */
	int		(*target)(struct cpufreq_policy *policy,
				  unsigned int target_freq,
				  unsigned int relation);	/* Deprecated */
	int		(*target_index)(struct cpufreq_policy *policy,
					unsigned int index);
	unsigned int	(*fast_switch)(struct cpufreq_policy *policy,
				       unsigned int target_freq);

	/*
	 * Caches and returns the lowest driver-supported frequency greater than
	 * or equal to the target frequency, subject to any driver limitations.
	 * Does not set the frequency. Only to be implemented for d
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值