简单总结就是申请一个buffer,再创建一个dmabuf 结构体然后将,然后将dmabuf中得指针指向buffer,dmabuf 再传递给一个匿名的inode,获取到一个file,这样file和dmabuf绑定起来,也就和buffer关联上。然后再从进程中分配一个空闲的fd,将fd 和file囊绑定。这样就能通过fd 快速查找到buffer。file 是个全系统的,他和进程无关,但是fd 是每个进程都是自己独立的,所以再跨进程传输时只需要保证fd 和同一个file绑定就可以实现buffer的共享。
高通camera 申请ion buffer 并且smmu 映射是在cam_mem_mgr_alloc_and_map 函数种。
cam_mem_mgr_alloc_and_map->cam_mem_util_ion_alloc
cam_mem_util_ion_alloc 函数会返回dma-buf 和fd。确定分配得buffer 大小, 从哪个heap种分配,ion有很多heap。会调用
cam_mem_util_get_dma_buf_fd。
cam_mem_mgr_alloc_and_map->cam_mem_util_ion_alloc->cam_mem_util_get_dma_buf_fd
static int cam_mem_util_get_dma_buf_fd(size_t len,
size_t align,
unsigned int heap_id_mask,
unsigned int flags,
struct dma_buf **buf,
int *fd)
{
struct dma_buf *dmabuf = NULL;
int rc = 0;
if (!buf || !fd) {
CAM_ERR(CAM_MEM, "Invalid params, buf=%pK, fd=%pK", buf, fd);
return -EINVAL;
}
// 分配一个buffer 并且将buffer 与dma-buf 结构体绑定
*buf = ion_alloc(len, heap_id_mask, flags);
if (IS_ERR_OR_NULL(*buf))
return -ENOMEM;
/* 从当前进程种分配一个空闲得fd,注意这里是从进程得到fd,fd 关联得file file却不是
*属于进程的,所以不能close 就万事大吉。close 是将fd重新放入进程得空闲数组种,file
*得索引值只是减1.file 如果索引值原本不是1,那么file 不会释放,分配得buffer就不会释放*/
*fd = dma_buf_fd(*buf, O_CLOEXEC);
if (*fd < 0) {
CAM_ERR(CAM_MEM, "get fd fail, *fd=%d", *fd);
rc = -EINVAL;
goto get_fd_fail;
}
//dma_buf_get 是对前面分配得匿名file 索引值加1.因为分配得时候已经加1,
//这里再加1 实际上所以值已经变为2
dmabuf = dma_buf_get(*fd);
if (IS_ERR_OR_NULL(dmabuf)) {
CAM_ERR(CAM_MEM, "dma_buf_get failed, *fd=%d", *fd);
rc = -EINVAL;
}
return rc;
get_fd_fail:
dma_buf_put(*buf);// dma_buf_put 是与dma_buf_get相对得,就是将所以值再减去1
return rc;
}
ion_alloc 函数主要事分配一个buffer ,并且将分配的buffer和dma-buf 结构体绑定,最后再将dmabuf 结构和file 绑定。
因为file 是全系统,这样能够实现共享。主要事调用了ion_alloc_dmabuf 函数。
struct dma_buf *ion_alloc_dmabuf(size_t len, unsigned int heap_id_mask,
unsigned int flags)
{
struct ion_device *dev = internal_dev;
struct ion_buffer *buffer = NULL;
struct ion_heap *heap;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
struct dma_buf *dmabuf;
char task_comm[TASK_COMM_LEN];
...
len = PAGE_ALIGN(len); //长度需要按照页对齐,ion分配得buffer 最小是一个页大小
if (!len)
return ERR_PTR(-EINVAL);
down_read(&dev->lock);
plist_for_each_entry(heap, &dev->heaps, node) {
/* if the caller didn't specify this heap id */
if (!((1 << heap->id) & heap_id_mask))
continue;
//根据选择得不同heap 从heap种分配一块内存
buffer = ion_buffer_create(heap, dev, len, flags);
if (!IS_ERR(buffer) || PTR_ERR(buffer) == -EINTR)
break;
}
up_read(&dev->lock);
if (!buffer)
return ERR_PTR(-ENODEV);
if (IS_ERR(buffer))
return ERR_CAST(buffer);
// 获得当前进程得进程名
get_task_comm(task_comm, current->group_leader);
//exp_info 是一些dma-buf 需要得信息
exp_info.ops = &dma_buf_ops; //操作此buffer 得函数
exp_info.size = buffer->size;
exp_info.flags = O_RDWR;//读写标志
exp_info.priv = buffer; //前面ion分配得buffer
exp_info.exp_name = kasprintf(GFP_KERNEL, "%s-%s-%d-%s", KBUILD_MODNAME,
heap->name, current->tgid, task_comm);
// dma_buf_export dmabuf API 创建一个dmabuf
dmabuf = dma_buf_export(&exp_info);
...
return dmabuf;
}
dma_buf_export 创建一个dmabuf 并将成员初始化,我认为里面最重要的就是分配一个匿名的inode 获得一个file
struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
{
struct dma_buf *dmabuf;
struct reservation_object *resv = exp_info->resv;
struct file *file;
size_t alloc_size = sizeof(struct dma_buf);
char *bufname;
int ret;
long cnt;
.....
cnt = atomic_long_inc_return(&name_counter);
bufname = kasprintf(GFP_KERNEL, "dmabuf%ld", cnt);
if (!bufname) {
ret = -ENOMEM;
goto err_module;
}
dmabuf = kzalloc(alloc_size, GFP_KERNEL);
if (!dmabuf) {
ret = -ENOMEM;
goto err_name;
}
dmabuf->priv = exp_info->priv; //指向ion buffer
//ionn 实现得操作函数集,最后file 中调用得函数最后会执行这里得
dmabuf->ops = exp_info->ops;
//buffer 大小
dmabuf->size = exp_info->size;
//分配得进程名
dmabuf->exp_name = exp_info->exp_name;
dmabuf->owner = exp_info->owner;
init_waitqueue_head(&dmabuf->poll);
dmabuf->cb_excl.poll = dmabuf->cb_shared.poll = &dmabuf->poll;
dmabuf->cb_excl.active = dmabuf->cb_shared.active = 0;
dmabuf->name = bufname;
dmabuf->ktime = ktime_get();
if (!resv) {
resv = (struct reservation_object *)&dmabuf[1];
reservation_object_init(resv);
}
dmabuf->resv = resv;
//通过一个匿名得inode 分配你一个file,dma_buf_fops 是dmabuf 相关得操作函数,要赋值给
// file->f_op 文件相关得read,release等是调用file->f_op 中得函数,实际上调用得dma_buf_fops
// 然后dma_buf_fops 又会调用dmabuf->ops
file = anon_inode_getfile(bufname, &dma_buf_fops, dmabuf,
exp_info->flags);
//file 分配完成后索引值(f_count)已经加1,具体实现是在__alloc_file 函数
//中atomic_long_set(&f->f_count, 1);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto err_dmabuf;
}
file->f_mode |= FMODE_LSEEK;
dmabuf->file = file; //指向了刚分配出来得file
mutex_init(&dmabuf->lock);
// 此buffer 管理得设备都加入到这个list 中
INIT_LIST_HEAD(&dmabuf->attachments);
dma_buf_ref_init(dmabuf);
dma_buf_ref_mod(dmabuf, 1);
mutex_lock(&db_list.lock);
list_add(&dmabuf->list_node, &db_list.head);
mutex_unlock(&db_list.lock);
return dmabuf;
err_dmabuf:
kfree(dmabuf);
err_name:
kfree(bufname);
err_module:
module_put(exp_info->owner);
return ERR_PTR(ret);
}
创建一个file 需要inode,具体关系如下图(下面两张图从他人文章中拷贝得)
https://blog.csdn.net/jasonchen_gbd/article/details/51511261
这里是分配完file,后面会分析分配fd,fd_array 数组中对应得是fd 对应得file。 获得得file将加入到fd array中
struct file *anon_inode_getfile(const char *name,
const struct file_operations *fops,
void *priv, int flags)
{
struct file *file;
if (IS_ERR(anon_inode_inode))
return ERR_PTR(-ENODEV);
if (fops->owner && !try_module_get(fops->owner))
return ERR_PTR(-ENOENT);
/*
* We know the anon_inode inode count is always greater than zero,
* so ihold() is safe.
*/
ihold(anon_inode_inode);
//fops 是dmabuf ops 不是ion 传入进来的ops。但是dmabuf ops 实际上是调用dmabuf->ops
//指向ion实现得ops。 fops 进入alloc_file_pseudo 传递给了file->f_op
file = alloc_file_pseudo(anon_inode_inode, anon_inode_mnt, name,
flags & (O_ACCMODE | O_NONBLOCK), fops);
if (IS_ERR(file))
goto err;
file->f_mapping = anon_inode_inode->i_mapping;
file->private_data = priv;//priv 指向dmabuf
return file;
err:
iput(anon_inode_inode);
module_put(fops->owner);
return file;
}
通过fd 对文件得read, write,release,mmap 操作实际上都是通过f_op 指针指向得函数,这里指向得就是
static const struct file_operations dma_buf_fops = {
.release = dma_buf_release,
.mmap = dma_buf_mmap_internal,
.llseek = dma_buf_llseek,
.poll = dma_buf_poll,
.unlocked_ioctl = dma_buf_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = dma_buf_ioctl,
#endif
};
dmabuf ops 只实现了部分函数,具体release 调用dmabuf->ops->release 函数,实现ion buffer 得释放.
总结:(1) 通过ion_buffer_create从ion的heap 中分配一块内存
(2)通过dma_buf_export 创建一个dmabuf 的结构体,dmabuf 结构体一些成员初始化。 主要关注priv 成员指向了ion 分配的buffer,ops 指向了ion 实现的dma_buf_ops. 需要注意再分配完成一个file后 ,dma_buf_export 函数中已经将文件得索引值+1
(3)通过anon_inode_getfile分配一个file,这个file 的priv 指向前面分配出来的dmabuf 结构体。file->f_op 指向dma_buf_fops
这个dma_buf_fops 和前面ion里实现的dma_buf_ops 并不是一个。dma_buf_fops 是file 的操作操作函数集,会调用dma_buf_ops (ion 内实现的函数)。file 的索引值变为0 后会调用release 函数释放掉ion buffer。
再cam_mem_util_get_dma_buf_fd 函数中ion_alloc 已经获得file,
dma_buf_fd 会从进程中分配一个fd,并且将ion_alloc 分配得file 加入到fd 所在得array。
int dma_buf_fd(struct dma_buf *dmabuf, int flags)
{
int fd;
if (!dmabuf || !dmabuf->file)
return -EINVAL;
//从进程中获取一个空闲得fd
fd = get_unused_fd_flags(flags);
if (fd < 0)
return fd;
// 将fd 和分配得file绑定
fd_install(fd, dmabuf->file);
return fd;
}
2其他一些dmabuf 函数
struct dma_buf *dma_buf_get(int fd)
{
struct file *file;
file = fget(fd); //这里会将file得索引值加1,与这个函数像对应得就是fput
if (!file)
return ERR_PTR(-EBADF);
if (!is_dma_buf_file(file)) {
fput(file);
return ERR_PTR(-EINVAL);
}
dma_buf_ref_mod(file->private_data, 1);
//前面提到过,创建file 时候private_data被设计成指向dmabuf
return file->private_data;
}
void dma_buf_put(struct dma_buf *dmabuf)
{
if (WARN_ON(!dmabuf || !dmabuf->file))
return;
dma_buf_ref_mod(dmabuf, -1);
fput(dmabuf->file);//file 得索引值减1,和fget函数对应
}
dma_buf_get 和dma_buf_put 需要成对使用。