struct dma_buf_list {
struct list_head head;
struct mutex lock;
};
用户自定义ops
/**
* struct dma_buf_ops - operations possible on struct dma_buf
* @vmap: [optional] creates a virtual mapping for the buffer into kernel
* address space. Same restrictions as for vmap and friends apply.
* @vunmap: [optional] unmaps a vmap from the buffer
*/
struct dma_buf_ops {
/**
* @cache_sgt_mapping:
*
* If true the framework will cache the first mapping made for each
* attachment. This avoids creating mappings for attachments multiple
* times.
*/
bool cache_sgt_mapping;
/**
* @attach:
*
* This is called from dma_buf_attach() to make sure that a given
* &dma_buf_attachment.dev can access the provided &dma_buf. Exporters
* which support buffer objects in special locations like VRAM or
* device-specific carveout areas should check whether the buffer could
* be move to system memory (or directly accessed by the provided
* device), and otherwise need to fail the attach operation.
*
* The exporter should also in general check whether the current
* allocation fulfills the DMA constraints of the new device. If this
* is not the case, and the allocation cannot be moved, it should also
* fail the attach operation.
*
* Any exporter-private housekeeping data can be stored in the
* &dma_buf_attachment.priv pointer.
*
* This callback is optional.
*
* Returns:
*
* 0 on success, negative error code on failure. It might return -EBUSY
* to signal that backing storage is already allocated and incompatible
* with the requirements of requesting device.
*/
int (*attach)(struct dma_buf *, struct dma_buf_attachment *);
/**
* @detach:
*
* This is called by dma_buf_detach() to release a &dma_buf_attachment.
* Provided so that exporters can clean up any housekeeping for an
* &dma_buf_attachment.
*
* This callback is optional.
*/
void (*detach)(struct dma_buf *, struct dma_buf_attachment *);
/**
* @pin:
*
* This is called by dma_buf_pin() and lets the exporter know that the
* DMA-buf can't be moved any more. Ideally, the exporter should
* pin the buffer so that it is generally accessible by all
* devices.
*
* This is called with the &dmabuf.resv object locked and is mutual
* exclusive with @cache_sgt_mapping.
*
* This is called automatically for non-dynamic importers from
* dma_buf_attach().
*
* Note that similar to non-dynamic exporters in their @map_dma_buf
* callback the driver must guarantee that the memory is available for
* use and cleared of any old data by the time this function returns.
* Drivers which pipeline their buffer moves internally must wait for
* all moves and clears to complete.
*
* Returns:
*
* 0 on success, negative error code on failure.
*/
int (*pin)(struct dma_buf_attachment *attach);
/**
* @unpin:
*
* This is called by dma_buf_unpin() and lets the exporter know that the
* DMA-buf can be moved again.
*
* This is called with the dmabuf->resv object locked and is mutual
* exclusive with @cache_sgt_mapping.
*
* This callback is optional.
*/
void (*unpin)(struct dma_buf_attachment *attach);
/**
* @map_dma_buf:
*
* This is called by dma_buf_map_attachment() and is used to map a
* shared &dma_buf into device address space, and it is mandatory. It
* can only be called if @attach has been called successfully.
*
* This call may sleep, e.g. when the backing storage first needs to be
* allocated, or moved to a location suitable for all currently attached
* devices.
*
* Note that any specific buffer attributes required for this function
* should get added to device_dma_parameters accessible via
* &device.dma_params from the &dma_buf_attachment. The @attach callback
* should also check these constraints.
*
* If this is being called for the first time, the exporter can now
* choose to scan through the list of attachments for this buffer,
* collate the requirements of the attached devices, and choose an
* appropriate backing storage for the buffer.
*
* Based on enum dma_data_direction, it might be possible to have
* multiple users accessing at the same time (for reading, maybe), or
* any other kind of sharing that the exporter might wish to make
* available to buffer-users.
*
* This is always called with the dmabuf->resv object locked when
* the dynamic_mapping flag is true.
*
* Note that for non-dynamic exporters the driver must guarantee that
* that the memory is available for use and cleared of any old data by
* the time this function returns. Drivers which pipeline their buffer
* moves internally must wait for all moves and clears to complete.
* Dynamic exporters do not need to follow this rule: For non-dynamic
* importers the buffer is already pinned through @pin, which has the
* same requirements. Dynamic importers otoh are required to obey the
* dma_resv fences.
*
* Returns:
*
* A &sg_table scatter list of the backing storage of the DMA buffer,
* already mapped into the device address space of the &device attached
* with the provided &dma_buf_attachment. The addresses and lengths in
* the scatter list are PAGE_SIZE aligned.
*
* On failure, returns a negative error value wrapped into a pointer.
* May also return -EINTR when a signal was received while being
* blocked.
*
* Note that exporters should not try to cache the scatter list, or
* return the same one for multiple calls. Caching is done either by the
* DMA-BUF code (for non-dynamic importers) or the importer. Ownership
* of the scatter list is transferred to the caller, and returned by
* @unmap_dma_buf.
*/
struct sg_table * (*map_dma_buf)(struct dma_buf_attachment *,
enum dma_data_direction);
/**
* @unmap_dma_buf:
*
* This is called by dma_buf_unmap_attachment() and should unmap and
* release the &sg_table allocated in @map_dma_buf, and it is mandatory.
* For static dma_buf handling this might also unpin the backing
* storage if this is the last mapping of the DMA buffer.
*/
void (*unmap_dma_buf)(struct dma_buf_attachment *,
struct sg_table *,
enum dma_data_direction);
/* TODO: Add try_map_dma_buf version, to return immed with -EBUSY
* if the call would block.
*/
/**
* @release:
*
* Called after the last dma_buf_put to release the &dma_buf, and
* mandatory.
*/
void (*release)(struct dma_buf *);
/**
* @begin_cpu_access:
*
* This is called from dma_buf_begin_cpu_access() and allows the
* exporter to ensure that the memory is actually coherent for cpu
* access. The exporter also needs to ensure that cpu access is coherent
* for the access direction. The direction can be used by the exporter
* to optimize the cache flushing, i.e. access with a different
* direction (read instead of write) might return stale or even bogus
* data (e.g. when the exporter needs to copy the data to temporary
* storage).
*
* Note that this is both called through the DMA_BUF_IOCTL_SYNC IOCTL
* command for userspace mappings established through @mmap, and also
* for kernel mappings established with @vmap.
*
* This callback is optional.
*
* Returns:
*
* 0 on success or a negative error code on failure. This can for
* example fail when the backing storage can't be allocated. Can also
* return -ERESTARTSYS or -EINTR when the call has been interrupted and
* needs to be restarted.
*/
int (*begin_cpu_access)(struct dma_buf *, enum dma_data_direction);
/**
* @end_cpu_access:
*
* This is called from dma_buf_end_cpu_access() when the importer is
* done accessing the CPU. The exporter can use this to flush caches and
* undo anything else done in @begin_cpu_access.
*
* This callback is optional.
*
* Returns:
*
* 0 on success or a negative error code on failure. Can return
* -ERESTARTSYS or -EINTR when the call has been interrupted and needs
* to be restarted.
*/
int (*end_cpu_access)(struct dma_buf *, enum dma_data_direction);
/**
* @mmap:
*
* This callback is used by the dma_buf_mmap() function
*
* Note that the mapping needs to be incoherent, userspace is expected
* to bracket CPU access using the DMA_BUF_IOCTL_SYNC interface.
*
* Because dma-buf buffers have invariant size over their lifetime, the
* dma-buf core checks whether a vma is too large and rejects such
* mappings. The exporter hence does not need to duplicate this check.
* Drivers do not need to check this themselves.
*
* If an exporter needs to manually flush caches and hence needs to fake
* coherency for mmap support, it needs to be able to zap all the ptes
* pointing at the backing storage. Now linux mm needs a struct
* address_space associated with the struct file stored in vma->vm_file
* to do that with the function unmap_mapping_range. But the dma_buf
* framework only backs every dma_buf fd with the anon_file struct file,
* i.e. all dma_bufs share the same file.
*
* Hence exporters need to setup their own file (and address_space)
* association by setting vma->vm_file and adjusting vma->vm_pgoff in
* the dma_buf mmap callback. In the specific case of a gem driver the
* exporter could use the shmem file already provided by gem (and set
* vm_pgoff = 0). Exporters can then zap ptes by unmapping the
* corresponding range of the struct address_space associated with their
* own file.
*
* This callback is optional.
*
* Returns:
*
* 0 on success or a negative error code on failure.
*/
int (*mmap)(struct dma_buf *, struct vm_area_struct *vma);
int (*vmap)(struct dma_buf *dmabuf, struct iosys_map *map);
void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map);
};
dmabuf结构体
/**
* struct dma_buf - shared buffer object
*
* This represents a shared buffer, created by calling dma_buf_export(). The
* userspace representation is a normal file descriptor, which can be created by
* calling dma_buf_fd().
*
* Shared dma buffers are reference counted using dma_buf_put() and
* get_dma_buf().
*
* Device DMA access is handled by the separate &struct dma_buf_attachment.
*/
struct dma_buf {
/**
* @size:
*
* Size of the buffer; invariant over the lifetime of the buffer.
*/
size_t size;
/**
* @file:
*
* File pointer used for sharing buffers across, and for refcounting.
* See dma_buf_get() and dma_buf_put().
*/
struct file *file;
/**
* @attachments:
*
* List of dma_buf_attachment that denotes all devices attached,
* protected by &dma_resv lock @resv.
*/
struct list_head attachments;
/** @ops: dma_buf_ops associated with this buffer object. */
const struct dma_buf_ops *ops;
/**
* @lock:
*
* Used internally to serialize list manipulation, attach/detach and
* vmap/unmap. Note that in many cases this is superseeded by
* dma_resv_lock() on @resv.
*/
struct mutex lock;
/**
* @vmapping_counter:
*
* Used internally to refcnt the vmaps returned by dma_buf_vmap().
* Protected by @lock.
*/
unsigned vmapping_counter;
/**
* @vmap_ptr:
* The current vmap ptr if @vmapping_counter > 0. Protected by @lock.
*/
struct iosys_map vmap_ptr;
/**
* @exp_name:
*
* Name of the exporter; useful for debugging. See the
* DMA_BUF_SET_NAME IOCTL.
*/
const char *exp_name;
/**
* @name:
*
* Userspace-provided name; useful for accounting and debugging,
* protected by dma_resv_lock() on @resv and @name_lock for read access.
*/
const char *name;
/** @name_lock: Spinlock to protect name acces for read access. */
spinlock_t name_lock;
/**
* @owner:
*
* Pointer to exporter module; used for refcounting when exporter is a
* kernel module.
*/
struct module *owner;
/** @list_node: node for dma_buf accounting and debugging. */
struct list_head list_node;
/** @priv: exporter specific private data for this buffer object. */
void *priv;
/**
* @resv:
*
* Reservation object linked to this dma-buf.
*
* IMPLICIT SYNCHRONIZATION RULES:
*
* Drivers which support implicit synchronization of buffer access as
* e.g. exposed in `Implicit Fence Poll Support`_ must follow the
* below rules.
*
* - Drivers must add a read fence through dma_resv_add_fence() with the
* DMA_RESV_USAGE_READ flag for anything the userspace API considers a
* read access. This highly depends upon the API and window system.
*
* - Similarly drivers must add a write fence through
* dma_resv_add_fence() with the DMA_RESV_USAGE_WRITE flag for
* anything the userspace API considers write access.
*
* - Drivers may just always add a write fence, since that only
* causes unecessarily synchronization, but no correctness issues.
*
* - Some drivers only expose a synchronous userspace API with no
* pipelining across drivers. These do not set any fences for their
* access. An example here is v4l.
*
* - Driver should use dma_resv_usage_rw() when retrieving fences as
* dependency for implicit synchronization.
*
* DYNAMIC IMPORTER RULES:
*
* Dynamic importers, see dma_buf_attachment_is_dynamic(), have
* additional constraints on how they set up fences:
*
* - Dynamic importers must obey the write fences and wait for them to
* signal before allowing access to the buffer's underlying storage
* through the device.
*
* - Dynamic importers should set fences for any access that they can't
* disable immediately from their &dma_buf_attach_ops.move_notify
* callback.
*
* IMPORTANT:
*
* All drivers and memory management related functions must obey the
* struct dma_resv rules, specifically the rules for updating and
* obeying fences. See enum dma_resv_usage for further descriptions.
*/
struct dma_resv *resv;
/** @poll: for userspace poll support */
wait_queue_head_t poll;
/** @cb_in: for userspace poll support */
/** @cb_out: for userspace poll support */
struct dma_buf_poll_cb_t {
struct dma_fence_cb cb;
wait_queue_head_t *poll;
__poll_t active;
} cb_in, cb_out;
#ifdef CONFIG_DMABUF_SYSFS_STATS
/**
* @sysfs_entry:
*
* For exposing information about this buffer in sysfs. See also
* `DMA-BUF statistics`_ for the uapi this enables.
*/
struct dma_buf_sysfs_entry {
struct kobject kobj;
struct dma_buf *dmabuf;
} *sysfs_entry;
#endif
};
创建dmabuf时的配置信息。
const char *exp_name
:调试用
const struct dma_buf_ops *ops
:用户定义的额外的dmabuf操作函数
size_t size
:dmabuf大小
void *priv
:用户分配这块dmabuf时的额外私有数据
/**
* struct dma_buf_export_info - holds information needed to export a dma_buf
* @exp_name: name of the exporter - useful for debugging.
* @owner: pointer to exporter module - used for refcounting kernel module
* @ops: Attach allocator-defined dma buf ops to the new buffer
* @size: Size of the buffer - invariant over the lifetime of the buffer
* @flags: mode flags for the file
* @resv: reservation-object, NULL to allocate default one
* @priv: Attach private data of allocator to this buffer
*
* This structure holds the information required to export the buffer. Used
* with dma_buf_export() only.
*/
struct dma_buf_export_info {
const char *exp_name;
struct module *owner;
const struct dma_buf_ops *ops;
size_t size;
int flags;
struct dma_resv *resv;
void *priv;
};
/**
* struct dma_buf_attachment - holds device-buffer attachment data
* @dmabuf: buffer for this attachment.
* @dev: device attached to the buffer.
* @node: list of dma_buf_attachment, protected by dma_resv lock of the dmabuf.
* @sgt: cached mapping.
* @dir: direction of cached mapping.
* @peer2peer: true if the importer can handle peer resources without pages.
* @priv: exporter specific attachment data.
* @importer_ops: importer operations for this attachment, if provided
* dma_buf_map/unmap_attachment() must be called with the dma_resv lock held.
* @importer_priv: importer specific attachment data.
*
* This structure holds the attachment information between the dma_buf buffer
* and its user device(s). The list contains one attachment struct per device
* attached to the buffer.
*
* An attachment is created by calling dma_buf_attach(), and released again by
* calling dma_buf_detach(). The DMA mapping itself needed to initiate a
* transfer is created by dma_buf_map_attachment() and freed again by calling
* dma_buf_unmap_attachment().
*/
struct dma_buf_attachment {
struct dma_buf *dmabuf;
struct device *dev;
struct list_head node;
struct sg_table *sgt;
enum dma_data_direction dir;
bool peer2peer;
const struct dma_buf_attach_ops *importer_ops;
void *importer_priv;
void *priv;
};
描述原文:
/**
* DOC: dma buf device access
*
* For device DMA access to a shared DMA buffer the usual sequence of operations
* is fairly simple:
*
* 1. The exporter defines his exporter instance using
* DEFINE_DMA_BUF_EXPORT_INFO() and calls dma_buf_export() to wrap a private
* buffer object into a &dma_buf. It then exports that &dma_buf to userspace
* as a file descriptor by calling dma_buf_fd().
*
* 2. Userspace passes this file-descriptors to all drivers it wants this buffer
* to share with: First the file descriptor is converted to a &dma_buf using
* dma_buf_get(). Then the buffer is attached to the device using
* dma_buf_attach().
*
* Up to this stage the exporter is still free to migrate or reallocate the
* backing storage.
*
* 3. Once the buffer is attached to all devices userspace can initiate DMA
* access to the shared buffer. In the kernel this is done by calling
* dma_buf_map_attachment() and dma_buf_unmap_attachment().
*
* 4. Once a driver is done with a shared buffer it needs to call
* dma_buf_detach() (after cleaning up any mappings) and then release the
* reference acquired with dma_buf_get() by calling dma_buf_put().
*
* For the detailed semantics exporters are expected to implement see
* &dma_buf_ops.
*/
解析:
DEFINE_DMA_BUF_EXPORT_INFO()
来定义dmabuf info实例,然后调用函数dma_buf_export()
将这块私有buffer转换为&dma_buf实例。然后通过函数dma_buf_fd()
把&dma_buf以文件描述符的形式暴露到用户态。dma_buf_get()
转换为&dma_buf,然后通过函数dma_buf_attach()
绑定到对应设备。dma_buf_map_attachment()
函数和dma_buf_unmap_attachment()
函数实现的。dma_buf_detach()
来释放内存,然后调用dma_buf_put()
来减去引用计数。函数原型:
static int __init dma_buf_init(void)
模块初始化函数,注册dma_buf_fs,初始化db_list,初始化debugfs。
dma_buf_init调用流程:
函数原型:
struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
创建一个dmabuf,并把它关联到一个anon file上,以便暴露这块内存。
成功会返回&dma_buf的指针,失败会返回一个负数(通过ERR_PTR包装)。
定义exp_info最简便的方法是通过宏定义:DEFINE_DMA_BUF_EXPORT_INFO
核心操作:
static const struct file_operations dma_buf_fops = {
.release = dma_buf_file_release,
.mmap = dma_buf_mmap_internal,
.llseek = dma_buf_llseek,
.poll = dma_buf_poll,
.unlocked_ioctl = dma_buf_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.show_fdinfo = dma_buf_show_fdinfo,
};
函数原型:
static void dma_buf_show_fdinfo(struct seq_file *m, struct file *file)
显示这个文件的描述信息:size/count/exp_name/name
函数原型:
static long dma_buf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
dmabuf的ioctl函数,支持操作:
函数原型:
int dma_buf_begin_cpu_access(struct dma_buf *dmabuf, enum dma_data_direction direction)
如果用户定义了begin_cpu_access
调用用户的begin_cpu_access
接口然后调用内部函数__dma_buf_begin_cpu_access
,等待dmabuf被访问。
函数原型:
static long dma_buf_set_name(struct dma_buf *dmabuf, const char __user *buf)
重新配置dmabuf的名字。
函数原型:
static __poll_t dma_buf_poll(struct file *file, poll_table *poll)
文件描述符poll函数的内核实现。
先调用poll_wait把进程挂到等待队列。然后判断等待的poll事件是不是POLLIN | POLLOUT
然后从resv获取fence,把dmabuf的poll callback添加进去。
函数原型:
static void dma_buf_poll_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
在pool时add的callback,主要作用是唤醒poll等待进程。
函数原型:
static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence)
返回base + offset。
函数原型:
static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma)
调用用户实现的mmap接口实现内存映射。
函数原型:
static int dma_buf_file_release(struct inode *inode, struct file *file)
做file close之后的清理工作。
函数原型:
int dma_buf_fd(struct dma_buf *dmabuf, int flags)
从系统中获取一个可用的fd,并把它跟dmabuf->file绑定起来。
调用函数:get_unused_fd_flags --> fd_install
函数原型:
struct dma_buf *dma_buf_get(int fd)
通过fd获取dmabuf的句柄指针。
调用函数:fget --> file->private_data
函数原型:
void dma_buf_put(struct dma_buf *dmabuf)
把dmabuf的文件句柄引用计数减1
函数原型:
struct dma_buf_attachment * dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev, const struct dma_buf_attach_ops *importer_ops, void *importer_priv)
把device设备绑定到dmabuf的attachment list里面。
初始化一个attach节点,并把它加入到dmabuf的attachments列表中。
函数原型:
struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf, struct device *dev)
把device设备绑定到dmabuf的attachment list里面。
没有importer_ops和importer_priv入参。
函数原型:
void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
函数原型:
int dma_buf_pin(struct dma_buf_attachment *attach)
锁住一块dmabuf。但是不允许用户用这个接口锁住任意数量的dmabuf。
void dma_buf_unpin(struct dma_buf_attachment *attach)
将锁住的dmabuf解锁。
struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, enum dma_data_direction direction)
调用用户定义的map_dma_buf回调。
struct sg_table *dma_buf_unmap_attachment(struct dma_buf_attachment *attach, enum dma_data_direction direction)
调用用户定义的unmap_dma_buf回调。
void dma_buf_move_notify(struct dma_buf *dmabuf)
通知所有attachments,dmabuf已经被移动,需要销毁或者重新创建映射。
int dma_buf_begin_cpu_access(struct dma_buf *dmabuf, enum dma_data_direction direction)
开始对dmabuf发起CPU访问,会等待fence被生产者唤醒。
int dma_buf_end_cpu_access(struct dma_buf *dmabuf, enum dma_data_direction direction)
结束对dmabuf的访问。
int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, unsigned long pgoff)
对dmabuf内存地址进行映射
int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map)
int dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map)
有大佬把DMA BUF做成一系列文章,深入描述了DMA BUF的运行机制,受益匪浅,链接: dma-buf 由浅入深(一) —— 最简单的 dma-buf 驱动程序