1

我正在尝试遵循以下说明:

https://developer.arm.com/products/system-design/cycle-models/knowledge-articles/system-performance-analysis-and-the-arm-performance-monitor-unit

我的目标是为 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:/ # 猫 /proc/modules

read_arm_pmu 16384 0 - 实时 0x0000000000000000 (PO)

enable_arm_pmu 16384 0 - 实时 0x0000000000000000 (PO)

juno:/ # 猫 /proc/devices

字符设备: 1 mem 5 /dev/tty 5 /dev/console 5 /dev/ptmx 10 misc 13 input 14 sound 29 fb 90 mtd 108 ppp 116 alsa 128 ptm 136 pts 180 usb 189 usb_device 204 ttyAMA 226 drm 249 roccat 250 hidraw 251 bsg 252 三通 253 rtc 254 gpiochip

块设备:1 ramdisk 259 blkext 7 loop 8 sd 31 mtdblock 65 sd 66 sd 67 sd 68 sd 69 sd 70 sd 71 sd 128 sd 129 sd 130 sd 131 sd 132 sd 133 sd 134 sd 134 134 sd 134 sd 134 sd 134 sd 134个设备虚拟块

猫/proc/sys/内核/污染

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 边缘 eth0

IPI0: 62569 1835646 1888285 43688 29194 29798 重新调度中断

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 - 内核模块应该启用用户空间访问,但它不工作。寻求帮助以了解原因。

谢谢,路易斯


编辑:格式化,让我的问题更清楚。

4

1 回答 1

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_el00xf不是清除它,看看我是否可以保持CONFIG_CPU_IDLE启用状态。这确实允许我从用户空间访问 PMU 寄存器,但是任何基准测试都是不可能的,因为计数器最终一直停止。

不幸的是,禁用CONFIG_CPU_IDLE需要你重建内核——仅仅加载一个新模块是不够的。

于 2019-01-14T08:31:28.790 回答