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 由浅入深(七)》中,我们学习了如何使用 alloc_page() 方式来分配内存,但是该驱动只能分配1个PAGE_SIZE。本篇我们将在上一篇的基础上,实现一个简化版的ION驱动,以此来实现任意 size 大小的内存分配。

如果你对 dma-buf 还不熟悉,强烈建议先阅读本系列教程的 1~6 篇,对 dma-buf 有一定理解后再回过头来阅读本文。

准备

为了和 kernel 标准 ion 驱动兼容,本篇引用了 driver/staging/android/uapi/ion.h 头文件,目的是为了方便 userspace 直接使用 struct ion_allocation_dataION_IOC_ALLOC 宏:

struct ion_allocation_data {__u64 len;__u32 heap_id_mask;__u32 flags;__u32 fd;__u32 unused;
};#define ION_IOC_MAGIC     'I'
#define ION_IOC_ALLOC       _IOWR(ION_IOC_MAGIC, 0, \struct ion_allocation_data)

本篇 ion 驱动只使用 ion_allocation_data 结构体中的 lenfd 这两个元素,其它元素不做处理。

示例

驱动程序

exporter-ion.c

#include <linux/dma-buf.h>
#include <linux/highmem.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>#include "../staging/android/uapi/ion.h"struct ion_data {int npages;struct page *pages[];
};static int ion_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 ion_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 *ion_map_dma_buf(struct dma_buf_attachment *attachment,enum dma_data_direction dir)
{struct ion_data *data = attachment->dmabuf->priv;struct sg_table *table;struct scatterlist *sg;int i;table = kmalloc(sizeof(*table), GFP_KERNEL);sg_alloc_table(table, data->npages, GFP_KERNEL);sg = table->sgl;for (i = 0; i < data->npages; i++) {sg_set_page(sg, data->pages[i], PAGE_SIZE, 0);sg = sg_next(sg);}dma_map_sg(NULL, table->sgl, table->nents, dir);return table;
}static void ion_unmap_dma_buf(struct dma_buf_attachment *attachment,struct sg_table *table,enum dma_data_direction dir)
{dma_unmap_sg(NULL, table->sgl, table->nents, dir);sg_free_table(table);kfree(table);
}static void ion_release(struct dma_buf *dma_buf)
{struct ion_data *data = dma_buf->priv;int i;pr_info("dmabuf release\n");for (i = 0; i < data->npages; i++)put_page(data->pages[i]);kfree(data);
}static void *ion_vmap(struct dma_buf *dma_buf)
{struct ion_data *data = dma_buf->priv;return vm_map_ram(data->pages, data->npages, 0, PAGE_KERNEL);
}static void ion_vunmap(struct dma_buf *dma_buf, void *vaddr)
{struct ion_data *data = dma_buf->priv;vm_unmap_ram(vaddr, data->npages);
}static void *ion_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num)
{struct ion_data *data = dma_buf->priv;return kmap_atomic(data->pages[page_num]);
}static void ion_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr)
{kunmap_atomic(addr);
}static void *ion_kmap(struct dma_buf *dma_buf, unsigned long page_num)
{struct ion_data *data = dma_buf->priv;return kmap(data->pages[page_num]);
}static void ion_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr)
{struct ion_data *data = dma_buf->priv;return kunmap(data->pages[page_num]);
}static int ion_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
{struct ion_data *data = dma_buf->priv;unsigned long vm_start = vma->vm_start;int i;for (i = 0; i < data->npages; i++) {remap_pfn_range(vma, vm_start, page_to_pfn(data->pages[i]),PAGE_SIZE, vma->vm_page_prot);vm_start += PAGE_SIZE; }return 0;
}static int ion_begin_cpu_access(struct dma_buf *dmabuf,enum dma_data_direction dir)
{struct dma_buf_attachment *attachment;struct sg_table *table;attachment = list_first_entry(&dmabuf->attachments, struct dma_buf_attachment, node);table = attachment->priv;dma_sync_sg_for_cpu(NULL, table->sgl, table->nents, dir);return 0;
}static int ion_end_cpu_access(struct dma_buf *dmabuf,enum dma_data_direction dir)
{struct dma_buf_attachment *attachment;struct sg_table *table;attachment = list_first_entry(&dmabuf->attachments, struct dma_buf_attachment, node);table = attachment->priv;dma_sync_sg_for_device(NULL, table->sgl, table->nents, dir);return 0;
}static const struct dma_buf_ops exp_dmabuf_ops = {.attach = ion_attach,.detach = ion_detach,.map_dma_buf = ion_map_dma_buf,.unmap_dma_buf = ion_unmap_dma_buf,.release = ion_release,.map = ion_kmap,.unmap = ion_kunmap,.map_atomic = ion_kmap_atomic,.unmap_atomic = ion_kunmap_atomic,.mmap = ion_mmap,.vmap = ion_vmap,.vunmap = ion_vunmap,.begin_cpu_access = ion_begin_cpu_access,.end_cpu_access = ion_end_cpu_access,
};static struct dma_buf *ion_alloc(size_t size)
{DEFINE_DMA_BUF_EXPORT_INFO(exp_info);struct dma_buf *dmabuf;struct ion_data *data;int i, npages;npages = PAGE_ALIGN(size) / PAGE_SIZE;data = kmalloc(sizeof(*data) + npages * sizeof(struct page *),GFP_KERNEL);data->npages = npages;for (i = 0; i < npages; i++)data->pages[i] = alloc_page(GFP_KERNEL);exp_info.ops = &exp_dmabuf_ops;exp_info.size = npages * PAGE_SIZE;exp_info.flags = O_CLOEXEC;exp_info.priv = data;dmabuf = dma_buf_export(&exp_info);return dmabuf;
}static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct dma_buf *dmabuf;struct ion_allocation_data alloc_data;/* currently just only support ION_IOC_ALLOC ioctl */if (cmd != ION_IOC_ALLOC)return -EINVAL;copy_from_user(&alloc_data, (void __user *)arg, sizeof(alloc_data));dmabuf = ion_alloc(alloc_data.len);alloc_data.fd = dma_buf_fd(dmabuf, O_CLOEXEC);copy_to_user((void __user *)arg, &alloc_data, sizeof(alloc_data));return 0;
}static struct file_operations ion_fops = {.owner   = THIS_MODULE,.unlocked_ioctl   = ion_ioctl,
};static struct miscdevice mdev = {.minor = MISC_DYNAMIC_MINOR,.name = "ion",.fops = &ion_fops,
};static int __init ion_init(void)
{return misc_register(&mdev);
}static void __exit ion_exit(void)
{misc_deregister(&mdev);
}module_init(ion_init);
module_exit(ion_exit);

