2019-10-24

文章目录

文章目录

Thermal概况

Thermal框架结构

1.Thermal zone device

2.Thermal cooling device

3.Thermal governor

4.Thermal core

Thermal概况

Thermal是Llinux内核中的温度控制模块,主要用于控制在系统运行过程中芯片产生的热量,通过传感器设备实时监控设备温度,在温度发生变化时候通过一定的cooling机制促使芯片和外设工作环境温度能够稳定在安全的范围内。因此,不管是在Linux或者Andriod系统中,Thermal都将对产品的热设计、功耗及CPU频率进行动态平衡性管理。

Thermal Design重要性有:过热导致CPU/GPU性能下降、影响用户体验以及会影响器件的使用寿命。

本文将以目前(2019-10-24)最新的Llinux kernel4.4版本的Thermal模块源码进行相应的框架分析,逐行分析kernel模块代码,旨在理解Thermal模块的整体框架功能及实现方式,为了方便在源码中查阅,相应位置会备注内核源码所在位置。


Thermal框架结构

Thermal框架结构图

1.Thermal zone device:获取温度的设备。作为框架读取温度的输入,同时从设备树中读取参数注册设备,一般一个Driver对应一个thermal zone。

2.Thermal cooling device:控制温度的设备。CPU/GPU一是可以加快散热,二是可以降低产热量。

3.Thermal governor:控制温度的策略(方法)。通过不同的算法实现不同的降温手段,如:set_ wise,fair_share等等。

4.Thermal core:Thermal框架的中枢核心,同时作为User space和kernel的接口。


1.Thermal zone device

//源码位置:kernel/include/linux/thermal.h

/*定义thermal_zone_device结构体*/

struct thermal_zone_device {

int id;  //每个设备对应的唯一设备ID

char type[THERMAL_NAME_LENGTH];//设备类型

struct device device;

struct thermal_attr *trip_temp_attrs;//sysfs的触发点属性:触发温度

struct thermal_attr *trip_type_attrs;//sysfs的触发点类型

struct thermal_attr *trip_hyst_attrs;

void *devdata;//设备数据指针

int trips;//thermal模块支持的触发点数量

int passive_delay;//执行设备cooling时,两次轮询之间要等待的毫秒数

int polling_delay;//检查触发点是否通过,两次轮询之间等待的毫秒数

int temperature;//当前温度。This is only for core code,drivers should use thermal_zone_get_temp() to get the current temperature

int last_temperature;//先前的温度读取

int emul_temperature;

int passive;//如果约过了触发点,置1,否则为0.

unsigned int forced_passive;

struct thermal_zone_device_ops *ops;//设备的操作函数(核心!!!)

struct thermal_zone_params *tzp;//thermal zone 参数

struct thermal_governor *governor;//定义降温策略结构体governor

void *governor_data;//governor指针

struct list_head thermal_instances;

struct idr idr;

struct mutex lock;//给Thermal_instances列表加锁保护

struct list_head node;//thermal_tz_list中的节点(在thermal_core.c中)

struct delayed_work poll_queue;//利用poll轮询机制来处理延迟工作

};

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

Thermal zone device源码位置:kernel/include/linux/thermal.h。thermal_zone_device结构体的代码各个参数的分析如上图所示,thermal_zone_device结构中的struct thermal_zone_device_ops 函数将对device进行相应的操作,相对而言比较重要,见下图分析。


//源码位置:kernel/include/linux/thermal.h

/*thermal_zone_device_ops设备的操作函数*/

struct thermal_zone_device_ops {

/*绑定函数*/

int (*bind) (struct thermal_zone_device *,

      struct thermal_cooling_device *);

/*解除绑定函数*/     

int (*unbind) (struct thermal_zone_device *,

        struct thermal_cooling_device *);

/*温度获取函数*/

int (*get_temp) (struct thermal_zone_device *, int *);

int (*get_mode) (struct thermal_zone_device *,

    enum thermal_device_mode *);

int (*set_mode) (struct thermal_zone_device *,

  enum thermal_device_mode);

int (*get_trip_type) (struct thermal_zone_device *, int,

  enum thermal_trip_type *);

/*获取触发点函数*/

int (*get_trip_temp) (struct thermal_zone_device *, int, int *);

/*设置触发点函数函数*/

int (*set_trip_temp) (struct thermal_zone_device *, int, int);

int (*get_trip_hyst) (struct thermal_zone_device *, int, int *);

int (*set_trip_hyst) (struct thermal_zone_device *, int, int);

int (*get_crit_temp) (struct thermal_zone_device *, int *);

int (*set_emul_temp) (struct thermal_zone_device *, int);

int (*get_trend) (struct thermal_zone_device *, int,

    enum thermal_trend *);

int (*notify) (struct thermal_zone_device *, int,

        enum thermal_trip_type);

};

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

