dma-fence使用demo

news2025/1/12 1:53:33

dma-fence是内核中一种比较常用的同步机制,本身的实现和使用并不复杂,其只有两种状态signaled和unsignaled。可能正是因为其本身的精简,在融入其他概念中时,在不同的环境下,赋予了dma-fence不同的含义。所以通常需要根据dma-fence的具体使用的情况来理解其含义。

dma-fence是内核中的同步原语,本身只能表示两种状态,这点上就和complete有点类似了。

但是dma-fence是可以跨设备,跨进程的。

具体来说:

1.就是A设备驱动程序中创建的dma-fence可以被B驱动程序使用。

2.dma-fence是由内核创建,但是可以在进程间传递,并且可以在用户层获取fence的当前状态。

而常规的内核中的同步方法,则不具备对上述两点的支持。

基本原理:

一个被初始化的dma-fence,使用wait函数后,会将当前进程换出,即当前进程会sleep,而当调用signal函数时会唤醒被wait函数换出的进程。

dma-fence的使用还可以通过向dma-fence添加一个或多个callback函数,当dma-fence调用signal操作时,会依次遍历callback list,并调用每个callback函数。当调用wait函数时,会把默认的一个callback函数加入到dma-fence中,而这个函数就起到唤醒的作用。

dma-fence在内核中被创建,可以通过导出一个文件描述符fd到user层,然后用户层可以对该fd做常规的文件操作,也可以把该fence传递给其他进程。这个fd给到内核中后,又可以还原出dma-fence的内核数据结构。所以在user层看到的dma-fence是一个文件描述符。

其中提到的几个操作对用函数如下:

  1. init:dma_fence_init()
  2. wait:dma_fence_wait()
  3. signal:dma_fence_signal()
  4. callback:dma_fence_add_callback()

dma-fence demo

demo的流程如下,本DEMO使用了两种唤醒机制,分别为POLL和fence唤醒,前者并没有使用FENCE机制同步,而是使用了驱动自己的就绪队列,后者使用了FENCE机制进行了同步,使用了FENCE对象自身的唤醒队列。

源码如下:

#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/types.h>
#include <asm/ioctl.h>
#include <asm/fcntl.h>
#include <linux/uaccess.h>
#include <linux/dma-fence.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/sync_file.h>
#include <linux/fs.h>
#include <linux/poll.h>

#define DMA_FENCE_WAIT_CMD              _IOWR('f', 0, int)
#define DMA_FENCE_EXPORT_CMD            _IOWR('f', 1, int)
#define DMA_FENCE_SIGNAL_CMD            _IO('f', 2)

static int in_fence_fd = -1;
static int out_fence_fd = -1;
static int poll_signaled = 0;

static struct dma_fence_cb cb;
static wait_queue_head_t poll_wait_head;

static DEFINE_SPINLOCK(fence_lock);

static void dma_fence_cb(struct dma_fence *f, struct dma_fence_cb *cb)
{
	//dump_stack();
	printk("dma-fence callback !.\n");
}

static const char *dma_fence_get_name(struct dma_fence *fence)
{
	return "dma-fence-example";
}

static const struct dma_fence_ops fence_ops = {
	.get_driver_name = dma_fence_get_name,
	.get_timeline_name = dma_fence_get_name,
};

static void iter_fence_callbac(struct dma_fence *fence)
{
	unsigned long flags;
	struct dma_fence_cb *cur, *tmp;

	spin_lock_irqsave(fence->lock, flags);
	list_for_each_entry_safe(cur, tmp, &fence->cb_list, node) {
		printk("%s line %d cur->func = 0x%px, 0x%pS.\n", __func__, __LINE__, cur->func, cur->func);
	}
	spin_unlock_irqrestore(fence->lock, flags);

	return;
}