本驱动代码其实有80%是照搬的 i915 selftests 中的 mock_dmabuf.c 文件,大家如果感兴趣也可以去看一下。

应用程序

ion_test.c

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>#include "ion.h"#define PAGE_SIZE 4096int main(int argc, char *argv[])
{int fd;struct ion_allocation_data alloc_data;fd = open("/dev/ion", O_RDWR);alloc_data.len = 3 * PAGE_SIZE;ioctl(fd, ION_IOC_ALLOC, &alloc_data);printf("ion alloc success: size = %llu, dmabuf_fd = %u\n",alloc_data.len, alloc_data.fd);close(fd);return 0;
}

该应用程序通过 ION_IOC_ALLOC ioctl 请求分配了3个 page 的物理 buffer,如果底层驱动分配成功,则会将该 dma-buf 所对应的 fd 返回给应用程序,以便后续执行 mmap 操作或将 fd 传给其它模块。

需要注意的是,这里的3个 pages 是通过3次调用 alloc_page() 来分配的,因此每个 page 之间可能是不连续的,也可以近似的认为该 ion 驱动分配的 buffer 属于 ION_HEAP_TYPE_SYSTEM。如果要分配物理连续的 pages,请使用 alloc_pages() 进行分配。

运行

在 my-qemu 仿真环境中执行如下命令:

# insmod /lib/modules/4.14.143/kernel/drivers/dma-buf/exporter-ion.ko
# ./ion_test

将看到如下打印结果:

ion alloc success: size = 12288, dmabuf_fd = 3

资源

内核源码 4.14.143
示例源码 hexiaolong2008-GitHub/sample-code/dma-buf/09
开发平台 Ubuntu14.04/16.04
运行平台 my-qemu 仿真环境

结语

《dma-buf 由浅入深》系列教程到此就彻底结束了,希望这8篇文章能够对你的工作有所帮助。

上一篇:《dma-buf 由浅入深(七) —— alloc page 版本》
文章汇总:《DRM(Direct Rendering Manager)学习简介》

