我尝试在我的 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_false
被 static_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/