performance - rdpmc 的 CR4.PCE 已清除

标签 performance assembly linux-kernel x86 intel

我尝试在我的 intel i7-4770K 上启用 CR4 的位 8(PCE 位)以使用 rdpmc。 然后我写了这个模块:

#include <linux/module.h>
#include <linux/kernel.h>

void printcr4(void)
{
    unsigned long long cr4;
    asm volatile(
        "xor %%rax,%%rax\n\t"
        "mov %%cr4,%%rax\n\t":"=a"(cr4));
    printk("CR4 : %llx\n",cr4);
}
void CR4_enablePMC(void)
{
    asm volatile(
        "mov %cr4,%rax\n\t"
        "or $(1<<8),%rax\n\t"
        "mov %rax,%cr4\n\t"
    );
}
int init_module(void)
{
   printcr4();
   CR4_enablePMC();
   printcr4();
   return 0;
}
void cleanup_module(void)
{
   printcr4();
}

我使用 insmod 加载模块。

第一次调用printcr4(在init_module中,在使用CR4_enablePMC启用PCE之前)打印我CR4:1406e0 所以第 8 位是 0。

第二次调用printcr4(在init_module中,并在使用CR4_enablePMC启用PCE之后)打印我CR4:1407e0 所以第 8 位是 1。

然后我用 rmmod 删除了我的模块,最后所有 printcr4 (在 cleanup_module 中)打印我 CR4 : 1406e0。 所以第 8 位又是 0,但我希望它是 1,因为我在第二次和最后一次调用 printcr4 之间没有做任何事情。 所以我猜有其他东西清除了这一点,但我不知道它是什么。

我尝试使用 on_each_cpu 函数执行我的模块,但得到了相同的结果。 我还尝试在启用一个进程(bash)和一个核心(我通过 grub 执行此操作)的最小环境中运行它,但结果相同。

你知道如何才能长时间设置这个PCE位吗?

如果有帮助,我使用 Ubuntu 16.04 LTS。

编辑(带有 smp 的代码):

#include <linux/module.h>
#include <linux/kernel.h>

void printcr4(void)
{
    unsigned long cr4=0;
    /* OLD CODE
     asm volatile(
        "push %%rax\n\t"
        "xor %%rax,%%rax\n\t"
        "mov %%cr4,%%rax\n\t":"=a"(cr4));
    asm volatile(
        "pop %rax\n\t");*/
    /* NEW */
    asm volatile(
    "mov %%cr4,%0\n\t":"=r"(cr4));
    printk("Proc: %d, CR4 : %llx\n",smp_processor_id(), cr4);
}
void CR4_enablePMC(void)
{
    asm volatile(
        "push %rax\n\t"
        "mov %cr4,%rax\n\t"
        "or $(1<<8),%rax\n\t"
        "mov %rax,%cr4\n\t"
        "pop %rax\n\t"
    );
}
void init_module_smp(void *param)
{
    printcr4();
    CR4_enablePMC();
    printcr4();

}
void cleanup_module_smp(void *param)
{
    printcr4();
}
int init_module(void)
{
    printk("\nInit module\n");
    on_each_cpu(init_module_smp, NULL, 1);
    return 0;
}
void cleanup_module(void)
{
    printk("\nCleanup module\n");
    on_each_cpu(cleanup_module_smp, NULL, 1);
}

然后我运行 insmod 然后 rmmod 并得到 (dmesg) :

[ 3438.920809] 
               Init module
[ 3438.920813] Proc: 5, CR4 : 1406e0
[ 3438.920814] Proc: 2, CR4 : 1406e0
[ 3438.920815] Proc: 6, CR4 : 1406e0
[ 3438.920815] Proc: 2, CR4 : 1407e0
[ 3438.920817] Proc: 6, CR4 : 1407e0
[ 3438.920818] Proc: 7, CR4 : 1406e0
[ 3438.920818] Proc: 3, CR4 : 1406e0
[ 3438.920819] Proc: 7, CR4 : 1407e0
[ 3438.920820] Proc: 3, CR4 : 1407e0
[ 3438.920824] Proc: 5, CR4 : 1407e0
[ 3438.920826] Proc: 0, CR4 : 1406f0
[ 3438.920827] Proc: 4, CR4 : 1406e0
[ 3438.920829] Proc: 4, CR4 : 1407e0
[ 3438.920830] Proc: 0, CR4 : 1407f0
[ 3438.920832] Proc: 1, CR4 : 1406e0
[ 3438.920833] Proc: 1, CR4 : 1407e0
[ 3442.120602] 
               Cleanup module 
[ 3442.120610] Proc: 0, CR4 : 1406f0
[ 3442.120624] Proc: 7, CR4 : 1406e0
[ 3442.120625] Proc: 3, CR4 : 1406e0
[ 3442.120626] Proc: 1, CR4 : 1406e0
[ 3442.120627] Proc: 2, CR4 : 1406e0
[ 3442.120628] Proc: 5, CR4 : 1406e0
[ 3442.120629] Proc: 6, CR4 : 1406e0
[ 3442.120643] Proc: 4, CR4 : 1406e0

最佳答案

经过一些阅读和内核版本之间的差异之后,内核文件 arch/x86/include/asm/mmu_context.h 中的以下函数似乎负责禁用 CR4.PCE :

static inline void load_mm_cr4(struct mm_struct *mm)
{
    if (static_key_false(&rdpmc_always_available) ||
        atomic_read(&mm->context.perf_rdpmc_allowed))
            cr4_set_bits(X86_CR4_PCE);
    else
            cr4_clear_bits(X86_CR4_PCE);
}

在内核 4.1.5(包括它)之后,static_key_falsestatic_key_true 取代。

我不知道rdpmc_always_available到底是什么,但这个名字很容易理解。

我们无需修改代码,只需以 root 用户身份执行 echo 2 >/sys/devices/cpu/rdpmc 即可。

然后我们可以根据需要配置 PMC(并且要小心,因为可能有其他进程使用它......)。

@PeterCordes @DavidWohlferd @Michael Petch:非常感谢您的帮助。

关于performance - rdpmc 的 CR4.PCE 已清除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38469233/

相关文章:

c - 在 makefile 中使用 'all'

c# - 如何提高 Mono 中 winforms 应用程序的性能?

java - 特定类型会影响 ArrayList 的性能吗?

c - 使用 ARM 内联汇编在没有 libc 的情况下进行系统调用

linux - dmesg 没有记录任何内容

linux-kernel - Linux CFS 志愿者上下文切换 SCHED_OTHER SCHED_FIFO

ruby - 查找数组是否包含另一个数组的任何成员的最快方法?

algorithm - 我们什么时候必须考虑运行时的常量

linux - x86 汇编语言 - 如何读取输入的字符数?

assembly - 在 x86 (NASM) 中为变量赋值