欢迎光临
我们一直在努力

5.3.1 PRIME 的两座桥:GEM ↔ dma-buf 桥接回调与 fd/handle 转换

5.2 节 分析 了dma-buf 这套「跨设备缓冲区共享」的通用机制,但它始终停在 dma-buf 自己的抽象层。回到 DRM/GEM 的场景:用户态访问 BO 的接口是一个进程内的 GEM handle(一个 32 位整数),渲染时用 handle 引用 BO,提交命令时用 handle 索引。可 handle 只在本进程本设备有意义,一旦要把 BO 交给另一个进程、另一块 GPU,就必须换成能跨进程传递的 dma-buf fd。然后 fd 要变成importer 进程中的 handle 才能被用户态访问。

PRIME 就是 GEM 世界与 dma-buf 世界之间的双向桥:它把「进程私有的 handle」翻译成「可传递的 fd」(导出),又把别处传来的「fd」翻译回「本进程的 handle」(导入)。用户态只需两个 ioctl,内核则用一组桥接回调把两套对象模型的生命周期、引用计数、缓存一一对齐。

1. 两个 ioctl,两座桥

用户态的全部入口就是两个对称的 ioctl:

ioctl方向内核入口作用
DRM_IOCTL_PRIME_HANDLE_TO_FD 导出 drm_prime_handle_to_fd_ioctl → drm_gem_prime_handle_to_fd 本进程 handle → 可传递的 dma-buf fd
DRM_IOCTL_PRIME_FD_TO_HANDLE 导入 drm_prime_fd_to_handle_ioctl → drm_gem_prime_fd_to_handle 别处传来的 fd → 本进程 handle

两个 ioctl 都留了「驱动可覆盖」的回调:若 dev->driver->prime_handle_to_fd / prime_fd_to_handle 非空则用驱动自己的,否则用 DRM 核心的默认实现。绝大多数驱动直接用默认的。

#mermaid-svg-KFoGBmistHtVvLoy{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KFoGBmistHtVvLoy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KFoGBmistHtVvLoy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KFoGBmistHtVvLoy .error-icon{fill:#552222;}#mermaid-svg-KFoGBmistHtVvLoy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KFoGBmistHtVvLoy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KFoGBmistHtVvLoy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KFoGBmistHtVvLoy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KFoGBmistHtVvLoy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KFoGBmistHtVvLoy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KFoGBmistHtVvLoy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KFoGBmistHtVvLoy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KFoGBmistHtVvLoy .marker.cross{stroke:#333333;}#mermaid-svg-KFoGBmistHtVvLoy svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KFoGBmistHtVvLoy p{margin:0;}#mermaid-svg-KFoGBmistHtVvLoy .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-KFoGBmistHtVvLoy .cluster-label text{fill:#333;}#mermaid-svg-KFoGBmistHtVvLoy .cluster-label span{color:#333;}#mermaid-svg-KFoGBmistHtVvLoy .cluster-label span p{background-color:transparent;}#mermaid-svg-KFoGBmistHtVvLoy .label text,#mermaid-svg-KFoGBmistHtVvLoy span{fill:#333;color:#333;}#mermaid-svg-KFoGBmistHtVvLoy .node rect,#mermaid-svg-KFoGBmistHtVvLoy .node circle,#mermaid-svg-KFoGBmistHtVvLoy .node ellipse,#mermaid-svg-KFoGBmistHtVvLoy .node polygon,#mermaid-svg-KFoGBmistHtVvLoy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KFoGBmistHtVvLoy .rough-node .label text,#mermaid-svg-KFoGBmistHtVvLoy .node .label text,#mermaid-svg-KFoGBmistHtVvLoy .image-shape .label,#mermaid-svg-KFoGBmistHtVvLoy .icon-shape .label{text-anchor:middle;}#mermaid-svg-KFoGBmistHtVvLoy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KFoGBmistHtVvLoy .rough-node .label,#mermaid-svg-KFoGBmistHtVvLoy .node .label,#mermaid-svg-KFoGBmistHtVvLoy .image-shape .label,#mermaid-svg-KFoGBmistHtVvLoy .icon-shape .label{text-align:center;}#mermaid-svg-KFoGBmistHtVvLoy .node.clickable{cursor:pointer;}#mermaid-svg-KFoGBmistHtVvLoy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KFoGBmistHtVvLoy .arrowheadPath{fill:#333333;}#mermaid-svg-KFoGBmistHtVvLoy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KFoGBmistHtVvLoy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KFoGBmistHtVvLoy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KFoGBmistHtVvLoy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KFoGBmistHtVvLoy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KFoGBmistHtVvLoy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KFoGBmistHtVvLoy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KFoGBmistHtVvLoy .cluster text{fill:#333;}#mermaid-svg-KFoGBmistHtVvLoy .cluster span{color:#333;}#mermaid-svg-KFoGBmistHtVvLoy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KFoGBmistHtVvLoy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KFoGBmistHtVvLoy rect.text{fill:none;stroke-width:0;}#mermaid-svg-KFoGBmistHtVvLoy .icon-shape,#mermaid-svg-KFoGBmistHtVvLoy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KFoGBmistHtVvLoy .icon-shape p,#mermaid-svg-KFoGBmistHtVvLoy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KFoGBmistHtVvLoy .icon-shape .label rect,#mermaid-svg-KFoGBmistHtVvLoy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KFoGBmistHtVvLoy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KFoGBmistHtVvLoy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KFoGBmistHtVvLoy :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