static long fence_ioctl(struct file *filp,
                        unsigned int cmd, unsigned long arg)
{
	struct sync_file *sync_file;
	struct dma_fence *in_fence;
	struct dma_fence *out_fence;

	out_fence = (struct dma_fence*)filp->private_data;
	if(out_fence == NULL) {
		pr_err("%s line %d. fence is null.\n", __func__, __LINE__);
		return -1;
	}

	switch (cmd) {
	case DMA_FENCE_SIGNAL_CMD:
		if (out_fence) {
			printk("Signal Fence.\n");
			iter_fence_callbac(out_fence);
			dma_fence_signal(out_fence);
			wake_up_interruptible(&poll_wait_head); 
			poll_signaled = 1;
		}

		break;

	case DMA_FENCE_WAIT_CMD:
		if (copy_from_user(&in_fence_fd, (void __user *)arg, sizeof(int)) != 0)
			return -EFAULT;

		in_fence = sync_file_get_fence(in_fence_fd);
		if (!in_fence)
			return -EINVAL;

		printk("Get in-fence from fd = %d, in_fence 0x%px.\n", in_fence_fd, in_fence);

		/* add a callback func */
		dma_fence_add_callback(in_fence, &cb, dma_fence_cb);

		printk("Waiting in-fence to be signaled, process is blocking ...\n");
		dma_fence_wait(in_fence, true);
		printk("in-fence signaled, process exit\n");

		dma_fence_put(in_fence);

		break;

	case DMA_FENCE_EXPORT_CMD:
		if (!out_fence)
			return -EINVAL;

		sync_file = sync_file_create(out_fence);
		out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
		fd_install(out_fence_fd, sync_file->file);

		if (copy_to_user((void __user *)arg, &out_fence_fd, sizeof(int)) != 0)
			return -EFAULT;

		printk("Created an out-fence fd = %d, out_fence = 0x%px.\n", out_fence_fd, out_fence);

		dma_fence_put(out_fence);
		break;

	default:
		printk("bad cmd.\n");
		break;
	}

	return 0;
}

static struct dma_fence *create_fence(void)
{
	struct dma_fence *fence;

	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
	if (!fence)
		return NULL;

	dma_fence_init(fence, &fence_ops, &fence_lock, 0, 0);
	dma_fence_get(fence);

	return fence;
}

static int fence_open(struct inode *inode, struct file *filp)
{
	struct dma_fence *out_fence;

	/* create an new fence */
	out_fence = create_fence();
	if (!out_fence)
		return -ENOMEM;

	filp->private_data = out_fence;
	init_waitqueue_head(&poll_wait_head);

	return 0;
}

static int fence_close(struct inode *inode, struct file *filp)
{
	struct dma_fence *out_fence = NULL;

	out_fence = (struct dma_fence*)filp->private_data;
	if(out_fence == NULL) {
		pr_err("%s line %d.fatal error. fence is null.\n", __func__, __LINE__);
		return -1;
	}
	
	dma_fence_put(out_fence);
	return 0;
}

static __poll_t fence_poll(struct file *filp, struct poll_table_struct *wait)
{
	__poll_t mask = 0;

	poll_wait(filp, &poll_wait_head, wait);
	if(poll_signaled) {
		mask = EPOLLIN | EPOLLRDNORM;
		poll_signaled = 0;
		printk("%s line %d, poll signaled.\n", __func__, __LINE__);
	}

	return mask;
}

static struct file_operations fence_fops = {
	.owner  = THIS_MODULE,
	.unlocked_ioctl = fence_ioctl,
	.open = fence_open,
	.poll = fence_poll,
	.release = fence_close,
};

static struct miscdevice mdev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "dma-fence",
	.fops = &fence_fops,
};

static int __init dma_fence_demo_init(void)
{
	return misc_register(&mdev);
}

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

module_init(dma_fence_demo_init);
module_exit(dma_fence_demo_unint);

MODULE_AUTHOR("czl");
MODULE_LICENSE("GPL v2");

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <poll.h>

#define DMA_FENCE_WAIT_CMD                _IOWR('f', 0, int)
#define DMA_FENCE_EXPORT_CMD               _IOWR('f', 1, int)
#define DMA_FENCE_SIGNAL_CMD            _IO('f', 2)
#define DEFAULT_POLLMASK                (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)

#define BLOCKING_IN_KERNEL

static int fd = -1;

static inline int sync_wait(int fd, int timeout)
{
	struct pollfd fds = {0};
	int ret;

	fds.fd = fd;
	fds.events = POLLIN;

	do {
		ret = poll(&fds, 1, timeout);
		if (ret > 0) {
			if (fds.revents & (POLLERR | POLLNVAL)) {
				errno = EINVAL;
				return -1;
			}

			printf("%s line %d, DEFAULT_POLLMASK = 0x%x, fds_revents = 0x%x.\n", \
			       __func__, __LINE__, DEFAULT_POLLMASK, fds.revents);
			return 0;
		} else if (ret == 0) {
			errno = ETIME;
			return -1;
		}
	} while (ret == -1 && (errno == EINTR || errno == EAGAIN));

	return ret;
}

static void *signal_pthread(void *arg)
{
	sleep(10);

#if 1
	if (ioctl(fd, DMA_FENCE_SIGNAL_CMD) < 0) {
		perror("get out fence fd fail\n");
	}

#endif
	return NULL;
}

