PAYATHON 2026

对 DAX huge pages 调用 set_memory_uc 时出现 PAT 冲突,如何处理?

数据小许

这条报错说明 PAT 检测到了同一物理区间上的 UC- 与 WB 缓存类型冲突。没有专门针对 huge pages 的替代函数可以直接消除这种冲突。先确认传入地址是否适用于 set_memory_uc(),再检查 device_dax 已建立的映射和缓存类型登记。

为什么普通内核页能成功,DAX 大页却失败?

问题中的普通内存来自 __get_free_page();DAX 内存则由设备映射路径管理,两者的生命周期和缓存属性登记方式不同。

原始日志为:

x86/PAT: main:140830 conflicting memory types c078200000-c078400000 uncached-minus<->write-back
[100514.590619] x86/PAT: memtype_reserve failed [mem 0xc078200000-0xc0783fffff], track uncached-minus, req uncached-minus

日志中的 uncached-minus<->write-back 表明,此次请求希望使用 UC-,但目标区间与已有的 WB 类型发生冲突。区间长度为 2 MiB,单凭大小不能证明失败由大页引起。

PAT 按物理地址范围跟踪内存类型,以避免同一物理内存通过不同虚拟地址使用冲突的缓存属性。相关约束见 Linux PAT 文档。

上游 Linux v5.15 的 set_memory_uc() 会先申请 UC- 类型,再修改页表属性。其实现包含:

ret = memtype_reserve(__pa(addr), __pa(addr) + numpages * PAGE_SIZE,
                     _PAGE_CACHE_MODE_UC_MINUS, NULL);

这里有两个需要注意的细节:

  • numpages 按 PAGE_SIZE 计数;在 4 KiB 基础页配置下,2 MiB 对应 512 页。
  • 请求的类型是 UC-,并非严格的 UC。

set_memory_uc() 不能接受任意虚拟地址,尤其不能直接使用用户态 mmap() 返回的地址。具体实现见 v5.15 的 set_memory.c。

device_dax 中的 WB 从哪里来?

上游 v5.15 的 dev_dax_probe() 将设备内存配置为 MEMORY_DEVICE_GENERIC,然后调用 devm_memremap_pages(),使该内存进入设备页映射的管理流程。参见 v5.15 的 device_dax 实现。

对应的 memremap_pages() 从 PAGE_KERNEL 属性开始,调用 track_pfn_remap() 跟踪物理区间;对于 CPU 可访问的设备内存,还会建立内核线性映射。在通常的 x86 配置下,这条路径使用 WB,可以从这里入手排查本例中已有的 WB 登记。参见 v5.15 的 memremap.c。

这一判断基于上游实现。要确定 Ubuntu 5.15.0-173-generic 中的具体调用路径,还需要对照该发行版的对应源码;仅凭两行日志,无法确定所有已有映射的来源。

应该怎样排查和处理?

  1. 确认地址来源:明确传入的是内核线性映射地址、其他内核映射地址,还是用户态地址,并记录调用参数和返回值。不要把物理地址、用户态地址直接当作 set_memory_uc() 所需的地址。

  2. 检查物理区间的类型与归属。在启用 CONFIG_DEBUG_FS 且 debugfs 已挂载时,可以读取:

    sudo cat /sys/kernel/debug/x86/pat_memtype_list
    sudo cat /proc/iomem

    查找与 0xc078200000 到 0xc0783fffff 重叠的记录。PAT 列表用于查看受跟踪的类型登记,不能单凭它枚举所有虚拟映射。调试接口见 PAT 调试说明。

  3. 追踪映射的建立与释放过程:优先检查 devm_memremap_pages()、内核线性映射,以及已有的用户态 DAX 映射,确认谁登记了 WB、谁仍在访问这段内存。

  4. 根据目标决定修改范围:如果确实需要让设备内存以不可缓存方式访问,应由拥有该物理区间的驱动协调映射、类型登记、并发访问和释放流程。这通常涉及驱动或内核路径调整,不能只在现有 DAX 映射上增加一次函数调用。

修改页表属性或换用 ioremap_uc 能解决吗?

不能把它们当作现成修复。

仅设置 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot),不会自动处理已经存在的内核 WB 映射。另建 ioremap_uc() 映射也不能绕过同一物理区间的缓存类型一致性要求。PAT 文档对用户映射与内核映射之间的属性协调有明确说明,见 驱动映射接口说明。

也不应通过直接修改 PAT 跟踪记录或绕过检查来压制错误。类型登记、实际页表和映射生命周期必须一致。

x86 页属性修改代码具备处理大页映射、必要时拆分页表映射的逻辑,因此把大页改成 4 KiB 页不能自行消除 WB/UC- 冲突。应先解决已有映射的属性协调问题,再判断是否需要改变映射粒度。

备注:内容仅供参考。