以高通camera 申请ion内存看dma-buf

1 fd 与ion buffer, file绑定

    简单总结就是申请一个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;
}

1.1获得一个ion buffer 和file

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

以高通camera 申请ion内存看dma-buf_第1张图片

以高通camera 申请ion内存看dma-buf_第2张图片

 这里是分配完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。

1.2 fd 和file的绑定

  再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 需要成对使用。

你可能感兴趣的:(linux,android)