Buffer Sharing and Synchronization
本文的标题来自Linux Kernel 5.6.0-rc4文档, dma-buf作为一个内核子系统,它的使用场景不局限于drm “PRIME“ multi-GPU支持,它主要由3个组件支撑:
dma_buf
代表sg_table, 暴露给用户FDdma_fence
通知机制dma_resv
管理共享的或专有的fences
DMA-BUF
dma-buf是Linux内核中在上下文间,进程间,设备间,子系统间进行 buffer 共享的一种实现。 它十几年前就已经合入内核了。
1 | commit 3248877ea1796915419fba7c89315fdbf00cb56a |
- exporter调用
1 | /** |
- importer调用
1 | /** |
从上面两个函数的接口看,它们共同涉及3个数据对象:
- drm_device
- dma_buf fd
- drm_gem_object handle
显然两个函数里的dev
一定是不同的drm_device
, dma_buf
fd一定是同一个FD(也即同一个dma-buf, 要不然也不叫共享了),那么handle
呢?肯定也是不同的handle
, 因为handle
其实是对device
而言的,它是一个设备持有的drm_gem_object
的ID. 但这个ID背后的东西(backing storage)可能是同一个东西。
可以打个比方,你去银行要办两种业务,两种业务分别排号,假如你要办的A业务排到7号,B业务也刚好排到7号(注意:号码相同,但是两个号),但是很有可能是同一个业务员为你办理这两种业务,这里的业务就是设备驱动,而那个业务员就是dma-buf(或者它封装的那块显存)。
dma_fence
signed long dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout) { struct default_wait_cb cb; unsigned long flags; signed long ret = timeout ? timeout : 1;
spin_lock_irqsave(fence->lock, flags); if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) goto out; if (intr && signal_pending(current)) { ret = -ERESTARTSYS; goto out; } if (!timeout) { ret = 0; goto out; } cb.base.func = dma_fence_default_wait_cb; cb.task = current; list_add(&cb.base.node, &fence->cb_list); while (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags) && ret > 0) { if (intr) __set_current_state(TASK_INTERRUPTIBLE); else __set_current_state(TASK_UNINTERRUPTIBLE); spin_unlock_irqrestore(fence->lock, flags); ret = schedule_timeout(ret); spin_lock_irqsave(fence->lock, flags); if (ret > 0 && intr && signal_pending(current)) ret = -ERESTARTSYS; } if (!list_empty(&cb.base.node)) list_del(&cb.base.node); __set_current_state(TASK_RUNNING);
out:
spin_unlock_irqrestore(fence->lock, flags);
return ret;
}
fence->ops->wait();
dma_fence_default_wait
是 dma-fence 默认的 wait 操作。该函数会让当前进程(task) 进入睡眠状态 (可中断睡眠或不可中断睡眠,取决于调用者传入的参数 intr
), 直到 dma-fence 被 signaled 或者设置的超时时间到。
1 | cb.base.func = dma_fence_default_wait_cb; |
dma_resv
dma_buf, dma_fence, dma_resv, 还有drm_gem_object, 这4者有什么关系?
在这里面dma_fence是用来实现drm_gem_object间的共享和同步的,而dma_resv就是所谓的“胶水”,将这两者联系在一起。