|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
在上面几章中,介绍了cpufreq的core层,core提供了cpufreq系统的初始化,公共数据结构的建立以及对cpufreq中其它子部件提供注册功能。core的最核心功能是对policy的管理,一个policy通过cpufreq_policy结构中的governor字段,和某个governor相关联,本章的内容正是要对governor进行讨论。/ \5 K" J. C7 q9 c* G# t
5 ?9 K" F, A0 }5 _, a5 _- a n3 p6 I$ S% v) I8 k
通过前面文章的介绍,我们知道,governor的作用是:检测系统的负载状况,然后根据当前的负载,选择出某个可供使用的工作频率,然后把该工作频率传递给cpufreq_driver,完成频率的动态调节。内核默认提供了5种governor供我们使用,在之前的内核版本中,每种governor几乎是独立的代码,它们各自用自己的方式实现对系统的负载进行监测,很多时候,检测的逻辑其实是很相似的,各个governor最大的不同之处其实是根据检测的结果,选择合适频率的策略。所以,为了减少代码的重复,在我现在分析的内核版本中(3.10.0),一些公共的逻辑代码被单独抽象出来,单独用一个文件来实现:/drivers/cpufreq/cpufreq_governor.c,而各个具体的governor则分别有自己的代码文件,如:cpufreq_ondemand.c,cpufreq_peRFormance.c。下面我们先从公共部分讨论。
, o) s W+ Q& D2 v; D) @1 l
& H0 I9 ?3 J7 i0 F8 G; c7 ^- V0 H4 q) Y# B! A9 Z4 w6 `
1. 数据结构
9 z5 h+ d! A: [7 T5 ~5 Y/ r5 M( j! l5 |8 L
8 \- I ~6 k/ } F# c" k+ \cpu_dbs_common_info 该结构把对计算cpu负载需要使用到的一些辅助变量整合在了一起,通常,每个cpu都需要一个cpu_dbs_common_info结构体,该结构体中的成员会在governor的生命周期期间进行传递,以用于统计当前cpu的负载,它的定义如下:- h b; M$ K0 n8 B2 n
! U8 P5 `* K& s* c* m
I. K1 t U. `7 B1 d+ q9 b
- /* Per cpu structures */
- struct cpu_dbs_common_info {
- int cpu;
- u64 prev_cpu_idle;
- u64 prev_cpu_wall;
- u64 prev_cpu_nice;
- struct cpufreq_policy *cur_policy;
- struct delayed_work work;
-
- struct mutex timer_mutex;
- ktime_t time_stamp;
- };
- U8 ?) Y: Q, A% c0 d6 U
d2 n, Q, K! H0 }! i: e2 u
% ?* g/ K! ?- @; a$ D$ L& N8 b6 Q$ w" d9 u2 v- i$ X( Q
8 y* \3 p: u9 p6 ] k* ?( H8 X
. |- J3 t& ?* L p* d- cpu 与该结构体相关联的cpu编号。
- prev_cpu_idle 上一次统计时刻该cpu停留在idle状态的总时间。
- prev_cpu_wall 上一次统计时刻对应的总工作时间。
- cur_policy 指向该cpu所使用的cpufreq_policy结构。
- work 工作队列,该工作队列会被定期地触发,然后定期地进行负载的更新和统计工作。$ u5 m+ T8 f) L
9 j- c3 q! C+ e, i
|2 U2 C( s3 F' @& i% h
& H; O! Y! G6 K
m) v+ ~' @3 a0 L" Q7 J6 r1 t$ v2 ~" s9 C( R% N5 K
dbs缩写,实际是:demand based switching,通常,因为cpu_dbs_common_info只包含了经过抽象后的公共部分,所以,各个governor会自己定义的一个包含cpu_dbs_common_info的自定义结构,例如对于ondemand,他会定义:3 g) o8 s4 Z$ I3 P4 }! U
( X. \' o3 ]9 f% {# H
, s( p! V, m1 Q8 ^$ ]8 a9 e& @
- struct od_cpu_dbs_info_s {
- struct cpu_dbs_common_info cdbs;
- struct cpufreq_frequency_table *freq_table;
- unsigned int freq_lo;
- unsigned int freq_lo_jiffies;
- unsigned int freq_hi_jiffies;
- unsigned int rate_mult;
- unsigned int sample_type:1;
- };( S2 G1 @6 j! Q! z/ K! L& O
9 B! H8 v2 @) V* b( P, w* V
1 G V s! Q. H' {: Z& d6 s: g" V) d3 Y% [8 Z
4 B( B( z: f$ N: p- I+ r
) L6 j( h& C# S" D* R/ i而对于Conservative,他的定义如下:
+ f" _' S7 E' g; k* v9 R' M2 ^/ s9 ]! H& y% ?3 o( G# T0 C, W0 X
3 u- X/ d. I6 D7 I7 R
- struct cs_cpu_dbs_info_s {
- struct cpu_dbs_common_info cdbs;
- unsigned int down_skip;
- unsigned int requested_freq;
- unsigned int enable:1;
- };) C- L3 }; q6 {2 |% M) q6 u- o4 Q! y) l- z
+ c7 i! n o- F$ k5 X
. m; r2 Y( m" d& r* @1 Q: k1 m: H9 S! X
- o+ R5 x$ ?) Q6 }# d4 j
# W& Y7 M" b1 T: \' }
把它理解为类似于C++语言的基类和子类的概念就是了。7 v) V, b( Y0 |7 C9 e
* a K( S7 q, v9 B; i0 J3 ]! [+ c q& b7 ~- T9 w& \& W9 ]
common_dbs_data 各个独立的governor,需要和governor的公共层交互,需要实现一套公共的接口,这个接口由common_dbs_data结构来提供:
! \& R ^/ C1 f
) N2 M( K9 i/ ? l: X7 u- F$ q+ t
- struct common_dbs_data {
- /* Common across governors */
- #define GOV_ONDEMAND 0
- #define GOV_CONSERVATIVE 1
- int governor;
- struct attribute_group *attr_group_gov_sys; /* one governor - system */
- struct attribute_group *attr_group_gov_pol; /* one governor - policy */
-
- /* Common data for platforms that don't set have_governor_per_policy */
- struct dbs_data *gdbs_data;
-
- struct cpu_dbs_common_info *(*get_cpu_cdbs)(int cpu);
- void *(*get_cpu_dbs_info_s)(int cpu);
- void (*gov_dbs_timer)(struct work_struct *work);
- void (*gov_check_cpu)(int cpu, unsigned int load);
- int (*init)(struct dbs_data *dbs_data);
- void (*exit)(struct dbs_data *dbs_data);
-
- /* Governor specific ops, see below */
- void *gov_ops;
- };
2 o' t/ d2 b& N. _
9 }' P" r, y% [4 r0 `6 p) C- s! {$ o8 R- }
2 c+ k+ E& r9 q+ L% I! s: x* w. W
8 R$ v0 P0 d1 Z9 J# n/ b0 |
7 x7 J) E9 M* a* N主要的字段意义如下:
, P3 f. e+ w5 }. e; ?: Z0 k( L% \
* `2 s. [/ X0 Y& |6 Q- governor 因为ondemand和conservative的实现部分有很多相似的地方,用该字段做一区分,可以设置为GOV_ONDEMAND或GOV_CONSERVATIVE的其中之一。
- attr_group_gov_sys 该公共的sysfs属性组。
- attr_group_gov_pol 各policy使用的属性组,有时候多个policy会使用同一个governor算法。
- gdbs_data 通常,当没有设置have_governor_per_policy时,表示所有的policy使用了同一种governor,该字段指向该governor的dbs_data结构。
- get_cpu_cdbs 回调函数,公共层用它取得对应cpu的cpu_dbs_common_info结构指针。
- get_cpu_dbs_info_s 回调函数,公共层用它取得对应cpu的cpu_dbs_common_info_s的派生结构指针,例如:od_cpu_dbs_info_s,cs_cpu_dbs_info_s。
- gov_dbs_timer 前面说过,cpu_dbs_common_info_s结构中有一个工作队列,该回调通常用作工作队列的工作函数。
- gov_check_cpu 计算cpu负载的回调函数,通常会直接调用公共层提供的dbs_check_cpu函数完成实际的计算工作。
- init 初始化回调,用于完成该governor的一些额外的初始化工作。
- exit 回调函数,governor被移除时调用。
- gov_ops 各个governor可以用该指针定义各自特有的一些操作接口。
* {! E5 S0 |1 Y6 H* k( E: q3 |
. [$ y, z& f) _. A+ O- ], Y' q7 ^# i8 P `% g: h% z. ?0 j3 P4 J
' E% }' @+ G3 s8 l: d0 i; Y3 T4 S# H, A" e
% n( o: n; {: I4 D! p& x) G! edbs_data 该结构体通常由governor的公共层代码在governor的初始化阶段动态创建,该结构的一个最重要的字段就是cdata:一个common_dbs_data结构指针,另外,该结构还包含一些定义governor工作方式的一些调节参数。该结构的详细定义如下:
f' I% L# I$ @
) @; L, V( k! K& r
& P' Y0 ~, g1 }* G7 _- struct dbs_data {
- struct common_dbs_data *cdata;
- unsigned int min_sampling_rate;
- int usage_count;
- void *tuners;
-
- /* dbs_mutex protects dbs_enable in governor start/stop */
- struct mutex mutex;
- };5 P1 }- q- \' j: A/ T
_% E& a* f3 Q" Z+ `( E/ ]& x8 J
" b" T/ I0 H2 u& b% w1 ^$ z/ a; t0 a6 ^- z" f# p
' a: ?5 F8 V3 `2 V* W' j. J5 W- S
6 L v4 D& T) v8 {几个主要的字段: n, N6 Z1 w8 \. w8 R8 R& P ^
# O+ E" F# g1 Q L
$ Y' H3 G0 A4 {0 A7 Y. _- cdata 一个common_dbs_data结构指针,通常由具体governor的实现部分定义好,然后作为参数,通过公共层的API:cpufreq_governor_dbs,传递到公共层,cpufreq_governor_dbs函数在创建好dbs_data结构后,把该指针赋值给该字段。
- min_sampling_rate 用于记录统计cpu负载的采样周期。
- usage_count 当没有设置have_governor_per_policy时,意味着所有的policy采用同一个governor,该字段就是用来统计目前该governor被多少个policy引用。
- tuners 指向governor的调节参数结构,不同的governor可以定义自己的tuner结构,公共层代码会在governor的初始化阶段调用common_dbs_data结构的init回调函数,governor的实现可以在init回调中初始化tuners字段。1 @8 k$ }+ ^/ l) F e% Y
% E3 W8 X b$ m! G! c& r5 m* p6 [6 n
, t( L5 U: g& `9 T
" a, [0 w- T. ~7 H: ^+ h如果设置了have_governor_per_policy,每个policy拥有各自独立的governor,也就是说,拥有独立的dbs_data结构,它会记录在cpufreq_policy结构的governor_data字段中,否则,如果没有设置have_governor_per_policy,多个policy共享一个governor,和同一个dbs_data结构关联,此时,dbs_data被赋值在common_dbs_data结构的gdbs_data字段中。9 v# H5 f$ A4 _$ i; q
7 S; J$ m& M* n, U' Q
$ r/ q2 |& b* m, fcpufreq_governor 这个结构在本系列文章的第一篇已经介绍过了,请参看Linux动态频率调节系统CPUFreq之一:概述。几个数据结构的关系如下图所示:! t6 G% e! ^- B3 B
4 C: m5 G! `1 Z" ?# ~ J( ?
& x- _5 \4 d, Q% l* i; c( `
4 t' c* s/ U* c& p
1 y! F( U, Y2 u1 X5 L
! Z6 q q! b* B, P1 {) a! ^图 1.1 governor的数据结构关系5 ~! D" L/ j/ H; D( e
3 X0 \8 D- q7 B- }1 r) S8 G
1 E) u6 H" [' ]! c3 d# T2 ]
下面我们以ondemand这个系统已经实现的governor为例,说明一下如何实现一个governor。具体的代码请参看:/drivers/cpufreq/cpufreq_ondemand.c。) b- N+ z6 s" ?+ u9 y
: |. w# n; s+ w8 G( F( A+ y* S- y t2 z: N! G# l( x
& C- @; X& u4 z% O" D
# a- ~# Q3 ]; a; S6 X' S
: y' B9 \9 K: S" {$ k
0 S) |# N/ z1 Q8 A: n6 Y! }/ m9 T7 `+ } K& M1 E
" m) b. \2 j9 Y2 D' r
& f& y6 c, y9 z/ z9 b' Q
+ e C3 {, K$ E2 N: p
|
|