难度 : 🟡 进阶级
预计学习时间 : 60分钟
前置知识 : 02-GPU显存管理基础
📋 概述
理解DRM和AMDGPU架构是深入学习Buddy分配器的关键。本章介绍:
- 🏗️ DRM子系统: Linux图形驱动的统一框架
- 🎯 AMDGPU架构: AMD GPU驱动的设计
- 🔗 集成关系: Buddy如何融入整个架构
- 📊 数据流向: 从应用到硬件的完整路径
3.1 DRM核心架构
DRM是什么?
DRM 是Linux内核的图形子系统:
历史演进:
早期 (1990s): 每个厂商独立驱动 → 混乱
↓
DRM诞生 (2000s): 统一内核接口 → 标准化
↓
现代 (2010s+): GEM/TTM/KMS → 功能完善
DRM提供:
✓ 统一的设备管理 (/dev/dri/*)
✓ 内存管理框架 (GEM/TTM)
✓ 显示模式设置 (KMS)
✓ GPU调度器
✓ 统一的用户态接口 (ioctl)
DRM子系统分层
┌─────────────────────────────────────────────┐
│ 用户空间 │
│ ┌──────────┐ ┌───────┐ ┌──────────┐ │
│ │ X Server │ │Wayland│ │ Mesa │ │
│ └────┬─────┘ └───┬───┘ └────┬─────┘ │
│ └────────────┼───────────┘ │
│ │ libdrm │
└────────────────────┼────────────────────────┘
│ ioctl
┌────────────────────┼────────────────────────┐
│ DRM Core (内核) │ │
│ ┌─────────────────▼──────────────┐ │
│ │ DRM Device Management │ │
│ │ - 设备注册 │ │
│ │ - IOCTL分发 │ │
│ └──────────────┬─────────────────┘ │
│ │ │
│ ┌──────────────▼──────────────┐ │
│ │ DRM Memory Management │ │
│ │ ┌──────┐ ┌────────┐ │ │
│ │ │ GEM │ │ TTM │ │ │
│ │ └──────┘ └────────┘ │ │
│ └─────────────────────────────┘ │
│ │ │
│ ┌──────────────▼──────────────┐ │
│ │ Display (KMS) │ │
│ │ - Mode setting │ │
│ │ - CRTC/Encoder/Connector │ │
│ └─────────────────────────────┘ │
└────────────────┬────────────────────────────┘
│
┌────────────────▼────────────────────────────┐
│ 厂商驱动 (amdgpu/i915/nouveau...) │
│ ┌──────────────────────────────┐ │
│ │ Hardware Specific Logic │ │
│ │ - 寄存器操作 │ │
│ │ - 中断处理 │ │
│ │ - 固件加载 │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────────────┘
GEM vs TTM
两种内存管理框架:
c
// GEM - Intel等使用
// 特点: 简单,基于BO (Buffer Object)
struct drm_gem_object {
struct drm_device *dev;
struct file *filp;
u32 handle; // 用户态句柄
size_t size; // 大小
// ...
};
// TTM - AMD/Nouveau使用
// 特点: 复杂,支持多种内存域和迁移
struct ttm_buffer_object {
struct drm_gem_object base; // 继承GEM
struct ttm_resource *resource; // 实际资源
struct ttm_placement placement; // 放置策略
// 支持VRAM ↔ GTT ↔ System 迁移
};
对比:
┌──────────┬─────────────┬──────────────┐
│ 特性 │ GEM │ TTM │
├──────────┼─────────────┼──────────────┤
│ 复杂度 │ 低 │ 高 │
│ 内存域 │ 单一 │ 多域+迁移 │
│ 用户 │ Intel等 │ AMD/Nouveau │
│ 灵活性 │ 基础 │ 强大 │
└──────────┴─────────────┴──────────────┘
3.2 DRM内存管理框架
TTM Resource Manager接口
c
// include/drm/ttm/ttm_resource.h
struct ttm_resource_manager_func {
// 分配资源
int (*alloc)(struct ttm_resource_manager *man,
struct ttm_buffer_object *bo,
const struct ttm_place *place,
struct ttm_resource **res);
// 释放资源
void (*free)(struct ttm_resource_manager *man,
struct ttm_resource *res);
// 查询接口
void (*debug)(struct ttm_resource_manager *man,
struct drm_printer *printer);
};
// 资源管理器实例
struct ttm_resource_manager {
const struct ttm_resource_manager_func *func;
u64 size; // 总大小
u64 usage; // 已用大小
void *priv; // 私有数据 (例如: amdgpu_vram_mgr)
// ...
};
TTM BO生命周期
c
// 创建和销毁流程
// 1. 创建BO
int ttm_bo_init_reserved(struct ttm_device *bdev,
struct ttm_buffer_object *bo,
size_t size,
struct ttm_placement *placement)
{
bo->base.size = size;
bo->bdev = bdev;
// 分配内存资源
ttm_bo_mem_space(bo, placement, &mem);
bo->resource = mem;
return 0;
}
// 2. 分配内存
static int ttm_bo_mem_space(struct ttm_buffer_object *bo,
struct ttm_placement *placement,
struct ttm_resource **mem)
{
// 遍历placement中的首选域
for (i = 0; i < placement->num_placement; i++) {
place = &placement->placement[i];
// 获取对应的资源管理器
man = ttm_manager_type(bdev, place->mem_type);
// 调用管理器的分配函数
ret = man->func->alloc(man, bo, place, mem);
if (!ret)
return 0; // 分配成功
}
return -ENOMEM; // 所有域都分配失败
}
// 3. 使用BO (应用程序操作)
// 4. 销毁BO
void ttm_bo_put(struct ttm_buffer_object *bo)
{
kref_put(&bo->kref, ttm_bo_release);
}
static void ttm_bo_release(struct kref *kref)
{
struct ttm_buffer_object *bo;
bo = container_of(kref, struct ttm_buffer_object, kref);
// 释放资源
if (bo->resource) {
man = ttm_manager_type(bdev, bo->resource->mem_type);
man->func->free(man, bo->resource); // 调用VRAM Manager
}
kfree(bo);
}
内存域和放置策略
c
// 定义BO的内存域偏好
static int amdgpu_bo_create(struct amdgpu_device *adev,
struct amdgpu_bo_param *bp,
struct amdgpu_bo **bo_ptr)
{
struct ttm_placement placement;
struct ttm_place places[3];
int num_places = 0;
// 场景1: 优先VRAM,GTT作为备选
if (bp->preferred_domain & AMDGPU_GEM_DOMAIN_VRAM) {
places[num_places].mem_type = TTM_PL_VRAM;
places[num_places].flags = 0;
num_places++;
if (bp->allowed_domain & AMDGPU_GEM_DOMAIN_GTT) {
places[num_places].mem_type = TTM_PL_TT; // GTT
num_places++;
}
}
placement.num_placement = num_places;
placement.placement = places;
// TTM会按顺序尝试每个domain
ttm_bo_init_reserved(adev->mman.bdev, &bo->tbo,
bp->size, &placement);
return 0;
}
// 结果:
// 1. 先尝试从VRAM分配
// 2. VRAM不足 → 尝试从GTT分配
// 3. GTT也不足 → 返回失败
3.3 AMDGPU驱动架构概览
AMDGPU模块结构
drivers/gpu/drm/amd/amdgpu/
├── amdgpu.h # 主头文件
├── amdgpu_drv.c # 驱动入口,模块注册
├── amdgpu_device.c # 设备初始化
├── amdgpu_kms.c # KMS接口实现
├── amdgpu_gem.c # GEM接口 (BO创建等)
│
├── amdgpu_ttm.c # TTM集成层
├── amdgpu_vram_mgr.c # VRAM管理器 ← 使用Buddy
├── amdgpu_gtt_mgr.c # GTT管理器
│
├── amdgpu_vm.c # GPU虚拟内存
├── amdgpu_cs.c # Command Submission
├── amdgpu_fence.c # 同步原语
│
├── amdgpu_gfx.c # Graphics Engine
├── amdgpu_sdma.c # SDMA Engine
├── amdgpu_display.c # Display子系统
│
└── ... (更多子模块)
核心数据结构:
struct amdgpu_device # 设备实例
├── struct amdgpu_mman # 内存管理器
│ └── struct amdgpu_vram_mgr # VRAM管理器
│ └── struct drm_buddy mm # Buddy实例
├── struct amdgpu_vm # GPU VM
└── ...
驱动初始化流程
c
// amdgpu_drv.c - 模块初始化
static int __init amdgpu_init(void)
{
// 注册PCI驱动
pci_register_driver(&amdgpu_kms_pci_driver);
return 0;
}
// PCI设备探测时调用
static int amdgpu_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
// 创建DRM设备
drm_dev_alloc(&amdgpu_kms_driver, &pdev->dev);
// 初始化AMDGPU设备
amdgpu_device_init(adev, pdev);
// 注册DRM设备
drm_dev_register(ddev, ent->driver_data);
return 0;
}
// amdgpu_device.c - 设备初始化
int amdgpu_device_init(struct amdgpu_device *adev,
struct pci_dev *pdev)
{
// 1. 早期初始化
amdgpu_device_early_init(adev);
// 2. 内存管理器初始化 ← 关键步骤
amdgpu_ttm_init(adev);
// 3. GPU初始化
amdgpu_device_ip_init(adev);
// 4. 其他子系统...
return 0;
}
// amdgpu_ttm.c - TTM初始化
int amdgpu_ttm_init(struct amdgpu_device *adev)
{
struct amdgpu_mman *mman = &adev->mman;
// 初始化TTM设备
ttm_device_init(&mman->bdev, &amdgpu_bo_driver,
adev->dev, ...);
// 注册VRAM管理器 ← Buddy在这里创建
amdgpu_vram_mgr_init(adev);
// 注册GTT管理器
amdgpu_gtt_mgr_init(adev);
return 0;
}
3.4 AMDGPU VRAM管理器
amdgpu_vram_mgr 结构
c
// drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
struct amdgpu_vram_mgr {
struct ttm_resource_manager manager; // 继承TTM管理器
struct drm_buddy mm; // ← Buddy分配器实例
atomic64_t usage; // 当前使用量
atomic64_t vis_usage; // Visible区域使用量
u64 default_page_size; // 默认页大小
struct list_head reservations_pending; // 待处理的预留
struct list_head reserved_pages; // 已预留的页面
struct mutex lock; // 保护并发访问
};
// VRAM块资源
struct amdgpu_vram_mgr_resource {
struct ttm_resource base; // TTM资源基类
struct list_head blocks; // Buddy分配的块列表
unsigned long flags; // 标志 (CONTIGUOUS, CLEAR等)
};
VRAM管理器初始化
c
// 初始化VRAM管理器
int amdgpu_vram_mgr_init(struct amdgpu_device *adev)
{
struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;
struct ttm_resource_manager *man = &mgr->manager;
// 获取VRAM大小
u64 vram_size = adev->gmc.real_vram_size;
u64 visible_size = adev->gmc.visible_vram_size;
// 初始化Buddy分配器 ← 关键调用
drm_buddy_init(&mgr->mm, vram_size, PAGE_SIZE);
// 注册为TTM资源管理器
man->func = &amdgpu_vram_mgr_func;
ttm_resource_manager_init(man, &adev->mman.bdev, vram_size);
// 设置为VRAM类型
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, man);
DRM_INFO("VRAM Manager initialized: %llu MB total, %llu MB visible\n",
vram_size >> 20, visible_size >> 20);
return 0;
}
// 示例输出:
// [drm] VRAM Manager initialized: 8192 MB total, 256 MB visible
VRAM分配实现
c
// TTM调用的分配接口
static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man,
struct ttm_buffer_object *bo,
const struct ttm_place *place,
struct ttm_resource **res)
{
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_vram_mgr_resource *vres;
u64 size = bo->base.size;
u64 min_block_size = PAGE_SIZE;
unsigned long flags = 0;
// 创建资源对象
vres = kzalloc(sizeof(*vres), GFP_KERNEL);
INIT_LIST_HEAD(&vres->blocks);
// 处理对齐要求
if (place->lpfn) {
min_block_size = place->lpfn << PAGE_SHIFT;
}
// 处理标志
if (place->flags & TTM_PL_FLAG_CONTIGUOUS)
flags |= DRM_BUDDY_CONTIGUOUS_ALLOCATION;
// 调用Buddy分配 ← 核心调用
mutex_lock(&mgr->lock);
drm_buddy_alloc_blocks(&mgr->mm,
0, // start
mgr->mm.size, // end
size, // size
min_block_size, // alignment
&vres->blocks, // output
flags);
mutex_unlock(&mgr->lock);
// 更新统计
atomic64_add(size, &mgr->usage);
*res = &vres->base;
return 0;
}
// 释放接口
static void amdgpu_vram_mgr_del(struct ttm_resource_manager *man,
struct ttm_resource *res)
{
struct amdgpu_vram_mgr *mgr = to_vram_mgr(man);
struct amdgpu_vram_mgr_resource *vres = to_amdgpu_vram_mgr_resource(res);
mutex_lock(&mgr->lock);
// 释放所有Buddy块
drm_buddy_free_list(&mgr->mm, &vres->blocks);
mutex_unlock(&mgr->lock);
// 更新统计
atomic64_sub(res->size, &mgr->usage);
kfree(vres);
}
// 资源管理器函数表
static const struct ttm_resource_manager_func amdgpu_vram_mgr_func = {
.alloc = amdgpu_vram_mgr_new,
.free = amdgpu_vram_mgr_del,
.debug = amdgpu_vram_mgr_debug,
};
3.5 Buddy分配器在整体架构中的位置
完整的调用链
用户空间应用 (Vulkan游戏)
↓ vkAllocateMemory()
Mesa Vulkan驱动 (libvulkan_radeon.so)
↓ radv_device_memory_create()
libdrm (libdrm_amdgpu.so)
↓ amdgpu_bo_alloc()
↓ ioctl(DRM_IOCTL_AMDGPU_GEM_CREATE)
════════════════════════════════════════
内核空间 (AMDGPU驱动)
↓
amdgpu_gem_create_ioctl() [amdgpu_gem.c]
↓ 验证参数,处理flags
amdgpu_bo_create() [amdgpu_object.c]
↓ 设置placement (VRAM优先)
ttm_bo_init_reserved() [ttm_bo.c]
↓ TTM核心逻辑
ttm_bo_mem_space() [ttm_bo.c]
↓ 遍历placement,选择内存域
amdgpu_vram_mgr_new() [amdgpu_vram_mgr.c]
↓ VRAM管理器
drm_buddy_alloc_blocks() [drm_buddy.c]
↓ Buddy算法核心
├─ alloc_from_freelist()
├─ split_block()
└─ 返回分配的块
════════════════════════════════════════
物理VRAM硬件
数据结构关系图
┌─────────────────────────────────────┐
│ struct amdgpu_device (设备实例) │
│ ┌───────────────────────────────┐ │
│ │ struct amdgpu_mman │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ struct ttm_device bdev │ │ │
│ │ │ - VRAM manager │ │ │
│ │ │ - GTT manager │ │ │
│ │ └───────┬─────────────────┘ │ │
│ │ │ │ │
│ │ ┌───────▼───────────────────┐│ │
│ │ │ struct amdgpu_vram_mgr ││ │
│ │ │ ┌─────────────────────┐ ││ │
│ │ │ │ struct drm_buddy mm │ ││ │
│ │ │ │ - chunk_size │ ││ │
│ │ │ │ - max_order │ ││ │
│ │ │ │ - free_list[] │ ││ │
│ │ │ │ - roots[] │ ││ │
│ │ │ └─────────────────────┘ ││ │
│ │ │ ││ │
│ │ │ atomic64_t usage ││ │
│ │ │ struct mutex lock ││ │
│ │ └───────────────────────────┘│ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
Buddy操作的对象
c
// Buddy管理的是VRAM的物理偏移
// 例如: 8GB VRAM,物理地址范围 0x0 - 0x200000000
// 分配16MB在偏移0x10000000处:
struct drm_buddy_block *block = ...;
block->offset = 0x10000000; // 256MB偏移
block->size = 16 * 1024 * 1024; // 16MB大小
// 转换为物理地址 (GPU可访问):
phys_addr = adev->gmc.vram_start + block->offset;
// = 0xE0000000 (例如PCIe BAR地址) + 0x10000000
// = 0xF0000000
// GPU访问这块内存:
gpu_addr = phys_addr; // GPU物理地址
// 或者通过页表映射到GPU虚拟地址
// CPU访问 (仅Visible VRAM):
if (block->offset < adev->gmc.visible_vram_size) {
void *cpu_ptr = adev->mman.aper_base_kaddr + block->offset;
// CPU可以直接读写
}
并发控制
c
// VRAM Manager使用互斥锁保护Buddy操作
// 分配路径
static int amdgpu_vram_mgr_new(...)
{
mutex_lock(&mgr->lock); // 加锁
drm_buddy_alloc_blocks(...); // 操作Buddy
mutex_unlock(&mgr->lock); // 解锁
return 0;
}
// 释放路径
static void amdgpu_vram_mgr_del(...)
{
mutex_lock(&mgr->lock); // 加锁
drm_buddy_free_list(...); // 操作Buddy
mutex_unlock(&mgr->lock); // 解锁
}
// 原因:
// - Buddy数据结构不是线程安全的
// - 多个CPU核心可能同时分配/释放
// - 需要互斥锁保证操作的原子性
💡 重点提示
-
DRM是框架: 提供统一接口,具体实现由各厂商驱动完成。
-
TTM是抽象层: 隔离了内存域细节,支持多种资源管理器。
-
VRAM Manager是策略层: 决定如何分配(对齐、范围、统计等)。
-
Buddy是执行层: 纯粹的分配算法,不关心上层策略。
-
层次分明: 每层职责清晰,修改Buddy不影响上层接口。
-
互斥保护: 所有Buddy操作都在锁保护下,确保线程安全。
⚠️ 常见陷阱
❌ 陷阱1: "可以直接调用drm_buddy API"
- ✅ 正确: 应该通过TTM接口,让VRAM Manager协调。
❌ 陷阱2: "Buddy管理虚拟地址"
- ✅ 正确: Buddy管理物理偏移(offset),虚拟地址由GPU VM管理。
❌ 陷阱3: "修改Buddy就能优化性能"
- ✅ 正确: 性能瓶颈可能在上层(TTM、BO管理),需要整体分析。
❌ 陷阱4: "VRAM分配失败就是Buddy的问题"
- ✅ 正确: 可能是碎片、可能是总量不足、可能是范围限制。
📝 实践练习
-
追踪完整调用链:
bash# 使用ftrace追踪一次BO创建 echo 'function_graph' > /sys/kernel/debug/tracing/current_tracer echo 'amdgpu_gem_create_ioctl' > /sys/kernel/debug/tracing/set_ftrace_filter echo 1 > /sys/kernel/debug/tracing/tracing_on # 运行一个简单的GPU程序 # ... cat /sys/kernel/debug/tracing/trace # 观察函数调用层次 -
查看VRAM使用统计:
bash# 查看VRAM Manager状态 cat /sys/kernel/debug/dri/0/amdgpu_vram_mm # 查看所有BO信息 cat /sys/kernel/debug/dri/0/amdgpu_gem_info # 分析输出,理解Buddy块的分布 -
理解placement策略:
c// 设计不同场景的placement // 场景1: 纹理(可以在VRAM或GTT) places[0] = {.mem_type = TTM_PL_VRAM}; places[1] = {.mem_type = TTM_PL_TT}; // 场景2: Framebuffer(必须Visible VRAM) places[0] = { .mem_type = TTM_PL_VRAM, .fpfn = 0, .lpfn = visible_size >> PAGE_SHIFT }; // 场景3: Compute buffer(只要VRAM) places[0] = {.mem_type = TTM_PL_VRAM};
本章小结
- DRM架构: 统一的Linux图形驱动框架,包含内存管理、显示、调度等
- TTM框架: 内存管理抽象层,支持多域、迁移、资源管理器
- AMDGPU结构: 模块化设计,VRAM Manager使用Buddy作为底层分配器
- 调用链: 从用户态ioctl到内核Buddy分配,层层传递
- Buddy定位: 最底层的物理内存块管理,职责单一清晰
理解了整体架构后,我们已经具备了深入学习Buddy数据结构和算法的基础。
📚 参考资料
➡️ 下一步
掌握了DRM和AMDGPU架构后,下一章将深入Buddy的核心数据结构,理解代码组织和设计思想。
👉 [04-核心数据结构详解]评审中...