linux 之dma_buf (4)- mmap

一、前言

前面几篇都是在 kernel space 对 dma-buf 进行访问的,本篇我们将一起来学习,如何在 user space 访问 dma-buf。当然,user space 访问 dma-buf 也属于 CPU Access 的一种。

二、mmap

为了方便应用程序能直接在用户空间读写 dma-buf 的内存,dma_buf_ops 为我们提供了一个 mmap 回调接口,可以把 dma-buf 的物理内存直接映射到用户空间,这样应用程序就可以像访问普通文件那样访问 dma-buf 的物理内存了。

在linux  设备驱动中,大多数驱动的 mmap 操作接口都是通过调用 remap_pfn_range() 函数来实现的,dma-buf 也不例外。

除了 dma_buf_ops 提供的 mmap 回调接口外,dma-buf 还为我们提供了 dma_buf_mmap() 内核 API,使得我们可以在其他设备驱动中就地取材,直接引用 dma-buf 的 mmap 实现,以此来间接的实现设备驱动的 mmap 文件操作接口.

 

接下来,我们将通过两个示例来演示如何在 Userspace 访问 dma-buf 的物理内存。

  • 示例一:直接使用 dma-buf 的 fd 做 mmap() 操作
  • 示例二:使用 exporter 的 fd 做 mmap() 操作

三、直接使用 dma-buf 的 fd 做 mmap() 操作

本示例主要演示如何在驱动层实现 dma-buf 的 mmap 回调接口,以及如何在用户空间直接使用 dma-buf 的 fd 进行 mmap() 操作。

export_test.c