进程 B / GPU-1

进程 A / GPU-0

HANDLE_TO_FD

SCM_RIGHTS 传递 fd

FD_TO_HANDLE

GEM handle

dma-buf fd

dma-buf fd

GEM handle

同一个 struct dma_buf同一块后端存储

2. 导出桥:handle → fd

drm_gem_prime_handle_to_fd() 本身只做「申请 fd → 拿 dma_buf → 安装 fd」三步:

int drm_gem_prime_handle_to_fd(struct drm_device *dev, struct drm_file *file_priv,
uint32_t handle, uint32_t flags, int *prime_fd)
{
struct dma_buf *dmabuf;
int fd = get_unused_fd_flags(flags); /* 1. 预留一个空 fd */
...
dmabuf = drm_gem_prime_handle_to_dmabuf(dev, file_priv, handle, flags); /* 2. handle→dma_buf */
...
fd_install(fd, dmabuf->file); /* 3. 把 dma_buf 的 file 装到 fd 上 */
*prime_fd = fd;
return 0;
}

真正有讲究的是第 2 步 drm_gem_prime_handle_to_dmabuf(),它按三种情况决定 dma_buf 从哪来:

/* 情况一:这个 GEM 对象本身就是从别处导入来的 → 复用原始 dma-buf */
if (obj->import_attach) {
dmabuf = obj->import_attach->dmabuf;
get_dma_buf(dmabuf);
goto out_have_obj;
}

/* 情况二:之前已经导出过 → 复用缓存的 dma_buf(不重复创建) */
if (obj->dma_buf) {
get_dma_buf(obj->dma_buf);
dmabuf = obj->dma_buf;
goto out_have_obj;
}

/* 情况三:第一次导出 → 真正创建并注册 dma_buf */
dmabuf = export_and_register_object(dev, obj, flags);

这三个分支保证了一条关键不变量——同一个 GEM 对象在其生命周期内只对应唯一一个 dma_buf:

  • 情况一(import_attach 非空):这块 BO 是导入来的「影子对象」,导出它时不能再包一层,而要把最初的原始 dma-buf 原样交出去——否则会出现「dma-buf 套 GEM 套 dma-buf」的荒谬嵌套。
  • 情况二(obj->dma_buf 已缓存):曾经导出过,直接复用并 get_dma_buf 加引用,避免同一 BO 产生两个互不相干的 dma_buf。
  • 情况三:首次导出才走 export_and_register_object,内部最终调 obj->funcs->export(默认是 drm_gem_prime_export)真正创建 dma_buf,并把它缓存进 obj->dma_buf。

drm_gem_prime_export() 就是那个「真正创建」的默认实现,它把 GEM 对象填进标准的 dma_buf_export_info:

