您的位置:首页 > 运维架构 > Linux

Linux cpuidle framework(2)_cpuidle core

2017-12-16 09:56 447 查看

1. 前言

cpuidle core是cpuidle framework的核心模块,负责抽象出cpuidle device、cpuidle driver和cpuidle governor三个实体,并提供如下功能(可参考“Linux cpuidle framework(1)_概述和软件架构”中的软件架构):

1)向底层的cpuidle driver模块提供cpudile device和cpuidle driver的注册/注销接口。

2)向cpuidle governors提供governor的注册接口。

3)提供全局的cpuidle机制的开、关、暂停、恢复等功能。

4)向用户空间程序提供governor选择的接口。

5)向kernel sched中的cpuidle entry提供cpuidle的级别选择、进入等接口,以方便调用。

本文会以这些功能为线索,逐一展开,分析cpuidle framework的实现思路和实现原理。

2.主要数据结构

cpuidle core抽象出了cpuidle device、cpuidle driver和cpuidle governor三个数据结构,cpuidle driver和cpuidle governor比较容易理解,但cpuidle device的实际意义是什么呢?我们来一一梳理一下。
2.1 cpuidle_state

蜗蜗在“Linux cpuidle framework(1)_概述和软件架构”中提到过,cpuidle framework提出的主要背景是,很多复杂的CPU,有多种不同的idle级别。这些idle级别有不同的功耗和延迟,从而可以在不同的场景下使用。Linux
kernel使用struct cpuidle_state结构抽象idle级别(后面将会统称为idle state),如下:

1: /* include/linux/cpuidle.h */

2: struct cpuidle_state {

3:         char            name[CPUIDLE_NAME_LEN];

4:         char            desc[CPUIDLE_DESC_LEN];

5: 

6:         unsigned int    flags;

7:         unsigned int    exit_latency; /* in US */

8:         int             power_usage; /* in mW */

9:         unsigned int    target_residency; /* in US */

10:         bool            disabled; /* disabled on all CPUs */

11: 

12:         int (*enter)    (struct cpuidle_device *dev,

13:                         struct cpuidle_driver *drv,

14:                         int index);

15: 

16:         int (*enter_dead) (struct cpuidle_device *dev, int index);

17: };


name、desc,该idle state的名称和简介;

exit_latency,CPU从该idle state下返回运行状态的延迟,单位为us。它决定了CPU在idle状态和run状态之间切换的效率,如果延迟过大,将会影响系统性能;

power_usage,CPU在该idle state下的功耗,单位为mW;

target_residency,期望的停留时间,单位为us。进入和退出idle state是需要消耗额外的能量的,如果在idle状态停留的时间过短,节省的功耗少于额外的消耗,则得不偿失。governor会根据该字段,结合当前的系统情况(如可以idle多久),选择idle level;

disabled,表示该idle state在所有CPU上都不可用;

flags,idle state的特性标志,当前支持如下三个: 

        CPUIDLE_FLAG_TIME_VALID,表明CPU停留于该idle state下的时间是可测量的,具体的使用场景,会在介绍governor时详细说明; 

        CPUIDLE_FLAG_COUPLED,表明该idle state会同时在多个CPU上起作用,软件需要特殊处理;

        CPUIDLE_FLAG_TIMER_STOP,表明在该idle state下,timer会停止;

enter,进入该state的回调函数;

enter_dead,CPU长时间不需要工作时(称作offline),可调用该回调函数。

总结说来,cpuidle state的功能有2: 

一是描述该idle state的特性,主要包括exit_latency、power_usage、target_residency。这些特性是governor制定idle策略的依据; 

二是提供进入该idle state的具体方法。
2.2 cpuidle_device

现实中,并没有“cpuidle device”这样一个真实的设备,因此cpuidle device是一个虚拟设备,我们可以把它类比为“cpu idle controller”,负责实现cpuidle相关的逻辑。在多核CPU中,每个CPU core,都会对应一个cpuidle device。Linux kernel使用struct cpuidle_device抽象cpuidle device,如下:

1: struct cpuidle_device {

2:         unsigned int            registered:1;

3:         unsigned int            enabled:1;

4:         unsigned int            cpu;

5: 

6:         int                     last_residency;

7:         int                     state_count;

8:         struct cpuidle_state_usage      states_usage[CPUIDLE_STATE_MAX];

9:         struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX];

