13. PAT(页属性表)

x86 页属性表 (PAT) 允许在页级别粒度上设置内存属性。PAT 是 MTRR 设置的补充,后者允许在物理地址范围上设置内存类型。但是,由于 PAT 能够在页级别设置属性,并且由于允许的此类属性设置的数量没有硬件限制,因此 PAT 比 MTRR 更灵活。增加的灵活性伴随着指导方针,即不为具有多个虚拟地址的相同物理内存进行内存类型别名。

PAT 允许不同类型的内存属性。目前将支持的最常用的是

WB

写回

UC

未缓存

WC

写组合

WT

写通

UC-

未缓存减

13.1. PAT API

内核中有许多不同的 API 允许在页级别设置内存属性。为了避免别名,应谨慎使用这些接口。以下是可用接口、其预期用途及其内存属性关系的表格。在内部,这些 API 在物理地址范围上使用 reserve_memtype()/free_memtype() 接口来避免任何别名。

API

RAM

ACPI,...

保留/空洞

ioremap

--

UC-

UC-

ioremap_cache

--

WB

WB

ioremap_uc

--

UC

UC

ioremap_wc

--

--

WC

ioremap_wt

--

--

WT

set_memory_uc,set_memory_wb

UC-

--

--

set_memory_wc,set_memory_wb

WC

--

--

set_memory_wt,set_memory_wb

WT

--

--

pci sysfs 资源

--

--

UC-

pci sysfs resource_wc 为 IORESOURCE_PREFETCH

--

--

WC

pci proc !PCIIOC_WRITE_COMBINE

--

--

UC-

pci proc PCIIOC_WRITE_COMBINE

--

--

WC

/dev/mem 读写

--

WB/WC/UC-

WB/WC/UC-

/dev/mem mmap SYNC 标志

--

UC-

UC-

/dev/mem mmap !SYNC 标志和此区域的任何别名

--

WB/WC/UC-

(来自现有别名)

WB/WC/UC-

(来自现有别名)

/dev/mem mmap !SYNC 标志,此区域没有别名,MTRR 表示 WB

--

WB

WB

/dev/mem mmap !SYNC 标志,此区域没有别名,MTRR 表示 !WB

--

--

UC-

13.2. 驱动程序的高级 API

A. 使用 remap_pfn_range、io_remap_pfn_range、vmf_insert_pfn 将页面导出到用户。

想要将一些页面导出到用户空间的驱动程序通过使用 mmap 接口和以下组合来实现

  1. pgprot_noncached()

  2. io_remap_pfn_range() 或 remap_pfn_range()vmf_insert_pfn()

在 PAT 支持下,正在添加新的 API pgprot_writecombine。因此,驱动程序可以继续使用上述序列,在步骤 1 中使用 pgprot_noncached() 或 pgprot_writecombine(),然后执行步骤 2。

此外,步骤 2 在内部将该区域跟踪为 memtype 列表中的 UC 或 WC,以确保没有冲突的映射。

请注意,这组 API 仅适用于 IO(非 RAM)区域。如果驱动程序想要导出 RAM 区域,则必须在上面的步骤 0 中执行 set_memory_uc() 或 set_memory_wc(),并跟踪这些页面的使用情况,并在释放页面到可用池之前使用 set_memory_wb()。

13.3. MTRR 对 PAT/非 PAT 系统的影响

下表提供了在 x86 上对非 PAT 和 PAT 系统使用 ioremap*() 调用时使用写组合 MTRR 的效果。理想情况下,mtrr_add() 的使用将逐步淘汰,转而使用 arch_phys_wc_add(),这将成为启用 PAT 的系统上的空操作。arch_phys_wc_add() 所在的区域应已使用 WC 属性或 PAT 条目进行了 ioremap,这可以通过使用 ioremap_wc() / set_memory_wc() 完成。希望将 IO 内存的不可缓存区域与需要写组合的区域组合的设备应考虑使用 ioremap_uc(),然后使用 set_memory_wc() 来将有效的写组合区域列入白名单。尽管如此,不鼓励这种使用,因为有效内存类型被认为是实现定义的,但此策略可以用作在大小受限的区域上的最后手段,否则 MTRR 写组合将不起作用。

====  =======  ===  =========================  =====================
MTRR  Non-PAT  PAT  Linux ioremap value        Effective memory type
====  =======  ===  =========================  =====================
      PAT                                        Non-PAT |  PAT
      |PCD                                               |
      ||PWT                                              |
      |||                                                |