struct dma_buf *drm_gem_prime_export(struct drm_gem_object *obj, int flags)
{
struct dma_buf_export_info exp_info = {
.exp_name = KBUILD_MODNAME,
.ops = &drm_gem_prime_dmabuf_ops, /* GEM 专用的 dma_buf_ops */
.size = obj->size,
.flags = flags,
.priv = obj, /* dma_buf->priv 指回 GEM 对象 */
.resv = obj->resv, /* 共用同一个 reservation object */
};
return drm_gem_dmabuf_export(dev, &exp_info);
}

两个字段值得记住:priv = obj 建立了「dma_buf → GEM 对象」的反向指针(前几节 amdgpu 里 gem_to_amdgpu_bo(dma_buf->priv) 就靠它);resv = obj->resv 让 dma-buf 与 GEM 对象共享同一把同步锁与 fence 容器,这是隐式同步(6.3)能跨设备生效的根基。

ops = &drm_gem_prime_dmabuf_ops 则是这座桥的另一半——它把 dma-buf 的通用回调转接到 GEM 的 drm_gem_object_funcs:

dma_buf_ops 回调GEM 桥接实现最终委派到
attach drm_gem_map_attach obj->funcs->pin
map_dma_buf drm_gem_map_dma_buf obj->funcs->get_sg_table
unmap_dma_buf drm_gem_unmap_dma_buf ——
release drm_gem_dmabuf_release obj 引用释放
mmap drm_gem_dmabuf_mmap obj->funcs->mmap
vmap / vunmap drm_gem_dmabuf_vmap obj->funcs->vmap

dma-buf core 通过 drm_gem_prime_dmabuf_ops 敲门,最终都落到某个 drm_gem_object_funcs 上。这也解释了 5.2.4 里 amdgpu 的 map_dma_buf 为何最终会走到 GEM 的 get_sg_table——中间正是这张转接表在牵线。若驱动的 get_sg_table 未实现,drm_gem_map_attach 会直接返回 -ENOSYS,即拒绝导出到别的设备。

3. 导入桥:fd → handle

反方向的 drm_gem_prime_fd_to_handle() 要把一枚陌生的 fd 变成本进程的 handle:

int drm_gem_prime_fd_to_handle(struct drm_device *dev, struct drm_file *file_priv,
int prime_fd, uint32_t *handle)
{
struct dma_buf *dma_buf = dma_buf_get(prime_fd); /* fd → struct dma_buf */
...
/* 1. 查本进程的导入缓存:这个 dma_buf 之前导入过吗? */
ret = drm_prime_lookup_buf_handle(&file_priv->prime, dma_buf, handle);
if (ret == 0)
goto out_put; /* 命中 → 复用旧 handle */

/* 2. 没见过 → 调驱动的 import 回调,造一个 GEM 对象 */
if (dev->driver->gem_prime_import)
obj = dev->driver->gem_prime_import(dev, dma_buf);
else
obj = drm_gem_prime_import(dev, dma_buf);
...
/* 3. 建立 GEM 对象 ↔ dma_buf 的双向引用 */
if (!obj->dma_buf) {
obj->dma_buf = dma_buf;
get_dma_buf(dma_buf);
}

/* 4. 为这个 GEM 对象分配本进程 handle,并写入缓存 */
ret = drm_gem_handle_create_tail(file_priv, obj, handle);
ret = drm_prime_add_buf_handle(&file_priv->prime, dma_buf, *handle);
...
}

关键仍是缓存 + 唯一性:file_priv->prime 是每个 DRM file 私有的双向缓存(dma_buf ↔ handle)。同一个 dma_buf 在同一进程被导入两次,第 1 步就会命中缓存返回同一个 handle——保证「一块共享 buffer 在一个进程里只有一个 handle」。

第 2 步的 drm_gem_prime_import() 转调核心的 drm_gem_prime_import_dev(),它才是真正「把 dma_buf 变成 GEM 对象」的地方,其中藏着一个重要优化:

struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev,
struct dma_buf *dma_buf, struct device *attach_dev)
{
/* 自导入优化:如果这个 dma_buf 正是本 dev 自己导出的,
* 不必 attach,直接给原 GEM 对象加引用即可 */

if (drm_gem_is_prime_exported_dma_buf(dev, dma_buf)) {
obj = dma_buf->priv;
drm_gem_object_get(obj);
return obj;
}

if (!dev->driver->gem_prime_import_sg_table)
return ERR_PTR(EINVAL);

/* 正常路径:attach → map 拿 sg_table → 让驱动据此造 GEM 对象 */
attach = dma_buf_attach(dma_buf, attach_dev);
get_dma_buf(dma_buf);
sgt = dma_buf_map_attachment_unlocked(attach, DMA_BIDIRECTIONAL);
obj = dev->driver->gem_prime_import_sg_table(dev, attach, sgt); /* 驱动回调 */

obj->import_attach = attach; /* 记住「我是导入来的」及其 attachment */
obj->resv = dma_buf->resv; /* 共用源 dma-buf 的 resv → 同步能跨设备生效 */
return obj;
}

三个要点串起了前几节:

  • 自导入优化(drm_gem_is_prime_exported_dma_buf):把自己导出的 dma-buf 再导入回来,只增加 GEM 对象引用、不增加 dma-buf 的 f_count——避免自己 attach 自己的病态循环。这与第 2 节导出侧「情况一」互为镜像。
  • attach + map 复用 5.2.4 的机制:导入桥并不自己发明轮子,而是老老实实走 dma_buf_attach → dma_buf_map_attachment 拿到 sg_table,再交给驱动的 gem_prime_import_sg_table 据此构造一个「影子 GEM 对象」。
  • import_attach + 共用 resv:obj->import_attach 标记这是导入对象(第 2 节导出侧据此复用原始 dma-buf);obj->resv = dma_buf->resv 让影子对象与源 buffer 共享 fence 容器——这是跨设备隐式同步(6.3)成立的前提。驱动还须在其 free 回调里调 drm_prime_gem_destroy() 收尾 attach。
  • 4. 一张全景图:两套对象模型如何对齐

    把导出与导入合起来看,PRIME 的本质是维护「GEM 对象 ↔ dma_buf」的一一对应与引用计数:

    #mermaid-svg-hUdqGAY787DTr4qE{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hUdqGAY787DTr4qE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hUdqGAY787DTr4qE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hUdqGAY787DTr4qE .error-icon{fill:#552222;}#mermaid-svg-hUdqGAY787DTr4qE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hUdqGAY787DTr4qE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hUdqGAY787DTr4qE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hUdqGAY787DTr4qE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hUdqGAY787DTr4qE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hUdqGAY787DTr4qE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hUdqGAY787DTr4qE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hUdqGAY787DTr4qE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hUdqGAY787DTr4qE .marker.cross{stroke:#333333;}#mermaid-svg-hUdqGAY787DTr4qE svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hUdqGAY787DTr4qE p{margin:0;}#mermaid-svg-hUdqGAY787DTr4qE .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-hUdqGAY787DTr4qE .cluster-label text{fill:#333;}#mermaid-svg-hUdqGAY787DTr4qE .cluster-label span{color:#333;}#mermaid-svg-hUdqGAY787DTr4qE .cluster-label span p{background-color:transparent;}#mermaid-svg-hUdqGAY787DTr4qE .label text,#mermaid-svg-hUdqGAY787DTr4qE span{fill:#333;color:#333;}#mermaid-svg-hUdqGAY787DTr4qE .node rect,#mermaid-svg-hUdqGAY787DTr4qE .node circle,#mermaid-svg-hUdqGAY787DTr4qE .node ellipse,#mermaid-svg-hUdqGAY787DTr4qE .node polygon,#mermaid-svg-hUdqGAY787DTr4qE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hUdqGAY787DTr4qE .rough-node .label text,#mermaid-svg-hUdqGAY787DTr4qE .node .label text,#mermaid-svg-hUdqGAY787DTr4qE .image-shape .label,#mermaid-svg-hUdqGAY787DTr4qE .icon-shape .label{text-anchor:middle;}#mermaid-svg-hUdqGAY787DTr4qE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hUdqGAY787DTr4qE .rough-node .label,#mermaid-svg-hUdqGAY787DTr4qE .node .label,#mermaid-svg-hUdqGAY787DTr4qE .image-shape .label,#mermaid-svg-hUdqGAY787DTr4qE .icon-shape .label{text-align:center;}#mermaid-svg-hUdqGAY787DTr4qE .node.clickable{cursor:pointer;}#mermaid-svg-hUdqGAY787DTr4qE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hUdqGAY787DTr4qE .arrowheadPath{fill:#333333;}#mermaid-svg-hUdqGAY787DTr4qE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hUdqGAY787DTr4qE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hUdqGAY787DTr4qE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hUdqGAY787DTr4qE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hUdqGAY787DTr4qE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hUdqGAY787DTr4qE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hUdqGAY787DTr4qE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hUdqGAY787DTr4qE .cluster text{fill:#333;}#mermaid-svg-hUdqGAY787DTr4qE .cluster span{color:#333;}#mermaid-svg-hUdqGAY787DTr4qE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hUdqGAY787DTr4qE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hUdqGAY787DTr4qE rect.text{fill:none;stroke-width:0;}#mermaid-svg-hUdqGAY787DTr4qE .icon-shape,#mermaid-svg-hUdqGAY787DTr4qE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hUdqGAY787DTr4qE .icon-shape p,#mermaid-svg-hUdqGAY787DTr4qE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hUdqGAY787DTr4qE .icon-shape .label rect,#mermaid-svg-hUdqGAY787DTr4qE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hUdqGAY787DTr4qE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hUdqGAY787DTr4qE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hUdqGAY787DTr4qE :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    dma-buf 世界

    GEM 世界

    export: funcs->export / drm_gem_prime_export

    import: gem_prime_import_sg_table

    handle ↔ dma_buf 缓存(file_priv->prime)

    fd ↔ handle

    drm_gem_objectobj->dma_buf(缓存)obj->import_attach(若导入)obj->resv

    struct dma_bufdma_buf->priv = objdma_buf->ops = drm_gem_prime_dmabuf_opsdma_buf->resv = obj->resv

    进程内 handle

    跨进程 fd

    • priv / dma_buf 互指:dma_buf 用 priv 指回 GEM 对象,GEM 对象用 dma_buf 缓存反向引用,二者一一对应。
    • resv 共用:导出时 dma_buf->resv = obj->resv,导入时 obj->resv = dma_buf->resv,最终跨设备的两个 GEM 影子对象共享同一个 dma_resv——同步语义得以贯通。
    • 两级缓存:file_priv->prime 管「fd/dma_buf ↔ handle」,obj->dma_buf 管「GEM 对象 ↔ dma_buf」,共同保证任何一块共享 buffer 在任一进程、任一方向都不会分裂出多个副本。

    5. 小结与延伸

  • PRIME 是 GEM 与 dma-buf 之间的双向桥:HANDLE_TO_FD 导出、FD_TO_HANDLE 导入,把「进程私有 handle」与「可传递 fd」互相翻译。
  • drm_gem_prime_dmabuf_ops 是转接表:dma-buf 的通用回调(attach/map/mmap/vmap)经它统一落到 GEM 的 drm_gem_object_funcs,map_dma_buf 最终委派 get_sg_table——这正是 5.2.4 amdgpu 那条回调链的中枢。
  • 唯一性与引用计数是灵魂:obj->dma_buf 缓存、import_attach 复用、file_priv->prime 双向缓存、自导入优化、共用 resv,五条机制共同保证「一块 buffer 在一个进程里只有一个 handle、一个 GEM 对象只对应一个 dma_buf」。
  • 到这里,PRIME 的核心桥接已经清晰,但还有一个实际问题:gem_prime_import_sg_table、get_sg_table、pin/unpin 这些回调,普通驱动要不要每个都自己写一遍?对大量基于系统内存的简单驱动,DRM 提供了开箱即用的 GEM shmem helper,把这套共享路径全部实现好了。这正是 [5.3.2 GEM shmem helper 的共享路径] 的主题。

    相关阅读:dma-buf 的 attach/map 机制见 5.2.4;get_sg_table / pin / unpin 等 GEM 回调的语义见 gem_object_funcs.md;共用 resv 带来的隐式同步见 6.3 dma_resv。

    赞(0)
    未经允许不得转载:171主机测评 » 5.3.1 PRIME 的两座桥:GEM ↔ dma-buf 桥接回调与 fd/handle 转换
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址