10:         struct cpuidle_driver_kobj *kobj_driver;

11:         struct cpuidle_device_kobj *kobj_dev;

12:         struct list_head        device_list;

13: 

14: #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED

15:         int                     safe_state_index;

16:         cpumask_t               coupled_cpus;

17:         struct cpuidle_coupled  *coupled;

18: #endif

19: };


registered,表示设备是否已经注册到kernel中;

enabled,表示设备是否已经使能;

cpu,该cpuidle device所对应的cpu number;

last_residency,该设备上一次停留在idle状态的时间,单位为us;

state_count,该设备具备的idle state(对应struct cpuidle_state结构)的个数;

states_usage,一个struct cpuidle_state_usage类型的数组,记录了该设备的每个idle state的统计信息,包括是否使能(enable)、设备进入该state的次数(usage)和设备停留在该state的总时间(time,单位为us);

kobjs、kobj_driver、kobj_dev,用于组织sysfs,具体可参考TODO

device_list,用于将该设备添加到一个全局的链表中(cpuidle_detected_devices);

safe_state_index、coupled_cpus、coupled,和coupled cpu idle有关,后面会单独介绍。

由上面的描述可知,cpuidle device和常见device不同,struct cpuidle_device中保存的信息,都是运行时动态创建的信息,不需要driver提供任何额外信息。
2.3 cpuidle_driver

cpuidle core使用struct cpuidle_driver抽象cpuidle驱动,如下:

1: struct cpuidle_driver {

2:         const char              *name;

3:         struct module           *owner;

4:         int                     refcnt;

5: 

6:         /* used by the cpuidle framework to setup the broadcast timer */

7:         unsigned int            bctimer:1;

8:         /* states array must be ordered in decreasing power consumption */

9:         struct cpuidle_state    states[CPUIDLE_STATE_MAX];

10:         int                     state_count;

11:         int                     safe_state_index;

12: 

13:         /* the driver handles the cpus in cpumask */

14:         struct cpumask          *cpumask;

15: };


bctimer,一个标志,用于指示在cpuidle driver注册和注销时,是否需要设置一个broadcast timer,有关broadcast timer后面再详细介绍;

states、state_count,该driver支持的cpuidle state及其个数。由于struct cpuidle_device中包含了某个state在该设备上是否enable的信息,这里的state应该是所有cpuidle device所支持的state的公倍数。另外,上面的注释很明确,这些states,需要以功耗大小的降序排列;

safe_state_index,和coupled cpu idle有关,后面会单独介绍;

cpumask,一个struct cpumask结构的bit map指针,用于说明该driver支持哪些cpu core。

struct cpuidle_driver结构比较简单,由此可知编写cpuidle driver的主要工作量,是定义所支持的cpuidle state,以及state的enter接口。
2.4 cpuidle_governor

cpuidle core使用struct cpuidle_governor结构抽象cpuidle governor,如下:

1: struct cpuidle_governor {

2:         char                    name[CPUIDLE_NAME_LEN];

3:         struct list_head        governor_list;

4:         unsigned int            rating;

5: 

6:         int  (*enable)          (struct cpuidle_driver *drv,

7:                                         struct cpuidle_device *dev);

8:         void (*disable)         (struct cpuidle_driver *drv,

9:                                         struct cpuidle_device *dev);

10: 

11:         int  (*select)          (struct cpuidle_driver *drv,

12:                                         struct cpuidle_device *dev);

13:         void (*reflect)         (struct cpuidle_device *dev, int index);

14: 

15:         struct module           *owner;

16: };


name,该governor的名称;

governor_list,用于将该governor添加到一个全局的governors列表中(cpuidle_governors),由此可见系统允许存在多个governor;

rating,governor的级别,正常情况下,kernel会选择系统中rating值最大的governor作为当前governor(除非用于主动修改,后面会介绍);

enable/disable,governor的enable/disable回调函数,一般会在enable中进行一些初始化操作,在disable中进行反操作;

select,根据当前系统的运行状况,以及各个idle state的特性,选择一个state(即决策);

