DRM驱动代码分析:gem_prime_import

struct scatterlist、struct sg_table

struct scatterlist {
	unsigned long	page_link;
	unsigned int	offset;
	unsigned int	length;
	dma_addr_t	dma_address;
#ifdef CONFIG_NEED_SG_DMA_LENGTH
	unsigned int	dma_length;
#endif
};
struct sg_table {
	struct scatterlist *sgl;	/* the list */
	unsigned int nents;		/* number of mapped entries */
	unsigned int orig_nents;	/* original size of list */
};

结构体说明查看wowotech网站的文章《Linux kernel scatterlist API介绍》

struct dma_buf、struct dma_buf_attachment、struct dma_buf_ops

struct dma_buf {
	size_t size;
	struct file *file;
	struct list_head attachments;
	const struct dma_buf_ops *ops;
	struct mutex lock;
	unsigned vmapping_counter;
	struct dma_buf_map vmap_ptr;
	const char *exp_name;
	const char *name;
	spinlock_t name_lock;
	struct module *owner;
	struct list_head list_node;
	void *priv;
	struct dma_resv *resv;
	wait_queue_head_t poll;
	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
	struct dma_buf_sysfs_entry {
		union {
			struct kobject kobj;
			struct work_struct sysfs_add_work;
		};
		struct dma_buf *dmabuf;
	} *sysfs_entry;
#endif
	ANDROID_KABI_RESERVE(1);
	ANDROID_KABI_RESERVE(2);
};

通过dma_buf_export创建dma buf;
通过dma_buf_fd创建文件描述符;
通过dma_buf_put()和get_dma_buf()对dma buf进行引用计数;
通过struct dma_buf_attachment访问设备DMA;



size:buffer的大小,在buffer的生命周期内保持不变。

file:文件指针,用于引用计数。

attachments:dma_buf_attachment列表,表示所有连接的设备。

ops:操作函数

vmapping_counter:vmap的计数

vmap_ptr:虚拟地址

exp_name:exporter名字

name:用户层提供的名字

owner:指向exporter模块

priv:exporter私有数据

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;
	unsigned long dma_map_attrs;

	ANDROID_KABI_RESERVE(1);
	ANDROID_KABI_RESERVE(2);
};

node:dma_buf_attachment列表。

dir:BIDIRECTIONAL、TO_DEVICE、FROM_DEVIC、NONE。

importer_priv:importer数据

priv:exporter数据

struct dma_buf_ops {
	bool cache_sgt_mapping;
	int (*attach)(struct dma_buf *, struct dma_buf_attachment *);
	void (*detach)(struct dma_buf *, struct dma_buf_attachment *);
	int (*pin)(struct dma_buf_attachment *attach);
	void (*unpin)(struct dma_buf_attachment *attach);
	struct sg_table * (*map_dma_buf)(struct dma_buf_attachment *, enum dma_data_direction);
	void (*unmap_dma_buf)(struct dma_buf_attachment *, struct sg_table *, enum dma_data_direction);
	void (*release)(struct dma_buf *);
	int (*begin_cpu_access)(struct dma_buf *, enum dma_data_direction);
	int (*begin_cpu_access_partial)(struct dma_buf *dmabuf,
					enum dma_data_direction, unsigned int offset, unsigned int len);
	int (*end_cpu_access)(struct dma_buf *, enum dma_data_direction);
	int (*end_cpu_access_partial)(struct dma_buf *dmabuf,
				      enum dma_data_direction, unsigned int offset, unsigned int len);
	int (*mmap)(struct dma_buf *, struct vm_area_struct *vma);
	int (*vmap)(struct dma_buf *dmabuf, struct dma_buf_map *map);
	void (*vunmap)(struct dma_buf *dmabuf, struct dma_buf_map *map);
	int (*get_flags)(struct dma_buf *dmabuf, unsigned long *flags);

	ANDROID_KABI_RESERVE(1);
	ANDROID_KABI_RESERVE(2);
};

map_dma_buf:将dma buf映射到设备地址空间。

dma_buf_attach

drivers\dma-buf\dma-buf.c
struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf, struct device *dev)
{
	return dma_buf_dynamic_attach(dmabuf, dev, NULL, NULL);
}

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)
{
	struct dma_buf_attachment *attach;
	...
	attach = kzalloc(sizeof(*attach), GFP_KERNEL);
	...
	attach->dev = dev;-----------------------------------------------------------1
	attach->dmabuf = dmabuf;-----------------------------------------------------1
	if (importer_ops)
		attach->peer2peer = importer_ops->allow_peer2peer;
	attach->importer_ops = importer_ops;
	attach->importer_priv = importer_priv;

	if (dmabuf->ops->attach) {
		ret = dmabuf->ops->attach(dmabuf, attach);-----------------------------------2
		if (ret)
			goto err_attach;
	}
	dma_resv_lock(dmabuf->resv, NULL);
	list_add(&attach->node, &dmabuf->attachments);---------------------------------3
	dma_resv_unlock(dmabuf->resv);

	if (dma_buf_attachment_is_dynamic(attach) != dma_buf_is_dynamic(dmabuf)) {-----4
		struct sg_table *sgt;

		if (dma_buf_is_dynamic(attach->dmabuf)) {
			dma_resv_lock(attach->dmabuf->resv, NULL);
			ret = dmabuf->ops->pin(attach);
			if (ret)
				goto err_unlock;
		}

		sgt = __map_dma_buf(attach, DMA_BIDIRECTIONAL);
		...
		if (dma_buf_is_dynamic(attach->dmabuf))
			dma_resv_unlock(attach->dmabuf->resv);
		attach->sgt = sgt;
		attach->dir = DMA_BIDIRECTIONAL;
	}

	return attach;
	...
	return ERR_PTR(ret);
}

