CR4.PCE 用于 rdpmc 已清除
CR4.PCE for rdpmc is cleared
我尝试在我的英特尔 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
经过一些阅读和内核版本之间的 diff
后,内核文件 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_false
被 static_key_true
取代。
我不知道具体是什么rdpmc_always_available
但是这个名字很好理解
无需修改代码,我们可以 echo 2 > /sys/devices/cpu/rdpmc
作为 root 用户。
然后我们可以根据需要配置 PMC(并且要小心,因为可能有其他进程使用它...)。
@PeterCordes @DavidWohlferd @Michael Petch:非常感谢您的帮助。
这个 CR4 寄存器是每个 cpu。因此,如果您在一个函数中执行所有操作,则不太可能(但由于中断仍然可能)您的进程上下文可以从当前 CPU 切换开,并且当切换回来继续执行时,它是 运行 在另一个 cpu.
每个 CPU 的 CR4 都完全独立于另一个 CPU 的 CR4。 (或者实际上是任何寄存器)。
但是如果有任何像/sys/xxxx或sysctl命令这样的全局内存,那么这些值要么存储到全局内存中,要么同时更新到所有CPU中。
我尝试在我的英特尔 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
经过一些阅读和内核版本之间的 diff
后,内核文件 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_false
被 static_key_true
取代。
我不知道具体是什么rdpmc_always_available
但是这个名字很好理解
无需修改代码,我们可以 echo 2 > /sys/devices/cpu/rdpmc
作为 root 用户。
然后我们可以根据需要配置 PMC(并且要小心,因为可能有其他进程使用它...)。
@PeterCordes @DavidWohlferd @Michael Petch:非常感谢您的帮助。
这个 CR4 寄存器是每个 cpu。因此,如果您在一个函数中执行所有操作,则不太可能(但由于中断仍然可能)您的进程上下文可以从当前 CPU 切换开,并且当切换回来继续执行时,它是 运行 在另一个 cpu.
每个 CPU 的 CR4 都完全独立于另一个 CPU 的 CR4。 (或者实际上是任何寄存器)。
但是如果有任何像/sys/xxxx或sysctl命令这样的全局内存,那么这些值要么存储到全局内存中,要么同时更新到所有CPU中。