RTFSC发现,设备的操作函数主要有三个比较重要的功能:

1.bind函数:绑定函数主要在LTF(Liunx Thermal Framework)中实现,zone_device和cooling_device会分别向LTF注册,并在LTF中实现绑定功能。

2.温度获取函数:温度的获取主要通过各个传感器的数据采集,经ADC后将模拟信号转变为数字信号,在device的驱动中实现数据的上报(向上封装),每个zone可以看做是一个温度控制区域,管理着一个特定的传感器数据,温度获取函数会去读取相应的zone的温度值。

3.触发点获取函数:set_trip_temp会先自设置trip的初始值,get_trip_temp可以获取触发温度值,通过和获取的温度值比较,governor将会执行相应的策略来实现设备的温度控制。


2.Thermal cooling device

Thermal cooling device/源码位置:kernel/include/linux/thermal.h,cooling device即控制温度的设备。cooling device 维护一个cooling等级,即state,一般state越高即系统的冷却需求越高。cooling device根据不同等级的冷却需求进行冷却行为。 cooling device只根据state进行冷却操作,是实施者,而state的计算由thermal governor完成。【Linux电源管理(五)thermal】


//源码位置:kernel/include/linux/thermal.h

struct thermal_cooling_device {

int id;

char type[THERMAL_NAME_LENGTH];

struct device device;

struct device_node *np;

void *devdata;

/* thermal_cooling_device_ops温控操作函数 */

const struct thermal_cooling_device_ops *ops;

bool updated; /* true if the cooling device does not need update */

struct mutex lock; /* protect thermal_instances list加锁进行保护 */

struct list_head thermal_instances;

struct list_head node;


```c

//源码位置:kernel/include/linux/thermal.h

struct thermal_cooling_device_ops {

/* 获取最大cooling等级 */

int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);

/* 获取当前的cooling等级 */

int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);

/* 设置当前的cooling等级 */

int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);


        int (*state2power)(struct thermal_cooling_device *,

        /*获取当前当前CPU的功耗值,包括dynamic功耗和static功耗。*/

int (*get_requested_power)(struct thermal_cooling_device *,

      struct thermal_zone_device *, u32 *);

/*将CPU cooling状态转换成需要消耗的功耗值*/

int (*state2power)(struct thermal_cooling_device *,

      struct thermal_zone_device *, unsigned long, u32 *);

/*将CPU所能获取的最大功耗值转换成cooling状态 */

int (*power2state)(struct thermal_cooling_device *,

      struct thermal_zone_device *, u32, unsigned long *);

};

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

thermal_cooling_device_ops操作函数的具体实现主要是根据governor提供的策略,进行相关的降温具体操作,既可以是降频从而降低热量的产生,也可以是增大降温操作的幅度,比如使用散热片或者风扇进行物理降温。


3.Thermal governor

//源码位置:kernel/include/linux/thermal.h

/**

* struct thermal_governor - structure that holds thermal governor information

* @name: name of the governor

* @bind_to_tz: callback called when binding to a thermal zone.  If it

* returns 0, the governor is bound to the thermal zone,

* otherwise it fails.

* @unbind_from_tz: callback called when a governor is unbound from a

* thermal zone.

* @throttle: callback called for every trip point even if temperature is

* below the trip point temperature

* @governor_list: node in thermal_governor_list (in thermal_core.c)

*/

struct thermal_governor {

char name[THERMAL_NAME_LENGTH];

/*绑定到thermal zone时的回调函数*/

int (*bind_to_tz)(struct thermal_zone_device *tz);

/*解除绑定函数*/

void (*unbind_from_tz)(struct thermal_zone_device *tz);

/*throttle函数是governor的核心回调程序,*/

int (*throttle)(struct thermal_zone_device *tz, int trip);

struct list_head governor_list;

};

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

int (*bind_to_tz)(struct thermal_zone_device *tz)是绑定到thermal zone时的回调函数,如果它返回0,governor绑定到thermal zone,否则失败。

int (*throttle)(struct thermal_zone_device tz, int trip)函数是governor的核心回调程序,所有的策略(如下/ Default Thermal Governor */)都将通过这个回调函数实现。


//源码位置:kernel/include/linux/thermal.h

/* Default Thermal Governor */

#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE)

#define DEFAULT_THERMAL_GOVERNOR      "step_wise"

#elif defined(CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE)

#define DEFAULT_THERMAL_GOVERNOR      "fair_share"

#elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE)

#define DEFAULT_THERMAL_GOVERNOR      "user_space"

#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR)

#define DEFAULT_THERMAL_GOVERNOR      "power_allocator"

#endif

1

2

3

4

5

6

7

8

9

10

上图代码是thermal的 governor默认的一些调整策略:

1.step_wise:开环控制。基于温度阈值和趋势,逐步浏览每个cooling设备的cooling等级。

2.fair_share:基于权重。 根据分配的权重划分确定cooling设备等级。

3.user_space:将热区的控制权移交给用户空间,用户可以客制化相关的热功耗参数。

4.power_allocator:闭环控制。功率基于每个相关设备的功率,温度和当前功耗。


4.Thermal core

thermar core作为中枢注册governor,注册Thermal类,并且基于Device Tree注册Thermal Zone;提供Thermal zone注册函数,Cooling Device注册函数,提供将Cooling设备绑定到Zone的函数,一个Thermal Zone可以有多个Cooling设备;同时还提供一个核心函数Thermal_zone_deviceupdate作为Thermal中断处理函数和轮询函数,轮询时间会根据不同Trip Delay调节。

Thermal core驱动源码位置:kernel/drivers/thermal/thermal_core.c

1.Thermal core驱动的入口函数


//源码位置:kernel/drivers/thermal/thermal_core.c

static int __init thermal_init(void)

{

int result;

/* thermal_register_governors()注册了默认的调整策略函数*/

        result = thermal_register_governors();

if (result)

  goto error;

/*创建一个class 型的类*/

result = class_register(&thermal_class);

if (result)

  goto unregister_governors;

result = genetlink_init();

if (result)

  goto unregister_class;

result = of_parse_thermal_zones();

if (result)

  goto exit_netlink;

return 0;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

thermal_register_governors()注册了以下默认的调整策略:

thermal_gov_step_wise_unregister();

thermal_gov_fair_share_unregister();

thermal_gov_bang_bang_unregister();

thermal_gov_user_space_unregister();

thermal_gov_power_allocator_unregister();

2.Thermal core驱动的注册函数


//源码位置:kernel/drivers/thermal/thermal_core.c

struct thermal_zone_device *thermal_zone_device_register(const char *type,

int trips, int mask, void *devdata,

struct thermal_zone_device_ops *ops,

struct thermal_zone_params *tzp,

int passive_delay, int polling_delay)

{

struct thermal_zone_device *tz;

enum thermal_trip_type trip_type;

int result;

int count;

int passive = 0;

struct thermal_governor *governor;

······//代码过长,省略

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

thermal_zone_device_register():注册一个新的thermal_zone驱动设备,返回值是指向创建thermal_zone_device的指针。

const char *type:设备类型。

int trips:thermal_zone支持的触发点数。

int mask:指示触发点的可写的字符位。

void *devdata:设备的私有数据指针。

struct thermal_zone_device_ops *ops:thermal_zone_device的标准回调函数。

struct thermal_zone_params *tzp:thermal zone平台参数

passive_delay:两次轮询之间等待的毫秒数

polling_delay:检查时两次轮询之间要等待的毫秒数

3.Thermal zone/cooling device 注册过程中thermal core会调用绑定函数,绑定的过程最主要是一个cooling device 绑定到一个thermal_zone的触发点上。【Linux Thermal】


//源码位置:kernel/drivers/thermal/thermal_core.c

/**

* thermal_zone_bind_cooling_device() - bind a cooling device to a thermal zone

* @tz: pointer to struct thermal_zone_device

* @trip: indicates which trip point the cooling devices is

* associated with in this thermal zone.

* @cdev: pointer to struct thermal_cooling_device

* @upper: the Maximum cooling state for this trip point.

* THERMAL_NO_LIMIT means no upper limit,

* and the cooling device can be in max_state.

* @lower: the Minimum cooling state can be used for this trip point.

* THERMAL_NO_LIMIT means no lower limit,

* and the cooling device can be in cooling state 0.

* @weight: The weight of the cooling device to be bound to the

* thermal zone. Use THERMAL_WEIGHT_DEFAULT for the

* default value

*

* This interface function bind a thermal cooling device to the certain trip

* point of a thermal zone device.

* This function is usually called in the thermal zone device .bind callback.

*

* Return: 0 on success, the proper error value otherwise.

*/

int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,

        int trip,

        struct thermal_cooling_device *cdev,

        unsigned long upper, unsigned long lower,

        unsigned int weight)

{

struct thermal_instance *dev;//用来描述zone和cooling设备在某个trip 上的关系

struct thermal_instance *pos;

struct thermal_zone_device *pos1;

struct thermal_cooling_device *pos2;

unsigned long max_state;

int result, ret;

if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))

  return -EINVAL;

        /*使得pos1指向tz设备*/

list_for_each_entry(pos1, &thermal_tz_list, node) {

  if (pos1 == tz)

  break;

}

/*pos2指向cooling设备*/

list_for_each_entry(pos2, &thermal_cdev_list, node) {

  if (pos2 == cdev)

  break;

}


if (tz != pos1 || cdev != pos2)

  return -EINVAL;

        /*使用cooling设备的get_max_state函数,得到最大等级状态*/

ret = cdev->ops->get_max_state(cdev, &max_state);

if (ret)

  return ret;


/* lower default 0, upper default max_state */

lower = lower == THERMAL_NO_LIMIT ? 0 : lower;

upper = upper == THERMAL_NO_LIMIT ? max_state : upper;


if (lower > upper || upper > max_state)

  return -EINVAL;


dev =

    kzalloc(sizeof(struct thermal_instance), GFP_KERNEL);//开辟内核内存空间

if (!dev)//判断是否开辟成功

  return -ENOMEM;

dev->tz = tz;//dev得到zone设备

dev->cdev = cdev; //dev得到cooling设备

dev->trip = trip; //dev得到温度触发的那个点

dev->upper = upper; //dev得到上限

dev->lower = lower; //dev得到下限

dev->target = THERMAL_NO_TARGET;

dev->weight = weight;

        /*调用idr_alloc,动态分配一个id号,并将该id号做为dev的id号*/

result = get_idr(&tz->idr, &tz->lock, &dev->id);

if (result)

  goto free_mem;

        //用id号做成dev的name

sprintf(dev->name, "cdev%d", dev->id);

//一个kobject对象就对应sys目录中的一个设备,代表这些驱动的结构

        //在tz->device.kobj目录下创建指向cdev->device.kobj目录的软链接,name为软链接文件名称。

result =

    sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);

if (result)

  goto release_idr;


sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);

sysfs_attr_init(&dev->attr.attr);

  //对属性进行赋值

dev->attr.attr.name = dev->attr_name;

dev->attr.attr.mode = 0444;

dev->attr.show = thermal_cooling_device_trip_point_show; //属性中show函数,具象为一个文件节点cat的调用

result = device_create_file(&tz->device, &dev->attr); //调用sysfs_create_file()在kobj对应的目录下创建attr对应的属性文件

if (result)

  goto remove_symbol_link;


sprintf(dev->weight_attr_name, "cdev%d_weight", dev->id);

sysfs_attr_init(&dev->weight_attr.attr);

dev->weight_attr.attr.name = dev->weight_attr_name;

dev->weight_attr.attr.mode = S_IWUSR | S_IRUGO;

dev->weight_attr.show = thermal_cooling_device_weight_show;

dev->weight_attr.store = thermal_cooling_device_weight_store;

result = device_create_file(&tz->device, &dev->weight_attr);

if (result)

  goto remove_trip_file;


mutex_lock(&tz->lock); //对zone列表上锁

mutex_lock(&cdev->lock); //对cooling列表上锁

  //遍历zone下的thermal_instances列表,看看有没有跟这个准备加入的instances一样的

list_for_each_entry(pos, &tz->thermal_instances, tz_node)

    if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {

  result = -EEXIST;//有就break

  break;

}

if (!result) { //没有的话,就分别在zone和cooling的设备的instances列表中加入

  list_add_tail(&dev->tz_node, &tz->thermal_instances);//把这个instances加入到zone的instances列表中

  list_add_tail(&dev->cdev_node, &cdev->thermal_instances);//把这个instances加入到cooling的instances列表中

}

mutex_unlock(&cdev->lock);//对cooling列表解锁

mutex_unlock(&tz->lock); //对zone列表解锁


if (!result)

  return 0;


device_remove_file(&tz->device, &dev->weight_attr);

remove_trip_file:

device_remove_file(&tz->device, &dev->attr);

remove_symbol_link:

sysfs_remove_link(&tz->device.kobj, dev->name);

release_idr:

release_idr(&tz->idr, &tz->lock, dev->id);

free_mem:

kfree(dev);

return result;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

本文旨在通过Liunx内核4.4分析Thermal框架,由于不同的平台在HAL层或者Framework层做了不同的客制化,所以需要根据不同的平台来设计相应的热功耗需求,linux内核层的thermal框架一般而言是不需要修改的,也是为了保证下层的统一性。客制化的需求在上层实现,如高通采用的thermal-engine,首先上层设置trip point,包括恢复温度与临界温度,thermal engine主要有algo_monitor运作sensor monitor起辅助作用够成。

作为从事功耗/性能优化方向工作者来讲,掌握thermal框架的机制原理对于在上层的功耗设计与实现有很大的帮助,从而定制出性能更稳定、功耗更低的Linux/Andriod系统。

PS:2019年10月24日,程序员节快乐,刚好在今天这个特殊的时间节点写了第一篇博客,虽然是看了好多大佬的参考文档,还是希望以后能坚持下去,早日成为一个优秀的程序员,加油。


参考文档:

Linux Thermal 框架解析

Thermal 框架梳理

Linux电源管理(五)thermal

Linux Thermal


文章最后发布于: 2019-10-24概况

Thermal框架结构

1.Thermal zone device

2.Thermal cooling device

3.Thermal governor

4.Thermal core

Thermal概况

Thermal是Llinux内核中的温度控制模块,主要用于控制在系统运行过程中芯片产生的热量,通过传感器设备实时监控设备温度,在温度发生变化时候通过一定的cooling机制促使芯片和外设工作环境温度能够稳定在安全的范围内。因此,不管是在Linux或者Andriod系统中,Thermal都将对产品的热设计、功耗及CPU频率进行动态平衡性管理。

Thermal Design重要性有:过热导致CPU/GPU性能下降、影响用户体验以及会影响器件的使用寿命。

本文将以目前(2019-10-24)最新的Llinux kernel4.4版本的Thermal模块源码进行相应的框架分析,逐行分析kernel模块代码,旨在理解Thermal模块的整体框架功能及实现方式,为了方便在源码中查阅,相应位置会备注内核源码所在位置。

Thermal框架结构

Thermal框架结构图

1.Thermal zone device:获取温度的设备。作为框架读取温度的输入,同时从设备树中读取参数注册设备,一般一个Driver对应一个thermal zone。

2.Thermal cooling device:控制温度的设备。CPU/GPU一是可以加快散热,二是可以降低产热量。

3.Thermal governor:控制温度的策略(方法)。通过不同的算法实现不同的降温手段,如:set_ wise,fair_share等等。

4.Thermal core:Thermal框架的中枢核心,同时作为User space和kernel的接口。

1.Thermal zone device

//源码位置:kernel/include/linux/thermal.h

/*定义thermal_zone_device结构体*/

struct thermal_zone_device {

int id;  //每个设备对应的唯一设备ID

char type[THERMAL_NAME_LENGTH];//设备类型

struct device device;

struct thermal_attr *trip_temp_attrs;//sysfs的触发点属性:触发温度

struct thermal_attr *trip_type_attrs;//sysfs的触发点类型

struct thermal_attr *trip_hyst_attrs;

void *devdata;//设备数据指针

int trips;//thermal模块支持的触发点数量

int passive_delay;//执行设备cooling时,两次轮询之间要等待的毫秒数

int polling_delay;//检查触发点是否通过,两次轮询之间等待的毫秒数

int temperature;//当前温度。This is only for core code,drivers should use thermal_zone_get_temp() to get the current temperature

int last_temperature;//先前的温度读取

int emul_temperature;

int passive;//如果约过了触发点,置1,否则为0.

unsigned int forced_passive;

struct thermal_zone_device_ops *ops;//设备的操作函数(核心!!!)

struct thermal_zone_params *tzp;//thermal zone 参数

struct thermal_governor *governor;//定义降温策略结构体governor

void *governor_data;//governor指针

struct list_head thermal_instances;

struct idr idr;

struct mutex lock;//给Thermal_instances列表加锁保护

struct list_head node;//thermal_tz_list中的节点(在thermal_core.c中)

struct delayed_work poll_queue;//利用poll轮询机制来处理延迟工作

};

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

Thermal zone device源码位置:kernel/include/linux/thermal.h。thermal_zone_device结构体的代码各个参数的分析如上图所示,thermal_zone_device结构中的struct thermal_zone_device_ops 函数将对device进行相应的操作,相对而言比较重要,见下图分析。

//源码位置:kernel/include/linux/thermal.h

/*thermal_zone_device_ops设备的操作函数*/

struct thermal_zone_device_ops {

/*绑定函数*/

int (*bind) (struct thermal_zone_device *,

    struct thermal_cooling_device *);

/*解除绑定函数*/     

int (*unbind) (struct thermal_zone_device *,

      struct thermal_cooling_device *);

/*温度获取函数*/

int (*get_temp) (struct thermal_zone_device *, int *);

int (*get_mode) (struct thermal_zone_device *,

enum thermal_device_mode *);

int (*set_mode) (struct thermal_zone_device *,

enum thermal_device_mode);

int (*get_trip_type) (struct thermal_zone_device *, int,

enum thermal_trip_type *);

/*获取触发点函数*/

int (*get_trip_temp) (struct thermal_zone_device *, int, int *);

/*设置触发点函数函数*/

int (*set_trip_temp) (struct thermal_zone_device *, int, int);

int (*get_trip_hyst) (struct thermal_zone_device *, int, int *);

int (*set_trip_hyst) (struct thermal_zone_device *, int, int);

int (*get_crit_temp) (struct thermal_zone_device *, int *);

int (*set_emul_temp) (struct thermal_zone_device *, int);

int (*get_trend) (struct thermal_zone_device *, int,

  enum thermal_trend *);

int (*notify) (struct thermal_zone_device *, int,

      enum thermal_trip_type);

};

RTFSC发现,设备的操作函数主要有三个比较重要的功能:

1.bind函数:绑定函数主要在LTF(Liunx Thermal Framework)中实现,zone_device和cooling_device会分别向LTF注册,并在LTF中实现绑定功能。

2.温度获取函数:温度的获取主要通过各个传感器的数据采集,经ADC后将模拟信号转变为数字信号,在device的驱动中实现数据的上报(向上封装),每个zone可以看做是一个温度控制区域,管理着一个特定的传感器数据,温度获取函数会去读取相应的zone的温度值。

3.触发点获取函数:set_trip_temp会先自设置trip的初始值,get_trip_temp可以获取触发温度值,通过和获取的温度值比较,governor将会执行相应的策略来实现设备的温度控制。

2.Thermal cooling device

Thermal cooling device/源码位置:kernel/include/linux/thermal.h,cooling device即控制温度的设备。cooling device 维护一个cooling等级,即state,一般state越高即系统的冷却需求越高。cooling device根据不同等级的冷却需求进行冷却行为。 cooling device只根据state进行冷却操作,是实施者,而state的计算由thermal governor完成。【Linux电源管理(五)thermal】

//源码位置:kernel/include/linux/thermal.h

struct thermal_cooling_device {

int id;

char type[THERMAL_NAME_LENGTH];

struct device device;

struct device_node *np;

void *devdata;

/* thermal_cooling_device_ops温控操作函数 */

const struct thermal_cooling_device_ops *ops;

bool updated; /* true if the cooling device does not need update */

struct mutex lock; /* protect thermal_instances list加锁进行保护 */

struct list_head thermal_instances;

struct list_head node;

```c

//源码位置:kernel/include/linux/thermal.h

struct thermal_cooling_device_ops {

/* 获取最大cooling等级 */

int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);

/* 获取当前的cooling等级 */

int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);

