dma-fence使用demo

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对象自身的唤醒队列。

dma-fence使用demo_第1张图片

源码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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.


结束

你可能感兴趣的:(linux)