#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>struct dma_buf *dmabuf_export;
EXPORT_SYMBOL(dmabuf_export);static int exporter_attach(struct dma_buf* dmabuf,  struct dma_buf_attachment *attachment)
{pr_info("dmanbuf attach device :%s \n",dev_name(attachment->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)
{
//      void *vaddr = attachment->dmabuf->priv;struct sg_table *table;int ret;table = kmalloc(sizeof(struct sg_table),GFP_KERNEL);ret = sg_alloc_table(table, 1, GFP_KERNEL);if(ret)pr_info("sg_alloc_table err\n");sg_dma_len(table->sgl) = PAGE_SIZE;pr_info("sg_dma_len: %d\n ", sg_dma_len(table->sgl));//      sg_dma_address(table->sgl) = dma_map_single(NULL, vaddr, PAGE_SIZE,dir);
//      pr_info("sg_dma_address: 0x%llx\n",(unsigned long long)sg_dma_address(table->sgl));return table;
}static void exporter_unmap_dma_buf(struct dma_buf_attachment *attachment,struct sg_table *table,enum dma_data_direction dir)
{dma_unmap_single(NULL, sg_dma_address(table->sgl), PAGE_SIZE, dir);sg_free_table(table);kfree(table);
}static void exporter_release(struct dma_buf *dmabuf)
{return kfree(dmabuf->priv);
}/*static void *exporter_kmap_atomic(struct dma_buf *dmabuf, unsigned long page_num)
{return NULL;
}static void *exporter_kmap(struct dma_buf *dmabuf, unsigned long page_num)
{return NULL;
}*/
static void* exporter_vmap(struct dma_buf *dmabuf)
{return dmabuf->priv;}
static int exporter_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{void *vaddr = dmabuf->priv;struct page * page_ptr = virt_to_page(vaddr);return remap_pfn_range(vma,vma->vm_start, page_to_pfn(page_ptr),PAGE_SIZE, vma->vm_page_prot);
}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_atomic = exporter_kmap_atomic,
//      .map = exporter_kmap,.vmap = exporter_vmap,.mmap = exporter_mmap,
};static struct dma_buf *exporter_alloc_page(void)
{DEFINE_DMA_BUF_EXPORT_INFO(exp_info);struct dma_buf *dmabuf;void *vaddr;vaddr = kzalloc(PAGE_SIZE,GFP_KERNEL);exp_info.ops = &exp_dmabuf_ops;exp_info.size = PAGE_SIZE;exp_info.flags = O_CLOEXEC;exp_info.priv = vaddr;dmabuf= dma_buf_export(&exp_info);if(dmabuf == NULL)printk(KERN_INFO"DMA buf export error\n");sprintf(vaddr, "hello world");return dmabuf;
}static long exporter_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{int fd = dma_buf_fd(dmabuf_export, O_CLOEXEC);if(unlikely(copy_to_user((void __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_export = exporter_alloc_page();return misc_register(&mdev);}static void __exit exporter_exit(void)
{misc_deregister(&mdev);}
module_init(exporter_init);
module_exit(exporter_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZWQ");
MODULE_DESCRIPTION("zwq dma used buffer");

从上面的示例可以看到,除了要实现 dma-buf 的 mmap 回调接口外,我们还引入了 misc driver,目的是想通过 misc driver 的 ioctl 接口将 dma-buf 的 fd 传递给上层应用程序,这样才能实现应用程序直接使用这个 dma-buf fd 做 mmap() 操作。

补充:

 

static int exporter_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{
        void *vaddr = dmabuf->priv;
        struct page * page_ptr = virt_to_page(vaddr);


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

 上面的虚拟地址 vaddr 如果使用:

remap_pfn_range(vma, vma->vm_start, virt_to_pfn(vaddr), PAGE_SIZE, vma->vm_page_prot);

这个会编译不过,只能先把vaddr 转换page ,在page 转换成页号

为什么非要通过 ioctl 的方式来传递 fd ?这个问题我会在下一篇中详细讨论。

 在 ioctl 接口中,我们使用到了 dma_buf_fd() 函数,该函数用于创建一个新的 fd,并与该 dma-buf 的文件相绑定。关于该函数,我也会在下一篇中做详细介绍。

userspace 程序

mmap_dmabuf.c

#include <stdio.h>
#include <stddef.h>#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/mman.h>int main(int argc, char *argv[])
{int fd;int dmabuf_fd = 0;fd = open("/dev/exporter", O_RDONLY);ioctl(fd, 0, &dmabuf_fd);close(fd);char *str = mmap(NULL, 4096, PROT_READ, MAP_SHARED, dmabuf_fd, 0);printf("read from dmabuf mmap: %s\n", str);return 0;
}

 编译运行后,结果如下:

可以看到,userspace 程序通过 mmap() 接口成功的访问到 dma-buf 的物理内存。

 

四、使用 exporter 的 fd 做 mmap() 操作

本示例主要演示如何使用 dma_buf_mmap() 内核 API,以此来简化设备驱动的 mmap 文件操作接口的实现。

export_test.c

新增 exporter_misc_mmap() 函数, 具体修改如下:

#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>struct dma_buf *dmabuf_export;
EXPORT_SYMBOL(dmabuf_export);static int exporter_attach(struct dma_buf* dmabuf,  struct dma_buf_attachment *attachment)
{pr_info("dmanbuf attach device :%s \n",dev_name(attachment->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)
{
//      void *vaddr = attachment->dmabuf->priv;struct sg_table *table;int ret;table = kmalloc(sizeof(struct sg_table),GFP_KERNEL);ret = sg_alloc_table(table, 1, GFP_KERNEL);if(ret)pr_info("sg_alloc_table err\n");sg_dma_len(table->sgl) = PAGE_SIZE;pr_info("sg_dma_len: %d\n ", sg_dma_len(table->sgl));//      sg_dma_address(table->sgl) = dma_map_single(NULL, vaddr, PAGE_SIZE,dir);
//      pr_info("sg_dma_address: 0x%llx\n",(unsigned long long)sg_dma_address(table->sgl));return table;
}static void exporter_unmap_dma_buf(struct dma_buf_attachment *attachment,struct sg_table *table,enum dma_data_direction dir)
{dma_unmap_single(NULL, sg_dma_address(table->sgl), PAGE_SIZE, dir);sg_free_table(table);kfree(table);
}static void exporter_release(struct dma_buf *dmabuf)
{return kfree(dmabuf->priv);
}/*static void *exporter_kmap_atomic(struct dma_buf *dmabuf, unsigned long page_num)
{return NULL;
}static void *exporter_kmap(struct dma_buf *dmabuf, unsigned long page_num)
{return NULL;
}*/
static void* exporter_vmap(struct dma_buf *dmabuf)
{return dmabuf->priv;}
static int exporter_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{void *vaddr = dmabuf->priv;struct page * page_ptr = virt_to_page(vaddr);return remap_pfn_range(vma,vma->vm_start, page_to_pfn(page_ptr),PAGE_SIZE, vma->vm_page_prot);
}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_atomic = exporter_kmap_atomic,
//      .map = exporter_kmap,.vmap = exporter_vmap,.mmap = exporter_mmap,
};static struct dma_buf *exporter_alloc_page(void)
{DEFINE_DMA_BUF_EXPORT_INFO(exp_info);struct dma_buf *dmabuf;void *vaddr;vaddr = kzalloc(PAGE_SIZE,GFP_KERNEL);exp_info.ops = &exp_dmabuf_ops;exp_info.size = PAGE_SIZE;exp_info.flags = O_CLOEXEC;exp_info.priv = vaddr;dmabuf= dma_buf_export(&exp_info);if(dmabuf == NULL)printk(KERN_INFO"DMA buf export error\n");sprintf(vaddr, "hello world");return dmabuf;
}static long exporter_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{int fd = dma_buf_fd(dmabuf_export, O_CLOEXEC);if(unlikely(copy_to_user((void __user*)arg, &fd,sizeof(fd)))){return -EFAULT;}return 0;}
static int exporter_misc_mmap(struct file *file, struct vm_area_struct *vma)
{return dma_buf_mmap(dmabuf_export, vma, 0);
}static struct file_operations  exporter_fops = {.owner = THIS_MODULE,.unlocked_ioctl = exporter_ioctl,.mmap = exporter_misc_mmap,
};static struct miscdevice mdev ={.minor = MISC_DYNAMIC_MINOR,.name = "exporter",.fops = &exporter_fops,};
static int __init exporter_init(void)
{dmabuf_export = exporter_alloc_page();return misc_register(&mdev);}static void __exit exporter_exit(void)
{misc_deregister(&mdev);}
module_init(exporter_init);
module_exit(exporter_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZWQ");
MODULE_DESCRIPTION("zwq dma used buffer");

与示例一的驱动相比,示例二的驱动可以不再需要把 dma-buf 的 fd 通过 ioctl 传给上层,而是直接将 dma-buf 的 mmap 回调接口嫁接到 misc driver 的 mmap 文件操作接口上。这样上层在对 misc device 进行 mmap() 操作时,实际映射的是 dma-buf 的物理内存。

userspace 程序

mmap_dmabuf.c

#include <stdio.h>
#include <stddef.h>#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/mman.h>int main(int argc, char *argv[])
{int fd;
//      int dmabuf_fd = 0;fd = open("/dev/exporter", O_RDONLY);
#if 0ioctl(fd, 0, &dmabuf_fd);close(fd);char *str = mmap(NULL, 4096, PROT_READ, MAP_SHARED, dmabuf_fd, 0);printf("read from dmabuf mmap: %s\n", str);
#endifchar *str = mmap(NULL, 4096, PROT_READ, MAP_SHARED,fd, 0);printf("read from dmabuf mmap: %s\n", str);return 0;
}

与示例一的 userspace 程序相比,示例二不再通过 ioctl() 方式获取 dma-buf 的 fd,而是直接使用 exporter misc device 的 fd 进行 mmap() 操作,此时执行的则是 misc driver 的 mmap 文件操作接口。当然最终输出的结果都是一样的

运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/17479.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nbcio-vue升级迁移flowable到最新的jeeg-boot-vue3的问题记录(一)

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、vue3 jeeg-boot-vue3新版本的流程定义的页面&#xff0c;刷新出现下面问题&#xff0c;或第一次进去也一样 看着好像就一个警告的信息&#xff0c;不知道是什么原因引起的&#xff0c;应…

111.二叉树的最小深度

给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明: 叶子节点是指没有子节点的节点。 示例: 给定二叉树 [3,9,20,null,null,15,7], 返回它的最小深度 2. 思路&#xff1a; 后序遍历&#xff08;左右中&#xff…

【设计模式深度剖析】【5】【结构型】【桥接模式】| 以电视和遥控器为例加深理解

&#x1f448;️上一篇:组合模式 设计模式-专栏&#x1f448;️ 目 录 桥接模式(Bridge Pattern)定义英文原话是&#xff1a;直译理解 4个角色UML类图代码示例 应用优点缺点使用场景 示例解析&#xff1a;电视和遥控器UML类图 桥接模式(Bridge Pattern) 定义 英文原话是&am…

CC工具箱使用指南:【淹没区分析(BHM)】

一、简介 群友定制工具。 这个工具适用面比较小。 工具的应用场景如下&#xff1a; 提供一个淹没区范围&#xff0c;类型是面要素。统计这个范围内的一些线、面要素的面积或长度。 给定的几个数据有&#xff1a;耕地、永久基本农田、房台、道路&#xff08;线&#xff09;…

基于Docker搭建属于你的CC++集成编译环境

常常&#xff0c;我会幻想着拥有一个随时可以携带、随时可以使用的开发环境&#xff0c;那该是多么美好的事情。 在工作中&#xff0c;编译环境的复杂性常常让我头疼不已。稍有不慎&#xff0c;删除了一些关键文件&#xff0c;整个编译链就会瞬间崩溃。更糟糕的是&#xff0c;…

如何保养和维护气膜体育馆—轻空间

随着经济的飞速发展&#xff0c;气膜体育馆以其新颖的外观、优美的造型、节能环保的特点&#xff0c;迅速进入体育市场。然而&#xff0c;对于气膜体育馆的维护和保养是不容忽视的问题&#xff0c;必须引起重视。下面我们将详细介绍气膜体育馆的维护需要从哪些方面着手。 一、保…

【电路笔记】-状态可变滤波器

状态可变滤波器 文章目录 状态可变滤波器1、概述2、**状态可变滤波器电路**3、状态可变滤波器示例4、陷波滤波器设计5、总结状态可变滤波器是一种多反馈滤波器电路,可以从同一单个有源滤波器设计中同时产生所有三种滤波器响应:低通、高通和带通。 1、概述 状态可变滤波器使用…

基于Java+SpringBoot+Mybaties-plus+Vue+elememt + uniapp 新闻资讯 的设计与实现

一.项目介绍 本系统分为 后端 和 小程序端 后端&#xff1a;点击登录按钮 设置个人中心、 管理员账号数据维护、 基础数据维护、 短视频信息维护(包括查看短视频留言、短视频收藏)、 论坛维护(增删改查帖子信息&#xff0c;包括查…

Rabbit MQ学习之《基础概念》

Message Queue 1 什么是MQ MQ(message queue)&#xff0c;本质是个队列&#xff0c;FIFO 先入先出&#xff0c;只不过队列中存放的内容是message而已&#xff0c;同时是一种跨进程的通信机制&#xff0c;用于上下游传递消息。 在互联网架构中&#xff0c;MQ 是一种非常常见的…

钡铼技术BL205模块在智能制造产线的灵活配置与优化

钡铼技术的OPC UA耦合器BL205模块在智能制造产线中的灵活配置与优化是当今工业领域中的一个关键议题。随着工业4.0和数字化转型的不断推进&#xff0c;生产线的灵活性和智能化程度成为了企业追求的目标。在这一背景下&#xff0c;BL205模块以其分布式、可插拔、结构紧凑、可编程…

Pytorch中的torch.save()文件保存格式探索以及mmdetection加载预训练模型参数对不齐和收到意外参数报错解决方案

使用mmdetection时遇到的问题比较多&#xff0c;首先要对自己要使用的预训练模型有一定的了解&#xff0c;并且懂得使用各种分类模型时不同的模型不同任务执行阶段需要参数上的对其。&#xff08;比如mask-rcnn和它的三个头之间的参数&#xff09;。 首先&#xff0c;谈谈torc…

Spring Boot集成六大常用中间件,附集成源码,亲测有效

目录 万字论文&#xff0c;从0到1&#xff0c;只需1小时获取途径1、Spring Boot如何集成Spring Data JPA&#xff1f;2、Spring Boot如何集成Spring Security&#xff1f;3、Spring Boot如何集成Redis&#xff1f;4、Spring Boot如何集成RabbitMQ&#xff1f;5、Spring Boot如何…

11 Goroutine-并发与并行、阻塞与非阻塞

并发 顺序执行&#xff1a;按照事先计划好的顺序&#xff0c;执行完一个操作后&#xff0c;再执行下一个操作。 顺序执行效率不高的原因&#xff1a; 每个操作由多个步骤组成&#xff0c;每个步骤所需要的时间长短不一&#xff0c;有些步骤可能相当耗时。顾客点菜需要时间&a…

VectorDBBench在windows的调试

VectorDBBench在windows的调试 VectorDBBench是一款向量数据库基准测试工具&#xff0c;支持milvus、Zilliz Cloud、Elastic Search、Qdrant Cloud、Weaviate Cloud 、 PgVector、PgVectorRS等&#xff0c;可以测试其QPS、时延、recall。 VectorDBBench是一款使用python编写的…

深度学习模型在OCR中的可解释性问题与提升探讨

摘要&#xff1a; 随着深度学习技术在光学字符识别&#xff08;OCR&#xff09;领域的广泛应用&#xff0c;人们对深度学习模型的可解释性问题日益关注。本文将探讨OCR中深度学习模型的可解释性概念及其作用&#xff0c;以及如何提高可解释性&#xff0c;使其在实际应用中更可…

Java 零基础入门学习(小白也能看懂!)

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 |《MySQL探索之旅》 |《Web世界探险家》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更…

第16篇:JTAG UART IP应用<三>

Q&#xff1a;如何通过HAL API函数库访问JTAG UART&#xff1f; A&#xff1a;Quartus硬件工程以及Platform Designer系统也和第一个Nios II工程--Hello_World的Quartus硬件工程一样。 Nios II软件工程对应的C程序调用HAL API函数&#xff0c;如open用于打开和创建文件&#…

使用YOLOv9训练和测试自己的数据集

任务&#xff1a;检测舌头上的裂纹和齿痕 已经有了labelme标注的数据集&#xff0c;并且转为了coco格式 参考&#xff1a; 详细&#xff01;正确&#xff01;COCO数据集&#xff08;.json&#xff09;训练格式转换成YOLO格式&#xff08;.txt&#xff09;_coco数据集的train…

服务器数据恢复—服务器raid常见故障表现原因解决方案

RAID&#xff08;磁盘阵列&#xff09;是一种将多块物理硬盘整合成一个虚拟存储的技术&#xff0c;raid模块相当于一个存储管理的中间层&#xff0c;上层接收并执行操作系统及文件系统的数据读写指令&#xff0c;下层管理数据在各个物理硬盘上的存储及读写。相对于单独的物理硬…

2023年全国青少年人工智能创新挑战赛真题

为了大家备考2024年第七届全国青少年人工智能创新挑战赛&#xff0c;今天分享2023年第6届全国青少年人工智能创新挑战赛C信息学专项真题&#xff0c;图形化编程及Python编程基本大同小异&#xff0c;参考6547网的Python及图形化编程题库。 一、单项选择题(共 15 题,每题 2 分,共…