之前提到了PCID的引入目的在于优化TLB flush的场景,从而提到系统性能。这里主要搞一个测试,用以验证以下几个方面。
验证TLB的有效(functionality)。
TLB Flush的作用(functionality)。
Flush TLB的方法。
具体而言,这里需要一个内核模块来完成上述操作,不需要用户态进程。测试的基本操作如下。
分配一个内存页kaddress_1,写入11H。
分配一个内存页kaddress_2,写入22H。
修改kaddress_1对应的页表项,使其指向kaddress2对应的页。
Flush kaddress_1所对应的TLB,再次读取kaddress_1,应该读到22H。
恢复kaddress_1之前的页表项,不flush TLB,读取的仍然是22H,这说明TLB在工作,页表是命中了TLB中的缓存,所以读取的还是步骤3当中的页的内容。
Flush TLB,再次读取kaddress_1,这次返回时11H,说明TLB flush有效。
完成上述操作的代码如下。
int test_main(u64 sym_addr)
{
u64 *kvad1, *kvad2;
u64 old_val = 0;
kvad2 = kmalloc(0x1000, GFP_KERNEL);
if (kvad2 == NULL) {
printk("Failed to kmalloc, return \n");
return -ENOMEM;
}
printk("kvad2 = %px, phys = %llx\n", kvad2, virt_to_phys(kvad2));
walk_pagetable((u64)kvad2, my_swapper_pg_dir);
memset(kvad2, 0x22, 0x10);
kvad1 = kmalloc(0x1000, GFP_KERNEL);
if (kvad1) {
printk("kvad1 = %px, phys = %llx\n", kvad1, virt_to_phys(kvad1));
walk_pagetable((u64)kvad1, my_swapper_pg_dir);
memset(kvad1, 0x11, 0x10);
old_val = virt_to_phys(kvad1);
update_pte(my_swapper_pg_dir, (u64)kvad1, virt_to_phys(kvad2));
printk("Changed the PTE, and dumping the new page table\n");
walk_pagetable((u64)kvad1, my_swapper_pg_dir);
printk("Flushed cache, read the data\n");
clflush((u64 *)kvad1);
printk("kvad1[0] = %llx\n", *(u64 *)kvad1);
flush_tlb((void *)kvad1);
printk("Flushed TLB, read the data = %llx\n", *(u64 *)kvad1);
// Now change back to the old pte value.
printk("Restored the Old PTE, w/o flushing tlb\n");
update_pte(my_swapper_pg_dir, (u64)kvad1, old_val);
printk("A: kvad1[0]=%llx, kvad2[0]=%llx\n", *kvad1, *kvad2);
flush_tlb(kvad1);
printk("Flushed TLB\n");
printk("B: kvad1[0]=%llx, kvad2[0]=%llx\n", *kvad1, *kvad2);
kfree(kvad1);
}
kfree(kvad2);
return 0;
}
kernel log显示了测试结果,如下
[764666.408725] Changed the PTE, and dumping the new page table
[764666.408725] ================ Walking page table ==================
[764666.408725] Vaddr is = 0xffff97a465c0b000
[764666.408726] PGD = 0x2ec40a000
[764666.408726] PGD [12f] (V: 0x00000000eb103258 P: 0x2ec40a978) = 0x2ecb64067
[764666.408727] PUD [091] (V: 0x00000000d7a343ca P: 0x2ecb64488) = 0x42d261063. P=1
[764666.408727] PMD [12e] (V: 0x00000000bf1731a6 P: 0x42d261970) = 0x421abb063. P=1
[764666.408728] PTE [00b] (V: 0x00000000cdc9f88c P: 0x421abb058) = 0x8000000425c0c063. P=1
[764666.408728] PTE --> page 0x425c0c000
[764666.408729] Flushed cache, read the data
[764666.408729] kvad1[0] = 1111111111111111
[764666.408730] Flushing TLB by reloading CR3 (non global page). 41e772004
[764666.408731] Writing CR3 takes 342 cycles
[764666.408731] Flushed TLB, read the data = 2222222222222222
[764666.408732] Restored the Old PTE, w/o flushing tlb
Done
[764666.408733] A: kvad1[0]=2222222222222222, kvad2[0]=2222222222222222
[764666.408734] Flushing TLB by reloading CR3 (non global page). 41e772004
[764666.408735] Writing CR3 takes 327 cycles
[764666.408735] Flushed TLB
[764666.408736] B: kvad1[0]=1111111111111111, kvad2[0]=2222222222222222
以上就是一个针对TLB和TLB flush的测试,从这个测试当中可以验证TLB的功能,以及TLB flush的功能。
下一篇讲一下Intel的TurboBoost,同样的,还是耳听为虚,实操为实,通过具体的测试来看一下Intel的动态变频是怎么工作的。