Linux内核使TLB条目无效化

7
在 Linux 内核中,我编写了类似 copy_page_range(mm/memory.c)的代码,以 COW 优化的方式将内存从一个进程复制到另一个进程。目标和源地址可以通过 PAGE_SIZE 进行偏移,COW 仍然有效。然而,我发现在用户程序中,当我从同一源地址复制到不同的目标地址时,TLB 似乎没有被正确刷新。在高层次上,我的用户级代码执行以下操作(每次精确复制一页,即 0x1000 字节):
SRC=0x20000000
  1. 向 SRC 写入数据(调用相关页 page1)。
  2. 调用系统调用将 SRC 复制到目标进程的 0x30000000。现在,源进程地址 0x20000000 和目标进程地址 0x30000000 指向同一页(page1)。
  3. 向 SRC 写入其他内容(这应该会触发页面错误处理 COW)。假设源地址现在指向 page2
  4. 调用系统调用将 SRC 复制到目标进程的 0x30001000。
此时,应存在两个单独的页面: SRC 0x20000000 page2 DST 0x30000000 page1 DST 0x30001000 page2 我发现在第三步时,当我向 src 0x20000000 写入其他内容时,没有生成页面错误。经检查,实际页面映射为: SRC 0x20000000 page1 DST 0x30000000 page1 DST 0x30001000 page1 在我的代码中,如果我调用 flush_tlb_page 并传递源地址,则用户代码将按预期工作,并具有正确的页面映射。因此,我确信我没有正确地维护 TLB。在 copy_page_range 中,内核在修改页表之前和之后调用 mmu_notifier_invalidate_range_start/end。我正在做完全相同的事情,并且已经仔细检查了我确实将正确的 struct_mm 和地址传递给 mmu_notifier_invalidate_range_start/end。这个函数不处理刷新 tlb 吗?
好的,就在我打完这段话之后,我检查了一下 dup_mmap,意识到主要调用者 copy_page_rangedup_mmap(kernel/fork.c)调用了 flush_tlb_mm。我猜我应该在我的内核代码之前和之后调用 flush_cache_rangeflush_tlb_range。这正确吗?mmu_notifier_invalidate_range_start/end 究竟是做什么的?
1个回答

9
是的,如果您正在执行更改页面表的操作,您需要确保按要求无效化转换后备存储器(TLB)。 mmu_notifier_invalidate_range_start/end 只是在调用 MMU 通知挂钩;这些挂钩只存在于其他内核代码需要了解 TLB 无效化正在发生的情况时。设置 MMU 通知程序的唯一位置为:
- KVM(硬件辅助虚拟化)使用它们处理页面交换;它需要知道有关主机 TLB 无效化的信息,以使虚拟客户机 MMU 与主机同步。 - GRU(大型 SGI 系统中专用硬件的驱动程序)使用 MMU 通知程序以使 GRU 硬件中的映射表与 CPU MMU 同步。
但是几乎任何调用 MMU 通知挂钩的地方,如果内核没有为您完成,则还应调用 TLB shootdown 函数。

请问您能否简单解释一下 mmu_notifiers 吗?我不确定这些钩子是仅由内核用于通知 kvm/vmm,还是反之亦然。这些钩子是否用于所有页面,还是仅用于 VMM 使用的页面。如果是,那么如何注册这些钩子? - incompetent

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接