dma-buf 由淺入深(七) —— alloc page 版本

dma-buf 由淺入深(一) —— 最簡單的 dma-buf 驅動程序
dma-buf 由淺入深(二) —— kmap / vmap
dma-buf 由淺入深(三) —— map attachment
dma-buf 由淺入深(四) —— mmap
dma-buf 由淺入深(五) —— File
dma-buf 由淺入深(六) —— begin / end cpu_access
dma-buf 由淺入深(七) —— alloc page 版本
dma-buf 由淺入深(八) —— ION 簡化版


前言

在前面的 dma-buf 系列文章中,exporter 所分配的內存都是通過 kzalloc() 來分配的。本篇我們換個方式,使用 alloc_page() 來分配內存。

對比

與之前的 kzalloc 方式相比,alloc_page 方式的主要區別如下:

dma_buf_ops kzalloc 方式 alloc_page 方式
map_dma_buf dma_map_single() dma_map_page()
unmap_dma_buf dma_unmap_single() dma_unmap_page()
begin_cpu_access dma_sync_single_for_cpu() dma_sync_sg_for_cpu()
end_cpu_access dma_sync_single_for_device() dma_sync_sg_for_device()
kmap return dmabuf->priv; kmap()
kmap_atomic return dmabuf->priv; kmap_atomic()
vmap return dmabuf->priv; vmap()
release kfree() put_page()

示例

exporter 驅動
結合前面幾篇文章的示例代碼,將 dma_buf_ops 全部替換成 page 方式。

exporter-page.c

#include <linux/dma-buf.h>
#include <linux/highmem.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>

static struct dma_buf *dmabuf_exported;

static int exporter_attach(struct dma_buf *dmabuf, struct device *dev,
			struct dma_buf_attachment *attachment)
{
	pr_info("dmabuf attach device: %s\n", dev_name(dev));
	return 0;
}

static void exporter_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attachment)
{
	pr_info("dmabuf detach device: %s\n", dev_name(attachment->dev));
}

static struct sg_table *exporter_map_dma_buf(struct dma_buf_attachment *attachment,
					 enum dma_data_direction dir)
{
	struct page *page = attachment->dmabuf->priv;
	struct sg_table *table;

	table = kmalloc(sizeof(*table), GFP_KERNEL);

	sg_alloc_table(table, 1, GFP_KERNEL);
	sg_set_page(table->sgl, page, PAGE_SIZE, 0);
	sg_dma_address(table->sgl) = dma_map_page(NULL, page, 0, PAGE_SIZE, dir);

	return table;
}

static void exporter_unmap_dma_buf(struct dma_buf_attachment *attachment,
			       struct sg_table *table,
			       enum dma_data_direction dir)
{
	dma_unmap_page(NULL, sg_dma_address(table->sgl), PAGE_SIZE, dir);
	sg_free_table(table);
	kfree(table);
}

static void exporter_release(struct dma_buf *dma_buf)
{
	struct page *page = dma_buf->priv;

	pr_info("dmabuf release\n");
	put_page(page);
}

static void *exporter_vmap(struct dma_buf *dma_buf)
{
	struct page *page = dma_buf->priv;

	return vmap(&page, 1, 0, PAGE_KERNEL);
}

static void exporter_vunmap(struct dma_buf *dma_buf, void *vaddr)
{
	vunmap(vaddr);
}

static void *exporter_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num)
{
	struct page *page = dma_buf->priv;

	return kmap_atomic(page);
}

static void exporter_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr)
{
	kunmap_atomic(addr);
}

static void *exporter_kmap(struct dma_buf *dma_buf, unsigned long page_num)
{
	struct page *page = dma_buf->priv;

	return kmap(page);
}

static void exporter_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr)
{
	struct page *page = dma_buf->priv;

	return kunmap(page);
}

static int exporter_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
{
	struct page *page = dma_buf->priv;

	return remap_pfn_range(vma, vma->vm_start, page_to_pfn(page),
				PAGE_SIZE, vma->vm_page_prot);
}

static int exporter_begin_cpu_access(struct dma_buf *dmabuf,
				      enum dma_data_direction dir)
{
	struct dma_buf_attachment *attachment;
	struct sg_table *table;

	if (list_empty(&dmabuf->attachments))
		return 0;

	attachment = list_first_entry(&dmabuf->attachments, struct dma_buf_attachment, node);
	table = attachment->priv;
	dma_sync_sg_for_cpu(NULL, table->sgl, 1, dir);

	return 0;
}

static int exporter_end_cpu_access(struct dma_buf *dmabuf,
				enum dma_data_direction dir)
{
	struct dma_buf_attachment *attachment;
	struct sg_table *table;

	if (list_empty(&dmabuf->attachments))
		return 0;

	attachment = list_first_entry(&dmabuf->attachments, struct dma_buf_attachment, node);
	table = attachment->priv;
	dma_sync_sg_for_device(NULL, table->sgl, 1, dir);

	return 0;
}

