Performance rdpmc的CR4.PCE已清除

Performance rdpmc的CR4.PCE已清除,performance,assembly,linux-kernel,x86,intel,Performance,Assembly,Linux Kernel,X86,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

我尝试在我的英特尔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,然后运行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包含它之后,静态密钥false被静态密钥true替换

我不知道什么是rdpmc_总是可用的,但是这个名字很容易理解

我们不需要修改代码,只需作为root用户执行echo2>/sys/devices/cpu/rdpmc即可

然后我们可以根据需要仔细地配置PMC,因为可能还有其他进程使用它


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

此CR4寄存器是每个cpu的。因此,如果您在一个函数中执行所有操作,则不太可能但仍有可能因为中断而将进程上下文从当前CPU切换出去,并且当切换回继续执行时,它正在另一个CPU上运行

每个CPU的CR4完全独立于另一个CPU的CR4。或任何事实上的登记册


但如果有任何全局内存,如/sys/xxxx或sysctl命令,则这些值要么存储在全局内存中,要么同时更新到所有CPU。

如果打印处理器号,则printcr4可能更有用。类似于printkProc:%d,CR4:%llx\n,smp\u处理器id,CR4。至少,你应该考虑在RA4LeNababelPMC中添加RAX,因为你破坏RAX。我怀疑你的PrtRCR4例程。您移动%%cr4、%%rax,但随后弹出%rax。在这个pop之后,c变量cr4中会有什么?将所有asm替换为asmmov%%cr4,%0:=r cr4;,怎么样;?如果您强烈希望显式使用rax,并且在使用前必须将其归零,我想不出原因,但是如果您这样做,那么asmmov%%cr4,%0:=a cr4:00@DavidWohlferd:为什么不将C变量cr4设置为零,且无符号长cr4=0;并在约束上使用+而不是=。@MichaelPetch:不确定这样更好,但可以。真正的问题是,你为什么要这样做?这与mov cr4不同,如果rax没有首先归零,rax可能无法覆盖所有位。Linux可能在内部使用性能计数器。假设你的错误代码恰巧像你预期的那样工作,Linux在你的模块卸载之前清除了cr4中的这一位,这看起来是可信的。我同意,这就是为什么我在每个cpu上都使用。这并不能解决任何问题,因为问题并不是由此造成的。值得一提的是,这是某种强化,使rdpmc对大多数用户进程不可用,以防止seccomp Sanbox中的各种定时和其他侧通道攻击。如果您使用perf_事件打开任何pmu事件,您还应该再次获得对rdpmc的访问权限,这是perf_rdpmc_允许的事情,这也是Linux上自我监视进程的推荐方式,请参阅中的cap_user_rdpmc部分。
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);
}