AMD GPU VMID

本文所讨论的均为AMD公司的显卡产品

显卡:AMD Radeon RX 5500 - 8GB 显存

Linux:Linux-5.4.y (fc944ddc0b4a)

AMD GPU HUB

在AMD GPU内部有很多种类的IP,这些不同种类的IP硬件一起构成了完整的GPU硬件,这些子IP在运行的时候也需要访问VRAM或者sysMem的资源,但是不同的是,这些IP并没有与实际的Memroy Controler(MC) 相连,而是汇总到一个称作为“HUB”的硬件单元上,由这个硬件单元代理进行统一的寻址,这个硬件和我们在计算机上看到南桥(PCH: Platfrom Controller Hub)很类似,计算机将一些低速的设备(NIC, SATA, USB ..) 连接到南桥,再由南桥芯片统一和CPU进行通信。

由于GPU硬件设计的需要,在AMD GPU内部集成了一个或者多个HUB硬件,系统将不同种类的IP硬件连接到不同的HUB上,由HUB代理IP进行IO访问,GPU内部IP大多数设备都是基于HUB来进行内存访问的。

HUB IP Note
GFX HUB (1个) GC (Graphic Core), sDMA 主要计算单元: SE/SH/CU/SIMD
MM HUB (1个或者多个) UVD, VCE, VCN, JPEG, DCN 多媒体: 编码解码等IP

GPU HUB 功能

GPU HUB主要由以下几种功能:

  • 地址翻译:将GPU的虚拟地址翻译成总线地址
  • TLB:Translation Lookaside Buffer 提供了一个Cache硬件用于加速地址翻译

AMD GPU硬件提供了2种地址翻译机制:

  • GPU VM:由GPU自身完成地址翻译,并将翻译的结果直接送到地址总线进行寻址。(主要模式)
  • ATC/ATS:由IOMMU提供地址翻译过程,并将翻译结果缓存到ATC中。(APU)
    • ATS: Address Translation Services 基于PCIE协议的ATS服务, 需要搭配IOMMU使用
    • ATC: Address Translation Cache 用于缓存地址翻译结果(aka IO-TLB)

VMID (Virtual Memory/Machine Identity)

由于HUB自身提供了地址翻译功能,那么上游的IP就可以用虚拟的GPU地址用于内存访问,简化了编程门槛,同时提隔离了不同VM的虚拟地址空间。

AMD GPU提供了为每个HUB提供了16份VMID,意味着每个HUB在统一时刻可以表示16份不同的地址空间,软件在提交Command的时候需要将自己VM的GPU虚拟地址空间绑定到一个VMID上,由这个VMID来表示GPU虚拟地址空间的layout,通俗来讲GPU有16个MMU单元可以同时地址翻译功能。

尽管每个HUB由16个VMID,但是在硬件设计的时候VMID-0是比较特殊的:

  1. GART 地址空间在VMID-0上 KMD提交Command是在VMID-0上
  2. VMID-0提供System Aperature概念,简化内核态的物理地址翻译过程

VMID 分配

在Linux平台上VMID按照如下用途进行分配

VMID Use
0 GART Kernel Submit Command
1 - 7 User 3D Application (/dev/card0, render128) opengl, vulkan, 等3D程序
8 - 15 KFD Application (/dev/kfd) Hip,Rocm等程序

VMID 虚拟地址空间

GPU虚拟地址范围

本文所讨论的AMD Radeon RX 5500显卡是一个64位的GPU,但是实际的虚拟地址范围是48bit,也就是寻址能力是2^48。

AMD 的GPU虚拟地址范围会影响GPU页表的翻译级数,随着地址宽度的 增加,翻译级数也需要随着增加,否则会浪费更多的页表内存,页表级数的增加意味着翻译效率的降低,在AMD GPU kernel Driver中,GPU VM的虚拟地址范围由 System Memory 和 VRAM的大小计算出来的。

计算方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void amdgpu_vm_adjust_size(struct amdgpu_device *adev, uint32_t min_vm_size,
uint32_t fragment_size_default, unsigned max_level,
unsigned max_bits)
{
unsigned int max_size = 1 << (max_bits - 30);
unsigned int vm_size;
uint64_t tmp;

/* adjust vm size first */
if (amdgpu_vm_size != -1) {
vm_size = amdgpu_vm_size;
if (vm_size > max_size) {
dev_warn(adev->dev, "VM size (%d) too large, max is %u GB\n",
amdgpu_vm_size, max_size);
vm_size = max_size;
}
} else {
struct sysinfo si;
unsigned int phys_ram_gb;

/* Optimal VM size depends on the amount of physical
* RAM available. Underlying requirements and
* assumptions:
*
* - Need to map system memory and VRAM from all GPUs
* - VRAM from other GPUs not known here
* - Assume VRAM <= system memory
* - On GFX8 and older, VM space can be segmented for
* different MTYPEs
* - Need to allow room for fragmentation, guard pages etc.
*
* This adds up to a rough guess of system memory x3.
* Round up to power of two to maximize the available
* VM size with the given page table size.
*/
si_meminfo(&si);
phys_ram_gb = ((uint64_t)si.totalram * si.mem_unit +
(1 << 30) - 1) >> 30;
vm_size = roundup_pow_of_two(
min(max(phys_ram_gb * 3, min_vm_size), max_size));
}

adev->vm_manager.max_pfn = (uint64_t)vm_size << 18;
...
}

另外你可以在kernel的log里看到如下信息

1
[   65.752676] [drm] vm size is 262144 GB, 4 levels, block size is 9-bit, fragment size is 9-bit

其中 262144 GB (= 0x1000000000000) 就是KMD 计算出来的VM SIZE,这个大小会通过ioctl命令report给libdrm,供libdrm va-range分配器使用。

GPU 页表级数

AMD GPU硬件支持 4 + 1级页表映射,一共最多可实现5级映射, +1级映射会影响TLB效率一般不使用。

AMD GPU Page Size 固定为4K 大小。

GPU页表翻译的时候使用的是物理地址翻译,但是GPU_PAGE_SIZE 为4k,当前的驱动并不能保证一定分配出多个连续的page作为PTE或者PDE。
那么一个4K的物理配置能装多少个PTE呢?

1 PTE = 8 Bytes
1 PAGE = 4 K
Count = 4K / 8 = 512 PTEs = 2 ^ 9 PTEs

通过上面的公式可以算出来,一个4k的物理page可以装下512个页表项,同理一个4k的物理page也可以装下512个目录项,所以对于4级页表一般按照如下分配:

  • 64 VA: 9 - 9 - 9 - 9 - 12
  • 32 VA : 10 - 10 - 10 -12

VMID的使用

GPU在提交Command到GPU上,必须获得一个可以使用的VMID,并把自己的页表基地址设置到对应的VMID寄存器上,当前VMID才能表示当前GPU进程的GPU虚拟地址空间。后面的shader,纹理,fence等GPU资源才能正常工作。

VMID作为GPU硬件资源他的数量是有限的,当多个GPU进程同时运行时,需要分时的复用这些VMID资源,才能保证不同GPU进程工作正常。

由于VMID自身硬件自身的Cache(TLB)存有地址翻译的结果,所以当GPU进程切换的时候务必需要进行Cache Flush操作,这样不可避免的会造成性能的浪费,后面的文章我会详细描述一下 VMID 的分配算法。

总结

希望这篇文章对你理解GPU VM有所帮助,错误的地方请联系作者改正。

作者

Wang Yang

发布于

2020-10-25

更新于

2020-10-25

许可协议