static const struct dma_buf_ops exp_dmabuf_ops = {
	.attach = exporter_attach,
	.detach = exporter_detach,
	.map_dma_buf = exporter_map_dma_buf,
	.unmap_dma_buf = exporter_unmap_dma_buf,
	.release = exporter_release,
	.map = exporter_kmap,
	.unmap = exporter_kunmap,
	.map_atomic = exporter_kmap_atomic,
	.unmap_atomic = exporter_kunmap_atomic,
	.mmap = exporter_mmap,
	.vmap = exporter_vmap,
	.vunmap = exporter_vunmap,
	.begin_cpu_access = exporter_begin_cpu_access,
	.end_cpu_access = exporter_end_cpu_access,
};

static struct dma_buf *exporter_alloc_page(void)
{
	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
	struct dma_buf *dmabuf;
	struct page *page;

	page = alloc_page(GFP_KERNEL);

	exp_info.ops = &exp_dmabuf_ops;
	exp_info.size = PAGE_SIZE;
	exp_info.flags = O_CLOEXEC;
	exp_info.priv = page;

	dmabuf = dma_buf_export(&exp_info);

	sprintf(page_address(page), "hello world!");

	return dmabuf;
}

static long exporter_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int fd = dma_buf_fd(dmabuf_exported, O_CLOEXEC);

	if (copy_to_user((int __user *)arg, &fd, sizeof(fd)))
		return -EFAULT;

	return 0;
}
 
static struct file_operations exporter_fops = {
	.owner   = THIS_MODULE,
	.unlocked_ioctl   = exporter_ioctl,
};
 
static struct miscdevice mdev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "exporter",
	.fops = &exporter_fops,
};
 
static int __init exporter_init(void)
{
	dmabuf_exported = exporter_alloc_page();
	return misc_register(&mdev);
}

static void __exit exporter_exit(void)
{
	misc_deregister(&mdev);
}

module_init(exporter_init);
module_exit(exporter_exit);

importer 驅動
將前幾篇的 importer-kmap.cimporter-sg.c 合二爲一,如下:

importer-page.c

#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

static int importer_test1(struct dma_buf *dmabuf)
{
	void *vaddr;

	vaddr = dma_buf_kmap(dmabuf, 0);
	pr_info("read from dmabuf kmap: %s\n", (char *)vaddr);
	dma_buf_kunmap(dmabuf, 0, vaddr);

	vaddr = dma_buf_vmap(dmabuf);
	pr_info("read from dmabuf vmap: %s\n", (char *)vaddr);
	dma_buf_vunmap(dmabuf, vaddr);

	return 0;
}

static int importer_test2(struct dma_buf *dmabuf)
{
	struct dma_buf_attachment *attachment;
	struct sg_table *table;
	struct device *dev;
	unsigned int reg_addr, reg_size;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	dev_set_name(dev, "importer");

	attachment = dma_buf_attach(dmabuf, dev);
	table = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL);

	reg_addr = sg_dma_address(table->sgl);
	reg_size = sg_dma_len(table->sgl);
	pr_info("reg_addr = 0x%08x, reg_size = 0x%08x\n", reg_addr, reg_size);

	dma_buf_unmap_attachment(attachment, table, DMA_BIDIRECTIONAL);
	dma_buf_detach(dmabuf, attachment);

	return 0;
}

static long importer_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int fd;
	struct dma_buf *dmabuf;

	if (copy_from_user(&fd, (void __user *)arg, sizeof(int)))
		return -EFAULT;

	dmabuf = dma_buf_get(fd);

	importer_test1(dmabuf);
	importer_test2(dmabuf);

	dma_buf_put(dmabuf);

	return 0;
}
 
static struct file_operations importer_fops = {
	.owner	= THIS_MODULE,
	.unlocked_ioctl	= importer_ioctl,
};
 
static struct miscdevice mdev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "importer",
	.fops = &importer_fops,
};
 
static int __init importer_init(void)
{
	return misc_register(&mdev);
}

static void __exit importer_exit(void)
{
	misc_deregister(&mdev);
}

module_init(importer_init);
module_exit(importer_exit);

運行

在 my-qemu 仿真環境中執行如下命令:

# insmod /lib/modules/4.14.143/kernel/drivers/dma-buf/exporter-page.ko
# insmod /lib/modules/4.14.143/kernel/drivers/dma-buf/importer-page.ko

將看到如下打印結果:

read from dmabuf kmap: hello world!
read from dmabuf vmap: hello world!
dmabuf attach device: importer
reg_addr = 0x7f6ee000, reg_size = 0x00001000
dmabuf detach device: importer

資源

內核源碼 4.14.143
示例源碼 hexiaolong2008-GitHub/sample-code/dma-buf/08
開發平臺 Ubuntu14.04/16.04
運行平臺 my-qemu 仿真環境

結語

其實本篇的真實目的是爲下篇《dma-buf 由淺入深 —— ION 簡化版》做鋪墊的,通過本篇能夠對 page 的相關操作有個印象,這樣才方便下一篇的理解。好了,我們趕緊去看下一篇吧!




上一篇:《dma-buf 由淺入深(六) —— begin / end cpu_access》
下一篇:《dma-buf 由淺入深(八)—— ION 簡化版》
文章彙總:《DRM(Direct Rendering Manager)學習簡介》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章