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进行处理,非常感谢!