int main(void)
{

	int out_fence_fd;
	pthread_t tidp;

	fd = open("/dev/dma-fence", O_RDWR | O_NONBLOCK, 0);
	if (-1 == fd) {
		printf("Cannot open dma-fence dev\n");
		exit(1);
	}

	if (ioctl(fd, DMA_FENCE_EXPORT_CMD, &out_fence_fd) < 0) {
		perror("get out fence fd fail\n");
		close(fd);
		return -1;
	}

	printf("Get an out-fence fd = %d\n", out_fence_fd);

	if ((pthread_create(&tidp, NULL, signal_pthread, NULL)) == -1) {
		printf("create error!\n");
		close(out_fence_fd);
		close(fd);
		return -1;
	}

#ifdef BLOCKING_IN_KERNEL
	printf("Waiting out-fence to be signaled on KERNEL side ...\n");
	if (ioctl(fd, DMA_FENCE_WAIT_CMD, &out_fence_fd) < 0) {
		perror("get out fence fd fail\n");
		close(out_fence_fd);
		close(fd);
		return -1;
	}
#else
	printf("Waiting out-fence to be signaled on USER side ...\n");
	sync_wait(fd, -1);
#endif

	printf("out-fence is signaled\n");
	if (pthread_join(tidp, NULL)) {
		printf("thread is not exit...\n");
		return -1;
	}

	close(out_fence_fd);
	close(fd);

	return 0;
}

测试过程,安装内核模块后,运行用例,程序运行卡10秒钟后,signal线程发出信号,主线程等到信号后退出。

fence中挂接了多个callback.


结束

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

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

相关文章

DragGAN论文阅读

文章目录 摘要问题3. 算法&#xff1a;3.1 基于点的交互式操作3.2 运动监督3.3 点跟踪 4. 实验4.1 质量评估4.2 量化评估4.3 讨论 结论 论文&#xff1a; 《Drag Your GAN: Interactive Point-based Manipulation on the Generative Image Manifold》 github&#xff1a; htt…

一文学会Git管理代码仓库

文章目录 一、预备知识1.Linux常用指令2.vim编辑器基本使用 二、Git基础1.工作区、暂存区、本地仓库和远程仓库2.git init3.git add4.git status5.git commit6.git push7.git pull8.git 分支管理(branch、checkout、merge)9.git clone和log10.git diff11.git fetch12.git rm13.…

汽车IVI中控开发入门及进阶(九):显示屏

前言: 显示屏Display panel和触控屏Touch panel,可以说随着汽车四化的进展,屏越来越多,越来越大,越来越高清,成为IVI中控、智能座舱系统的重要组成部分。比如如下一个电阻触摸屏。 正文: 显示屏 主要功能就是显示,车载内容和信息的传递全靠显示屏,目前车载显示屏的…

Java企业级开发学习笔记(4.4)Spring Boot加载自定义配置文件

一、使用PropertySource加载自定义配置文件 1.1 创建Spring Boot项目 创建Spring Boot项目 单击【创建】按钮 1.2 创建自定义配置文件 在resources里创建myconfig.properties文件 设置文件编码 设置学生的四个属性值 1.3 创建自定义配置类 在cn.kox.boot包里创建confi…

毕业季Android开发面试,有哪些常见的题?

前言 对于计算机行业早已烂大街&#xff0c;随之而来的毕业季。还会有大批的程序员涌进来&#xff0c;而我们想要继续进入Android开发岗位的人员&#xff0c;最先考虑的是面试。面试题是我们决定踏进工作的重要环节。 对于刚毕业的实习生来说&#xff0c;如何在应聘中脱颖而出…

uniapp 使用app-plus自定义导航栏(titleNView)右侧自定义图标+文字按钮

前言 最近在使用uni-app写H5移动端时候&#xff0c;在pages.json文件配置导航栏的图标时发现不生效 去官网查阅了很久&#xff0c;意思是不支持本地图片&#xff0c;支持iconfont.ttf&#xff08;iconfont本地下载&#xff09;格式图片 意思是在导航栏的图标需要在iconfont图…

Synchronized 偏向锁、轻量级锁、自旋锁、锁消除

一、重量级锁 上篇文章中向大家介绍了Synchronized的用法及其实现的原理。现在我们应该知道&#xff0c;Synchronized是通过对象内部的一个叫做监视器锁&#xff08;monitor&#xff09;来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实…

【从Spring Cloud到Spring Cloud Alibaba,这些改变你都知道吗?】—— 每天一点小知识

&#x1f4a7; 从 S p r i n g C l o u d 到 S p r i n g C l o u d A l i b a b a &#xff0c;这些改变你都知道吗&#xff1f; \color{#FF1493}{从Spring Cloud到Spring Cloud Alibaba&#xff0c;这些改变你都知道吗&#xff1f;} 从SpringCloud到SpringCloudAlibaba&#…

【LeetCode训练营 189】轮转数组详解

&#x1f4af; 博客内容&#xff1a;【LeetCode训练营 189】轮转数组详解 &#x1f600; 作  者&#xff1a;陈大大陈 &#x1f680; 个人简介&#xff1a;一个正在努力学技术的准前端&#xff0c;专注基础和实战分享 &#xff0c;欢迎私信&#xff01; &#x1f496; 欢迎大…

