ION是google在Android4.0为了解决内存碎片化管理而引入的通用内存管理器,用来支持不同的内存分配机制,如CARVOUT(PMEM),物理连续内存(kmalloc),虚拟地址连续但物理地址不连续内存(vmalloc),IOMMU等。
内核版本:linux-4.9
ion client:ion的使用者,用户空间和内核驱动要使用ion的buffer,必须先创建一个client,一个client可以有多个ion buffer,用struct ion_buffer结构表示。
ion handle:将ion buffer抽象出来,通过ion handle来管理该buffer,一般用户直接拿到的都是handle,而不是buffer,ion handle用struct ion_handle表示。
ion head:用来表示内存分配的相关信息,包括id,type,name等,用struct ion_heap表示,在调用ion_device_create时指定。
各个数据结构关系如下图所示:
ion_device : ion设备,一个平台只能创建一个,属于misc设备,vexpress_ion_probe中调用ion_device_create创建.
struct ion_device {
struct miscdevice dev; //混杂设备
struct rb_root buffers; //连接通过该ion_device创建的所有ion_buffer的树
struct mutex buffer_lock;
struct rw_semaphore lock;
struct plist_head heaps; //该ion_device所支持的ion_heap
long (*custom_ioctl)(struct ion_client *client, unsigned int cmd,
unsigned long arg); //用户指定的ioctl函数,当使用ION_IOC_CUSTOM命令时,会由ion_ioctl()委托给custom_ioctl
struct rb_root clients; //连接该ion_device下的所有ion_client的树,ion_client可以在用户空间通过ion_open创建,也可以在驱动通过ion_client_create创建
struct dentry *debug_root;
struct dentry *heaps_debug_root;
struct dentry *clients_debug_root;
int heap_cnt; //ion heaps的数目
};
ion_client : ion的使用者,用户空间和内核空间要使用ION的buffer,必须先创建一个client,一个client可以有多个buffer,用struct ion_buffer表示。
struct ion_client {
struct rb_node node; //挂在ion_device->clients树的节点
struct ion_device *dev; //指向所属的ion_device
struct rb_root handles; //管理该client下所有ion_handle的树
struct idr idr;
struct mutex lock; //保护该client管理的handles树的互斥锁,修改该handles时需持锁
const char *name;
char *display_name;
int display_serial;
struct task_struct *task;
pid_t pid;
struct dentry *debug_root;
};
ion_handle :访问ion_buffer的句柄.一个ion_buffer可能被不同ion_client共享, ion_client通过ion_handle来访问ion_buffer.
struct ion_handle {
struct kref ref; //该ion_handle的引用计数
struct ion_client *client; //指向所属的ion_client
struct ion_buffer *buffer; //指向所关联的ion_buffer
struct rb_node node; //用于添加到ion_client->handles树的节点
unsigned int kmap_cnt; //该handle map到kernel的次数. 实际是对ion_buffer->kmap_cnt的封装
int id; //由ion_client->idr分配的唯一id,每个ion_handle有且只有一个独立id
};
ion_buffer:ion分配的buffer的元数据. ion_device分配的所有buffer, 记录在ion_device->buffers链上
struct ion_buffer {
struct kref ref; //ion_buffer的引用计数
union {
struct rb_node node; //用于添加到ion_device->buffers树的节点
struct list_head list;
};
struct ion_device *dev; //指向所属的ion_device
struct ion_heap *heap; //指向所属的ion_heap
unsigned long flags; //分配ion_buffer指定的flag
unsigned long private_flags;
size_t size; //buffer的大小
void *priv_virt; //ion_buffer管理的对应ion_heap的私有数据
struct mutex lock;
int kmap_cnt; //映射给内核的次数. 第一次时, 才真正调用heap->ops->map_kernel, 此后只增加kmap_cnt计数
void *vaddr; //映射给内核的虚拟地址
int dmap_cnt; //dma映射次数
struct sg_table *sg_table; //dma映射得到的散列表
struct page **pages; //保存sg_table中内存对应的页帧信息,用于同步cache用
struct list_head vmas; //记录ion_bufer对应的内存区域,用户空间使用mmap系统调用时将相应内存区域加入该链表
/* used to track orphaned buffers */
int handle_count; //对于共享buffer,用于记录多少个ion_handle关联到该buffer
char task_comm[TASK_COMM_LEN];
pid_t pid;
};
ion_heap:ion堆,用于管理各种类型内存的分配,释放,映射,解除映射等操作的抽象对象
struct ion_heap {
struct plist_node node; //用于添加到ion_device->heaps链表的节点
struct ion_device *dev; //指向所属的ion_device
enum ion_heap_type type; //ion_heap的类型
struct ion_heap_ops *ops; //ion_heap对应的函数操作集合
unsigned long flags;
unsigned int id; //ion heap的唯一id,在分配buffer时,决定哪一种类型的heap的优先级
const char *name;
struct shrinker shrinker;
struct list_head free_list;
size_t free_list_size;
spinlock_t free_lock; //用于保护free_list的原子锁
wait_queue_head_t waitqueue;
struct task_struct *task;
int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *);
};
常见的ion_heap介绍:
ION_HEAP_TYPE_SYSTEM: System类型,系统非物理连续类型堆,使用vmalloc分配,比如上层要求分配10M内存,实际会得到4M + 4M + 2M共一个内存块,组成散列数组(sg_table),这个散列数组可直接给dma使用,用户态和内核态cpu访问时,需要通过map_user,map_kernel映射,映射后的是连续虚拟空间。
ION_HEAP_TYPE_SYSTEM_CONTIG: system_contig类型,系统物理连续堆,分配由kmalloc完成,一次最多不能分配超过4M(linux系统限制)。
ION_HEAP_TYPE_CARVEOUT: Carveout类型,用户预留一块连续大物理内存(预留区不能在bank内,不能用memblock_reserve预留,可用memblock_remove预留),通过gen_pool_alloc进行分配,一次能分配的大小不受系统4M限制,由预留量和使用情况决定,这种堆能满足de/ve对连续大块(超过4M)物理内存的需求,但缺点是预留后,系统不能使用,造成浪费。
ION_HEAP_TYPE_CHUNK: 和carveout类似,先预留一块大物理内存,再用gen_pool_alloc进行分配。不同是,只能按指定单元大小(chunk_size)来分配,比如要求分配10M内存,指定单元大小为4M,则最终得到的是3块内存区,每块大小4M,两块之间可能不连续。
ION_HEAP_TYPE_DMA: cma类型,内存分配由内存管理进行,主要通过dma map的相关ops完成。
ION_HEAP_TYPE_CUSTOM: 作为与用户自定义的堆的分界线,不实际使用;
ion_heap_ops:对于各种ion_heap的操作集合,实现buffer分配,释放,映射,解除映射等操作的集合。
struct ion_heap_ops {
int (*allocate)(struct ion_heap *heap,
struct ion_buffer *buffer, unsigned long len,
unsigned long align, unsigned long flags);
void (*free)(struct ion_buffer *buffer);
void * (*map_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
void (*unmap_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
int (*map_user)(struct ion_heap *mapper, struct ion_buffer *buffer,
struct vm_area_struct *vma);
int (*shrink)(struct ion_heap *heap, gfp_t gfp_mask, int nr_to_scan);
int (*phys)(struct ion_heap *heap, struct ion_buffer *buffer,
ion_phys_addr_t *addr, size_t *len);
};
在注册ION驱动中,实现对ion的初始化和封装,万事都从设备probe开始进行,代码如下:
static int vexpress_ion_probe(struct platform_device *pdev)
{
struct vexpress_ion_dev *ipdev;
int i;
ipdev = devm_kzalloc(&pdev->dev, sizeof(*ipdev), GFP_KERNEL);
if (!ipdev)
return -ENOMEM;
platform_set_drvdata(pdev, ipdev);
ipdev->idev = ion_device_create(NULL);
if (IS_ERR(ipdev->idev))
return PTR_ERR(ipdev->idev);
g_idev = ipdev->idev;
ipdev->data = ion_parse_dt(pdev, vexpress_heaps);
if (IS_ERR(ipdev->data))
return PTR_ERR(ipdev->data);
ipdev->heaps = devm_kzalloc(&pdev->dev,
sizeof(struct ion_heap) * ipdev->data->nr,
GFP_KERNEL);
if (!ipdev->heaps) {
ion_destroy_platform_data(ipdev->data);
return -ENOMEM;
}
for (i = 0; i < ipdev->data->nr; i++) {
ipdev->heaps[i] = ion_heap_create(&ipdev->data->heaps[i]);
if (!ipdev->heaps) {
ion_destroy_platform_data(ipdev->data);
return -ENOMEM;
}
ion_device_add_heap(ipdev->idev, ipdev->heaps[i]);
}
return 0;
}
static int vexpress_ion_remove(struct platform_device *pdev)
{
struct vexpress_ion_dev *ipdev;
int i;
ipdev = platform_get_drvdata(pdev);
for (i = 0; i < ipdev->data->nr; i++)
ion_heap_destroy(ipdev->heaps[i]);
ion_destroy_platform_data(ipdev->data);
ion_device_destroy(ipdev->idev);
return 0;
}
static const struct of_device_id vexpress_ion_match_table[] = {
{.compatible = "vexpress,ion"},
{},
};
static struct platform_driver vexpress_ion_driver = {
.probe = vexpress_ion_probe,
.remove = vexpress_ion_remove,
.driver = {
.name = "ion-vexpress",
.of_match_table = vexpress_ion_match_table,
},
};
static int __init vexpress_ion_init(void)
{
return platform_driver_register(&vexpress_ion_driver);
}
subsys_initcall(vexpress_ion_init);
在用户空间,如果想使用ion,需要进行以下两步操作:
1.创建ion client,只需简单的通过open系统调用打开/dev/ion设备,即可创建一个ion client。代码如下所示:
//在ion_device_create()中,将ion_device的文件操作集初始化为ion_fops
static const struct file_operations ion_fops = {
.owner = THIS_MODULE,
.open = ion_open,
.release = ion_release,
.unlocked_ioctl = ion_ioctl,
.compat_ioctl = compat_ion_ioctl,
};
static int ion_open(struct inode *inode, struct file *file)
{
struct miscdevice *miscdev = file->private_data;
struct ion_device *dev = container_of(miscdev, struct ion_device, dev);
struct ion_client *client;
char debug_name[64];
snprintf(debug_name, 64, "%u", task_pid_nr(current->group_leader));
client = ion_client_create(dev, debug_name); //创建ion client
if (IS_ERR(client))
return PTR_ERR(client);
file->private_data = client;
return 0;
}
2.在创建client之后,用户程序可以通过ioctl()函数的命令进行内存分配,释放,映射和解除映射等操作
3.ion在用户态使用ion的demo如下所示:
int main(void)
{
/*数据结构见drivers/staging/android/uapi/ion.h*/
struct ion_allocation_data alloc_data;
struct ion_handle_data handle_data;
struct ion_fd_data fd_data;
int fd, ret = 0, ret1 = 0;
unsigned char *buffer;
//open device
fd = open("/dev/ion", O_RDONLY);
if (fd < 0) {
printf("open device error!\n");
ret = -1;
return ret;
}
//设置ion alloc的参数
alloc_data.len = 1024 * 768 *4;
alloc_data.align = 0;
alloc_data.heap_id_mask = ION_HEAP_TYPE_DMA_MASK; //使用cma堆
alloc_data.flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
ret = ioctl(fd, ION_IOC_ALLOC, &alloc_data); //分配ion内存
if (ret) {
printf("ion alloc error!\n");
goto out1;
}
//获取dmabuf的fd
fd_data.handle = alloc_data.handle;
ret = ioctl(fd, ION_IOC_MAP, &fd_data);
if (ret) {
printf("ion map error!\n");
goto out2;
}
//mmap to user space
buffer = mmap(NULL, alloc_data.len, PROT_READ|PROT_WRITE, MAP_SHARED, fd_data.fd, 0);
if (MAP_FAILED == buffer) {
printf("mmap to user space error!\n");
goto out3;
}
//use in user space
memset(buffer, 0, alloc_data.len);
//after use, munmap user buffer
ret = munmap(buffer, alloc_data.len);
if (ret) {
printf("munmap error!\n");
}
out3:
//close dmabuf fd
close(fd_data.fd);
out2:
//free buffer
handle_data.handle = alloc_data.handle;
ret = ioctl(fd, ION_IOC_FREE, &handle_data);
if (ret) {
printf("ion free buffer error!\n");
}
out1:
//close ion device
close(fd);
return ret;
}
内核空间使用ion跟用户空间差不多,只是它的使用对用户空间来说是透明的而已。
1.同样,通过调用ion_client_create()函数创建一个ion client,该函数在kernel空间被ION驱动中封装成以下函数:
struct ion_device *g_idev = NULL;
struct ion_client *vexpress_ion_client_create(char *name)
{
/*
* The assumption is that if there is a NULL device, the ion
* driver has not yet probed.
*/
if (IS_ERR_OR_NULL(g_idev))
return ERR_PTR(-EPROBE_DEFER);
if (IS_ERR(g_idev))
return (struct ion_client *)g_idev;
return ion_client_create(g_idev, name);
}
EXPORT_SYMBOL(vexpress_ion_client_create);
2.通过ion_alloc()来进行内存分配
3.通过ion_map_kernel()来进行地址映射
4.如果有些硬件只能通过physical addresses来操作physically-contiguous buffers,那么,这些对应的drivers就需通过调用以下接口来获取虚拟地址所对应的物理地址才行。但ion框架中不推荐使用这种方式,而是建议通过ion_sg_table方式来使用。
int ion_phys(struct ion_client *client, struct ion_handle *handle,ion_phys_addr_t *addr, size_t *len)
5.如果多个设备使用到同一个ion buffer,可以使用以下几个个接口实现buffer的共享,减少buffer的拷贝过程,实现zero-copy.
struct dma_buf *ion_share_dma_buf(struct ion_client *client,struct ion_handle *handle);
int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle);
int ion_share_dma_buf_fd2(struct ion_client *client, struct ion_handle *handle);
struct ion_handle *ion_import_dma_buf(struct ion_client *client,struct dma_buf *dmabuf);
struct ion_handle *ion_import_dma_buf_fd(struct ion_client *client, int fd);
以上两个接口分别实现了dma_buf的共享和获取。
内存共享和大块内存的使用,在实际场景下面的需求是很大的,这里举三个简单的应用场景:
用户态和内核态共享内存
用户态不同进程共享内存
内核态使用ION分配内存
用户态和内核态共享内存分两种情况,一种是内核态申请buffer,共享给用户态,另一种是用户态申请buffer,共享给内核态,这两种方法,都是通过fd方式进行共享即可,不用进行copy_to_user()或者copy_from_user()这些copy操作,即zero-copy。
#include
#include
#include
#include
#include
#include
#include
#include "dmabuf_test.h"
#include "ion.h"
#define PAGE_SIZE 4096
static int test_share_dmabuf_to_kernel(int fd)
{
int ret = 0;
struct dmabuf_test_rw_data data = {0};
int ion_fd = 0;
struct ion_allocation_data alloc_data;
struct ion_handle_data handle_data;
struct ion_fd_data fd_data;
unsigned char *buffer;
ion_fd = open("/dev/ion", O_RDONLY);
if (ion_fd < 0) {
printf("open ion device faile\n");
return ion_fd;
}
alloc_data.len = PAGE_SIZE;
alloc_data.align = 0;
alloc_data.heap_id_mask = ION_HEAP_SYSTEM_MASK;
alloc_data.flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
/* alloc dmabuf */
ret = ioctl(ion_fd, ION_IOC_ALLOC, &alloc_data);
if (ret < 0) {
printf("ion alloc error\n");
goto close_fd;
}
/* get dmabuf fd */
fd_data.handle = alloc_data.handle;
ret = ioctl(ion_fd, ION_IOC_MAP, &fd_data);
if (ret < 0) {
printf("ion map error\n");
goto free_buf;
}
//mmap to user space
buffer = mmap(NULL, alloc_data.len, PROT_READ|PROT_WRITE, MAP_SHARED,
fd_data.fd, 0);
if (MAP_FAILED == buffer) {
printf("mmap to user space error!\n");
goto close_dmabuf_fd;
}
buffer[0] = 'a';
buffer[1] = 'b';
buffer[2] = 'c';
buffer[3] = 'd';
buffer[4] = '\n';
data.fd = fd_data.fd;
/* share dmabuf fd to kernel */
ret = ioctl(fd, DMABUF_IOC_TEST_GET_FD_FROM_USER, &data);
if (ret < 0) {
printf("share dmabuf fd to kernel space error\n");
}
ret = munmap(buffer, alloc_data.len);
if (ret < 0) {
printf("munmap error\n!");
}
close_dmabuf_fd:
close(fd_data.fd);
free_buf:
handle_data.handle = alloc_data.handle;
ret = ioctl(ion_fd, ION_IOC_FREE, &handle_data);
if (ret)
printf("ion free buffer error\n");
close_fd:
close(ion_fd);
return ret;
}
int main()
{
int fd;
int ret = 0;
printf("start demo\n");
fd = open("/dev/dmabuf-test", O_RDONLY);
if (fd < 0) {
printf("open device faile\n");
return fd;
}
ret = test_share_dmabuf_to_kernel(fd);
close(fd);
return ret;
}
static long dmabuf_test_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct miscdevice *miscdev = filp->private_data;
struct dmabuf_test_device *dev = container_of(miscdev, struct dmabuf_test_device, misc);
unsigned int dir;
struct dmabuf_test_rw_data data;
dir = dmabuf_test_ioctl_dir(cmd);
if (_IOC_SIZE(cmd) > sizeof(data))
return -EINVAL;
if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
return -EFAULT;
if (!(dir & _IOC_WRITE))
memset(&data, 0, sizeof(data));
switch(cmd) {
case DMABUF_IOC_TEST_GET_FD_FROM_KERNEL:
{
/* share fd must keep it in device ioctl, otherwise userspace
* mapping file faile
*/
data.fd = ion_share_dma_buf_fd(dev->client, dev->handle);
if (data.fd < 0) {
pr_err("get dmabuf fd error\n");
return -EBADF;
}
break;
}
case DMABUF_IOC_TEST_GET_FD_FROM_USER:
{
struct dma_buf *dmabuf;
unsigned char *buf;
int ret;
dmabuf = dma_buf_get(data.fd);
if (IS_ERR_OR_NULL(dmabuf)) {
pr_err("get dmabuf from dmabuf fd:%d error\n", data.fd);
return -EINVAL;
}
ret = dma_buf_begin_cpu_access(dmabuf, DMA_BIDIRECTIONAL);
if (ret < 0) {
pr_err("begin cpu access error\n");
dma_buf_put(dmabuf);
return -EFAULT;
}
/* buf size is PAGE_SIZE */
buf = (unsigned char *)dma_buf_kmap(dmabuf, 0);
if (IS_ERR_OR_NULL(buf)) {
pr_err("map dmabuf to kernel space error\n");
return -EFAULT;
}
printk("buf data:%s\n", buf);
ret = dma_buf_end_cpu_access(dmabuf, DMA_BIDIRECTIONAL);
if (ret < 0) {
pr_err("end cpu access error\n");
dma_buf_kunmap(dmabuf, 0, (void *)buf);
dma_buf_put(dmabuf);
return -EFAULT;
}
dma_buf_kunmap(dmabuf, 0, (void *)buf);
dma_buf_put(dmabuf);
break;
}
default:
return -ENOTTY;
}
if (dir & _IOC_READ) {
if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) {
return -EFAULT;
}
}
return 0;
}
/*
*
* Copyright (C) 2013 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) "dma-buf-test: " fmt
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ion.h"
#include "../uapi/dmabuf_test.h"
struct dmabuf_test_device {
struct miscdevice misc;
struct ion_client *client;
struct ion_handle *handle;
};
static int dmabuf_alloc_dmabuf(struct dmabuf_test_device *dev)
{
struct ion_handle *handle = NULL;
unsigned char *buf = NULL;
unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
handle = ion_alloc(dev->client, PAGE_SIZE, 0, (1 << ION_HEAP_TYPE_SYSTEM), flags);
if (IS_ERR_OR_NULL(handle)) {
pr_err("ion alloc handle error\n");
return -EINVAL;
}
dev->handle = handle;
buf = (unsigned char *)ion_map_kernel(dev->client, handle);
buf[0] = 'e';
buf[1] = 'f';
buf[2] = 'g';
buf[3] = 'h';
buf[4] = '\n';
ion_unmap_kernel(dev->client, handle);
return 0;
}
static int dmabuf_test_open(struct inode *inode, struct file *file)
{
return 0;
}
static int dmabuf_test_release(struct inode *inode, struct file *file)
{
return 0;
}
static unsigned long dmabuf_test_ioctl_dir(unsigned int cmd)
{
switch(cmd) {
case DMABUF_IOC_TEST_GET_FD_FROM_KERNEL:
case DMABUF_IOC_TEST_GET_FD_FROM_USER:
return _IOC_DIR(cmd);
default:
return _IOC_DIR(cmd);
}
}
static long dmabuf_test_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct miscdevice *miscdev = filp->private_data;
struct dmabuf_test_device *dev = container_of(miscdev, struct dmabuf_test_device, misc);
unsigned int dir;
struct dmabuf_test_rw_data data;
dir = dmabuf_test_ioctl_dir(cmd);
if (_IOC_SIZE(cmd) > sizeof(data))
return -EINVAL;
if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
return -EFAULT;
if (!(dir & _IOC_WRITE))
memset(&data, 0, sizeof(data));
switch(cmd) {
case DMABUF_IOC_TEST_GET_FD_FROM_KERNEL:
{
/* share fd must keep it in device ioctl, otherwise userspace
* mapping file faile
*/
data.fd = ion_share_dma_buf_fd(dev->client, dev->handle);
if (data.fd < 0) {
pr_err("get dmabuf fd error\n");
return -EBADF;
}
break;
}
case DMABUF_IOC_TEST_GET_FD_FROM_USER:
{
struct dma_buf *dmabuf;
unsigned char *buf;
int ret;
dmabuf = dma_buf_get(data.fd);
if (IS_ERR_OR_NULL(dmabuf)) {
pr_err("get dmabuf from dmabuf fd:%d error\n", data.fd);
return -EINVAL;
}
ret = dma_buf_begin_cpu_access(dmabuf, DMA_BIDIRECTIONAL);
if (ret < 0) {
pr_err("begin cpu access error\n");
dma_buf_put(dmabuf);
return -EFAULT;
}
/* buf size is PAGE_SIZE */
buf = (unsigned char *)dma_buf_kmap(dmabuf, 0);
if (IS_ERR_OR_NULL(buf)) {
pr_err("map dmabuf to kernel space error\n");
return -EFAULT;
}
printk("buf data:%s\n", buf);
ret = dma_buf_end_cpu_access(dmabuf, DMA_BIDIRECTIONAL);
if (ret < 0) {
pr_err("end cpu access error\n");
dma_buf_kunmap(dmabuf, 0, (void *)buf);
dma_buf_put(dmabuf);
return -EFAULT;
}
dma_buf_kunmap(dmabuf, 0, (void *)buf);
dma_buf_put(dmabuf);
break;
}
default:
return -ENOTTY;
}
if (dir & _IOC_READ) {
if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) {
return -EFAULT;
}
}
return 0;
}
static const struct file_operations dmabuf_test_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = dmabuf_test_ioctl,
.compat_ioctl = dmabuf_test_ioctl,
.open = dmabuf_test_open,
.release = dmabuf_test_release,
};
static int __init dmabuf_test_probe(struct platform_device *pdev)
{
int ret;
struct dmabuf_test_device *testdev;
testdev = devm_kzalloc(&pdev->dev, sizeof(struct dmabuf_test_device),
GFP_KERNEL);
if (!testdev)
return -ENOMEM;
testdev->misc.minor = MISC_DYNAMIC_MINOR;
testdev->misc.name = "dmabuf-test";
testdev->misc.fops = &dmabuf_test_fops;
testdev->misc.parent = &pdev->dev;
ret = misc_register(&testdev->misc);
if (ret) {
pr_err("failed to register misc device.\n");
return ret;
}
/*create ion client */
testdev->client = vexpress_ion_client_create("dmabuf-test-client");
if (!testdev->client) {
pr_err("failed to create ion client\n");
return -EINVAL;
}
dmabuf_alloc_dmabuf(testdev);
platform_set_drvdata(pdev, testdev);
return 0;
}
static int dmabuf_test_remove(struct platform_device *pdev)
{
struct dmabuf_test_device *testdev;
testdev = platform_get_drvdata(pdev);
if (!testdev)
return -ENODATA;
misc_deregister(&testdev->misc);
return 0;
}
static struct platform_device *dmabuf_test_pdev;
static struct platform_driver dmabuf_test_platform_driver = {
.remove = dmabuf_test_remove,
.driver = {
.name = "dmabuf-test",
},
};
static int __init dmabuf_test_init(void)
{
dmabuf_test_pdev = platform_device_register_simple("dmabuf-test",
-1, NULL, 0);
if (IS_ERR(dmabuf_test_pdev))
return PTR_ERR(dmabuf_test_pdev);
return platform_driver_probe(&dmabuf_test_platform_driver, dmabuf_test_probe);
}
static void __exit dmabuf_test_exit(void)
{
platform_driver_unregister(&dmabuf_test_platform_driver);
platform_device_unregister(dmabuf_test_pdev);
}
module_init(dmabuf_test_init);
module_exit(dmabuf_test_exit);
MODULE_LICENSE("GPL v2");
#include
#include
#include
#include
#include "dmabuf_test.h"
#include "ion.h"
#define PAGE_SIZE 4096
static int test_share_dmabuf_from_kernel(int fd)
{
int ret = 0;
unsigned char *buf = NULL;
struct dmabuf_test_rw_data data = {0};
ret = ioctl(fd, DMABUF_IOC_TEST_GET_FD_FROM_KERNEL, &data);
if (ret < 0) {
printf("get dmabuf fd faile\n");
return ret;
}
buf = (unsigned char *)mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, data.fd, 0);
if (buf == MAP_FAILED) {
close(data.fd);
printf("map dmabuf fail\n");
return -1;
}
printf("user space buffer data:%s\n", buf);
ret = munmap(buf, PAGE_SIZE);
if (ret < 0) {
printf("munmap error\n!");
}
close(data.fd);
return 0;
}
int main()
{
int fd;
int ret = 0;
printf("start demo\n");
fd = open("/dev/dmabuf-test", O_RDONLY);
if (fd < 0) {
printf("open device faile\n");
return fd;
}
ret = test_share_dmabuf_from_kernel(fd);
close(fd);
return ret;
}
服务进程
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PAGE_SIZE 4096
int main()
{
int clientfd, listenfd;
struct sockaddr_un servaddr, cliaddr;
int ret;
struct msghdr msg;
struct iovec iov[1];
char buf[100];
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
int recvfd;
socklen_t len;
unsigned char *buffer;
listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (listenfd < 0) {
printf("socket failed\n");
return listenfd;
}
unlink("test_socket");
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path, "test_socket");
//servaddr.sun_path = "test_socket";
ret = bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
if (ret < 0) {
printf("bind socket failed.\n");
close(listenfd);
return ret;
}
listen(listenfd, 5);
while(1) {
len = sizeof(cliaddr);
clientfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
if (clientfd < 0) {
printf("accept failed\n");
continue;
}
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf)/sizeof(buf[0]);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
ret = recvmsg(clientfd, &msg, 0);
if (ret < 0) {
return ret;
}
//check recv data and len
if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL && (cmptr->cmsg_len == CMSG_LEN(sizeof(int)))) {
if(cmptr->cmsg_level != SOL_SOCKET) {
printf("cmsg_level is not SOL_SOCKET\n");
continue;
}
if (cmptr->cmsg_type != SCM_RIGHTS) {
printf("cmsg_type is not SCM_RIGHTS\n");
continue;
}
recvfd = *((int *)CMSG_DATA(cmptr));
break;
}
}
printf("recv fd:%d\n", recvfd);
buffer = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, recvfd, 0);
if (buffer == MAP_FAILED) {
close(recvfd);
printf("map dmmbuf fail\n");
return MAP_FAILED;
}
printf("service buffer data:%s\n", buffer);
ret = munmap(buffer, PAGE_SIZE);
if (ret < 0) {
printf("munmap error\n!");
}
close(recvfd);
return ret;
}
服务进程主要工作:
监听本地socket,等待客户端进程共享dmabuf fd
拿到共享的dmabuf fd后,map到自己进程空间,访问该buffer
关闭dmabuf fd,退出进程
客户端进程
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "dmabuf_test.h"
#include "ion.h"
#define PAGE_SIZE 4096
int test_share_dmabuf_in_process()
{
int ret;
int ion_fd = 0;
struct ion_allocation_data alloc_data;
struct ion_handle_data handle_data;
struct ion_fd_data fd_data;
unsigned char *buffer;
/* use to process communication */
int socket_fd;
struct sockaddr_un addr;
struct msghdr msg;
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
struct iovec iov[1];
char buf[100]; //for what?
ion_fd = open("/dev/ion", O_RDONLY);
if (ion_fd < 0) {
printf("open ion device faile\n");
return ion_fd;
}
alloc_data.len = PAGE_SIZE;
alloc_data.align = 0;
alloc_data.heap_id_mask = ION_HEAP_SYSTEM_MASK;
alloc_data.flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
/* alloc dmabuf */
ret = ioctl(ion_fd, ION_IOC_ALLOC, &alloc_data);
if (ret < 0) {
printf("ion alloc error\n");
goto close_fd;
}
/* get dmabuf share fd */
fd_data.handle = alloc_data.handle;
ret = ioctl(ion_fd, ION_IOC_MAP, &fd_data);
if (ret < 0) {
printf("ion map error\n");
goto free_buf;
}
//mmap to user space
buffer = mmap(NULL, alloc_data.len, PROT_READ|PROT_WRITE, MAP_SHARED,
fd_data.fd, 0);
if (MAP_FAILED == buffer) {
printf("mmap to user space error!\n");
goto close_dmabuf_fd;
}
buffer[0] = '1';
buffer[1] = '2';
buffer[2] = '3';
buffer[3] = '4';
buffer[4] = '\n';
printf("client buffer data:%s\n", buffer);
ret = munmap(buffer, alloc_data.len);
if (ret < 0) {
printf("munmap error\n!");
goto close_dmabuf_fd;
}
socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (socket_fd < 0) {
printf("create socket failed\n");
ret = socket_fd;
goto close_dmabuf_fd;
}
bzero(&addr, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, "test_socket");
//addr.sun_path = "test_socket";
ret = connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr));
if(ret < 0) {
printf("connect failed!\n");
goto close_dmabuf_fd;
}
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len = CMSG_LEN(sizeof(int));
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
*((int *) CMSG_DATA(cmptr)) = fd_data.fd;
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf)/sizeof(buf[0]);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
ret = sendmsg(socket_fd, &msg, 0);
if (ret < 0) {
printf("sendmsg failed\n");
goto close_dmabuf_fd;
}
printf("ret :%d, fd:%d\n", ret, fd_data.fd);
close_dmabuf_fd:
close(fd_data.fd);
free_buf:
handle_data.handle = alloc_data.handle;
ret = ioctl(ion_fd, ION_IOC_FREE, &handle_data);
if (ret)
printf("ion free buffer error\n");
close_fd:
close(ion_fd);
return ret;
}
int main()
{
int fd;
int ret = 0;
printf("start demo\n");
fd = open("/dev/dmabuf-test", O_RDONLY);
if (fd < 0) {
printf("open device faile\n");
return fd;
}
ret = test_share_dmabuf_in_process();
if (ret < 0) {
printf("test share dmabuf in process faile\n");
}
close(fd);
return 0;
}
客户端进程主要工作:
通过ION申请dmabuf,并且获取到dmabuf对应的fd
通过socket,把dmabuf fd共享到其他进程
释放自己进程申请的资源,解除buffer映射关系等
内核态使用ion分配内存,demo如下:
static int dmabuf_alloc_dmabuf(struct dmabuf_test_device *dev)
{
struct ion_handle *handle = NULL;
unsigned char *buf = NULL;
unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
struct ion_client *client;
client = vexpress_ion_client_create("dmabuf-test-client");
handle = ion_alloc(client, PAGE_SIZE, 0, (1 << ION_HEAP_TYPE_SYSTEM), flags);
if (IS_ERR_OR_NULL(handle)) {
pr_err("ion alloc handle error\n");
return -EINVAL;
}
dev->handle = handle;
buf = (unsigned char *)ion_map_kernel(client, handle);
buf[0] = 'e';
buf[1] = 'f';
buf[2] = 'g';
buf[3] = 'h';
buf[4] = '\n';
ion_unmap_kernel(client, handle);
return 0;
}
创建ion client
申请ion handle
通过ion_map_kernel()接口map dmabuf到kernel space
使用完后,调用ion_unmap_kernel()接口做解除映射