【问题标题】:rdpmc in user mode does not work even with PCE set即使设置了 PCE,用户模式下的 rdpmc 也不起作用
【发布时间】:2014-04-14 01:55:17
【问题描述】:

根据Wikipedia entry 以及英特尔手册,只要设置了CR4bit 8rdpmc 就应该可用于用户模式进程。但是,即使设置了该位,我在尝试从用户空间运行 rdpmc 时仍然遇到 general protection 错误。

我在内核 2.6.32-279.el6.x86_64 上运行 8 核 Intel X3470

这是我正在尝试执行的用户模式程序:

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

#include <sched.h>
#include <assert.h>

uint64_t
read_pmc(int ecx)
{
    unsigned int a, d;
    __asm __volatile("rdpmc" : "=a"(a), "=d"(d) : "c"(ecx));
    return ((uint64_t)a) | (((uint64_t)d) << 32);
}

int main(int ac, char **av)
{
    uint64_t start, end;
    cpu_set_t cpuset;
    unsigned int c;
    int i;

    if (ac != 3) {
        fprintf(stderr, "usage: %s cpu-id pmc-num\n", av[0]);
        exit(EXIT_FAILURE);
    }

    i = atoi(av[1]);
    c = atoi(av[2]);

    CPU_ZERO(&cpuset);
        CPU_SET(i, &cpuset);
        assert(sched_setaffinity(0, sizeof(cpuset), &cpuset) == 0);

    printf("%lu\n", read_pmc(c));
    return 0;
}

这是设置位并读出 CR4 的内核模块,因此我可以手动验证该位是否已设置。

/*  
 *  Enable PMC in user mode.
 */
#include <linux/module.h>
#include <linux/kernel.h>



int init_module(void)
{
    typedef long unsigned int uint64_t;
    uint64_t output;
    // Set CR4, Bit 8  to enable PMC
__asm__("push   %rax\n\t"
                "mov    %cr4,%rax;\n\t"
                "or     $(1 << 7),%rax;\n\t"
                "mov    %rax,%cr4;\n\t"
                "wbinvd\n\t"
                "pop    %rax"
    );

    // Read back CR4 to check the bit.
    __asm__("\t mov %%cr4,%0" : "=r"(output));
    printk(KERN_INFO "%lu", output);

    return 0;
}
void cleanup_module(void)
{
__asm__("push   %rax\n\t"
        "push   %rbx\n\t"
                "mov    %cr4,%rax;\n\t"
                "mov  $(1 << 7), %rbx\n\t"
                "not  %rbx\n\t"
                "and   %rbx, %rax;\n\t"
                "mov    %rax,%cr4;\n\t"
                "wbinvd\n\t"
                "pop    %rbx\n\t"
                "pop    %rax\n\t"
    ); 
}

【问题讨论】:

  • 既然你不知道内核设置在哪个cpu上CR4我假设你在测试程序中尝试了所有?
  • 是的。我最初希望我的模块在所有内核上设置 CR4,但我不知道如何在模块内设置 CPU 亲和性。
  • 您可以使用on_each_cpuon_each_cpu_mask from smp.h
  • @Jester,它看起来很有希望,但我不确定如何解释 wait 的参数 on_each_cpu 并且顶部的评论并没有说明任何问题。 wait 应该是布尔值还是超时?如果超时,它的单位是多少?
  • 这是一个布尔值。我相信你自己也能找到。

标签: performance assembly linux-kernel x86 intel


【解决方案1】:

显然,当 Intel 说 Bit 8 时,他们指的是右边的 第 9 位,因为他们的索引从 0 开始。用$(1 &lt;&lt; 8) 全局替换$(1 &lt;&lt; 7) 可以解决问题,并允许从用户模式调用rdpmc

这里是更新的内核模块,同样使用on_each_cpu 来确保在每个内核上都设置了它。

/*  
 *  Read PMC in kernel mode.
 */
#include <linux/module.h>   /* Needed by all modules */
#include <linux/kernel.h>   /* Needed for KERN_INFO */

static void printc4(void) {
    typedef long unsigned int uint64_t;
    uint64_t output;
    // Read back CR4 to check the bit.
    __asm__("\t mov %%cr4,%0" : "=r"(output));
    printk(KERN_INFO "%lu", output);
}

static void setc4b8(void * info) {
    // Set CR4, Bit 8 (9th bit from the right)  to enable
__asm__("push   %rax\n\t"
                "mov    %cr4,%rax;\n\t"
                "or     $(1 << 8),%rax;\n\t"
                "mov    %rax,%cr4;\n\t"
                "wbinvd\n\t"
                "pop    %rax"
    );

    // Check which CPU we are on:
    printk(KERN_INFO "Ran on Processor %d", smp_processor_id());
    printc4();
}

static void clearc4b8(void * info) {
    printc4();
__asm__("push   %rax\n\t"
        "push   %rbx\n\t"
                "mov    %cr4,%rax;\n\t"
                "mov  $(1 << 8), %rbx\n\t"
                "not  %rbx\n\t"
                "and   %rbx, %rax;\n\t"
                "mov    %rax,%cr4;\n\t"
                "wbinvd\n\t"
                "pop    %rbx\n\t"
                "pop    %rax\n\t"
    );
    printk(KERN_INFO "Ran on Processor %d", smp_processor_id());
}



int init_module(void)
{
    on_each_cpu(setc4b8, NULL, 0);
    return 0;
}
void cleanup_module(void)
{
    on_each_cpu(clearc4b8, NULL, 0);
}

【讨论】:

    【解决方案2】:

    向 /sys/bus/event_source/devices/cpu/rdpmc 回显“2”允许用户进程 通过 rdpmc 指令访问性能计数器。 请注意,行为已更改。在 4.0 之前,“1”表示“启用” 而意味着“0”禁用。现在“1”表示仅允许具有活动性能事件的进程。更多详情:http://man7.org/linux/man-pages/man2/perf_event_open.2.html

    【讨论】:

      猜你喜欢
      • 2015-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-18
      • 2020-04-17
      • 2011-10-30
      相关资源
      最近更新 更多