reflect,通过该回调函数,可以告知governor,系统上一次所处的idle state是哪个(即系统从哪一个state回来),具体的用处,后面再分析。

3.  功能说明

3.1 cpuidle_device管理

cpuidle_device管理主要负责cpuidle device注册/注销、使能/禁止,位于drivers/cpuidle/cpuidle.c中,由下面的四个接口实现:

1: extern int cpuidle_register_device(struct cpuidle_device *dev);

2: extern void

3: cpuidle_unregister_device(struct cpuidle_device *dev);

4: extern int

5: cpuidle_enable_device(struct cpuidle_device *dev);

6: extern void

7: cpuidle_disable_device(struct cpuidle_device *dev);


1)cpuidle_register_device

该接口的内部动作如下:

调用__cpuidle_device_init接口,初始化struct cpuidle_device变量,主要是将dev->states_usage、dev->last_residency等状态信息清零;

调用__cpuidle_register_device接口,将struct cpuidle_device指针保存在一个全局的per cpu的指针中(cpuidle_devices),同时将它添加到一个全局的列表中(cpuidle_detected_devices);

调用cpuidle_add_sysfs接口,添加该设备有关的sysfs文件(3.6小节会详细介绍);

调用cpuidle_enable_device接口,使能该该设备(由此可知,新注册的设备默认是使能的);

调用cpuidle_install_idle_handler接口,install idle handler。所谓的idle handler,其实就是一个内部的全局变量(initialized),后面讲到cpuidle的核心功能时,会再说明。 

2)cpuidle_enable_device

我们通过cpuidle_enable_device接口,来理解一下何为idle device的enable?

调用cpuidle_get_cpu_driver接口,获取该设备对应的driver,如果设备没有绑定driver,或者当前没有governor(cpuidle_curr_governor为NULL),则不能enable,返回错误;

依据driver提供的idle state的个数(drv->state_count),初始化设备的state_count变量(dev->state_count);

调用cpuidle_add_device_sysfs接口注册cpuidle device的sysfs文件,注意和前面的cpuidle_add_sysfs不同,会在3.6小节一并介绍;

如果current governor提供了enable接口(cpuidle_curr_governor->enable),调用之;

置位设备的enabled标志(dev->enabled = 1),同时将全局变量enabled_devices加1,cpuidle_install_idle_handler/cpuidle_uninstall_idle_handler就是利用该全局变量设置或者清零initialized标志的。

cpuidle_unregister_device和cpuidle_disable_device为相反动作,不再细说。

注1:有关per-CPU变量

由前面的描述可知,在多核系统中,每个CPU会对应一个cpuidle device,因此cpuidle_register_device每被执行一次,就会注册一个不同的设备。如果cpuidle需要将它们分别记录下来,就要借助per-CPU变量,以cpuidle_devices指针为例,其声明和定义分别如下:

1: /* include/linux/cpuidle.h */

2: DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices);

3: 

4: 

5: /* include/linux/cpuidle.h */

6: DEFINE_PER_CPU(struct cpuidle_device *, cpuidle_devices);


使用方法如下:per_cpu(cpuidle_devices, dev->cpu) = dev。
3.2 cpuidle driver管理

cpuidle driver管理位于drivers/cpuidle/driver.c中,主要负责cpuidle driver的注册、获取等逻辑的实现。

cpuidle_register_driver用于注册一个cpuidle driver,它主要完成如下内容:

注册之前,调用者需要提供详细的states信息(drv->states\drv->state_count);

进行一些合法性检查,包括idle state的个数是否大于零、cpuidle功能是否使能等等;

调用__cpuidle_driver_init初始化driver的内部数据,包括drv->refcnt、drv->cpumask、drv->bctimer等,下面会对这几个内部数据进行较为详细的说明;

调用__cpuidle_set_driver,将该driver“注册”到系统。何为“注册”呢?下面会详细解释;

如果drv->bctimer为1,则调用on_each_cpu_mask,在每个CPU上,执行一次cpuidle_setup_broadcast_timer,设置broadcast timer;

最后,调用poll_idle_init,为那些具有POLL idle state的平台,注册默认的poll idle state。

1)cpuidle driver注册的意义