Tensorflow训练代码1.x接口自动升级2.x踩坑记录

Tensorflow训练代码1.x接口自动升级2.x踩坑记录 TF准备工作环境问题解决自动升级脚本&#xff0c;从TF1.0调通到TF2.0 一起学AI系列博客&#xff1a;目录索引 本文小结Tensorflow训练代码1.x接口自动升级2.x踩坑过程和问题解决的方法。 TF准备工作 Tensorflow环境准备 前提已…

【MySQL高级篇笔记-多版本并发控制MVCC(下) 】

此笔记为尚硅谷MySQL高级篇部分内容 目录 一、什么是MVCC 二、快照读与当前读 1、快照读 2、当前读 三、复习 1、再谈隔离级别 2、隐藏字段、Undo Log版本链 四、MVCC实现原理之ReadView 1、什么是ReadView 2、设计思路 3、ReadView的规则 4、MVCC整体操作流程…

操作系统(5.2)--请求分页储存管理模式

目录 请求分页的硬件支持 1.页表机制 2.缺页中断机构 3.地址变换机构 请求分页中的内存分配 1.最小物理块数的确定 2.内存分配策略 3.物理块分配算法(采用固定分配策略时) 页面调度策略 1.何时调入页面 2.从何处调入页面 3.页面调入过程 请求分页的硬件支持 内存、…

基础知识学习---牛客网C++面试宝典(四)C/C++基础之STL

1、本栏用来记录社招找工作过程中的内容&#xff0c;包括基础知识学习以及面试问题的记录等&#xff0c;以便于后续个人回顾学习&#xff1b; 暂时只有2023年3月份&#xff0c;第一次社招找工作的过程&#xff1b; 2、个人经历&#xff1a; 研究生期间课题是SLAM在无人机上的应…

TCP协议流程详解,抓包分析

目录 TCP概念TCP工作层TCP协议头部解析TCP抓包解析TCP三次握手&#xff0c;数据收发&#xff0c;四次挥手抓包TCP状态迁移 TCP概念 传输控制协议&#xff08;TCP&#xff0c;Transmission Control Protocol&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议&…

MyBatis操作数据库(查询功能)

目录 一、MyBatis的概念 二、配置MyBits环境 三、 MyBatis连接数据库查询操作&#xff08;示例&#xff09; 创建MySQL数据库表 配置MyBatis 配置连接数据库和MyBatis xml文件 ​编辑 四、添加业务代码 实体类entity 数据持久层mapper 创建接口类 创建xml文件 服务层…

偏向锁、轻量级锁、重量级锁、自旋锁、自适应自旋锁

1. 偏向锁 偏向锁就是在运行过程中&#xff0c;对象的锁偏向某个线程。即在开启偏向锁机制的情况下&#xff0c;某个线程获得锁&#xff0c;当该线程下次再想要获得锁时&#xff0c;不需要重新申请获得锁&#xff08;即忽略synchronized关键词&#xff09;&#xff0c;直接就可…

python 房价数据可视化以数据缺失处理、及回归算法

基本信息概述 房价数据为他国地区使用工具为JupyterLab、python3用到的包 绘图包&#xff1a;seaborn、matplotlib数据处理包&#xff1a;numpy、pandas统计计算包&#xff1a;math、scipy回归模型包&#xff1a;make_pipeline、 RobustScaler、ElasticNet,Lasso、KernelRidge…

设计模式(十四):行为型之策略模式

设计模式系列文章 设计模式(一)&#xff1a;创建型之单例模式 设计模式(二、三)&#xff1a;创建型之工厂方法和抽象工厂模式 设计模式(四)&#xff1a;创建型之原型模式 设计模式(五)&#xff1a;创建型之建造者模式 设计模式(六)&#xff1a;结构型之代理模式 设计模式…

Python使用最新版pyinstaller将项目或程序打包成exe或者mac中的可执行文件

1、pyinstaller的说明&#xff1a; pyinstaller 能够在 Windows、Linux、Mac 等操作系统下将 Python 源文件打包&#xff0c;通过对源文件打包&#xff0c; Python 程序可以在没有安装 Python 的环境中运行&#xff0c;也可以作为一个独立文件方便传递和管理。 PyInstaller 支…

进程管道:父进程和子进程

在接下来的对pipe调用的研究中&#xff0c;我们将学习如何在子进程中运行一个与其父进程完全不同的另外一个程序&#xff0c;而不是仅仅运行一个相同程序。我们用exec调用来完成这一工作。这里的一个难点是&#xff0c;通过exec调用的进程需要知道应该访问哪个文件描述符。在前…