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是一个文件描述符。
其中提到的几个操作对用函数如下:
- init:dma_fence_init()
 - wait:dma_fence_wait()
 - signal:dma_fence_signal()
 - 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.




