从本质上将,cpuidle driver是一个“driver”,它驱动的对象是cpuidle device,也即CPU。在多核系统(SMP)中,会有多个CPU,也就有多个cpuidle device。如果这些device的idle功能相同(最关键的是idle state的个数、参数相同),那么一个cpuidle driver就可以驱动这些device。否则,则需要多个driver才能驱动。

基于上面的事实,cpuidle core提供一个名称为“CONFIG_CPU_IDLE_MULTIPLE_DRIVERS”的配置项,用于设置是否需要多个cpuidle driver。

如果没有使能这个配置项,说明所有CPU的idle功能相同,一个cpuidle driver即可。此时cpuidle driver的注册,就是将driver保存在一个名称为cpuidle_curr_driver的全局指针中,且只能注册一次。同理,cpuidle driver的获取,就是返回这个指针的值。如下:

1: static struct cpuidle_driver *cpuidle_curr_driver;

2: 

3: /**

4:  * __cpuidle_get_cpu_driver - return the global cpuidle driver pointer.

5:  * @cpu: ignored without the multiple driver support

6:  *

7:  * Return a pointer to a struct cpuidle_driver object or NULL if no driver was

8:  * previously registered.

9:  */

10: static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)

11: {

12:         return cpuidle_curr_driver;

13: }

14: 

15: /**

16:  * __cpuidle_set_driver - assign the global cpuidle driver variable.

17:  * @drv: pointer to a struct cpuidle_driver object

18:  *

19:  * Returns 0 on success, -EBUSY if the driver is already registered.

20:  */

21: static inline int __cpuidle_set_driver(struct cpuidle_driver *drv)

22: {

23:         if (cpuidle_curr_driver)

24:                 return -EBUSY;

25: 

26:         cpuidle_curr_driver = drv;

27: 

28:         return 0;

29: }


另外,如果使能这个配置项,说明不同CPU的idle功能不同,每个CPU都要有一个driver。此时cpuidle idle的注册,又要依赖per cpu变量,就是将当前的注册的driver,保存在对应cpu的per cpu指针中。同理,cpuidle driver的获取,就是返回per cpu指针的值。如下:

1: static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers);

2: 

3: /**

4:  * __cpuidle_get_cpu_driver - return the cpuidle driver tied to a CPU.

5:  * @cpu: the CPU handled by the driver

6:  *

7:  * Returns a pointer to struct cpuidle_driver or NULL if no driver has been

8:  * registered for @cpu.

9:  */

10: static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)

11: {

12:         return per_cpu(cpuidle_drivers, cpu);

13: }

14: 

15: /**

16:  * __cpuidle_set_driver - set per CPU driver variables for the given driver.

17:  * @drv: a valid pointer to a struct cpuidle_driver

18:  *

19:  * For each CPU in the driver's cpumask, unset the registered driver per CPU

20:  * to @drv.

21:  *

22:  * Returns 0 on success, -EBUSY if the CPUs have driver(s) already.

23:  */

24: static inline int __cpuidle_set_driver(struct cpuidle_driver *drv)

25: {

26:         int cpu;

27: 

28:         for_each_cpu(cpu, drv->cpumask) {

29: 

30:                 if (__cpuidle_get_cpu_driver(cpu)) {

31:                         __cpuidle_unset_driver(drv);

32:                         return -EBUSY;

33:                 }

34: 

35:                 per_cpu(cpuidle_drivers, cpu) = drv;

36:         }

37: 

38:         return 0;

39: }


2)broadcast timer功能

前面2.1小节提供过cpuidle state中的“CPUIDLE_FLAG_TIMER_STOP” flag。当cpuidle driver的idle state中有state设置了这个flag时,说明对应的CPU在进入idle state时,会停掉该CPU的local timer,此时Linux kernel的clock event framework(具体可参考本站“时间子系统”相关的文章)便不能再依赖本CPU的local
timer。

针对这种情况,设计者会提供一个broadcast timer,该timer独立于所有CPU运行,并可以把tick广播到每个CPU上,因而不受idle state的影响。因此,如果cpuidle state具有STOP TIMER的特性的话,需要在driver注册时,调用clock events提供的notify接口(clockevents_notify),告知clock events模块,打开broadcast timer。如下:

1: /* cpuidle_register_driver->__cpuidle_register_driver */

