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 */
};
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版本做了区分。扫描二维码关注公众号,回复: 15392299 查看本文章
一开始我是基于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介绍》