/* 设置当前的cooling等级 */

int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);

        int (*state2power)(struct thermal_cooling_device *,

        /*获取当前当前CPU的功耗值,包括dynamic功耗和static功耗。*/

int (*get_requested_power)(struct thermal_cooling_device *,

  struct thermal_zone_device *, u32 *);

/*将CPU cooling状态转换成需要消耗的功耗值*/

int (*state2power)(struct thermal_cooling_device *,

  struct thermal_zone_device *, unsigned long, u32 *);

/*将CPU所能获取的最大功耗值转换成cooling状态 */

int (*power2state)(struct thermal_cooling_device *,

  struct thermal_zone_device *, u32, unsigned long *);

};

thermal_cooling_device_ops操作函数的具体实现主要是根据governor提供的策略,进行相关的降温具体操作,既可以是降频从而降低热量的产生,也可以是增大降温操作的幅度,比如使用散热片或者风扇进行物理降温。

3.Thermal governor

//源码位置:kernel/include/linux/thermal.h

/**

* struct thermal_governor - structure that holds thermal governor information

* @name: name of the governor

* @bind_to_tz: callback called when binding to a thermal zone.  If it

* returns 0, the governor is bound to the thermal zone,

* otherwise it fails.

* @unbind_from_tz: callback called when a governor is unbound from a

* thermal zone.

* @throttle: callback called for every trip point even if temperature is

* below the trip point temperature

* @governor_list: node in thermal_governor_list (in thermal_core.c)

*/