2: static int __cpuidle_register_driver(struct cpuidle_driver *drv)

3: {

4:         ...

5:         __cpuidle_driver_init(drv);

6:         ...

7: 

8:         if (drv->bctimer)

9:                 on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer,

10:                                  (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1);

11:         ...

12: }

13: 

14: 

15: static void __cpuidle_driver_init(struct cpuidle_driver *drv)

16: {

17:         ...

18: 

19:         /*

20:          * Look for the timer stop flag in the different states, so that we know

21:          * if the broadcast timer has to be set up.  The loop is in the reverse

22:          * order, because usually one of the deeper states have this flag set.

23:          */

24:         for (i = drv->state_count - 1; i >= 0 ; i--) {

25:                 if (drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP) {

26:                         drv->bctimer = 1;

27:                         break;

28:                 }

29:         }

30: }

31:


__cpuidle_driver_init接口负责检查所有的idle state,如果有state设置了CPUIDLE_FLAG_TIMER_STOP flag,则置位drv->bctimer变量,表示需要开启broadcast timer; 

注2:这里有一个细节,查找idle state时,用的是倒序,结合2.3小节的描述,idle state是以功耗大小的倒序排列的,意味着功耗比较小的state,最有可能关闭timer,因而倒序可以节省查找次数(虽然不多)。这充分体现了kerne编程人员的细致程度,值得我们学习!

然后在__cpuidle_register_driver中,根据drv->bctimer的状态,调用cpuidle_setup_broadcast_timer接口,打开具体CPU上的broadcat timer。其中on_each_cpu_mask可以在指定的CPU上运行函数(cpuidle_setup_broadcast_timer),这里就不再详细介绍了。 

有关clock event的描述,可以参考“Linux时间子系统之(十六):clockevent”。

3)POLL idle state

POLL idle又称作CPU relax,是一种相对标准的idle state,在诸如64位Power PC等体系结构上,会提供这种state。如定义了CONFIG_ARCH_HAS_CPU_RELAX,cpuidle core会在注册cpuidle driver时,自动将driver的state[0]注册为POLL idle state,如下:

1: static int poll_idle(struct cpuidle_device *dev,

2:                 struct cpuidle_driver *drv, int index)

3: {

4:         local_irq_enable();

5:         if (!current_set_polling_and_test()) {

6:                 while (!need_resched())

7:                         cpu_relax();

8:         }

9:         current_clr_polling();

10: 

11:         return index;

12: }

13: 

14: static void poll_idle_init(struct cpuidle_driver *drv)

15: {

16:         struct cpuidle_state *state = &drv->states[0];

17: 

18:         snprintf(state->name, CPUIDLE_NAME_LEN, "POLL");

19:         snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE");

20:         state->exit_latency = 0;

21:         state->target_residency = 0;

22:         state->power_usage = -1;

23:         state->flags = CPUIDLE_FLAG_TIME_VALID;

24:         state->enter = poll_idle;

25:         state->disabled = false;

26: }


3.3 cpuidle governor管理

governor管理位于drivers/cpuidle/governor.c中,包括governor的注册、获取和切换功能,分别由下面三个接口实现:

1: /**

2:  * cpuidle_switch_governor - changes the governor

3:  * @gov: the new target governor

4:  *

5:  * NOTE: "gov" can be NULL to specify disabled

6:  * Must be called with cpuidle_lock acquired.

7:  */

8: int cpuidle_switch_governor(struct cpuidle_governor *gov)

9: {

10:         struct cpuidle_device *dev;

11: 

12:         if (gov == cpuidle_curr_governor)

13:                 return 0;

14: 

15:         cpuidle_uninstall_idle_handler();

16: 

17:         if (cpuidle_curr_governor) {

18:                 list_for_each_entry(dev, &cpuidle_detected_devices, device_list)

19:                         cpuidle_disable_device(dev);

20:                 module_put(cpuidle_curr_governor->owner);

21:         }

22: 

23:         cpuidle_curr_governor = gov;

24: 

25:         if (gov) {

26:                 if (!try_module_get(cpuidle_curr_governor->owner))

27:                         return -EINVAL;

28:                 list_for_each_entry(dev, &cpuidle_detected_devices, device_list)

29:                         cpuidle_enable_device(dev);

30:                 cpuidle_install_idle_handler();

31:        printk(KERN_INFO "cpuidle: using governor %s\n", gov->name);

32:         }

33: 

34:         return 0;

35: }

