
do_set_pmd是文件映射缺页处理路径中的一个巨页建立尝试函数。当从文件系统读入的页面是一个合适的复合页如 THP时do_set_pmd会尝试跳过 PTE 层级直接在 PMD 层级建立巨页映射。核心职责尝试建立 PMD 巨页在finish_fault()中如果 PMD 表项为空且页面是复合页内核会调用do_set_pmd()作为一次优化尝试。如果成功整个 2MB 范围一次性建立映射避免了逐页填充 PTE 的开销。核心逻辑与前置检查函数接收vm_fault *vmf、struct folio *folio和struct page *page。它的执行流程包含一系列严格的前置检查1. VMA 适合性检查首先调用transhuge_vma_suitable(vma, haddr)其中haddr vmf-address HPAGE_PMD_MASK。这个检查确认 VMA 的大小和地址对齐是否允许在haddr处建立 PMD 映射。2. Folio 阶数检查if (folio_order(folio) ! HPAGE_PMD_ORDER) return VM_FAULT_FALLBACK;如果 folio 不是 PMD 大小的复合页例如不是 2MB 的 THP则直接返回VM_FAULT_FALLBACK让调用者走 PTE 路径 。3. PMD 空检查在获取 PMD 锁后再次检查pmd_none(*vmf-pmd)。如果在等待锁期间 PMD 已被其他线程填充则放弃本次巨页建立返回VM_FAULT_FALLBACK。映射建立与记账检查通过后函数执行以下操作建立 PMD 表项通过mk_huge_pmd(page, vma-vm_page_prot)构造巨页 PTE。如果是写缺页还会设置脏位和写权限。刷新 I-cache调用flush_icache_pages(vma, page, HPAGE_PMD_NR)将逐个页面刷新优化为一次批量调用 。更新记账增加MM_FILEPAGES计数增加HPAGE_PMD_NR并调用folio_add_file_rmap_pmd()建立 PMD 级别的反向映射。安装 PMD通过set_pmd_at()写入页表并调用update_mmu_cache_pmd()通知架构刷新 TLB。返回值语义do_set_pmd返回vm_fault_t语义非常明确0巨页映射建立成功。调用者如finish_fault或filemap_map_pmd会解锁页面并直接返回不再走 PTE 路径。VM_FAULT_FALLBACK巨页映射不适用或失败调用者应回退到do_set_pte逐页建立 PTE 映射。历史演进该函数由 Kirill Shutemov 在 2016 年引入内核 4.8作为“推迟页表分配”补丁系列的一部分 。在此之前__do_fault会提前分配 PTE 页表导致即使可以映射为巨页也无法利用。引入do_set_pmd后内核可以先获取页面判断是否适合巨页再决定分配 PTE 还是直接建立 PMD 映射。2024 年Andrew Bresticker 修复了一个回归在folio_add_file_rmap重构后do_set_pmd开始要求传入 head page否则即使 VMA 和 folio 都适合也会被拒绝。修复方式是让finish_fault显式传入compound_head(page)。