1 填充struct dma_buf_attachment *attach变量,device、dma_buf和dma_buf_attachment联系起来了。

2 todo

3 将新的attach添加到dmabuf->attachments的列表中

dma_buf_map_attachment (qcom版)

在这里插入图片描述

static const struct dma_buf_ops drm_gem_prime_dmabuf_ops =  {
	.cache_sgt_mapping = true,
	.attach = drm_gem_map_attach,
	.detach = drm_gem_map_detach,
	.map_dma_buf = drm_gem_map_dma_buf,
	.unmap_dma_buf = drm_gem_unmap_dma_buf,
	.release = drm_gem_dmabuf_release,
	.mmap = drm_gem_dmabuf_mmap,
	.vmap = drm_gem_dmabuf_vmap,
	.vunmap = drm_gem_dmabuf_vunmap,
};

我不确定用的dma_buf_ops是不是drm_gem_prime_dmabuf_ops。
我想知道的是,怎么通过dma_buf_attachment得到sg_table,我觉得map_dma_buf的实现原理应该是类似的。先继续分析。

在这里插入图片描述

高通安卓源码的两个仓库下都有drm driver代码:
kernel\drivers\gpu\drm\msm
vendor\qcom\opensource\display-drivers
第一个应该是高通合入Linux主干的代码,第二个是厂商实际使用的代码。

CSDN 何小龙 Linux Graphics 周刊(第 1 期)文章中知道“彻底废弃 drm_driver 中的 prime callbacks,全面启用 GEM Object functions”。
高通平台的相关修改:[PATCH v2 08/21] drm/msm: Introduce GEM object funcs

在这里插入图片描述

android_kernel_modules_and_devicetree_oneplus_sm8475-oneplus-sm8475_s_12.1_oneplus_10t_5g这份代码里看到struct drm_driver msm_driver里gem_prime_get_sg_table这些函数接口还是保留着的。

android_kernel_modules_and_devicetree_oneplus_sm8550-oneplus_sm8550_t_13.0_oneplus_11这份代码里根据kernel版本做了区分。

在这里插入图片描述

一开始我是基于sm8475的vendor\qcom\opensource\display-drivers仓和sm8550的kernel\drivers\gpu\drm\仓分析代码,因为接口的变化,分析过程出现矛盾。所以这两个仓库还是要用同一版本。接下来的高通代码用的是sm8550版本。

static const struct drm_gem_object_funcs msm_gem_object_funcs = {
	.free = msm_gem_free_object,
	.pin = msm_gem_prime_pin,
	.unpin = msm_gem_prime_unpin,
	.get_sg_table = msm_gem_prime_get_sg_table,
	.vmap = msm_gem_prime_vmap,
	.vunmap = msm_gem_prime_vunmap,
	.vm_ops = &vm_ops,
};

在这里插入图片描述

怎么通过dma_buf_attachment得到sg_table:
struct dma_buf_attachment -> struct dma_buf -> struct drm_gem_object -> struct msm_gem_object -> struct page -> struct sg_table

在这里插入图片描述

从sg_table得到pfn:
struct sg_table -> struct scatterlist -> struct page -> pfn -> phys addr

在这里插入图片描述

关于page、pfn、memory model的概念可以看这篇文章wowotech linuxer《Linux内存模型》
这篇文章写得真好,我有些地方没看懂,以后再学习了。

msm_gem_prime_import

在这里插入图片描述

疑问:
drm_gem_map_dma_buf函数里就已经可以得到 struct drm_gem_object变量。
struct drm_gem_object *obj = attach->dmabuf->priv;
这里又通过obj = dev->driver->gem_prime_import_sg_table(dev, attach, sgt);得到一个struct drm_gem_object 变量,这两个有什么关系?

dma_buf_map_attachment (unisoc版)

realmeC31_C35_narzo50A-Prime-AndroidS-kernel-source 是搭载了unisoc的芯片,这版代码没有使用drm_gem_object_funcs。

static struct drm_driver sprd_drm_drv = {
	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
	.fops			= &sprd_drm_fops,
	.gem_vm_ops		= &drm_gem_cma_vm_ops,
	.gem_free_object_unlocked	= sprd_gem_free_object,
	.dumb_create		= sprd_gem_dumb_create,
	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
	.gem_prime_import	= drm_gem_prime_import,
	.gem_prime_export	= drm_gem_prime_export,
	.gem_prime_import_sg_table = sprd_gem_prime_import_sg_table,
	.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
	.gem_prime_vmap		= drm_gem_cma_prime_vmap,
	.gem_prime_vunmap	= drm_gem_cma_prime_vunmap,
	.gem_prime_mmap		= drm_gem_cma_prime_mmap,
	...
};

在这里插入图片描述

drm_gem_map_dma_buf里没有调用struct drm_gem_object_funcs的get_sg_table,而是使用struct drm_driver的gem_prime_get_sg_table。

怎么通过dma_buf_attachment得到sg_table:
struct dma_buf_attachment -> struct dma_buf -> struct drm_gem_object -> struct drm_gem_cma_object -> struct page -> struct sg_table

在这里插入图片描述

drm_gem_prime_import (unisoc版)

在这里插入图片描述

缩写

pfn:page-frame number

参考资料

DMA那些事儿
CSDN 何小龙《dma-buf 由浅入深(三) —— map attachment》
wowotech linuxer 《Linux内存模型》
wowotech wowo 《Linux kernel scatterlist API介绍》

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值