36: 

37: /**

38:  * cpuidle_register_governor - registers a governor

39:  * @gov: the governor

40:  */

41: int cpuidle_register_governor(struct cpuidle_governor *gov)

42: {

43:         int ret = -EEXIST;

44: 

45:         if (!gov || !gov->select)

46:                 return -EINVAL;

47: 

48:         if (cpuidle_disabled())

49:                 return -ENODEV;

50: 

51:         mutex_lock(&cpuidle_lock);

52:         if (__cpuidle_find_governor(gov->name) == NULL) {

53:                 ret = 0;

54:                 list_add_tail(&gov->governor_list, &cpuidle_governors);

55:                 if (!cpuidle_curr_governor ||

56:                     cpuidle_curr_governor->rating < gov->rating)

57:                         cpuidle_switch_governor(gov);

58:         }

59:         mutex_unlock(&cpuidle_lock);

60: 

61:         return ret;

62: }


cpuidle_register_governor接口的逻辑非常简单,将governor保存到一个全局链表(cpuidle_governors)中,并判断新注册governor的rating是否大于当前governor(保存在cpuidle_curr_governor指针中),如果大于,则将新注册governor切换为当前governor;

cpuidle_switch_governor用于切换当前的governor,需要注意的是,切换之前,要disable所有的device(cpuidle_disable_device),并在切换之后打开。

3.4 cpuidle整体的管理功能

整体的管理功能位于drivers/cpuidle/cpuidle.c中,负责如下事项:

1)cpuidle framework的初始化,由cpuidle_init实现

1: /**

2:  * cpuidle_init - core initializer

3:  */

4: static int __init cpuidle_init(void)

5: {

6:         int ret;

7: 

8:         if (cpuidle_disabled())

9:                 return -ENODEV;

10: 

11:         ret = cpuidle_add_interface(cpu_subsys.dev_root);

12:         if (ret)

13:                 return ret;

14: 

15:         latency_notifier_init(&cpuidle_latency_notifier);

16: 

17:         return 0;

18: }

19: core_initcall(cpuidle_init);


主要完成:

调用cpuidle_add_interface接口,添加CPU global sysfs attributes;

调用latency_notifier_init接口,添加一个pm qos notifier,以便当系统有新的latency需求时,通知到cpuidle framework。有关PM QOS,会在另外idea文章中介绍,这里不再详细说明。

2)系统cpuidle功能的disable、pause、resume等功能,由如下接口实现(比较简单,不再详细说明)

1: extern void disable_cpuidle(void);

2: extern voidcpuidle_pause_and_lock(void); 

3: extern void cpuidle_resume_and_unlock(void);

4: extern void cpuidle_pause(void);

5: extern void cpuidle_resume(void);


 

3)idle state的select和enter

由下面两个接口实现:

1: extern int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev);

2: extern int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev, int index);


cpuidle_select的实现非常简单,首先判断一个“use_deepest_state”变量,如果为1,选一个最低功耗的state即可。如果为0,调用当前governor的select函数,让governor选择。use_deepest_state的状态可以通过cpuidle_use_deepest_state接口设置;

cpuidle_enter接口根据state index,进入指定的state,调用state的enter函数,并记录相关的统计信息即可。

4)cpuidle driver注册的简单接口

由cpuidle_register接口实现,主要目的是在简单的平台上(所有cpuidle device的功能一样),省去cpuidle device的注册过程(由cpuidle core帮忙实现)。driver只需要定义各个idle state,并通过cpuidle_register注册cpuidle driver即可。arm64平台就是使用这个方式。
3.5 coupled idle

coupled idle功能是一个比较有意思的功能,设计者在该功能的源代码中(drivers\cpuidle\coupled.c)写了将近70行的注释,说明这个功能的缘由目的。因此在这里不能三言两语说明白,蜗蜗会单独开一篇文章,介绍该功能。
3.6 sysfs

cpuidle core通过sysfs,向用户控件提供了状态统计、governor选择等信息。先看一个例子,了解一下cpuidle有关的sysfs目录结构:

