13. PAT (页属性表)

x86 页属性表 (PAT) 允许在页级别粒度上设置内存属性。 PAT 是对 MTRR 设置的补充,MTRR 允许在物理地址范围上设置内存类型。 但是,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 在内部将该区域作为 UC 或 WC 跟踪在 memtype 列表中,以确保没有冲突的映射。

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

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

下表提供了在非 PAT 和 PAT 系统上为 x86 使用 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。 必须由 Linux 更新 PAT MSR,才能支持 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 stub -> PAT 禁用

已禁用

BIOS

图例

E

CPU 中启用的特性

D

CPU 中禁用/不支持的特性

np

指定的 “nopat” 启动选项

!P

未设置 CONFIG_X86_PAT 选项

!M

未设置 CONFIG_MTRR 选项

已启用

PAT 状态设置为已启用

已禁用

PAT 状态设置为已禁用

操作系统

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

BIOS

PAT 使用 BIOS 设置保留 PAT MSR