前面的幾篇筆記記錄dma-fence的一些用途,這里簡單寫了個DMA-FENCE的測試代碼。
dma-fence本身的實現和使用並不復雜,其只有兩種狀態signaled和unsigned。可能正是因為其本身的精簡,在融入其他概念中時,在不同的環境下,賦予了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()
具體的例子:
driver通過一個字符設備驅動程序實現的。在字符設備被open時,會新創建一個dma-fence的內核數據對象。
通過ioctl實現了三個命令,主要實現三個功能,1.導出內核dma-fence的fd到user層;2.user層導入一個fd到內核中,並還原為內核dma-fence對象;3.觸發dma-fence變為signaled。
user層的測試代碼會使用driver層提供的ioctl命令,操作dma-fence。並通過系統函數poll(),在user層等待內核中dma-fence的變為signaled。
ioctl命令實現對dma-fence的操作:
DMA_FENCE_IN_CMD:通過將fd將一個外來的fence傳遞到driver層,driver中先向該dma-fence添加一個callback回調函數,然后在顯示調用wait函數,進程會阻塞直到該dma-fence變成signaled。
DMA_FENCE_OUT_CMD:將driver創建的dma-fence通過fd的方式導出到user層
DMA_FENCE_SIGNAL_CMD:對driver創建的dma-fence調用signal操作
1.內核中對dma-fence初始化:
static DEFINE_SPINLOCK(fence_lock);
static void dma_fence_cb(struct dma_fence *f, struct dma_fence_cb *cb)
{
printk("dma-fence callback !");
}
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 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);
return fence;
}
2.內核中,dma-fence導出fd到user層:
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;
3.內核中,接收user層傳遞的fd,還原出dma-fence內核對象,並通過調用dma_fence_wait()等待in-fence變為signaled:
if (copy_from_user(&in_fence_fd, (void __user *)arg, sizeof(int)) != 0)
return -EFAULT;
in_fence = sync_file_get_fence(in_fence_fd);
/* add a callback func */
dma_fence_add_callback(in_fence, &cb, dma_fence_cb);
dma_fence_wait(in_fence, true);
4.user層中,通過系統調用poll()函數在用戶層判斷dma-fence的狀態值:
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;
}
return 0;
} else if (ret == 0) {
errno = ETIME;
return -1;
}
} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
return ret;
}
5.signal dma-fence:
dma_fence_signal(out_fence);