struct thermal_governor {

char name[THERMAL_NAME_LENGTH];

/*绑定到thermal zone时的回调函数*/

int (*bind_to_tz)(struct thermal_zone_device *tz);

/*解除绑定函数*/

void (*unbind_from_tz)(struct thermal_zone_device *tz);

/*throttle函数是governor的核心回调程序,*/

int (*throttle)(struct thermal_zone_device *tz, int trip);

struct list_head governor_list;

};

int (*bind_to_tz)(struct thermal_zone_device *tz)是绑定到thermal zone时的回调函数,如果它返回0,governor绑定到thermal zone,否则失败。

int (*throttle)(struct thermal_zone_device tz, int trip)函数是governor的核心回调程序,所有的策略(如下/ Default Thermal Governor */)都将通过这个回调函数实现。

//源码位置:kernel/include/linux/thermal.h

/* Default Thermal Governor */

#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE)

#define DEFAULT_THERMAL_GOVERNOR      "step_wise"

#elif defined(CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE)

#define DEFAULT_THERMAL_GOVERNOR      "fair_share"

#elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE)

#define DEFAULT_THERMAL_GOVERNOR      "user_space"

#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR)

#define DEFAULT_THERMAL_GOVERNOR      "power_allocator"

#endif


上图代码是thermal的 governor默认的一些调整策略:

1.step_wise:开环控制。基于温度阈值和趋势,逐步浏览每个cooling设备的cooling等级。

2.fair_share:基于权重。 根据分配的权重划分确定cooling设备等级。

3.user_space:将热区的控制权移交给用户空间,用户可以客制化相关的热功耗参数。

4.power_allocator:闭环控制。功率基于每个相关设备的功率,温度和当前功耗。

4.Thermal core

thermar core作为中枢注册governor,注册Thermal类,并且基于Device Tree注册Thermal Zone;提供Thermal zone注册函数,Cooling Device注册函数,提供将Cooling设备绑定到Zone的函数,一个Thermal Zone可以有多个Cooling设备;同时还提供一个核心函数Thermal_zone_deviceupdate作为Thermal中断处理函数和轮询函数,轮询时间会根据不同Trip Delay调节。

Thermal core驱动源码位置:kernel/drivers/thermal/thermal_core.c

1.Thermal core驱动的入口函数

//源码位置:kernel/drivers/thermal/thermal_core.c

static int __init thermal_init(void)

{

int result;

/* thermal_register_governors()注册了默认的调整策略函数*/

        result = thermal_register_governors();

if (result)

goto error;

/*创建一个class 型的类*/

result = class_register(&thermal_class);

if (result)

goto unregister_governors;

result = genetlink_init();

if (result)

goto unregister_class;

result = of_parse_thermal_zones();

if (result)

goto exit_netlink;

return 0;


thermal_register_governors()注册了以下默认的调整策略:

thermal_gov_step_wise_unregister();

thermal_gov_fair_share_unregister();

thermal_gov_bang_bang_unregister();

thermal_gov_user_space_unregister();

thermal_gov_power_allocator_unregister();

2.Thermal core驱动的注册函数

//源码位置:kernel/drivers/thermal/thermal_core.c

struct thermal_zone_device *thermal_zone_device_register(const char *type,

int trips, int mask, void *devdata,

struct thermal_zone_device_ops *ops,

struct thermal_zone_params *tzp,

int passive_delay, int polling_delay)

{

struct thermal_zone_device *tz;

enum thermal_trip_type trip_type;

int result;

int count;

int passive = 0;

struct thermal_governor *governor;

······//代码过长,省略

}

thermal_zone_device_register():注册一个新的thermal_zone驱动设备,返回值是指向创建thermal_zone_device的指针。

const char *type:设备类型。

int trips:thermal_zone支持的触发点数。

int mask:指示触发点的可写的字符位。

void *devdata:设备的私有数据指针。

struct thermal_zone_device_ops *ops:thermal_zone_device的标准回调函数。

struct thermal_zone_params *tzp:thermal zone平台参数

passive_delay:两次轮询之间等待的毫秒数

polling_delay:检查时两次轮询之间要等待的毫秒数

3.Thermal zone/cooling device 注册过程中thermal core会调用绑定函数,绑定的过程最主要是一个cooling device 绑定到一个thermal_zone的触发点上。【Linux Thermal】

//源码位置:kernel/drivers/thermal/thermal_core.c

/**

* thermal_zone_bind_cooling_device() - bind a cooling device to a thermal zone

* @tz: pointer to struct thermal_zone_device

* @trip: indicates which trip point the cooling devices is

* associated with in this thermal zone.

* @cdev: pointer to struct thermal_cooling_device

* @upper: the Maximum cooling state for this trip point.

* THERMAL_NO_LIMIT means no upper limit,

* and the cooling device can be in max_state.

* @lower: the Minimum cooling state can be used for this trip point.

* THERMAL_NO_LIMIT means no lower limit,

* and the cooling device can be in cooling state 0.

* @weight: The weight of the cooling device to be bound to the

* thermal zone. Use THERMAL_WEIGHT_DEFAULT for the

* default value

*

* This interface function bind a thermal cooling device to the certain trip

* point of a thermal zone device.

* This function is usually called in the thermal zone device .bind callback.

*

* Return: 0 on success, the proper error value otherwise.

*/

int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,

    int trip,

    struct thermal_cooling_device *cdev,

    unsigned long upper, unsigned long lower,

    unsigned int weight)

