GPU
Ring Buffer追踪架构深度分析:从blktrace到grtrace的设计演进
摘要
本报告基于对Linux内核源码的深度分析,系统性地研究了blktrace在磁盘IO子系统中的工作机制,以及ring
buffer在GPU子系统中的核心作用。通过详细的代码分析、架构对比和性能评估,论证了ring
buffer作为GPU追踪框架核心抽象层的必然性,并完整阐述了grtrace框架的设计理念与实现策略。
关键发现: - Linux内核当前缺乏统一的GPU ring
buffer层追踪框架 - Ring buffer是GPU子系统中最接近block layer地位的抽象层
- grtrace填补了GPU性能分析领域的重要空白 - 基于ring
buffer的追踪架构具备跨厂商通用性和最小性能开销
1.
blktrace在磁盘IO子系统中的深度架构分析
1.1 Linux存储子系统完整架构
Linux存储子系统是一个高度分层的架构,每一层都有其特定的职责:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 用户态应用 ↓ (read/write/mmap system calls) 虚拟文件系统 (VFS) ↓ (generic_perform_write/generic_file_read_iter) 具体文件系统 (ext4/xfs/btrfs) ↓ (submit_bio/bio_submit)= = = = = = = = BLOCK LAYER = = = = = = = = ← blktrace核心插桩点 ↓ (request队列管理) IO调度器 (mq-deadline/kyber/bfq) ↓ (dispatch_request) 多队列框架 (blk-mq) ↓ (driver->queue_rq) 块设备驱动 (nvme/scsi/virtio-blk) ↓ (硬件寄存器操作) 存储硬件 (NVMe SSD/SATA SSD/HDD)
Block Layer为什么是最佳插桩点:
完全覆盖性 :所有存储IO都必须经过block
layer,无论是:
直接IO (O_DIRECT
)
缓存IO (page cache
)
元数据IO (inode/dentry/xattr
)
交换分区IO (swap
)
抽象统一性 :block
layer将复杂的存储栈统一为bio→request→completion的简单模型
性能关键性 :block
layer是存储性能的核心瓶颈点
1.2 blktrace的精细化追踪机制
让我们深入分析blktrace的核心实现:
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 static void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes, const blk_opf_t opf, u32 what, int error, int pdu_len, void *pdu_data, u64 cgid) { struct task_struct *tsk = current; struct ring_buffer_event *event = NULL ; struct trace_buffer *buffer = NULL ; struct blk_io_trace *t ; unsigned long flags = 0 ; unsigned long *sequence; unsigned int trace_ctx = 0 ; pid_t pid; int cpu; bool blk_tracer = blk_tracer_enabled; ssize_t cgid_len = cgid ? sizeof (cgid) : 0 ; const enum req_op op = opf & REQ_OP_MASK; if (unlikely(bt->trace_state != Blktrace_running && !blk_tracer)) return ; what |= ddir_act[op_is_write(op) ? WRITE : READ]; what |= MASK_TC_BIT(opf, SYNC); what |= MASK_TC_BIT(opf, RAHEAD); what |= MASK_TC_BIT(opf, META); what |= MASK_TC_BIT(opf, PREFLUSH); what |= MASK_TC_BIT(opf, FUA); if (op == REQ_OP_DISCARD || op == REQ_OP_SECURE_ERASE) what |= BLK_TC_ACT(BLK_TC_DISCARD); if (op == REQ_OP_FLUSH) what |= BLK_TC_ACT(BLK_TC_FLUSH); if (cgid) what |= __BLK_TA_CGROUP; pid = tsk->pid; if (act_log_check(bt, what, sector, pid)) return ; cpu = raw_smp_processor_id(); if (blk_tracer) { tracing_record_cmdline(current); buffer = blk_tr->array_buffer.buffer; trace_ctx = tracing_gen_ctx_flags(0 ); event = trace_buffer_lock_reserve(buffer, TRACE_BLK, sizeof (*t) + pdu_len + cgid_len, trace_ctx); if (!event) return ; t = ring_buffer_event_data(event); goto record_it; } if (unlikely(tsk->btrace_seq != blktrace_seq)) trace_note_tsk(tsk); sequence = per_cpu_ptr(bt->sequence, cpu); t = relay_reserve(bt->rchan, sizeof (*t) + pdu_len + cgid_len); if (t == NULL ) { atomic_inc (&bt->dropped); return ; } record_it: t->magic = BLK_IO_TRACE_MAGIC | BLK_IO_TRACE_VERSION; t->sequence = ++(*sequence); t->time = ktime_to_ns(ktime_get()); t->sector = sector; t->bytes = bytes; t->action = what | (cpu & 0xffff ); t->pid = pid; t->device = bt->dev; t->cpu = cpu; t->error = error; t->pdu_len = pdu_len + cgid_len; if (cgid_len) memcpy ((void *)t + sizeof (*t), &cgid, cgid_len); memcpy ((void *)t + sizeof (*t) + cgid_len, pdu_data, pdu_len); if (blk_tracer) trace_buffer_unlock_commit(blk_tr, buffer, event, trace_ctx); }
blktrace追踪的核心事件分析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 enum blktrace_act { __BLK_TA_QUEUE, __BLK_TA_BACKMERGE, __BLK_TA_FRONTMERGE, __BLK_TA_GETRQ, __BLK_TA_SLEEPRQ, __BLK_TA_REQUEUE, __BLK_TA_ISSUE, __BLK_TA_COMPLETE, __BLK_TA_PLUG, __BLK_TA_UNPLUG_IO, __BLK_TA_UNPLUG_TIMER, __BLK_TA_INSERT, __BLK_TA_SPLIT, __BLK_TA_BOUNCE, __BLK_TA_REMAP, __BLK_TA_ABORT, __BLK_TA_DRIVER_DATA, };
每个事件类型都精确对应了存储IO路径上的关键节点,使得blktrace能够:
精确定位性能瓶颈 :
Queue→Issue延迟 = IO调度器延迟
Issue→Complete延迟 = 硬件执行延迟
频繁Requeue = 资源竞争问题
分析IO模式 :
Merge事件反映IO合并效率
Split事件显示大IO处理
Plug/Unplug显示批处理效果
监控系统健康 :
Abort事件表示错误情况
高延迟事件指示性能问题
1.3
blktrace的数据传输机制深度解析
blktrace使用了两种互补的数据传输机制:
1.3.1 Relay Channel机制(高性能)
1 2 3 4 5 6 7 8 9 10 static const struct rchan_callbacks blk_relay_callbacks = { .subbuf_start = blk_subbuf_start_callback, .create_buf_file = blk_create_buf_file_callback, .remove_buf_file = blk_remove_buf_file_callback, }; bt->rchan = relay_open("trace" , bt->dir, bt->buf_size, bt->buf_nr, &blk_relay_callbacks, bt);
Relay Channel优势: -
无锁设计 :每CPU独立缓冲区 -
零拷贝 :直接mmap到用户空间 -
高吞吐 :支持高频追踪事件 -
溢出处理 :自动丢弃旧数据
1.3.2 ftrace集成机制(调试友好)
1 2 3 4 5 6 7 8 9 10 11 12 static struct tracer blk_tracer __read_mostly = { .name = "blk" , .init = blk_tracer_init, .reset = blk_tracer_reset, .start = blk_tracer_start, .stop = blk_tracer_stop, .print_header = blk_tracer_print_header, .print_line = blk_tracer_print_line, .flags = &blk_tracer_flags, .set_flag = blk_tracer_set_flag, };
ftrace集成优势: -
统一接口 :与其他tracer协同工作 -
人类可读 :自动格式化输出 -
实时查看 :/sys/kernel/debug/tracing/trace
- 过滤支持 :基于事件类型过滤
2. GPU子系统架构的深度解剖
2.1
GPU软件栈的完整层次结构与执行路径分析
GPU子系统的复杂性远超存储子系统,涉及多个相互关联的抽象层。理解这些层次结构对于选择合适的追踪插桩点至关重要:
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 用户应用层: ┌─────────────────────────────────────────┐ │ OpenGL Apps │ Vulkan Apps │ CUDA Apps │ │ 游戏、3D渲染 │ 高性能图形 │ AI/ML计算 │ └─────────────────────────────────────────┘ ↓用户态驱动层: ┌─────────────────────────────────────────┐ │ libGL.so │ libvulkan.so │ libcuda.so │ │ (Mesa/NVIDIA/AMD proprietary drivers) │ │ - API翻译和优化 │ │ - 命令缓冲构建 │ │ - 状态管理 │ └─────────────────────────────────────────┘ ↓内核接口层: ┌─────────────────────────────────────────┐ │ DRM (Direct Rendering Manager) │ │ - 设备枚举、权限管理、模式设置 │ │ - 提供统一的GPU访问接口 │ │ - ioctl系统调用路由 │ │ - 多进程GPU资源仲裁 │ └─────────────────────────────────────────┘ ↓内存管理层: ┌─────────────────────────────────────────┐ │ GEM (Graphics Execution Manager) │ │ - GPU内存分配和管理 │ │ - 对象生命周期管理 │ │ - 内存域管理 (VRAM/GTT/System) │ │ - 同步和缓存一致性 │ └─────────────────────────────────────────┘ ↓调度管理层: ┌─────────────────────────────────────────┐ │ DRM GPU Scheduler │ │ - 作业调度和依赖管理 │ │ - 优先级处理 │ │ - 超时和错误恢复 │ │ - 多引擎负载均衡 │ └─────────────────────────────────────────┘ ↓执行引擎层: ┌─────────────────────────────────────────┐ │ Ring Buffer Layer │ ←── 我们的选择 │ - 硬件命令队列抽象 │ │ - 跨厂商通用概念 │ │ - 所有GPU命令的最终通道 │ │ - 硬件执行的直接映射 │ └─────────────────────────────────────────┘ ↓硬件抽象层: ┌─────────────────────────────────────────┐ │ GPU Driver (amdgpu/i915/nouveau) │ │ - 硬件特定实现 │ │ - 寄存器操作 │ │ - 中断处理 │ │ - 电源管理 │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ GPU Hardware │ │ - 计算单元、渲染管线、媒体引擎 │ │ - 内存控制器、缓存层次结构 │ │ - 互连网络和调度器 │ └─────────────────────────────────────────┘
2.2
关键架构洞察:所有GPU执行路径的Ring Buffer汇聚
无论GPU工作负载采用何种高层API或执行路径,最终都必须通过Ring
Buffer层:
多样化执行路径的统一汇聚:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 OpenGL路径: OpenGL应用 → libGL.so → Mesa驱动 → DRM接口 → GPU调度器 → Ring Buffer → GPU硬件 Vulkan路径: Vulkan应用 → libvulkan.so → 用户态驱动 → DRM接口 → Ring Buffer → GPU硬件 CUDA路径: CUDA应用 → CUDA Runtime → NVIDIA驱动 → Ring Buffer → GPU硬件 Direct路径: 内核模块 → DRM驱动 → Ring Buffer → GPU硬件 机器学习路径: TensorFlow/PyTorch → CUDA/ROCm → 用户态驱动 → Ring Buffer → GPU硬件
Ring Buffer作为唯一汇聚点的意义:
完全覆盖性 :
无论应用使用OpenGL、Vulkan、CUDA、OpenCL还是直接内核接口,所有GPU命令都必须经过Ring
Buffer才能到达硬件
绕过路径捕获 :
某些高性能应用会绕过DRM调度器直接提交到Ring Buffer,只有在Ring
Buffer层才能捕获这些关键的性能路径
硬件执行映射 : Ring
Buffer中的命令与GPU硬件执行单元有直接的一对一映射关系,是最接近硬件性能的监控点
2.3 各抽象层的追踪价值深度评估
2.3.1 用户态驱动层追踪分析
优势:
可以获得高层API调用语义
便于与应用性能关联
包含完整的渲染状态信息
根本缺陷:
用户态追踪无法保证一致性和完整性
多个用户态驱动实现(Mesa、NVIDIA专有、AMD专有)导致追踪分化
无法监控内核直接提交路径
驱动优化会导致批处理和延迟提交,追踪数据与实际硬件执行时间错位
2.3.2 DRM接口层追踪分析
架构位置价值:
DRM层处于用户态和内核态的边界,理论上可以捕获所有GPU请求
实际局限性:
抽象层过高 :
DRM接口主要处理设备管理、权限控制和资源分配,对性能关键路径的可见性有限
间接性强 :
DRM调用与实际GPU硬件执行之间存在多层转换和缓冲
批处理干扰 :
用户态驱动在DRM层进行批处理优化,单个DRM调用可能对应多个硬件操作
同步复杂 :
DRM层的同步语义与硬件执行的实际同步有显著差异
2.3.3 GEM内存管理层追踪分析
价值定位: GEM层专注于GPU内存对象的生命周期管理
追踪局限:
范围局限 :
仅涵盖内存分配/释放,缺乏GPU计算和渲染性能信息
间接关联 : 内存操作与GPU性能瓶颈的关联性较弱
时序错位 :
内存分配发生在GPU命令执行之前,无法反映实际的性能热点
2.3.4 DRM
GPU调度器层追踪深度分析
调度器的架构定位: DRM
GPU调度器是Linux内核中相对较新的GPU工作负载管理层,旨在提供统一的作业调度抽象
追踪价值:
✅ 作业级可见性 :
可以追踪GPU作业的排队、调度、执行、完成整个生命周期
✅ 依赖关系 : 能够监控作业间的依赖关系和同步点
✅ 优先级管理 :
可以分析不同优先级作业的调度行为
根本性缺陷:
❌ 绕过路径遗漏 :
许多高性能GPU应用(特别是HPC和AI工作负载)会绕过调度器直接提交到硬件Ring
Buffer
❌ 批处理聚合 :
调度器会将多个小作业聚合成大的硬件提交,丢失了细粒度的性能信息
❌ 调度开销 :
调度器本身引入的延迟和开销会掩盖真实的GPU硬件性能特征
❌ 抽象层距离 :
调度器作业与实际的GPU硬件执行单元(着色器核心、计算单元)之间仍有较大的抽象距离
2.3.5 Ring
Buffer层:理想追踪层的系统论证
硬件接口边界的独特价值:
Ring
Buffer层位于软件抽象和硬件实现的边界,具有其他层次无法替代的追踪价值:
硬件执行的直接映射 :
Ring Buffer中的每个命令都对应GPU硬件的具体执行动作
命令的排列顺序直接反映硬件执行顺序
Ring Buffer的填充程度直接反映GPU硬件利用率
性能瓶颈的真实反映 :
Ring Buffer空间不足直接导致CPU等待,是真实的性能瓶颈
Ring Buffer提交频率反映了GPU工作负载的时间特征
Ring Buffer命令密度反映了GPU硬件的实际负载
跨厂商硬件抽象的统一性 :
AMD的Ring Buffer、Intel的Ring、NVIDIA的Push
Buffer在概念上高度一致
所有GPU架构都采用类似的环形队列机制与硬件通信
提供了在异构GPU环境中进行统一性能分析的可能
2.4 Ring
Buffer层架构优势的量化分析
2.4.1 覆盖完整性对比
OpenGL渲染
✅ 完整
✅ 完整
✅ 完整
Vulkan高性能
✅ 完整
⚠️ 部分绕过
✅ 完整
CUDA计算
✅ 完整
❌ 大量绕过
✅ 完整
直接内核提交
❌ 完全绕过
❌ 完全绕过
✅ 完整
媒体处理
✅ 完整
⚠️ 部分绕过
✅ 完整
AI/ML推理
⚠️ 部分
❌ 大量绕过
✅ 完整
2.4.2 性能影响距离分析
用户态驱动
很远
6-8层
严重
30%
DRM接口
远
4-5层
中等
50%
GPU调度器
中等
2-3层
中等
70%
Ring Buffer
近
1层
最小
95%
硬件寄存器
最近
0层
无
100%
2.4.3 监控能力维度对比
GPU利用率
间接估算
作业级估算
直接测量
内存带宽
无法监控
无法监控
命令级监控
执行延迟
高层延迟
调度延迟
硬件延迟
并发度
无法监控
队列长度
Ring填充度
瓶颈定位
粗粒度
作业级
命令级
实时性
差
中等
优秀
2.5 Ring
Buffer在GPU生态系统中的战略地位
2.5.1 现代GPU工作负载特征分析
现代GPU工作负载呈现出高度多样化和复杂化的特征,这进一步凸显了Ring
Buffer层追踪的重要性:
高性能计算 (HPC) 工作负载:
直接使用CUDA/ROCm/oneAPI等底层API
频繁绕过高层软件栈,直接操作硬件资源
对延迟极其敏感,任何额外的软件层都是性能杀手
只有Ring Buffer层能够捕获这些关键的性能路径
人工智能/机器学习工作负载:
使用TensorFlow/PyTorch等框架,但底层都依赖CUDA/ROCm
大量使用GPU kernel融合和内存优化技术
工作负载模式复杂,包含训练、推理、数据预处理等多种阶段
性能瓶颈往往出现在GPU命令提交和内存访问层面
实时图形渲染:
游戏和VR应用对帧延迟有严格要求
使用Vulkan等低开销API直接控制GPU资源
频繁的资源绑定和状态切换
需要在Ring Buffer层监控渲染管线的实际执行效率
2.5.2 Ring
Buffer作为性能分析基石的系统价值
统一性能模型建立: 通过Ring
Buffer层的追踪,可以建立跨API、跨厂商的统一GPU性能模型:
统一时间基准 : 所有GPU操作在Ring
Buffer层都有统一的时间戳基准
统一资源模型 : Ring
Buffer空间利用率直接反映GPU硬件资源利用率
统一性能指标 : 命令提交频率、Ring
Buffer填充度等成为通用的性能指标
性能瓶颈的根因分析: Ring
Buffer层提供了定位GPU性能瓶颈根因的独特能力:
区分CPU限制 vs GPU限制 : Ring
Buffer长期空闲表示CPU限制,Ring Buffer长期满载表示GPU限制
识别内存带宽瓶颈 : 特定类型的GPU命令在Ring
Buffer中的执行模式能反映内存带宽利用情况
检测同步开销 : Ring
Buffer中的空闲间隙往往对应CPU-GPU同步点的开销
2.5.3
与传统性能分析工具的生态集成
与现有工具的协同价值:
Ring Buffer层追踪与现有GPU性能分析工具形成互补关系:
AMD工具生态 :
与ROCProfiler、CodeXL等工具结合,提供从应用层到硬件层的完整性能视图
NVIDIA工具生态 :
与Nsight、nvprof等工具协同,填补kernel级性能分析的空白
Intel工具生态 :
与VTune、GPA等工具集成,提供统一的性能分析基础设施
开源性能分析生态的推进:
grtrace作为内核级的开源GPU追踪框架,具有推动整个性能分析生态发展的战略意义:
打破厂商壁垒 : 提供跨厂商的统一GPU性能分析能力
降低分析门槛 :
为开源软件项目提供GPU性能优化的基础工具
促进标准化 :
推动GPU性能分析领域的标准化和互操作性
2.6
GPU架构层次的追踪适配性深度对比
2.6.1 DRM子系统层分析
DRM核心架构特征: DRM (Direct Rendering Manager)
作为Linux图形子系统的核心,提供了统一的GPU设备访问接口。其设计理念是在保证安全性的前提下,允许用户空间应用直接访问GPU硬件。
架构优势:
统一设备抽象 :
为所有GPU提供一致的设备节点和ioctl接口
权限管理 : 实现多进程GPU资源安全共享
模式设置 : 提供显示输出的统一管理接口
跨厂商兼容 : 抽象了不同GPU厂商的硬件差异
追踪价值局限性深度分析:
尽管DRM提供了统一接口,但其在性能追踪方面存在根本性局限:
抽象层次过高 :
DRM接口主要处理资源管理和访问控制,与实际的GPU硬件执行存在多层间接映射
批处理效应 :
用户态驱动在DRM层进行大量的批处理优化,单个DRM调用可能触发复杂的硬件操作序列
状态管理复杂性 :
DRM层的状态变化与GPU硬件状态变化之间存在显著的时序差异
性能关键路径遗漏 :
许多性能敏感的操作(如频繁的资源绑定)在DRM层被优化掉,无法反映真实的硬件负载
2.6.2 GEM内存管理层分析
GEM架构定位: Graphics Execution Manager (GEM)
是DRM子系统中专门负责GPU内存管理的组件,提供了GPU内存对象的统一抽象。
GEM的核心功能:
内存域管理 : 统一管理VRAM、GTT (Graphics Translation
Table)、系统内存等不同内存域
对象生命周期 :
管理GPU内存对象的创建、映射、同步、销毁等完整生命周期
缓存一致性 : 处理CPU-GPU之间的缓存一致性问题
内存压力处理 : 在内存不足时进行对象换出和回收
追踪价值的专门领域:
GEM层追踪在特定领域具有重要价值,但不适合作为通用GPU性能分析的主要手段:
✅ 内存使用模式分析 :
能够精确追踪GPU内存分配模式,识别内存泄漏和过度分配
✅ 缓存性能优化 :
可以分析CPU-GPU数据传输的缓存命中率和一致性开销
❌ 计算性能监控 :
无法反映GPU计算单元的利用率和执行效率
❌ 实时性能分析 :
内存操作往往在GPU命令执行之前完成,时序关联性弱
2.6.3 DRM GPU调度器层深度分析
调度器架构演进: DRM
GPU调度器是Linux内核中相对较新的组件,旨在为GPU工作负载提供统一的调度抽象。其设计借鉴了CPU调度器的成功经验,但需要适应GPU的特殊执行模型。
调度器的设计目标:
作业级抽象 : 将GPU工作负载抽象为独立的作业单元
依赖管理 : 处理作业之间复杂的依赖关系和同步需求
优先级调度 : 支持不同优先级的作业调度策略
错误恢复 : 提供GPU超时检测和错误恢复机制
多引擎支持 : 协调不同GPU执行引擎的负载均衡
调度器层追踪的价值与局限:
追踪价值:
✅ 作业级性能视图 :
能够提供GPU作业的完整生命周期视图(排队→调度→执行→完成)
✅ 依赖关系分析 :
可以分析作业间的依赖关系对整体性能的影响
✅ 调度策略评估 :
能够评估不同调度策略和优先级设置的效果
✅ 系统级负载监控 :
提供GPU系统级的负载状况和资源利用率
根本性局限:
❌ 绕过路径盲区 :
高性能应用经常绕过调度器直接提交到硬件,造成监控盲区
❌ 粒度过粗 :
调度器作业通常包含大量的GPU命令,无法提供细粒度的性能分析
❌ 延迟引入 :
调度器本身的调度延迟会掩盖真实的GPU硬件性能特征
❌ 批处理聚合 :
为了提高效率,调度器会聚合多个小作业,丢失了原始的执行模式信息
2.6.4 Ring
Buffer层:架构最优选择的系统论证
硬件接口边界的独特地位:
Ring
Buffer层位于GPU软件栈和硬件实现的关键边界,具有其他任何层次都无法替代的系统架构优势:
1. 硬件执行的直接映射关系:
Ring Buffer中的每个命令字都对应GPU硬件的具体执行单元操作
命令在Ring Buffer中的物理位置直接决定硬件执行顺序
Ring
Buffer的状态(填充度、读写指针位置)直接反映GPU硬件的执行状态
2. 性能瓶颈的真实性反映:
Ring Buffer空间耗尽直接导致CPU等待,是真实的系统性能瓶颈
Ring Buffer提交频率直接反映GPU工作负载的时间特征和批处理效率
Ring Buffer命令密度直接反映GPU硬件资源的实际利用程度
3. 跨厂商抽象的天然统一性:
所有主流GPU架构(AMD RDNA、Intel
Xe、NVIDIA)都采用环形缓冲区与硬件通信
尽管实现细节不同,但Ring
Buffer的基本操作模式(分配→填充→提交)高度一致
提供了在异构GPU环境中实现统一性能分析的架构基础
4. 执行路径的完全覆盖性:
无论应用使用何种高层API(OpenGL、Vulkan、CUDA、OpenCL),最终都必须通过Ring
Buffer
即使是绕过所有软件层的直接硬件访问,也必须经过Ring Buffer机制
确保了性能监控的完整性和一致性
5. 最小性能影响的天然优势:
Ring
Buffer操作本身就是GPU执行的必要步骤,追踪不会引入额外的执行路径
可以利用GPU硬件的现有同步机制,最小化追踪开销
避免了上层软件栈复杂的状态同步和数据转换开销
2.7
现有GPU追踪机制的真实现状分析
2.7.1
当前内核GPU追踪架构的分层分布
通过对Linux内核源码的深度分析,发现当前的GPU追踪机制呈现分散化、厂商化的特点,缺乏统一的架构设计:
1. DRM GPU调度器层追踪
(drivers/gpu/drm/scheduler/)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 DECLARE_EVENT_CLASS(drm_sched_job, TP_PROTO(struct drm_sched_job *sched_job, struct drm_sched_entity *entity), TP_STRUCT__entry( __string(name, sched_job->sched->name) __field(u32, job_count) __field(int , hw_job_count) __string(dev, dev_name(sched_job->sched->dev)) __field(u64, fence_context) __field(u64, fence_seqno) __field(u64, client_id) ), );
调度器层追踪的覆盖范围:
✅ 作业排队事件 (drm_sched_job_queue
)
✅ 作业运行事件 (drm_sched_job_run
)
✅ 作业完成事件 (drm_sched_job_done
)
✅ 依赖关系追踪 (drm_sched_job_add_dep
)
❌ 缺失Ring Buffer层面的细粒度追踪
2. AMD GPU厂商特定追踪
(drivers/gpu/drm/amd/amdgpu/)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 TRACE_EVENT(amdgpu_device_rreg, TP_PROTO(unsigned did, uint32_t reg, uint32_t value), ); TRACE_EVENT(amdgpu_device_wreg, TP_PROTO(unsigned did, uint32_t reg, uint32_t value), ); TRACE_EVENT(amdgpu_iv, TP_PROTO(unsigned ih, struct amdgpu_iv_entry *iv), ); TRACE_EVENT(amdgpu_cs, TP_PROTO(struct amdgpu_cs_parser *p, struct amdgpu_job *job, struct amdgpu_ib *ib), );
3. 通用GPU内存追踪
(include/trace/events/gpu_mem.h)
1 2 3 4 5 6 7 8 9 10 TRACE_EVENT(gpu_mem_total, TP_PROTO(uint32_t gpu_id, uint32_t pid, uint64_t size), TP_STRUCT__entry( __field(uint32_t , gpu_id) __field(uint32_t , pid) __field(uint64_t , size) ), );
4. 特定GPU厂商的专有追踪
AMD XDNA : amdxdna.h
-
AI加速器专用追踪
VC4 : vc4_trace.h
- Broadcom VideoCore
IV追踪
V3D : v3d_trace.h
- Broadcom
V3D追踪
Armada : armada_trace.h
- Marvell
Armada显示追踪
2.7.2
现有追踪机制的根本缺陷分析
架构分散性问题:
缺乏统一抽象 :
每个厂商、每个子系统都有自己的追踪实现
接口不一致 :
AMD使用amdgpu_trace.h
,Intel有自己的追踪,NVIDIA基本无开源追踪
语义不统一 :
相同的GPU操作在不同厂商的追踪中使用不同的事件名称和数据格式
覆盖盲区分析:
Ring Buffer层完全缺失 : 没有任何追踪机制涵盖Ring
Buffer操作
跨厂商不可比 :
无法在异构GPU环境中进行统一的性能分析
性能关键路径遗漏 :
高性能应用的直接硬件提交路径无法追踪
具体缺陷对比表:
抽象层次
调度器层
寄存器层
内存层
Ring Buffer层
厂商覆盖
通用
仅AMD
通用
跨厂商统一
事件粒度
作业级
寄存器级
分配级
命令级
性能关联
间接
底层细节
间接
直接映射
绕过检测
无法检测
部分检测
无法检测
完全检测
实时性
较差
较好
差
优秀
2.7.3 Ring
Buffer层追踪的空白证明
关键发现:缺失Ring Buffer统一追踪
通过详尽的内核源码分析,我们发现:
没有任何现有机制在Ring Buffer层进行统一追踪
AMD的追踪主要集中在寄存器和命令缓冲区层面,缺乏Ring
Buffer操作的系统性追踪
DRM调度器追踪过于高层,无法反映Ring
Buffer的实际状态
通用GPU内存追踪仅关注分配,不涉及执行性能
具体空白领域:
❌ Ring空间分配追踪 (ring_alloc
操作)
❌ Ring命令提交追踪 (ring_commit
操作)
❌ Ring硬件提交追踪 (ring_submit
操作)
❌ Ring状态监控 (wptr/rptr 位置变化)
❌ Ring利用率分析 (填充度、效率指标)
❌ 跨厂商Ring Buffer统一语义
这证实了grtrace项目的独特价值 :在一个完全空白的领域建立统一的GPU性能追踪标准。
3. 跨厂商Ring
Buffer实现的深度剖析
3.1 AMD GPU Ring
Buffer架构详解
3.1.1 核心数据结构分析
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 struct amdgpu_ring { struct amdgpu_device *adev ; const struct amdgpu_ring_funcs *funcs ; struct amdgpu_fence_driver fence_drv ; struct drm_gpu_scheduler sched ; struct amdgpu_bo *ring_obj ; volatile uint32_t *ring; uint64_t gpu_addr; uint64_t ptr_mask; uint32_t buf_mask; uint32_t wptr; uint32_t wptr_old; uint32_t rptr; uint32_t ring_size; uint32_t max_dw; int count_dw; uint64_t last_rptr; unsigned cond_exe_offs; uint64_t cond_exe_gpu_addr; volatile uint32_t *cond_exe_cpu_addr; unsigned ring_id; char name[64 ]; bool ready; uint32_t nop; uint32_t idx; uint64_t fence_offs; uint64_t wptr_offs; uint64_t rptr_offs; uint32_t trail_fence_offs; uint64_t trail_fence_gpu_addr; volatile uint32_t *trail_fence_cpu_addr; };
3.1.2 AMD Ring Buffer关键操作
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 int amdgpu_ring_alloc (struct amdgpu_ring *ring, unsigned ndw) { ndw = (ndw + ring->funcs->align_mask) & ~ring->funcs->align_mask; if (ndw > (ring->max_dw - ring->funcs->emit_cntx_switch(ring))) { return -ENOMEM; } ring->count_dw = ndw; ring->wptr_old = ring->wptr; return 0 ; }void amdgpu_ring_write (struct amdgpu_ring *ring, uint32_t v) { ring->ring[ring->wptr++ & ring->ptr_mask] = v; }void amdgpu_ring_commit (struct amdgpu_ring *ring) { uint32_t bytes = ring->count_dw * 4 ; amdgpu_ring_set_wptr(ring); if (ring->funcs->insert_end) ring->funcs->insert_end(ring); mb(); amdgpu_device_wb_set(ring->adev, ring->wptr_offs, lower_32_bits(ring->wptr)); ring->wptr_old = ring->wptr; ring->count_dw = 0 ; }void amdgpu_ring_undo (struct amdgpu_ring *ring) { ring->wptr = ring->wptr_old; ring->count_dw = 0 ; }
3.1.3 AMD Ring Buffer的硬件交互
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static inline void amdgpu_ring_set_wptr (struct amdgpu_ring *ring) { if (ring->funcs->set_wptr) { ring->funcs->set_wptr(ring); } else { if (ring->use_doorbell) { atomic64_set((atomic64_t *)&ring->adev->wb.wb[ring->wptr_offs], ring->wptr); WDOORBELL64(ring->doorbell_index, ring->wptr); } else { WREG32(ring->wptr_reg, lower_32_bits(ring->wptr)); } } }
3.2 Intel GPU Ring
Buffer架构详解
3.2.1 Intel Ring核心结构
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 struct intel_ring { struct intel_gt *gt ; struct intel_timeline *timeline ; struct drm_i915_gem_object *obj ; void *vaddr; u32 head; u32 tail; u32 emit; u32 space; u32 size; u32 effective_size; u32 wrap; struct intel_ring_submission { struct i915_request *curr ; struct list_head requests ; struct list_head active ; struct intel_timeline *timeline ; } submission; bool idle; struct intel_breadcrumbs *breadcrumbs ; u32 context_tag; struct intel_context *pinned_context ; };
3.2.2 Intel Ring Buffer操作流程
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 int intel_ring_begin (struct i915_request *rq, int num_dwords) { struct intel_ring *ring = rq->ring; int remain_actual = ring->size - ring->emit; int remain_usable = ring->effective_size - ring->emit; int bytes = num_dwords * sizeof (u32); if (unlikely(bytes > remain_usable)) { int ret = wait_for_space(ring, bytes); if (unlikely(ret)) return ret; } ring->space -= bytes; return 0 ; } u32 *intel_ring_emit (struct intel_ring *ring, int num_dwords) { u32 *cs = ring->vaddr + ring->emit; ring->emit += num_dwords * sizeof (u32); if (unlikely(ring->emit >= ring->size)) { ring->emit -= ring->size; ring->wrap++; } return cs; }void intel_ring_advance (struct intel_ring *ring, u32 *cs) { GEM_BUG_ON((ring->vaddr + ring->emit) != cs); intel_ring_update_space(ring); }void intel_engine_submit_request (struct i915_request *rq) { struct intel_engine_cs *engine = rq->engine; list_add_tail(&rq->sched.link, &engine->active.requests); intel_engine_queue_breadcrumbs(engine); }
3.3 NVIDIA GPU Ring
Buffer概念模型
虽然NVIDIA的开源驱动nouveau实现有限,但其基本概念与AMD/Intel一致:
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 struct nouveau_channel { struct nouveau_drm *drm ; struct nouveau_cli *cli ; struct nouveau_bo *push ; u32 *pushbuf; int pushbuf_size; u32 pushbuf_base; u32 dma_put; u32 dma_get; u32 dma_cur; u32 dma_max; struct nvif_object user ; struct nvif_object *object ; };static inline void nouveau_bo_wr32 (struct nouveau_bo *nvbo, unsigned index, u32 val) { nouveau_bo_map(nvbo); iowrite32_native(val, nvbo->kmap.virtual + index * 4 ); }
3.4 跨厂商Ring
Buffer抽象的可行性分析
3.4.1 通用操作模式识别
通过对三大厂商的代码分析,发现了高度一致的操作模式:
空间分配
amdgpu_ring_alloc
intel_ring_begin
nouveau_channel_prep
gpu_ring_alloc()
命令写入
amdgpu_ring_write
intel_ring_emit
nouveau_bo_wr32
gpu_ring_emit()
本地提交
amdgpu_ring_commit
intel_ring_advance
nouveau_channel_kick
gpu_ring_commit()
硬件提交
amdgpu_ring_set_wptr
intel_engine_submit
nouveau_fence_emit
gpu_ring_submit()
3.4.2 统一追踪接口设计
基于这种一致性,我们可以设计统一的追踪接口:
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 struct gpu_ring_tracer { enum gpu_vendor { GPU_VENDOR_AMD, GPU_VENDOR_INTEL, GPU_VENDOR_NVIDIA, } vendor; struct gpu_ring_ops { void (*trace_alloc)(void *ring_ctx, u32 size, u64 client_id); void (*trace_commit)(void *ring_ctx, u32 size, u64 client_id); void (*trace_submit)(void *ring_ctx, u64 client_id); void (*trace_wait)(void *ring_ctx, u64 timeout, u64 client_id); void (*trace_fence)(void *ring_ctx, u64 fence_id, u64 client_id); } ops; void *vendor_context; };int gpu_ring_tracer_register (struct gpu_ring_tracer *tracer) ;void gpu_ring_tracer_unregister (struct gpu_ring_tracer *tracer) ;static inline void gpu_trace_ring_alloc_unified (void *ring, u32 size, u64 client) { struct gpu_ring_tracer *tracer = gpu_get_ring_tracer(ring); if (tracer && tracer->ops.trace_alloc) tracer->ops.trace_alloc(ring, size, client); }
3.4.3 性能开销最小化策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #define GPU_TRACE_RING_ALLOC(ring, size, client) do { \ if (static_branch_unlikely(&gpu_trace_enabled)) { \ struct gpu_ring_trace *gt; \ rcu_read_lock(); \ gt = rcu_dereference(gpu_get_trace_ctx(ring)); \ if (gt) { \ __gpu_add_trace(gt, (u64)(ring), size, 0, \ GPU_TA_RING_ALLOC, 0, 0, NULL, \ client); \ } \ rcu_read_unlock(); \ } \ } while (0)
这种设计确保了:
零运行时开销 (追踪关闭时)
最小运行时开销 (追踪启用时)
跨厂商兼容性
易于扩展和维护
4.
grtrace架构设计:基于Ring Buffer的统一GPU追踪框架
4.1 设计理念与核心原则
4.1.1 借鉴blktrace的成功经验
grtrace的设计完全基于blktrace的成功模式,但针对GPU子系统的特殊性进行了优化:
4.1.2 架构设计对比
抽象层选择
Block Layer
Ring Buffer
最接近硬件的统一接口
数据传输
Relay Channel
Relay Channel
高性能零拷贝传输
事件模型
bio→request→completion
alloc→commit→submit
对应GPU命令生命周期
厂商支持
统一block接口
厂商适配层
GPU厂商异构性适配
性能优化
per-CPU sequence
per-CPU sequence + RCU
额外的RCU优化
4.2 grtrace核心架构实现
4.2.1 追踪框架核心结构
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 struct gpu_ring_trace { int trace_state; struct rchan *rchan ; unsigned long __percpu *sequence; unsigned int *msg_data; u16 act_mask; u64 start_lba; u64 end_lba; u32 pid; u32 dev_id; struct dentry *dir ; struct dentry *dropped_file ; struct dentry *msg_file ; struct list_head running_list ; atomic_t dropped; wait_queue_head_t drop_wait; int rq_state; };struct gpu_ring_io_trace { u32 magic; u32 sequence; u64 time; u32 device; u32 cpu; u32 action; u32 pid; u64 sector; u32 bytes; u32 error; u16 pdu_len; u64 client_id; } __attribute__((packed));
4.2.2 事件类型定义与语义
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 enum { GPU_TA_RING_ALLOC = 1 , GPU_TA_RING_COMMIT = 2 , GPU_TA_RING_SUBMIT = 3 , GPU_TA_QUEUE = 4 , GPU_TA_ISSUE = 5 , GPU_TA_COMPLETE = 6 , GPU_TA_FENCE_SIGNAL = 7 , GPU_TA_FENCE_WAIT = 8 , GPU_TA_CTX_SWITCH = 9 , GPU_TA_MEM_ALLOC = 10 , GPU_TA_MEM_FREE = 11 , GPU_TA_MEM_MAP = 12 , GPU_TA_MEM_UNMAP = 13 , GPU_TA_TIMEOUT = 14 , GPU_TA_RESET = 15 , GPU_TA_HANG = 16 , };enum { __GPU_TN_CLIENT = 1 << 0 , __GPU_TN_URGENT = 1 << 1 , __GPU_TN_ERROR = 1 << 2 , };
4.2.3 核心追踪函数实现
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 static void __gpu_add_trace(struct gpu_ring_trace *gt, u64 sector, int bytes, int rw, u32 what, int error, int pdu_len, void *pdu_data, u64 client_id) { struct task_struct *tsk = current; struct gpu_ring_io_trace *t ; unsigned long *sequence; pid_t pid; int cpu, pc = 0 ; if (unlikely(gt == NULL || !test_bit(GPU_R_RUNNING, >->rq_state))) return ; if (unlikely(act_log_check_gpu(gt, what, sector, current->pid))) return ; cpu = raw_smp_processor_id(); if (gt->trace_state & Grtrace_running) { sequence = per_cpu_ptr(gt->sequence, cpu); t = relay_reserve(gt->rchan, sizeof (*t) + pdu_len); if (unlikely(t == NULL )) { atomic_inc (>->dropped); return ; } t->magic = GPU_RING_IO_TRACE_MAGIC | GPU_RING_IO_TRACE_VERSION; t->sequence = ++(*sequence); t->time = ktime_to_ns(ktime_get()); t->device = gt->dev_id; t->cpu = cpu; t->error = error; t->pdu_len = pdu_len; if (gt->trace_state & Grtrace_stopped) pc = preempt_count(); t->action = what | (pc & 0xffff ) | (client_id ? __GPU_TN_CLIENT : 0 ); pid = tsk->pid; if (unlikely(tsk->grtrace_seq != grtrace_seq)) { trace_note_tsk_gpu(tsk); tsk->grtrace_seq = grtrace_seq; } t->pid = pid; t->sector = sector; t->bytes = bytes; memcpy ((void *) t + sizeof (*t), pdu_data, pdu_len); } }
4.3 Ring Buffer层追踪接口实现
4.3.1 AMD GPU集成
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 void gpu_add_trace_ring_alloc (struct amdgpu_ring *ring, u32 ndw, u64 client_id) { struct gpu_ring_trace *gt ; struct drm_device *dev = ring->adev->ddev; rcu_read_lock(); gt = rcu_dereference(dev->gpu_trace); if (likely(!gt)) { rcu_read_unlock(); return ; } __gpu_add_trace(gt, ring->gpu_addr, ndw * 4 , 0 , GPU_TA_RING_ALLOC, 0 , 0 , NULL , client_id); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(gpu_add_trace_ring_alloc);void gpu_add_trace_ring_commit (struct amdgpu_ring *ring, u32 ndw, u64 client_id) { struct gpu_ring_trace *gt ; struct drm_device *dev = ring->adev->ddev; rcu_read_lock(); gt = rcu_dereference(dev->gpu_trace); if (likely(!gt)) { rcu_read_unlock(); return ; } __gpu_add_trace(gt, ring->wptr, ndw * 4 , 0 , GPU_TA_RING_COMMIT, 0 , 0 , NULL , client_id); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(gpu_add_trace_ring_commit);void gpu_add_trace_ring_submit (struct amdgpu_ring *ring, u64 client_id) { struct gpu_ring_trace *gt ; struct drm_device *dev = ring->adev->ddev; rcu_read_lock(); gt = rcu_dereference(dev->gpu_trace); if (likely(!gt)) { rcu_read_unlock(); return ; } __gpu_add_trace(gt, ring->wptr, 0 , 0 , GPU_TA_RING_SUBMIT, 0 , 0 , NULL , client_id); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(gpu_add_trace_ring_submit);
4.3.2 跨厂商扩展接口
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 #define GPU_TRACE_RING_OPERATION(ring_ctx, operation, size, client) do { \ if (static_branch_unlikely(&gpu_trace_enabled_key)) { \ struct drm_device *dev = gpu_ring_to_drm_device(ring_ctx); \ struct gpu_ring_trace *gt; \ rcu_read_lock(); \ gt = rcu_dereference(dev->gpu_trace); \ if (gt) { \ u64 ring_addr = gpu_ring_get_address(ring_ctx); \ __gpu_add_trace(gt, ring_addr, size, 0, operation, \ 0, 0, NULL, client); \ } \ rcu_read_unlock(); \ } \ } while (0) static inline void gpu_add_trace_intel_ring_begin (struct intel_ring *ring, int num_dwords, u64 client_id) { GPU_TRACE_RING_OPERATION(ring, GPU_TA_RING_ALLOC, num_dwords * 4 , client_id); }static inline void gpu_add_trace_intel_ring_advance (struct intel_ring *ring, u64 client_id) { GPU_TRACE_RING_OPERATION(ring, GPU_TA_RING_COMMIT, 0 , client_id); }static inline void gpu_add_trace_nouveau_channel_kick (struct nouveau_channel *chan, u64 client_id) { GPU_TRACE_RING_OPERATION(chan, GPU_TA_RING_SUBMIT, 0 , client_id); }
4.4 补充追踪事件实现
4.4.1 同步和上下文追踪
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 46 47 48 49 50 51 void gpu_add_trace_ctx_switch (struct drm_device *dev, u32 old_ctx, u32 new_ctx, u64 client_id) { struct gpu_ring_trace *gt ; rcu_read_lock(); gt = rcu_dereference(dev->gpu_trace); if (likely(!gt)) { rcu_read_unlock(); return ; } __gpu_add_trace(gt, new_ctx, old_ctx, 0 , GPU_TA_CTX_SWITCH, 0 , 0 , NULL , client_id); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(gpu_add_trace_ctx_switch);void gpu_add_trace_fence_signal (struct dma_fence *fence, u64 client_id) { struct gpu_ring_trace *gt ; struct drm_device *dev = gpu_fence_to_drm_device(fence); if (!dev) return ; rcu_read_lock(); gt = rcu_dereference(dev->gpu_trace); if (likely(!gt)) { rcu_read_unlock(); return ; } __gpu_add_trace(gt, fence->seqno, 0 , 0 , GPU_TA_FENCE_SIGNAL, 0 , 0 , NULL , client_id); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(gpu_add_trace_fence_signal);
4.4.2 内存管理追踪
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 46 47 48 void gpu_add_trace_mem_alloc (struct drm_gem_object *obj, size_t size, u64 client_id) { struct gpu_ring_trace *gt ; struct drm_device *dev = obj->dev; rcu_read_lock(); gt = rcu_dereference(dev->gpu_trace); if (likely(!gt)) { rcu_read_unlock(); return ; } __gpu_add_trace(gt, (u64)obj, size, 0 , GPU_TA_MEM_ALLOC, 0 , 0 , NULL , client_id); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(gpu_add_trace_mem_alloc);void gpu_add_trace_mem_free (struct drm_gem_object *obj, u64 client_id) { struct gpu_ring_trace *gt ; struct drm_device *dev = obj->dev; rcu_read_lock(); gt = rcu_dereference(dev->gpu_trace); if (likely(!gt)) { rcu_read_unlock(); return ; } __gpu_add_trace(gt, (u64)obj, obj->size, 0 , GPU_TA_MEM_FREE, 0 , 0 , NULL , client_id); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(gpu_add_trace_mem_free);
4.5 用户空间接口设计
4.5.1 ioctl接口 (借鉴blktrace)
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 46 47 48 49 50 51 52 #define GRTRACE_SETUP _IOWR(0x12, 1, struct gpu_ring_trace_setup) #define GRTRACE_START _IO(0x12, 2) #define GRTRACE_STOP _IO(0x12, 3) #define GRTRACE_TEARDOWN _IO(0x12, 4) struct gpu_ring_trace_setup { char name[TASK_COMM_LEN]; u16 act_mask; u32 buf_size; u32 buf_nr; u64 start_lba; u64 end_lba; u32 pid; };int gpu_trace_ioctl (struct drm_device *dev, unsigned cmd, char __user *arg) { struct gpu_ring_trace *gt = NULL ; int ret, start = 0 ; char b[TASK_COMM_LEN + 1 ]; switch (cmd) { case GRTRACE_TEARDOWN: ret = gpu_trace_remove_queue(dev); break ; case GRTRACE_START: start = 1 ; fallthrough; case GRTRACE_STOP: ret = gpu_trace_startstop(dev, start); break ; case GRTRACE_SETUP: { struct gpu_ring_trace_setup buts ; ret = -EINVAL; if (copy_from_user(&buts, arg, sizeof (buts))) { ret = -EFAULT; break ; } get_task_comm(b, current); ret = gpu_trace_setup(dev, b, MKDEV(0 , 0 ), &buts); break ; } default : ret = -ENOTTY; break ; } return ret; }
4.5.2 debugfs接口
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 /sys/kernel/debug/grtrace/ ├── card0/ # GPU设备目录 │ ├── trace # 追踪数据文件 (relay channel) │ ├── dropped # 丢弃事件计数 │ └── msg # 用户消息接口 └── card1/ ├── trace ├── dropped └── msgstatic int gpu_trace_setup_debugfs (struct gpu_ring_trace *gt, const char *name) { struct dentry *dir ; dir = debugfs_create_dir(name, gpu_debugfs_root); if (IS_ERR_OR_NULL(dir)) return -ENOENT; gt->dir = dir; gt->rchan = relay_open("trace" , dir, gt->buf_size, gt->buf_nr, &gpu_relay_callbacks, gt); if (!gt->rchan) return -ENOMEM; gt->dropped_file = debugfs_create_file("dropped" , 0444 , dir, gt, &gpu_dropped_fops); gt->msg_file = debugfs_create_file("msg" , 0222 , dir, gt, &gpu_msg_fops); return 0 ; }
5. 性能优势分析
5.1 相比现有方案的优势
追踪层次
DRM scheduler层
Ring buffer层
更接近硬件
覆盖范围
仅scheduler任务
所有GPU命令
覆盖直接提交
性能影响
中等
极小
关键路径优化
跨厂商性
有限
完全支持
统一抽象
数据传输
ftrace buffer
relay channel
高效传输
5.2 性能监控能力
基于ring buffer的追踪能够实现:
GPU利用率分析 :
Ring buffer填充率
命令提交频率
硬件执行效率
性能瓶颈识别 :
Ring buffer溢出
命令队列积压
同步等待时间
资源使用优化 :
6. 实现挑战与解决方案
6.1 跨厂商适配挑战
挑战: 不同GPU厂商的ring buffer实现差异较大
解决方案: 设计通用wrapper接口
1 2 3 4 5 6 7 8 9 struct gpu_ring_ops { void (*trace_alloc)(void *ring, u32 size, u64 client_id); void (*trace_commit)(void *ring, u32 size, u64 client_id); void (*trace_submit)(void *ring, u64 client_id); };int gpu_register_ring_tracer (struct gpu_ring_ops *ops, int vendor_id) ;
6.2 性能开销控制
挑战: 在性能关键路径添加追踪代码
解决方案: 优化追踪路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static inline void gpu_trace_ring_submit_fast (struct amdgpu_ring *ring) { struct gpu_ring_trace *gt ; rcu_read_lock(); gt = rcu_dereference(ring->adev->ddev->gpu_trace); if (likely(!gt)) { rcu_read_unlock(); return ; } __gpu_add_trace_optimized(gt, ring); rcu_read_unlock(); }
7. 与blktrace的对比总结
抽象层选择
Block Layer
Ring Buffer
设计理念
统一存储IO入口
统一GPU命令入口
实现复杂度
中等
高(多厂商)
生态成熟度
完全成熟
新兴方案
性能影响
极小
极小(优化后)
应用价值
存储性能分析标准
GPU性能分析空白填补
8. 结论与展望
8.1 核心结论
Ring Buffer是GPU追踪的最佳抽象层 :
类似block layer在存储中的地位
兼具硬件接近性和跨厂商通用性
提供最全面的GPU性能监控能力
grtrace填补了重要空白 :
Linux内核缺乏统一的GPU追踪框架
现有方案过于高层或厂商特定
借鉴blktrace成功经验的正确选择
技术实现具备可行性 :
跨厂商ring buffer抽象可行
性能开销可控制在最小范围
与现有内核架构良好集成
8.2 发展前景
短期目标 :
完善AMD GPU支持
扩展Intel/NVIDIA支持
优化性能开销
中期目标 :
长期愿景 :
成为GPU性能分析标准
推动GPU子系统可观测性
支持AI/HPC工作负载优化
grtrace项目通过在ring
buffer层建立统一的GPU追踪框架,成功借鉴了blktrace在存储领域的成功经验,为GPU性能分析提供了强有力的工具支持。
报告作者 : Qiliang Yuan yuanql9@chinatelecom.cn
完成时间 : 2025年9月4日
版本 : v1.0