找回密码
 注册
关于网站域名变更的通知
查看: 227|回复: 1
打印 上一主题 下一主题

Linux动态频率调节系统CPUFreq之governor数据结构

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2019-11-12 13:38 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

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& N
8 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 r
1 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
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-11-25 03:46 , Processed in 0.156250 second(s), 27 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表