WC    000      WB   _PAGE_CACHE_MODE_WB             WC   |   WC
WC    001      WC   _PAGE_CACHE_MODE_WC             WC*  |   WC
WC    010      UC-  _PAGE_CACHE_MODE_UC_MINUS       WC*  |   UC
WC    011      UC   _PAGE_CACHE_MODE_UC             UC   |   UC
====  =======  ===  =========================  =====================

(*) denotes implementation defined and is discouraged

注意

-- 在上表中表示“不建议 API 使用”。有些 --‘s 是由内核严格执行的。其他一些今天并未真正强制执行,但将来可能会强制执行。

对于通过 /sys 或 /proc 进行的 ioremap 和 pci 访问 - 如果该地址存在任何现有别名,则返回的实际类型可能更具限制性。例如:如果存在现有的未缓存映射,则新的 ioremap_wc 可以返回未缓存映射来代替请求的写组合。

set_memory_[uc|wc|wt] 和 set_memory_wb 应该成对使用,其中驱动程序将首先将区域设置为 uc、wc 或 wt,并在使用后将其切换回 wb。

随着时间的推移,写入 /proc/mtrr 将被弃用,转而使用基于 PAT 的接口。建议写入 /proc/mtrr 的用户使用以上接口。

驱动程序应使用 ioremap_[uc|wc] 来访问具有 [uc|wc] 访问类型的 PCI BAR。

驱动程序应使用 set_memory_[uc|wc|wt] 来设置 RAM 范围的访问类型。

13.4. PAT 调试

启用 CONFIG_DEBUG_FS 后,可以通过以下方式检查 PAT memtype 列表

# mount -t debugfs debugfs /sys/kernel/debug
# cat /sys/kernel/debug/x86/pat_memtype_list
PAT memtype list:
uncached-minus @ 0x7fadf000-0x7fae0000
uncached-minus @ 0x7fb19000-0x7fb1a000
uncached-minus @ 0x7fb1a000-0x7fb1b000
uncached-minus @ 0x7fb1b000-0x7fb1c000
uncached-minus @ 0x7fb1c000-0x7fb1d000
uncached-minus @ 0x7fb1d000-0x7fb1e000
uncached-minus @ 0x7fb1e000-0x7fb25000
uncached-minus @ 0x7fb25000-0x7fb26000
uncached-minus @ 0x7fb26000-0x7fb27000
uncached-minus @ 0x7fb27000-0x7fb28000
uncached-minus @ 0x7fb28000-0x7fb2e000
uncached-minus @ 0x7fb2e000-0x7fb2f000
uncached-minus @ 0x7fb2f000-0x7fb30000
uncached-minus @ 0x7fb31000-0x7fb32000
uncached-minus @ 0x80000000-0x90000000

此列表显示物理地址范围和用于访问这些物理地址范围的各种 PAT 设置。

另一种更详细的获取 PAT 相关调试消息的方式是使用“debugpat”引导参数。使用此参数,各种调试消息将打印到 dmesg 日志。

13.5. PAT 初始化

下表描述了在各种配置下如何初始化 PAT。PAT MSR 必须由 Linux 更新,以支持 WC 和 WT 属性。否则,PAT MSR 具有固件编程的值。请注意,Xen 在 PAT MSR 中为访客启用了 WC 属性。

MTRR

PAT

调用顺序

PAT 状态

PAT MSR

E

E

MTRR -> PAT 初始化

已启用

操作系统

E

D

MTRR -> PAT 初始化

已禁用

D

E

MTRR -> PAT 禁用

已禁用

BIOS

D

D

MTRR -> PAT 禁用

已禁用

np/E

PAT -> PAT 禁用

已禁用

BIOS

np/D

PAT -> PAT 禁用

已禁用

E

!P/E

MTRR -> PAT 初始化

已禁用

BIOS

D

!P/E

MTRR -> PAT 禁用

已禁用

BIOS

!M

!P/E

MTRR 存根 -> PAT 禁用

已禁用

BIOS

图例

E

CPU 中启用的特性

D

CPU 中禁用的/不支持的特性

np

指定了“nopat”引导选项

!P

CONFIG_X86_PAT 选项未设置

!M

CONFIG_MTRR 选项未设置

已启用

PAT 状态设置为已启用

已禁用

PAT 状态设置为已禁用

操作系统

PAT 使用操作系统设置初始化 PAT MSR

BIOS

PAT 将 PAT MSR 保留在 BIOS 设置中