ARM v7平台周期计数不一致且不匹配CPU频率问题排查
解决Odroid XU3上ARM PMU周期计数偏差的问题
我之前在处理类似Big.Little架构的PMU计数问题时,也遇到过类似的情况,结合你的场景(Odroid XU3/Exynos 5422,固定1.4GHz核心0,100ms采样周期),导致周期数偏差巨大的原因主要有这几个:
1. CPU空闲状态下的时钟门控
ARM Cortex-A15/A7都支持时钟门控和低功耗休眠机制——当核心没有任务运行时,Linux内核会自动让核心进入cpuidle状态,此时核心的时钟会被关闭或者降频,PMU的周期计数器会停止计数(因为它是基于核心时钟的)。这就导致100ms的采样窗口内,核心实际运行的时间远小于100ms,测得的周期数自然远低于预期的1.4e8。
2. Big.Little架构的跨核心调度问题
Odroid XU3的Big(A15)和Little(A7)集群是独立的时钟域,如果你的内核模块没有严格绑定到核心0,内核调度器可能会把模块的采样代码调度到其他核心上运行。不同核心的时钟频率不同(A7默认频率更低),甚至计数器的基准时钟都不一样,这会直接导致计数结果混乱,出现极大的差异。
3. PMU计数器的配置疏漏
ARM PMU的周期计数需要正确配置寄存器才能正常工作,可能你漏掉了一些关键配置:
- 没有启用PMU的全局使能位;
- 没有正确选择周期计数事件(ARM标准的周期事件是
0x00,需要写入对应寄存器); - Exynos 5422作为定制化SoC,可能需要额外配置SoC专属的PMU时钟使能寄存器,否则计数器可能根本没在运行。
4. 采样定时器的精度问题
如果你的采样是用普通内核定时器实现的,它的精度可能只有jiffies级别(通常是10ms或1ms),100ms的定时误差会被放大,导致实际采样窗口和预期不符。另外,读取计数器时如果没有关闭中断,可能会被打断,导致读取的数值不完整。
对应的解决办法:
- 禁用核心0的空闲状态:临时禁用核心0的cpuidle状态,让核心保持持续运行:
测试完成后记得恢复:echo 0 > /sys/devices/system/cpu/cpu0/cpuidle/state0/disableecho 1 > /sys/devices/system/cpu/cpu0/cpuidle/state0/disable - 绑定模块到核心0:在模块代码中添加核心绑定逻辑,确保采样代码只在核心0上运行:
#include <linux/sched.h> #include <linux/cpumask.h> static int __init pmu_module_init(void) { // 绑定当前进程到核心0 set_cpus_allowed_ptr(current, cpumask_of(0)); // 其他初始化逻辑... return 0; } - 完善PMU配置:确保初始化时正确配置所有关键寄存器(以ARMv7为例):
// 禁用所有计数器,重置PMU asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r"(0x0)); // 选择计数器0,设置为周期计数事件 asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r"(0x0)); // PMSELR asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r"(0x0)); // PMXEVTYPER // 启用计数器,开启全局使能 asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r"(0x1)); // PMCR.E = 1 // 启用计数器0 asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r"(0x1 << 0)); // PMCNTENSET - 使用高精度定时器:替换普通内核定时器为
hrtimer,提升定时精度,同时读取计数器时关闭中断,保证原子性:static struct hrtimer sample_timer; static enum hrtimer_restart sample_timer_callback(struct hrtimer *timer) { u64 cycles; local_irq_disable(); // 读取计数器0的数值 asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(cycles)); local_irq_enable(); // 处理cycles数值... // 重新设置定时器 hrtimer_forward_now(timer, ms_to_ktime(100)); return HRTIMER_RESTART; }
内容的提问来源于stack exchange,提问作者blancm
相关产品推荐
相关产品推荐

