对 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 中的具体调用路径,还需要对照该发行版的对应源码;仅凭两行日志,无法确定所有已有映射的来源。
应该怎样排查和处理?
-
确认地址来源:明确传入的是内核线性映射地址、其他内核映射地址,还是用户态地址,并记录调用参数和返回值。不要把物理地址、用户态地址直接当作
set_memory_uc()所需的地址。 -
检查物理区间的类型与归属。在启用
CONFIG_DEBUG_FS且 debugfs 已挂载时,可以读取:sudo cat /sys/kernel/debug/x86/pat_memtype_list sudo cat /proc/iomem查找与
0xc078200000到0xc0783fffff重叠的记录。PAT 列表用于查看受跟踪的类型登记,不能单凭它枚举所有虚拟映射。调试接口见 PAT 调试说明。 -
追踪映射的建立与释放过程:优先检查
devm_memremap_pages()、内核线性映射,以及已有的用户态 DAX 映射,确认谁登记了 WB、谁仍在访问这段内存。 -
根据目标决定修改范围:如果确实需要让设备内存以不可缓存方式访问,应由拥有该物理区间的驱动协调映射、类型登记、并发访问和释放流程。这通常涉及驱动或内核路径调整,不能只在现有 DAX 映射上增加一次函数调用。
修改页表属性或换用 ioremap_uc 能解决吗?
不能把它们当作现成修复。
仅设置 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot),不会自动处理已经存在的内核 WB 映射。另建 ioremap_uc() 映射也不能绕过同一物理区间的缓存类型一致性要求。PAT 文档对用户映射与内核映射之间的属性协调有明确说明,见 驱动映射接口说明。
也不应通过直接修改 PAT 跟踪记录或绕过检查来压制错误。类型登记、实际页表和映射生命周期必须一致。
x86 页属性修改代码具备处理大页映射、必要时拆分页表映射的逻辑,因此把大页改成 4 KiB 页不能自行消除 WB/UC- 冲突。应先解决已有映射的属性协调问题,再判断是否需要改变映射粒度。
备注:内容仅供参考。