dma-buf 由浅入深(八) —— ION 简化版相关推荐

  1. v4l2 use V4L2_MEMORY_MMAP方式导出为 DMA BUF fd 方式使用

    V4L2_MEMORY_MMAP 导出 fd 需要使用 vb2_ioctl_expbuf (只能使用于VB2_MEMORY_MMAP 方式). int buffer_export(int v4lfd, ...

  2. dma-buf 由浅入深(三) —— map attachment

    dma-buf 由浅入深(一) -- 最简单的 dma-buf 驱动程序 dma-buf 由浅入深(二) -- kmap / vmap dma-buf 由浅入深(三) -- map attachmen ...

  3. dma-buf 由浅入深(一) —— 最简单的 dma-buf 驱动程序

    dma-buf 由浅入深(一) -- 最简单的 dma-buf 驱动程序 dma-buf 由浅入深(二) -- kmap / vmap dma-buf 由浅入深(三) -- map attachmen ...

  4. dma-buf 由浅入深(二) —— kmap / vmap

    dma-buf 由浅入深(一) -- 最简单的 dma-buf 驱动程序 dma-buf 由浅入深(二) -- kmap / vmap dma-buf 由浅入深(三) -- map attachmen ...

  5. dma-buf 由浅入深(六) —— begin / end cpu_access

    dma-buf 由浅入深(一) -- 最简单的 dma-buf 驱动程序 dma-buf 由浅入深(二) -- kmap / vmap dma-buf 由浅入深(三) -- map attachmen ...

  6. ION to SMMU

    参考:DMA-BUF 由浅入深 参考:dma-buf 由浅入深(三) -- map attachment 本文基于:arm64 linux5.4.226 分析. ION to IOMMU概述 本文主要 ...

  7. dma-buf 由浅入深(五) —— File

    dma-buf 由浅入深(一) -- 最简单的 dma-buf 驱动程序 dma-buf 由浅入深(二) -- kmap / vmap dma-buf 由浅入深(三) -- map attachmen ...

  8. dma-buf 由浅入深(四) —— mmap

    dma-buf 由浅入深(一) -- 最简单的 dma-buf 驱动程序 dma-buf 由浅入深(二) -- kmap / vmap dma-buf 由浅入深(三) -- map attachmen ...

  9. dma-buf 由浅入深(七) —— alloc page 版本

    dma-buf 由浅入深(一) -- 最简单的 dma-buf 驱动程序 dma-buf 由浅入深(二) -- kmap / vmap dma-buf 由浅入深(三) -- map attachmen ...

最新文章

  1. cstring 比较_属牛人和属蛇人姻缘婚配关系比较和谐
  2. Linux中设置服务自启动的三种方式(转)
  3. 区域内点的个数_JAVA
  4. 词法分析(3)---DFA
  5. C++实现图的深度优先遍历和广度优先遍历
  6. Android中的NavigationView
  7. 深入了解AI在金融、能源领域的网络安全应用
  8. JDBC04 PreparedStatement
  9. 【年终总结系列 2017】求职之路
  10. vs html复选框,组合框和复选框
  11. 存储单位:位、字节、字符、千字节KB、兆字节MB
  12. 【NYNU 1151】轻羽飞扬 数塔DP
  13. LXR( Linux超文本交叉代码检索工具)
  14. 汉信码生成和识别SDK
  15. HDMI切换器的各种版本能不能互通?
  16. mysql-8.0.16-winx64_mysql-8.0.16-winx64的最新安装教程
  17. 58同城post登陆参数分析,典型的eval加密js案例
  18. java中slacc意思,家用联通光纤开启IPv6
  19. vim 代码格式设置
  20. C语言中ASCII的应用

热门文章

  1. 【不仅仅有面经】2020大厂Android面试经历,Android从入门到精通
  2. 【Spring系列】- Bean生命周期底层原理
  3. 玩骰子的儿童(赫拉克利特)
  4. spark触发adaptive skewed join的例子code
  5. Kafka-Broker Spread,Broker Skewed,Broker Leader Skewed指标含义
  6. 汇编c语言混合编程pdf,C语言及汇编语言混合编程的方法.pdf
  7. 加减乘除计算机英语,英语里的加减乘除
  8. 高质量毕业答辩PPT模板+PPT网站
  9. 阿拉伯数字转中文读法
  10. 无界函数的反常积分的收敛法