/sys/devices/system/cpu/cpuidle 

|—current_driver 

|—current_governor_ro(or available_governors & current_governor) 

|—cpu0 

|    |—cpuidle 

|    |    |—state0 

|    |    |    |—name 

|    |    |    |—desc 

|    |    |    |—disable 

|    |    |    |—latency 

|    |    |    |—power 

|    |    |    |—time 

|    |    |    |—usage 

|    |    |—state1 

… 

|—cpu1 

|    |—cpuidle 



1)”/sys/devices/system/cpu/cpuidle"为cpuidle sysfs的顶级目录,由cpuidle_init注册,我们来看一下注册过程,顺便复习一下设备模型的知识。

1: static int __init cpuidle_init(void)

2: {

3:         ...

4:         ret = cpuidle_add_interface(cpu_subsys.dev_root);

5:         ...

6: }


上面代码以“cpu_subsys.dev_root”为参数,调用cpuidle_add_interface接口,注册sysfs interface;

“cpu_subsys”在drivers/base/cpu.c中定义,实际上是一个struct bus_type类型的变量,由cpu_dev_init调用subsys_system_register注册,结合“Linux设备模型(6)_Bus”中有关subsystem的描述,它会创建/sys/devices/system/cpu目录;

cpuidle_add_interface位于drivers/cpuidle/sysfs.c中,会调用sysfs_create_group,创建cpuidle的子目录以及default attribute,如下。

1: static DEVICE_ATTR(current_driver, 0444, show_current_driver, NULL);

2: static DEVICE_ATTR(current_governor_ro, 0444, show_current_governor, NULL);

3: 

4: static struct attribute *cpuidle_default_attrs[] = {

5:         &dev_attr_current_driver.attr,

6:         &dev_attr_current_governor_ro.attr,

7:         NULL

8: };

9: 

10: static DEVICE_ATTR(available_governors, 0444, show_available_governors, NULL);

11: static DEVICE_ATTR(current_governor, 0644, show_current_governor,

12:                    store_current_governor);

13: 

14: static struct attribute *cpuidle_switch_attrs[] = {

15:         &dev_attr_available_governors.attr,

16:         &dev_attr_current_driver.attr,

17:         &dev_attr_current_governor.attr,

18:         NULL

19: };

20: 

21: int cpuidle_add_interface(struct device *dev)

22: {

23:         if (sysfs_switch)

24:                 cpuidle_attr_group.attrs = cpuidle_switch_attrs;

25: 

26:         return sysfs_create_group(&dev->kobj, &cpuidle_attr_group);

27: }


默认情况下,sysfs_create_group会使用cpuidle_default_attrs,该attribute只有两个文件:current_driver和current_governor_ro。如果使能了sysfs_switch(由bootloader传入),则允许通过sysfs改变当前的governor,使用cpuidle_switch_attrs,提供current_driver、available_governors和 current_governor三个attribute文件。

2)current_driver、current_governor_ro/available_governors、current_governor

通过current_driver attribute文件,可以获取当前的cpuidle driver名字;

如果没有使能sysfs switch,通过current_governor_ro,可以获取当前的governor名字;

如果使能sysfs switch,通过available_governors,可以获取可供使用的governors;通过current_governor,可以读取或者设置当前的governor。

上面attribute文件的注册,已经在上面1)中说明。

3)/sys/devices/system/cpu/cpuidle/cpuX/cpuidle/stateX

这个目录下的attribute文件提供了指定cpuidle device下指定state的统计信息,包括:

name,名称;

desc,较为详细的描述;

disable,cpuidle使能状态的读取和设置;

latency,退出该state所需的时间,单位为us;

power,该state下的功耗,单位为mW;

time,停留在改状态的总时间,单位为us

usage,进入该状态的次数。

这些attribute文件的注册过程,这里就不再描述,感兴趣的同学可以参考drivers/cpuidle/sysfs.c的实现。下面贴一个统计信息的实例,供大家参考:

[xxx@cs state0]# cat name; cat desc; cat latency; cat power; cat time; cat usage

POLL

CPUIDLE CORE POLL IDLE

0

4294967295

6777250341

563407
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: