完整教程:第三章:GEM分析:3.1 GEM设计目标和核心概念

1. 背景与需求

1.1 GPU内存管理的挑战

现代图形应用以及AI应用程序需要大量的显存来存储帧缓冲区(framebuffers)、纹理(textures)、顶点缓冲(vertices)等图形相关数据和各种AI model以及训练数据。由于这些数据的高度动态特性,高效地管理显存存对图形栈和计算栈至关重要,并在DRM基础设施中扮演核心角色。

具体挑战包括:

  • 大容量需求:GPU需要存储大量的图形数据和训练数据
  • CPU-GPU数据共享:CPU和GPU之间需要频繁共享和传输数据
  • 多进程安全共享:多个用户空间进程需要安全地共享GPU资源,同时保证进程隔离

1.2 传统方案的问题

在GEM出现之前,DRM的内存管理存在以下问题:

  • 功能有限:早期DRM只提供framebuffer管理,缺乏通用的GPU内存对象抽象
  • 驱动碎片化:各驱动各自实现内存管理,缺乏统一接口,代码重复
  • 文件描述符限制:最初考虑使用文件描述符(fd)作为对象句柄,但存在致命缺陷:
    • 进程默认限制只能打开1024个左右的文件描述符
    • 高fd值会影响X Server的select()处理性能,也影响GL客户端应用
  • 生命周期管理困难:用户空间难以高效管理GPU对象的生命周期
  • AI训练推理的新需求带来的挑战

正如drivers/gpu/drm/drm_gem.c注释所述:

“The goal was to have swap-backed object allocation managed through struct file. However, file descriptors as handles to a struct file have two major failings: Process limits prevent more than 1024 or so being used at a time by default. Inability to allocate high fds will aggravate the X Server’s select() handling.”

2. 设计目标

基于上述挑战,GEM的设计目标明确定义为:

2.1 统一的内存对象抽象

  • 提供通用的GPU内存对象(GEM Object)表示
  • 屏蔽底层硬件差异,但允许驱动扩展
  • 数据无关性:GEM管理抽象的缓冲对象,不需要知道单个缓冲区的具体内容

2.2 简化的内存管理

GEM的设计哲学与TTM完全不同。如Documentation/gpu/drm-mm.rst所述:

“GEM started as an Intel-sponsored project in reaction to TTM’s complexity. Its design philosophy is completely different: instead of providing a solution to every graphics memory-related problems, GEM identified common code between drivers and created a support library to share it.”

关键特性:

  • 简化的初始化和执行需求:相比TTM更轻量级
  • 支持标准操作:内存分配/释放、映射、命令执行等

2.3 安全的多进程访问

  • 基于handle的访问控制:避免直接暴露内核指针
  • 进程隔离:每个进程维护独立的handle命名空间
  • 引用计数管理:自动管理对象生命周期

2.4 灵活的扩展性

  • 核心只提供共享的公共代码
  • 设备特定操作(如命令执行、pinning、domain管理)留给驱动通过私有ioctl实现
  • 驱动可以扩展drm_gem_object结构,添加私有数据

3. 核心概念与设计

3.1 drm_gem_object:核心对象抽象

drm_gem_object是GEM的核心数据结构,定义在include/drm/drm_gem.h

关键特性

  • 引用计数管理:通过kref refcount实现自动生命周期管理
    • 使用drm_gem_object_get()获取引用
    • 使用drm_gem_object_put()释放引用
  • 双重计数机制handle_count独立追踪用户空间handle数量
  • 灵活的存储
    • 可使用shmem作为后端(filp字段)
    • 也支持驱动私有存储(private GEM objects,filp为NULL)
  • 可扩展性:驱动通过嵌入drm_gem_object创建自己的对象类型

3.2 Handle机制:用户空间接口

GEM采用32位整数handle作为用户空间对对象的引用,而不是文件描述符。这一设计解决了fd的限制问题。

实现机制(源自drivers/gpu/drm/drm_gem.c):

/**
* This led to a plan of using our own integer IDs (called handles, following
* DRM terminology) to mimic fds, and implement the fd syscalls we need as
* ioctls.
*/

Handle管理

  • 每个进程(drm_file)维护独立的handle到对象的映射表(通过IDR实现)
  • Handle创建:drm_gem_handle_create()分配handle并建立映射
  • Handle查找:drm_gem_object_lookup()根据handle查找对象
  • Handle删除:drm_gem_handle_delete()移除映射并释放引用

安全性保证

  • 进程A的handle 5和进程B的handle 5可以指向不同的对象
  • 内核通过drm_file上下文隔离不同进程的handle空间
  • 对象访问权限通过drm_vma_node_allow()/revoke()控制

3.3 drm_vma_offset_manager:地址空间管理

为了支持mmap操作,GEM需要为每个对象分配一个虚拟地址空间的偏移量。drm_vma_offset_manager负责管理这个伪造的地址空间。

设计目标(源自drivers/gpu/drm/drm_vma_manager.c):

/**
* The vma-manager is responsible to map arbitrary driver-dependent memory
* regions into the linear user address-space. It provides offsets to the
* caller which can then be used on the address_space of the drm-device.
*/

3.4 drm_mm:通用范围分配器

drm_mm是GEM的底层内存分配器,管理连续的地址空间范围。

设计特点(源自drivers/gpu/drm/drm_mm.c):

/**
* drm_mm provides a simple range allocator. The drivers are free to use the
* resource allocator from the linux core if it suits them, the upside of drm_mm
* is that it's in the DRM core.
*/

四、核心概念关系图

用户空间应用
    │
    ├─ handle (32位整数)
    │     │
    │     ├─ DRM_IOCTL_GEM_CLOSE
    │     ├─ DRM_IOCTL_GEM_FLINK (全局名称)
    │     └─ 驱动私有ioctl
    │
    ↓
┌──────────────────────────────────────┐
│         drm_file (进程上下文)          │
│  - object_idr: handle→object映射      │
│  - prime: dma-buf handle映射          │
└──────────────────────────────────────┘
    │
    ↓
┌──────────────────────────────────────┐
│       drm_gem_object (核心抽象)       │
│  - refcount: 引用计数                 │
│  - handle_count: handle计数          │
│  - vma_node: 地址空间节点             │
│  - filp: shmem文件(可选)            │
│  - funcs: 驱动操作函数                │
└──────────────────────────────────────┘
    │
    ├─────────────────┬─────────────────┐
    │                 │                 │
    ↓                 ↓                 ↓
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│drm_vma_     │ │  shmem/      │ │  驱动私有     │
│offset_      │ │  私有存储     │ │  扩展字段     │
│manager      │ │              │ │              │
│(mmap偏移)   │ │               │ │  (如amdgpu_  │
└─────────────┘ └──────────────┘ │   bo)        │
    │                            └──────────────┘
    ├─ drm_mm (底层范围分配器)
    │  - interval_tree: 区间树
    │  - holes_size/addr: hole管理
    │  - 支持对齐、颜色、多种策略
    │
    ↓
物理/虚拟地址空间

5. GEM的优势

5.1 简洁性

  • 相比TTM的"一刀切"方案,GEM更轻量级
  • 初始化简单,不需要复杂的配置
  • 适合简单的UMA设备

5.2 灵活性

  • 只提供共享的公共功能,不强加不必要的复杂性
  • 驱动可以自由扩展和定制
  • 通过函数指针表(drm_gem_object_funcs)实现多态

5.3 标准化

  • 统一的用户态API(虽然部分操作仍是驱动特定的)
  • 标准的handle管理和对象生命周期
  • 促进了代码共享和复用

6. GEM的局限性

6.1 功能范围有限

  • 不处理视频RAM管理:GEM没有VRAM分配和管理能力
  • 不支持内存迁移:无法在系统内存和VRAM间迁移对象
  • 不管理placement策略:无法指定内存域(memory domains)优先级

6.2 主要面向UMA设备

Documentation/gpu/drm-mm.rst所述:

“GEM has simpler initialization and execution requirements than TTM, but has no video RAM management capabilities and is thus limited to UMA devices.”

6.3 缺乏复杂的资源管理

  • 没有内存预算管理
  • 没有内存驱逐(eviction)策略
  • 没有自动的内存迁移和放置优化

这些局限性正是引入TTM的原因。现代DRM驱动通常:

  • 使用GEM作为基础对象抽象和handle管理
  • 在需要复杂内存管理时,在GEM之上使用TTM
  • AMD等驱动通过amdgpu_bo扩展GEM对象,整合TTM功能

7. 总结

GEM作为DRM的图形执行管理器,通过以下核心设计实现了其目标:

  1. drm_gem_object:提供统一的GPU内存对象抽象
  2. Handle机制:解决文件描述符限制,实现安全的多进程隔离
  3. drm_vma_offset_manager:管理mmap虚拟地址空间
  4. drm_mm:提供灵活高效的底层范围分配器

GEM的设计哲学是"提供共享的公共代码,而不是试图解决所有问题"。这使得它简洁、灵活且易于扩展。对于需要更复杂内存管理的场景(如独立显卡的VRAM管理),驱动可以在GEM基础上整合TTM等更高级的内存管理器。

GEM与TTM的关系是互补而非替代:GEM提供基础抽象和用户接口,TTM提供高级内存管理功能。现代GPU驱动通常同时使用两者,充分发挥各自的优势。

posted @ 2026-01-08 09:23  yangykaifa  阅读(9)  评论(0)    收藏  举报