ROCm rocr-libhsakmt分析系列8-1: doorbell机制概览 📅 发布时间:2026/9/4 12:41:40 👁 浏览次数: 1. doorbell概念在 AMD GPU如基于 GCN 或 RDNA 架构的显卡中doorbell门铃机制是一种轻量级的异步通知技术用于高效实现CPU 到GPU 之间的通知。其核心思想是通过特定寄存器操作触发硬件响应避免传统轮询或中断带来的高开销。Doorbell 的本质是一种单向事件通知机制类似现实中的门铃 —— 当主机CPU向 GPU 发送任务后然后通过写入门铃寄存器Doorbell Register“按下按钮”GPU 检测到该操作后自动处理新任务。这种机制广泛应用于高性能计算场景如命令队列提交、内存同步等。关键特点低延迟通过直接操作寄存器而非复杂的中断流程显著降低通信延迟。高吞吐量支持批量任务提交例如一次门铃操作可触发多个命令处理。资源高效无需 CPU 持续轮询或频繁中断节省系统资源。异步性CPU 和 GPU 可并行工作提升整体效率。应用场景命令队列管理主机将 GPU 任务写入环形缓冲区后通过门铃通知 GPU 处理。内存同步当 CPU 更新共享内存时触发门铃告知 GPU 数据可用。多引擎协作协调 GPU 内部不同处理单元如计算引擎、显示引擎的工作。2.doorbell原理Doorbell 机制的实现涉及硬件寄存器、内存映射和异步通信协议。用户空间程序 GPU 硬件 │ │ │ 写入命令到队列 │ │ │ v │ ┌─────────────┐ │ │ Ring Buffer │ │ └─────────────┘ │ │ │ │ 写入 Doorbell ─────────────┼── 触发中断/轮询 │ │ v v ┌─────────────┐ ┌─────────────┐ │ Doorbell │ 映射到 │ GPU 读取 │ │ (用户可写) │ ──────── │ Doorbell │ └─────────────┘ PCIe BAR └─────────────┘ │ v 处理队列中的命令2.1. 硬件基础BAR内存映射内存映射Doorbell 是一个映射到 GPU BARBase Address Register空间的内存区域用户空间可以直接写入GPU 硬件可以直接读取。2.2. 软件流程触发与响应主机端CPU填充命令队列将 GPU 任务如着色器指令、内存操作写入共享内存的环形缓冲区。更新生产者指针wptr记录当前写入位置。触发门铃通过写入门铃寄存器通知 GPU写入值通常包含队列编号和指针信息。设备端GPU检测门铃事件GPU 通过硬件逻辑监测门铃寄存器变化。读取命令队列根据生产者指针从环形缓冲区获取任务并更新消费者指针rptr。执行任务GPU 处理单元如 CU、ACE解析命令并执行计算或渲染操作。完成通知任务完成后GPU 通过硬件中断告知主机。2.3. 硬件实现寄存器操作与协议细节PCIe 事务主机通过 PCIe Memory Write TLP事务层包写入门铃寄存器GPU 作为 PCIe 端点接收该请求。3. doorbell的实现分析3.1 doorbell的初始化下面是struct amdgpu_doorbell的定义。amd的kgd和kfd是一套驱动所以doorbell资源是共用的这个从struct amdgpu_doorbell的定义也可以看出。变量num_kernel_doorbells表示kgd使用的数量剩下的就是给kfd的。/* * GPU doorbell structures, functions helpers */ struct amdgpu_doorbell { /* doorbell mmio */ resource_size_t base; resource_size_t size; /* Number of doorbells reserved for amdgpu kernel driver */ u32 num_kernel_doorbells; /* Kernel doorbells */ struct amdgpu_bo *kernel_doorbells; /* For CPU access of doorbells */ uint32_t *cpu_addr; };接下来看下是如何初始化内部值的。由下面base和size的赋值代码可以看出doorbell的地址就是该GPU BAR2的空间。那么doorbell可用的空间大小请各位看官用lspci命令去查下吧。cpu_addr和kernel_doorbells是通过调用amdgpu_bo_create_kernel初始化的这个过程是amdgpu_bo的标准创建流程。如果不清楚这个流程的请移步amdgpu_bo的设计。在这里你只要关注AMDGPU_GEM_DOMAIN_DOORBELL这个参数的作用是告诉创建者在这个区域里分配空间。/* doorbell bar mapping */ adev-doorbell.base pci_resource_start(adev-pdev, 2); adev-doorbell.size pci_resource_len(adev-pdev, 2); adev-doorbell.num_kernel_doorbells min(u32, adev-doorbell.size / sizeof(u32), adev-doorbell_index.max_assignment 1); //kernel_doorbells和cpu_addr的初始化 amdgpu_bo_create_kernel(adev, size, PAGE_SIZE, AMDGPU_GEM_DOMAIN_DOORBELL, adev-doorbell.kernel_doorbells, NULL, (void **)adev-doorbell.cpu_addr); //amdgpu_bo_create_kernel的调用栈 amdgpu_bo_create_kernel amdgpu_bo_create_reserved amdgpu_bo_create(adev, bo) amdgpu_bo_pin(bo) amdgpu_ttm_alloc_gart amdgpu_bo_kmap(*bo_ptr, cpu_addr);3.2 doorbell的读写有了doorbell空间后就可以进行读写了。下面的代码仅给出了amdgpu_mm_rdoorbell的代码实现实际上就一句核心调用readl(adev-doorbell.cpu_addr index)。其他的自己翻代码吧。u32 amdgpu_mm_rdoorbell(struct amdgpu_device *adev, u32 index); void amdgpu_mm_wdoorbell(struct amdgpu_device *adev, u32 index, u32 v); u64 amdgpu_mm_rdoorbell64(struct amdgpu_device *adev, u32 index); void amdgpu_mm_wdoorbell64(struct amdgpu_device *adev, u32 index, u64 v); /** * amdgpu_mm_rdoorbell - read a doorbell dword * * adev: amdgpu_device pointer * index: doorbell index * * Returns the value in the doorbell aperture at the * requested doorbell index (CIK). */ u32 amdgpu_mm_rdoorbell(struct amdgpu_device *adev, u32 index) { if (amdgpu_device_skip_hw_access(adev)) return 0; if (index adev-doorbell.num_kernel_doorbells) return readl(adev-doorbell.cpu_addr index); DRM_ERROR(reading beyond doorbell aperture: 0x%08x!\n, index); return 0; }3.3 doordell销毁void amdgpu_doorbell_fini(struct amdgpu_device *adev);本篇对doorbell涉及的技术以及doorbell的使用方法和功能做了简要的概述后续具体分析KFD里的实现。