{

struct thermal_instance *dev;//用来描述zone和cooling设备在某个trip 上的关系

struct thermal_instance *pos;

struct thermal_zone_device *pos1;

struct thermal_cooling_device *pos2;

unsigned long max_state;

int result, ret;

if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))

return -EINVAL;

        /*使得pos1指向tz设备*/

list_for_each_entry(pos1, &thermal_tz_list, node) {

if (pos1 == tz)

break;

}

/*pos2指向cooling设备*/

list_for_each_entry(pos2, &thermal_cdev_list, node) {

if (pos2 == cdev)

break;

}

if (tz != pos1 || cdev != pos2)

return -EINVAL;

        /*使用cooling设备的get_max_state函数,得到最大等级状态*/

ret = cdev->ops->get_max_state(cdev, &max_state);

if (ret)

return ret;

/* lower default 0, upper default max_state */

lower = lower == THERMAL_NO_LIMIT ? 0 : lower;

upper = upper == THERMAL_NO_LIMIT ? max_state : upper;

if (lower > upper || upper > max_state)

return -EINVAL;

dev =

    kzalloc(sizeof(struct thermal_instance), GFP_KERNEL);//开辟内核内存空间

if (!dev)//判断是否开辟成功

return -ENOMEM;

dev->tz = tz;//dev得到zone设备

