我正在尝试遵循以下说明:
我在这里的目标是为PMU启用用户空间访问。这些是我正在使用的模块的一些说明。
static void enable_cpu_counters(void* data)
{
asm volatile("msr pmuserenr_el0, %0" :: "r"(0xf));
armv8pmu_pmcr_write (ARMV8_PMCR_LC|ARMV8_PMCR_E);
asm volatile("msr PMCNTENSET_EL0, %0" :: "r" ((u32)(1<<31)));
armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMCR_E|ARMV8_PMCR_LC);
printk("\nCPU:%d \n", smp_processor_id());
}
static void disable_cpu_counters (void* data)
{
printk(KERN_INFO "\ndisabling user-mode PMU access on CPU #%d \n",
smp_processor_id());
/* Program PMU and disable all counters */
armv8pmu_pmcr_write(armv8pmu_pmcr_read() |~ARMV8_PMCR_E);
asm volatile("msr pmuserenr_el0, %0" ::"r"((u64)0));
}
static int __init init(void)
{
unsigned int reguser=0;
isb();
asm volatile("mrs %0, pmcr_el0" : "=r" (reguser));
printk(KERN_INFO "\nPMCR_EL0 register before : %x\n", reguser);
asm volatile("mrs %0, pmuserenr_el0" : "=r" (reguser));
printk(KERN_INFO "\nPMUSERENR register before : %x\n", reguser);
on_each_cpu (enable_cpu_counters, NULL, 1);
printk(KERN_INFO "\nEnable Access PMU Initialized\n");
/* Enable counters */
asm volatile("mrs %0, pmcr_el0" : "=r" (reguser));
printk(KERN_INFO "\nPMCR_EL0 register after : %x\n", reguser);
asm volatile("mrs %0, pmuserenr_el0" : "=r" (reguser));
printk(KERN_INFO "\nPMUSERENR register after : %x\n", reguser);
return 0;
}
static void __exit fini(void)
{
on_each_cpu(disable_cpu_counters, NULL, 1);
printk(KERN_INFO "\nAccess PMU Disabled\n");
}
module_init (init);
module_exit (fini);
输出(dmesg):
[79371.445026] \x0aPMCR_EL0 register before : 41023040
[79371.450997] \x0aPMUSERENR register before : 0
[79371.456511] \x0aCPU:3
[79371.456546] \x0aCPU:2
[79371.456614] \x0aCPU:0
[79371.456631] \x0aCPU:4
[79371.456652] \x0aCPU:5
[79371.473784] \x0aCPU:1
[79371.477254] \x0aEnable Access PMU Initialized
[79371.482693] \x0aPMCR_EL0 register after : 41023001
[79371.488560] \x0aPMUSERENR register after : f
接着是第二个模块,这次仅从寄存器中读取值。
模块(测试其是否将值保留在寄存器中):
static int __init init(void)
{
unsigned int reguser=0;
isb();
/* Enable counters */
asm volatile("mrs %0, pmcr_el0" : "=r" (reguser));
printk(KERN_INFO "\nPMCR_EL0 register : %x\n", reguser);
asm volatile("mrs %0, pmuserenr_el0" : "=r" (reguser));
printk(KERN_INFO "\nPMUSERENR register : %x\n", reguser);
return 0;
}
static void __exit fini(void)
{
printk(KERN_INFO "\nDisabling read_arm_pmu.ko\n");
}
module_init (init);
module_exit (fini);
输出(dmesg):
[79385.429198] \x0aPMCR_EL0 register : 41023040
[79385.434584] \x0aPMUSERENR register : 0
使用一个简单的程序在用户空间上测试功能可以得到:
juno:/data/data/papi/workplace # ./monitoring 2 2 2 3
[1359092.706711] monitoring[11095]: undefined instruction: pc=000000000040065c
[1359092.713652] Code: 00000000 00000000 00000000 d10043ff (d53b9c00)
Illegal instruction
注意::我知道这可能是因为PMCR_EL0的第一位为0,而PMUSERENR不是f。不知道如何做到这一点,以使模块不会更改寄存器中的值...
有用的命令可能有助于调试:
juno:/#cat / proc / modules
read_arm_pmu 16384 0-实时0x0000000000000000(PO)
enable_arm_pmu 16384 0-实时0x0000000000000000(PO)
juno:/#cat / proc / devices
字符设备: 1个记忆 5 / dev / tty 5 / dev /控制台 5 / dev / ptmx 10杂项 13输入 14声音 29磅 90吨 108 ppp 116 alsa 128点 136分 180 USB 189 usb_device 第204章 226台 249 roccat 250人 251桶 252三通 253 rtc 254 gpiochip
块设备: 1个虚拟磁盘 第259章 7循环 8 sd 31吨 65标清 66标 67标清 68标准 69标清 70标准 71标准 128标清 129标清 130标准 131标 132标清 133标清 134标清 135标清 179毫米 253个设备映射器 254 virtblk
猫/ proc / sys / kernel /污染的
4097
juno:/#cat / proc / interrupts
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5
2:1844410 1197983 1353732 16779 3199 5419 GIC v2 92等级arch_mem_timer
3:0 0 0 0 0 0 GIC v2 29级别arch_timer
4:894927 272142 288023 18616 39333 15390 GIC v2 30等级arch_timer
7:0 0 0 0 0 0 GIC v2 198液位计时器
14:419017 0 0 0 0 0 GIC v2 68级别mhu_link
15:0 0 0 0 0 0 GIC v2 67级别mhu_link
16:0 0 0 0 0 0 GIC v2 120级别7ff00000.dma
17:0 0 0 0 0 0 GIC v2 121级别7ff00000.dma
18:0 0 0 0 0 0 GIC v2 122级别7ff00000.dma
19:0 0 0 0 0 0 GIC v2 123级别7ff00000.dma
20:0 0 0 0 0 0 GIC v2 124级别7ff00000.dma
21:0 0 0 0 0 0 GIC v2 140级别7ff00000.dma
22:0 0 0 0 0 0 GIC v2 141级别7ff00000.dma
23:0 0 0 0 0 0 GIC v2 142级别7ff00000.dma
24:0 0 0 0 0 0 GIC v2 143级别7ff00000.dma
25:0 0 0 0 0 0 GIC v2 125级别hdlcd
26:0 0 0 0 0 0 GIC v2 117级别hdlcd
27:6336 0 0 0 0 0 GIC v2 115级别uart-pl011
28:53666 0 0 0 0 0 GIC v2 136级别7ffa0000.i2c
30:47345 0 0 0 0 0 GIC v2 149级别ehci_hcd:usb1
33:14370 0 0 0 0 0 GIC v2 65级别2d000000.gpu
34:0 0 0 0 0 0 GIC v2 66级别2d000000.gpu
35:39222 0 0 0 0 0 GIC v2 64级别2d000000.gpu
37:52 0 0 0 0 0 GIC v2 194级别mmci-pl18x(cmd)
40:0 0 0 0 0 0 GIC v2 100级别rtc-pl031
43:0 0 0 0 0 0 GIC v2 169级别sata_sil24 [0000:03:00.0]
45:0 0 0 0 0 0 M SI 0边缘PCIe PME,aerdrv
52:1049234 0 0 0 0 0 M SI 4194304 Edge eth0
IPI0:62569 1835646 1888285 43688 29194 29798 R调度中断
IPI1:315 1276 725 386 307 207功能呼叫中断
IPI2:0 0 0 0 0 0 C PU停止中断
IPI3:829962 24938 62500 1274 498 1029广播中断
IPI4:800250 925302 1266671 10821 9264 7192 I RQ工作中断
IPI5:0 0 0 0 0 0 C PU唤醒中断
错误:0
我希望你们能帮助我,感觉我在很多可能很简单的事情上浪费了时间。
TLDR -内核模块应启用用户空间访问,并且不起作用。寻找帮助以了解原因。
谢谢, 路易斯
编辑:格式化,使我的问题更清楚。
答案 0 :(得分:0)
FWIW,我已经在较旧的开发板上成功使用了内核模块技巧,但是在新的开发板上,我遇到了完全相同的问题。
https://devtalk.nvidia.com/default/topic/955554/jetson-tx1/performance-counters-reset-itself/中建议禁用CONFIG_CPU_IDLE
可以有所帮助,这确实对我有帮助。禁用该功能后,我可以通过内核模块设置pmuserenr_el0
中的位,并通过用户空间启用/使用计数器。
但是,我已经在运行Debian 83映像的Dragonboard 410c上成功使用了内核模块,该模块运行debian-qcom-dragonboard410c-16.04内核。该特定内核还启用了CONFIG_CPU_IDLE
,因此显然在某些时候已经发生了变化,使得CONFIG_CPU_IDLE
中断了PMU访问。
出于好奇,我还测试了调整reset_pmuserenr_el0
宏以将pmuserenr_el0
设置为0xf
而不是清除它,以查看是否可以使CONFIG_CPU_IDLE
保持启用状态。那确实允许我从用户空间访问PMU寄存器,但是任何基准测试都是不可能的,因为计数器一直都在停止。
不幸的是,禁用CONFIG_CPU_IDLE
要求您重建内核-仅加载新模块是不够的。