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是一个文件描述符。
其中提到的几个操作对用函数如下:
demo的流程如下,本DEMO使用了两种唤醒机制,分别为POLL和fence唤醒,前者并没有使用FENCE机制同步,而是使用了驱动自己的就绪队列,后者使用了FENCE机制进行了同步,使用了FENCE对象自身的唤醒队列。
源码如下:
#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.