dev->cdev = cdev; //dev得到cooling设备

dev->trip = trip; //dev得到温度触发的那个点

dev->upper = upper; //dev得到上限

dev->lower = lower; //dev得到下限

dev->target = THERMAL_NO_TARGET;

dev->weight = weight;

        /*调用idr_alloc,动态分配一个id号,并将该id号做为dev的id号*/

result = get_idr(&tz->idr, &tz->lock, &dev->id);

if (result)

goto free_mem;

        //用id号做成dev的name

sprintf(dev->name, "cdev%d", dev->id);

//一个kobject对象就对应sys目录中的一个设备,代表这些驱动的结构

        //在tz->device.kobj目录下创建指向cdev->device.kobj目录的软链接,name为软链接文件名称。

result =

    sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);

if (result)

goto release_idr;

sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);

sysfs_attr_init(&dev->attr.attr);

//对属性进行赋值

dev->attr.attr.name = dev->attr_name;

dev->attr.attr.mode = 0444;

dev->attr.show = thermal_cooling_device_trip_point_show; //属性中show函数,具象为一个文件节点cat的调用

result = device_create_file(&tz->device, &dev->attr); //调用sysfs_create_file()在kobj对应的目录下创建attr对应的属性文件

if (result)

goto remove_symbol_link;

