linux cpu功耗软件,Linux CPU core的电源管理(3)_cpu ops

Linux CPU core的电源管理(3)_cpu ops

分类:电源管理子系统

1. 前言

2. cpu ops的功能

要弄明白cpu ops真正意义,我们需要回忆两个知识点:“Linux CPU core的电源管理(2)_cpu topology”中描述的ARM CPU的拓扑结构,主要包括Cluster、Core、Thread等概念;“ARMv8-a架构简介”中提到的Exception level的概念,如下:

1)系统上电后,每个CPU都开始执行bootloader(这里以u-boot为例),boot CPU在进行必要的初始化后,继续执行。其它CPU(secondary CPU),则等待在一个地方,直到一个地址(由CPU_RELEASE_ADDR指定,因此该地址就为spin table)变为非零值,就会跳到改地址所指定的位置继续执行。为了节省power,这里的等待可以通过WFE指令,使CPU进入idle状态。

2)boot CPU进入kernel,在初始化的过程中,依次执行.cpu_init(),.cpu_prepare(),.cpu_boot()三个回调函数,boot secondary CPUs。以本节所描述的spin-table operations为例,这三个回调函数分别完成如下功能:

1: static int smp_spin_table_cpu_init(struct device_node *dn, unsigned int cpu)

2: {

3: /*

4: * Determine the address from which the CPU is polling.

5: */

6: if (of_property_read_u64(dn, “cpu-release-addr”,

7: &cpu_release_addr[cpu])) {

8: pr_err(“CPU %d: missing or invalid cpu-release-addr propertyn”,

9: cpu);

10:

11: return -1;

12: }

13:

14: return 0;

15: }

.cpu_init()接口,负责从DTS中解析出每个secondary CPU的“cpu-release-addr”,其实就是上面所说的CPU_RELEASE_ADDR。

1: static int smp_spin_table_cpu_prepare(unsigned int cpu)

2: {

3: __le64 __iomem *release_addr;

4:

5: if (!cpu_release_addr[cpu])

6: return -ENODEV;

7:

8: /*

9: * The cpu-release-addr may or may not be inside the linear mapping.

10: * As ioremap_cache will either give us a new mapping or reuse the

11: * existing linear mapping, we can use it to cover both cases. In

12: * either case the memory will be MT_NORMAL.

13: */

14: release_addr = ioremap_cache(cpu_release_addr[cpu],

15: sizeof(*release_addr));

16: if (!release_addr)

17: return -ENOMEM;

18:

19: /*

20: * We write the release address as LE regardless of the native

21: * endianess of the kernel. Therefore, any boot-loaders that

22: * read this address need to convert this address to the

23: * boot-loader’s endianess before jumping. This is mandated by

24: * the boot protocol.

25: */

26: writeq_relaxed(__pa(secondary_holding_pen), release_addr);

27: __flush_dcache_area((__force void *)release_addr,

28: sizeof(*release_addr));

29:

30: /*

31: * Send an event to wake up the secondary CPU.

32: */

33: sev();

34:

35: iounmap(release_addr);

36:

37: return 0;

38: }

.cpu_prepare()接口,将secondary_holding_pen的物理地址写入“cpu-release-addr”,并调用sev()指令,唤醒那些处于WFE状态的secondary CPUs,它们开始执行secondary_holding_pen函数。

.cpu_boot()的功能后面再介绍。

3)secondary CPUs开始执行secondary_holding_pen,该接口的主要共功能是:

1: /* arch/arm64/kernel/head.S */

2:

3: .align 3

4: 1: .quad .

5: .quad secondary_holding_pen_release

6:

7: /*

8: * This provides a “holding pen” for platforms to hold all secondary

9: * cores are held until we’re ready for them to initialise.

10: */

11: ENTRY(secondary_holding_pen)

12: bl el2_setup // Drop to EL1, w20=cpu_boot_mode

13: bl __calc_phys_offset // x24=PHYS_OFFSET, x28=PHYS_OFFSET-PAGE_OFFSET

14: bl set_cpu_boot_mode_flag

15: mrs x0, mpidr_el1

16: ldr x1, =MPIDR_HWID_BITMASK

17: and x0, x0, x1

18: adr x1, 1b

19: ldp x2, x3, [x1]

20: sub x1, x1, x2

21: add x3, x3, x1

22: ldr x4, [x3]

23: pen: cmp x4, x0

24: b.eq secondary_startup

25: wfe

26: b pen

27: ENDPROC(secondary_holding_pen)

首先,执行secondary_holding_pen,就意味着secondary CPUs们已经进入了kernel。

在这里,CPU继续等到,直到secondary_holding_pen_release变量和该CPU的ID(由寄存器mpidr_el1读取)相同,才继续执行后续操作(secondary_startup)。

那什么时候secondary_startup变量和CPU ID相同呢一下.cpu_boot()就知道了。

1: /*

2: * Write secondary_holding_pen_release in a way that is guaranteed to be

3: * visible to all observers, irrespective of whether they’re taking part

4: * in coherency or not. This is necessary for the hotplug code to work

5: * reliably.

6: */

7: static void write_pen_release(u64 val)

8: {

9: void *start = (void *)&secondary_holding_pen_release;

10: unsigned long size = sizeof(secondary_holding_pen_release);

11:

12: secondary_holding_pen_release = val;

13: __flush_dcache_area(start, size);

14: }

15:

16: static int smp_spin_table_cpu_boot(unsigned int cpu)

17: {

18: /*

19: * Update the pen release flag.

20: */

21: write_pen_release(cpu_logical_map(cpu));

22:

23: /*

24: * Send an event, causing the secondaries to read pen_release.

25: */

26: sev();

27:

28: return 0;

29: }

原来是.cpu_boot()向secondary_holding_pen_release中写了自己的CPU ID。为什么要这样呢/p>

.cpu_prepare()只是尽快让secondary CPUs进入kernel(以便释放bootloader占用的资源)。但是,要继续执行,必须在boot CPU执行完必要的初始化之后,那就等待咯。

注3:smp spin table operations是非常简单的一种cpu ops,不支持其它的电源管理功能,不支持cpu hotplug,不支持virtualization和security。因此,在ARMv8处理器中,几乎很少使用它作为CPU操作的底层实现。

5. psci operations

文章知识点与官方知识档案匹配,可进一步学习相关知识CS入门技能树Linux入门初识Linux24801 人正在系统学习中 相关资源:电脑耗电量测量软件

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2021年4月8日
下一篇 2021年4月8日

相关推荐