ARM Linux 如何模拟 PTE 的脏位、访问位和文件位?

How does ARM Linux emulate the dirty, accessed, and file bits of a PTE?

根据pgtable-2-level.h,ARM Linux 有两个版本的PTE; Linux PTE 和 H/W PTE。 Linux PTE 存储在 1024 字节的偏移量以下。

handle_pte_fault 等各种函数(如 pte_filepte_mkdirtypte_mkyoung 中处理页面错误时,使用版本 H/W PTE 调用。

但实际上 ARM H/W 不支持其 PTE 中的脏位、访问位和文件位。

我的问题是它如何检查 H/W PTE 上页面的脏、访问、文件位?理想情况下,它应该检查 Linux PTE 上存储在 1024 字节偏移量以下的那些位?

My question is how does it check the dirty, accessed, file bit of a page on H/W PTE?

TL;DR - 它们是通过在初始访问时出现页面错误来模拟的。

答案在pgtable-2-level.h,

The "dirty" bit is emulated by only granting hardware write permission iff the page is marked "writable" and "dirty" in the Linux PTE. This means that a write to a clean page will cause a permission fault, and the Linux MM layer will mark the page dirty via handle_pte_fault(). For the hardware to notice the permission change, the TLB entry must be flushed, and ptep_set_access_flags() does that for us.

为例,页面的初始 MMU 映射被标记为只读。当一个进程写入它时,会产生一个页面错误。这是引用的handle_pte_fault,主要代码在fault.c as do_page_fault and will call the generic handle_mm_fault which eventually ends at handle_pte_fault中。可以看代码,

if (flags & FAULT_FLAG_WRITE) {
        if (!pte_write(entry))
            return do_wp_page(mm, vma, address,
                    pte, pmd, ptl, entry);
        entry = pte_mkdirty(entry);  /** Here is the dirty emulation. **/
}

因此 Linux 通用代码将检查页面的权限,如果它是可写的,则调用 pte_mkdirty 将页面标记为脏;整个过程通过故障处理程序启动或模拟。在 dirty 页面在 Linux PTE 中标记后,ARM PTE 被标记为可写,因此后续写入不会导致错误。

accessed 是相同的,只是读和写最初都会出错。 file 位也完全未映射,当发生故障时,会咨询 Linux PTE 以查看它是否由文件支持或者是否完全 未映射 页面错误。

在硬件 table 更新了新权限并完成簿记后,用户模式程序在错误指令处重新启动,除了处理错误的时间间隔外,它不会注意到差异.


ARM Linux 使用 4k 页,而 ARM 二级页 table 大小为 1k(256 个条目 * 4 字节)。来自 pgtable-2-level.h 评论,

Therefore, we tweak the implementation slightly - we tell Linux that we have 2048 entries in the first level, each of which is 8 bytes (iow, two hardware pointers to the second level.) The second level contains two hardware PTE tables arranged contiguously, preceded by Linux versions which contain the state information Linux needs. We, therefore, end up with 512 entries in the "PTE" level.

为了使用完整的 4K 页面,PTE 条目的结构如下,

  1. Linux PTE [n]
  2. Linux PTE [n+1]
  3. ARM PTE [n]
  4. ARM PTE [n+1]

一个完整的 4k 页面有四个 1k 项目。这些页面集合必须按进程进行管理,以便为每个进程提供独特的内存视图,并且共享一些信息以节省实际 RAM。函数 cpu_set_pte_ext 用于更改物理 ARM 条目。由于每个 ARM CPU 修订版使用略有不同的 tables 结构和功能,因此 proc-marcos.S[=69= 中的 processor function table that points to an assembler routine. For instance, cpu_v7_set_pte_ext is the ARMv7 or typical original Cortex CPU implementation. This routine is responsible for examining the Linux flags and updating the hardware bits accordingly. As can be seen, the r3 is written to pte+2048 (offset from Linux PTE to hardware PTE) at the end of this routine. The assembler macro armv3_set_pte_ext 中有一个条目] 被许多较旧的 CPU 变体使用。

参见:Tim's notes on ARM MM
Page table entry (PTE) descriptor in Linux kernel for ARM