sprintf(dev->weight_attr_name, "cdev%d_weight", dev->id);

sysfs_attr_init(&dev->weight_attr.attr);

dev->weight_attr.attr.name = dev->weight_attr_name;

dev->weight_attr.attr.mode = S_IWUSR | S_IRUGO;

dev->weight_attr.show = thermal_cooling_device_weight_show;

dev->weight_attr.store = thermal_cooling_device_weight_store;

result = device_create_file(&tz->device, &dev->weight_attr);

if (result)

goto remove_trip_file;

mutex_lock(&tz->lock); //对zone列表上锁

mutex_lock(&cdev->lock); //对cooling列表上锁

//遍历zone下的thermal_instances列表,看看有没有跟这个准备加入的instances一样的

list_for_each_entry(pos, &tz->thermal_instances, tz_node)

    if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {

result = -EEXIST;//有就break

break;

}

if (!result) { //没有的话,就分别在zone和cooling的设备的instances列表中加入

list_add_tail(&dev->tz_node, &tz->thermal_instances);//把这个instances加入到zone的instances列表中

list_add_tail(&dev->cdev_node, &cdev->thermal_instances);//把这个instances加入到cooling的instances列表中

}

mutex_unlock(&cdev->lock);//对cooling列表解锁

mutex_unlock(&tz->lock); //对zone列表解锁

if (!result)

return 0;

device_remove_file(&tz->device, &dev->weight_attr);

remove_trip_file:

device_remove_file(&tz->device, &dev->attr);

remove_symbol_link:

sysfs_remove_link(&tz->device.kobj, dev->name);

release_idr:

release_idr(&tz->idr, &tz->lock, dev->id);

free_mem:

kfree(dev);

return result;

}

本文旨在通过Liunx内核4.4分析Thermal框架,由于不同的平台在HAL层或者Framework层做了不同的客制化,所以需要根据不同的平台来设计相应的热功耗需求,linux内核层的thermal框架一般而言是不需要修改的,也是为了保证下层的统一性。客制化的需求在上层实现,如高通采用的thermal-engine,首先上层设置trip point,包括恢复温度与临界温度,thermal engine主要有algo_monitor运作sensor monitor起辅助作用够成。

作为从事功耗/性能优化方向工作者来讲,掌握thermal框架的机制原理对于在上层的功耗设计与实现有很大的帮助,从而定制出性能更稳定、功耗更低的Linux/Andriod系统。

参考文档:

Linux Thermal 框架解析

Thermal 框架梳理

Linux电源管理(五)thermal

Linux Thermal

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • (非原创,引用,入门阅读) 源码位置: kernel3.18/ include/linux/thermal.h d...
    tiger桂阅读 1,839评论 0 2
  • (重点其实在最后一段。) 设有函数: 1) 正规的换元法 设, 可得。 这里重要的是,而不是。因为。表示是两个不同...
    YanJC阅读 708评论 0 0
  • 振动信号的滤波处理 振动信号的滤波处理 在振动信号分析中,数据滤波就是把我们所关注和感兴趣的部分信号从采集得到的信...
    石显阅读 309评论 0 0
  • 2018年2月5日 星期一 晚 晴 今天立春都第二天了但是还那么冷,孩子按计划完成作业任务,天冷给孩...
    小雨点儿_5f38阅读 122评论 0 0
  • 陌生少年人如玉 ,谁家公子世无双 2013年8月6日,一个大陆团体在中国横空出世,组合名叫tfboys,...
    么么哒小姐姐阅读 556评论 0 1