目 录
一、简述 1
二、操作环境 2
三、主要原理及关键结构体、函数分析 2
cpufreq_policy结构体 2
cpufreq_frequency_table结构体 4
eps_cpu_data结构体 4
四、具体函数分析 6
eps_cpu_init分析 6
eps_cpu_exit分析 7
eps_verify分析 7
eps_target分析 7
eps_set_state分析 8
eps_get分析 8
参考文献 8
目前在做编写适配兆芯Nano CPU节能驱动模块,分析Linux内核源码中关于PowerSaver驱动程序源码,本文主要讲述PowerSaver驱动程序的关键结构体、函数的作用及关系,通过本文可以了解Linux内核关于能耗模块的概述。
操作系统 |
mint17 |
内核 |
3.8.0 |
编译器 |
gcc4.7.3 |
CPU |
VIA Nano X2 L4530 @ 1.6+ GHz |
内存 |
4G |
多核 |
2个 |
PowerSaver驱动的功能是兆芯VIA CPU能耗驱动,它是VIA的一大特色【1】,它主要是通过调节CPU电压、频率达到降低能耗的作用。有关VIA CPU能耗驱动主要有两个:Longhaul和PowerSaver,PowerSaver是Longhaul的升级版。Longhaul支持VIA的老版本(C3,C7)PowerSaver支持较为新的CPU(C7+),但是它不支持Nano,分析PowerSaver驱动的目的在于参考其驱动程序,来编写兆芯Nano CPU的能耗驱动程序。
PowerSaver驱动程序在driver/cpufreq/e_powersaver.c中。
该驱动的主要作用原理是通过读写msr寄存器(rdmsr/wrmsr)来调节CPU的电压和频率。
首先介绍关键结构体和函数:
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 */ 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; };
其中的各个字段的解释如下【2】:
l cpus和related_cpus 这两个都是cpumask_var_t变量,cpus表示的是这一policy控制之下的所有还出于online状态的cpu,而related_cpus则是online和offline两者的合集。主要是用于多个cpu使用同一种policy的情况,实际上,我们平常见到的大多数系统中都是这种情况:所有的cpu同时使用同一种policy。我们需要related_cpus变量指出这个policy所管理的所有cpu编号。
l cpu和last_cpu 虽然一种policy可以同时用于多个cpu,但是通常一种policy只会由其中的一个cpu进行管理,cpu变量用于记录用于管理该policy的cpu编号,而last_cpu则是上一次管理该policy的cpu编号(因为管理policy的cpu可能会被plug out,这时候就要把管理工作迁移到另一个cpu上)。
l cpuinfo 保存cpu硬件所能支持的最大和最小的频率以及切换延迟信息。
l min/max/cur 该policy下的可使用的最小频率,最大频率和当前频率。
l policy 该变量可以取以下两个值:CPUFREQ_POLICY_POWERSAVE和CPUFREQ_POLICY_PERFORMANCE,该变量只有当调频驱动支持setpolicy回调函数的时候有效,这时候由驱动根据policy变量的值来决定系统的工作频率或状态。如果调频驱动(cpufreq_driver)支持target回调,则频率由相应的governor来决定。
l governor和governor_data 指向该policy当前使用的cpufreq_governor结构和它的上下文数据。governor是实现该policy的关键所在,调频策略的逻辑由governor实现。
l update 有时在中断上下文中需要更新policy,需要利用该工作队列把实际的工作移到稍后的进程上下文中执行。
l user_policy 有时候因为特殊的原因需要修改policy的参数,比如溫度过高时,最大可允许的运行频率可能会被降低,为了在适当的时候恢复原有的运行参数,需要使用user_policy保存原始的参数(min,max,policy,governor)。
l kobj 该policy在sysfs中对应的kobj的对象。
cpufreq_frequency_table结构体是用于记录CPU支持的频率表,index标示检索值,frequency表示频率,单位kHz。
struct cpufreq_frequency_table { unsigned int index; /* any */ unsigned int frequency; /* kHz - doesn't need to be in ascending * order */ };
eps_cpu_data是PowerSaver独有的结构体,专门用于在驱动程序各个函数之间传递信息,包含fsb 前端总线频率和cpufreq_frequency_table项。如果Linux内核配置了
CONFIG_ACPI_PROCESSOR或者CONFIG_ACPI_PROCESSOR_MODULE选项。定义bios_limit,bios限制频率。在eps_get(),eps_set_state(),eps_target(),eps_cpu_init()这几个函数中应用。
struct eps_cpu_data { u32 fsb; #if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE u32 bios_limit; #endif struct cpufreq_frequency_table freq_table[]; };
以上介绍了PowerSaver驱动的关键结构体,下面介绍关键函数。Linux内核能耗驱动框架是统一的,即cpufreq_driver。支持不同硬件、版本的驱动程序本质上就是填写这个cpufreq_driver驱动结构体(cpufreq.h中定义)。
struct cpufreq_driver { struct module *owner; char name[CPUFREQ_NAME_LEN]; u8 flags; /* 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); 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; };
相关的字段的意义解释如下:
l name 该频率驱动的名字。
l init 回调函数,该回调函数必须实现,CPUFreq Core会通过该回调函数对该驱动进行必要的初始化工作。
l verify 回调函数,该回调函数必须实现,CPUFreq Core会通过该回调函数检查policy的参数是否被驱动支持。
l setpolicy/target 回调函数,驱动必须实现这两个函数中的其中一个,如果不支持通过governor选择合适的运行频率,则实现setpolicy回调函数,这样系统只能支持CPUFREQ_POLICY_POWERSAVE
和CPUFREQ_POLICY_PERFORMANCE这两种工作策略。反之,实现target回调函数,通过target回调设定governor所需要的频率。
l get 回调函数,用于获取cpu当前的工作频率。
l getavg 回调函数,用于获取cpu当前的平均工作频率。
下面是PowerSaver驱动的实例化:
static struct cpufreq_driver eps_driver = { .verify = eps_verify, .target = eps_target, .init = eps_cpu_init, .exit = eps_cpu_exit, .get = eps_get, .name = "e_powersaver", .owner = THIS_MODULE, .attr = eps_attr, };
eps_cpu_init函数,主要用于初始化。读写msr寄存器使能PowerSaver。显示当前电压频率,对当前电压、步进、频率依据cpufreq_policy进行判断验证正确性。计算前端总线频率。设置频率表,设置cpufreq_policy的最大、小频率。测试CPU是否在降频状态。计算fsb速度。计算当前电压、转换延时到policy结构体中,这个函数主要是设置policy结构体,为 后续函数处理做准备。该初始化函数只是针对C3、C7的初始化,没有针对Nano的。有关如何读写msr寄存器需要结合硬件手册来看。
函数原型:
static int eps_cpu_init(struct cpufreq_policy *policy)
其中cpufreq_policy是之前讲的CPU频率规定结构体。
eps_cpu_exit函数的作用是:注销并释放空间
函数原型为:static int eps_cpu_exit(struct cpufreq_policy *policy)
eps_verify主要是验证处理器频率设定,检查频率表中的频率是否越界,是否不正确。
原型:static int eps_verify(struct cpufreq_policy *policy)
内部核心处理函数是:
int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table)
eps_target是具体实现函数,policy是频率规定结构体,target_freq是需要设定的频率。relation表示取值策略,取下限值还是上限值【3】。
relation可以取下列值:
#define CPUFREQ_RELATION_L 0 /* lowest frequency at or above target 取上限值*/
#define CPUFREQ_RELATION_H 1 /* highest frequency below or at target 下限值*/
函数原型:
static int eps_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
该函数内部有两个关键函数:
1)cpufreq_frequency_table_target(),该函数的作用根据policy和relation(取值策略)和target_freq(目标频率)遍历policy的频率表,找到和target_freq最相近的频率值,把该检索点传递给newstate。
2) dest_state = centaur->freq_table[newstate].index & 0xffff;
ret = eps_set_state(centaur, cpu, dest_state);//通过wrmsr来设置频率。
下面具体介绍eps_set_state函数,这个函数是具体设置CPU频率的核心函数!!
eps_set_state函数原型:
static int eps_set_state(struct eps_cpu_data *centaur,
unsigned int cpu,
u32 dest_state)
centaur是频率设定结构体,之前讲过的,cpu表示设定特定的CPU,dest_state是设置目的频率值。
该函数就是通过读写msr寄存器来设置CPU频率,这个需要结合硬件手册来分析。对于后续做耗电驱动程序来说需要参考这个还需要结合硬件厂商提供的硬件手册。
eps_get函数的作用返回特定CPU的频率。
函数原型如下:
static unsigned int eps_get(unsigned int cpu)