Android中使用ioctl方式操作fence

  Android的hwc可以通过ioctl,把buffer data信息送入fb driver,由fb控制acquireFence和retireFence,以达到buffer的producer和consumer互相同步的机制,但是假如我们的hwc没有现成的ioctl可以用,又没有办法改到driver的code,我们还有一个选择:hwc可以打开/dev/sw_sync设备,通过一系列的ioctl来监控和控制fence.

    代码下载地址: https://android.googlesource.com/kernel/exynos.git
    文章中用到的code:
    exynos/include/linux/sync.h
    exynos/drivers/base/sync.c
    exynos/include/linux/sw_sync.h
    exynos/drivers/base/sw_sync.c

    在切入正文之前,我们先来了解下sync_timeline和sync_fence中出现的kref成员.在Android fb driver中的fence机制一文中,由于篇幅所限,没有对kref做过多的解释,在这一篇中我们来了解下kref的实现和工作原理.
关于kref

    sync_timeline和sync_fence中都有一个struct kref kref的成员,这东西是干什么用的呢?
sync_pt中有直接指向sync_timeline和sync_fence的指针,当sync_pt通过这些指针对sync_timeline或sync_fence做一些操作的时候,如果不能保证指向sync_time和sync_fence的指针仍然有效,我们就有大麻烦了.
    kref作为一个引用计数器可以我们帮助管理sync_timeline和sync_fence的生命周期,每当一个新的指针指向sync_time或者sync_fence时,通过kref_get增加计数;当指针不再需要的时候,通过kref_put减小计数;当kref引用为0时,自动调用对应struct的清理函数并释放资源.
那么kref是如何实现呢?其实非常简单,全部code都在单独的一个.h之中:include/linux/kref.h

  1. struct kref {  
  2.     atomic_t refcount;  
  3. };  
kref只有一个原子类型的refcount成员.所有对于refcount的操作都是原子的,不需要加锁.
  1. static inline void kref_init(struct kref *kref)  
  2. {  
  3.     atomic_set(&kref->refcount, 1);   
  4. }  
kref_init初始化refcount的值为1.
  1. static inline void kref_get(struct kref *kref)  
  2. {  
  3.     WARN_ON(!atomic_read(&kref->refcount));  
  4.     atomic_inc(&kref->refcount);  
  5. }  
kref_get增加refcount的计数.
  1. static inline int kref_sub(struct kref *kref, unsigned int count,  
  2.          void (*release)(struct kref *kref))  
  3. {  
  4.     WARN_ON(release == NULL);  
  5.   
  6.     if (atomic_sub_and_test((int) count, &kref->refcount)) {  
  7.         release(kref);  
  8.         return 1;  
  9.     }  
  10.     return 0;  
  11. }  
kref_sub减小count个refcount的计数,如果recount归零,则调用传进来的release函数指针.
static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
{
    return kref_sub(kref, 1, release);
}

kref_put是count为1的kref_sub特例.

我们来看下kref在sync_time,sync_pt之间是如何运用的.

  1. struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,  
  2.                        int size, const char *name)  
  3. {  
  4.     struct sync_timeline *obj;  
  5.     obj = kzalloc(size, GFP_KERNEL);  
  6.     ...  
  7.     kref_init(&obj->kref);  
  8.     ...  
  9.     return obj;  
  10. }  
sync_timeline_create的时候,对sync_timeline的kref成员做了初始化,kref->refcount == 1;
  1. static void sync_timeline_free(struct kref *kref)  
  2. {  
  3.     struct sync_timeline *obj =  
  4.         container_of(kref, struct sync_timeline, kref);  
  5.     unsigned long flags;  
  6.   
  7.     if (obj->ops->release_obj)  
  8.         obj->ops->release_obj(obj);  
  9.   
  10.     spin_lock_irqsave(&sync_timeline_list_lock, flags);  
  11.     list_del(&obj->sync_timeline_list);  
  12.     spin_unlock_irqrestore(&sync_timeline_list_lock, flags);  
  13.   
  14.     kfree(obj);  
  15. }  
  16. void sync_timeline_destroy(struct sync_timeline *obj)  
  17. {  
  18.     obj->destroyed = true;  
  19.   
  20.     /* 
  21.      * If this is not the last reference, signal any children 
  22.      * that their parent is going away. 
  23.      */  
  24.   
  25.     if (!kref_put(&obj->kref, sync_timeline_free))  
  26.         sync_timeline_signal(obj);  
  27. }  
sync_timeline_destroy的时候调用kref_put减小kref的refcount,如果refcount归零,则调用sync_timeline_free清理sync_timeline并释放资源.
为什么在sync_time_destroy的时候refcount可能会不为0呢?因为在sync_pt_create的时候会调用kref_get增加refcount计数.如果sync_timeline_destroy的时候,sync_timeline中的sync_pt还没有释放,sync_timeline的资源就不能释放,否则sync_pt中指向sync_timeline的的parent指针就会指向无效的地址.
  1. struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size)  
  2. {  
  3.     struct sync_pt *pt;  
  4.   
  5.     if (size < sizeof(struct sync_pt))  
  6.         return NULL;  
  7.   
  8.     pt = kzalloc(size, GFP_KERNEL);  
  9.     if (pt == NULL)  
  10.         return NULL;  
  11.   
  12.     INIT_LIST_HEAD(&pt->active_list);  
  13.     //先增加parent的kref计数,再把自己加入到timeline中,保证在加入timeline的过程中timeline一直是有效的.  
  14.     kref_get(&parent->kref);  
  15.     sync_timeline_add_pt(parent, pt);  
  16.   
  17.     return pt;  
  18. }  
  19. void sync_pt_free(struct sync_pt *pt)  
  20. {  
  21.     if (pt->parent->ops->free_pt)  
  22.         pt->parent->ops->free_pt(pt);  
  23.   
  24.     sync_timeline_remove_pt(pt);  
  25.     //pt从timeline移除后,减小parent的kref计数,如果计数归零,说明timieline不再需要,可以清理了.  
  26.     kref_put(&pt->parent->kref, sync_timeline_free);  
  27.   
  28.     kfree(pt);  
  29. }  
sync_fence中的kref的用法也是类似的,这里不再赘述.关于kref有兴趣的话还可以参考kernel中的说明文档:Documentation/kref.txt

ioctl方式操作sync_timeline和sync_fence

在上一篇文章中,我们讨论了fb driver中对于fence的处理,我们知道sw_sync_timeline"继承"了sync_time.阅读sw_sync.c可以发现,sw_sync_time还在/dev下生成了一个/dev/sw_sync的设备

  1. static const struct file_operations sw_sync_fops = {  
  2.     .owner = THIS_MODULE,  
  3.     .open = sw_sync_open,  
  4.     .release = sw_sync_release,  
  5.     .unlocked_ioctl = sw_sync_ioctl,  
  6. };  
  7.   
  8. static struct miscdevice sw_sync_dev = {  
  9.     .minor  = MISC_DYNAMIC_MINOR,  
  10.     .name   = "sw_sync",  
  11.     .fops   = &sw_sync_fops,  
  12. };  
  13.   
  14. int __init sw_sync_device_init(void)  
  15. {  
  16.     return misc_register(&sw_sync_dev);  
  17. }  
  18.   
  19. void __exit sw_sync_device_remove(void)  
  20. {  
  21.     misc_deregister(&sw_sync_dev);  
  22. }  
  23.   
  24. module_init(sw_sync_device_init);  
  25. module_exit(sw_sync_device_remove);  

我们可以看到对于/dev/sw_sync设备,sw_sync_timeline实现了三个方法

1. sw_sync_open

2. sw_sync_release

3. sw_sync_ioctl

我们分别来分析这三个函数

1. sw_sync_open

  1. int sw_sync_open(struct inode *inode, struct file *file)  
  2. {  
  3.     struct sw_sync_timeline *obj;  
  4.     char task_comm[TASK_COMM_LEN];  
  5.   
  6.     get_task_comm(task_comm, current);  
  7.   
  8.     obj = sw_sync_timeline_create(task_comm);  
  9.     if (obj == NULL)  
  10.         return -ENOMEM;  
  11.   
  12.     file->private_data = obj;  
  13.   
  14.     return 0;  
  15. }  
sw_sync_timeline_create我们在上一篇文章 Android fb driver中的fence机制中已经分析过,这里还是调用的同样的函数,只不过timeline直接使用了task_name作为name.之后sw_sync_timeline作为file的private_data保存起来,之后对该file操作的函数都可以通过private_data拿到sw_sync_timeline.


2. sw_sync_release

  1. int sw_sync_release(struct inode *inode, struct file *file)  
  2. {  
  3.     struct sw_sync_timeline *obj = file->private_data;  
  4.     sync_timeline_destroy(&obj->obj);  
  5.     return 0;  
  6. }  
从preivate_data中拿出之前open的时候保存的sw_sync_timeline,调用sync_timeline_destroy来销毁timeline.
  1. void sync_timeline_destroy(struct sync_timeline *obj)  
  2. {  
  3.     obj->destroyed = true;  
  4.   
  5.     /* 
  6.      * If this is not the last reference, signal any children 
  7.      * that their parent is going away. 
  8.      */  
  9.   
  10.     if (!kref_put(&obj->kref, sync_timeline_free))  
  11.         sync_timeline_signal(obj);  
  12. }  
调用sync_timeline_free,并通知调用sync_timeline_signal通知所有的pt.
  1. 3. sw_sync_ioctl  
  2. long sw_sync_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  
  3. {  
  4.     struct sw_sync_timeline *obj = file->private_data;  
  5.   
  6.     switch (cmd) {  
  7.     case SW_SYNC_IOC_CREATE_FENCE:  
  8.         return sw_sync_ioctl_create_fence(obj, arg);  
  9.   
  10.     case SW_SYNC_IOC_INC:  
  11.         return sw_sync_ioctl_inc(obj, arg);  
  12.   
  13.     default:  
  14.         return -ENOTTY;  
  15.     }  
  16. }  
ioctl支持两种操作: SW_SYNC_IOC_CREATE_FENCE和SW_SYNC_IOC_INC,顾名思义,一个用来create fence,另一个用来增加sw_sync_timeline的value,进而signal sw_sync_pt和对应的sync_fence.其中sw_sync_ioctl_inc只是直接调用了sw_sync_timeline_inc,关于sw_sync_timeline_inc的详细内容请参考 Android fb driver中的fence机制这一篇文章.我们来看下sw_sync_ioctl_create_fence.
  1. struct sw_sync_create_fence_data {  
  2.     //value将赋值给sw_sync_pt->value  
  3.     __u32   value;  
  4.     //sync_fence的name  
  5.     char    name[32];  
  6.     //return给user space的fence fd  
  7.     __s32   fence; /* fd of new fence */  
  8. };  
  9. long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj, unsigned long arg)  
  10. {  
  11.     //获取一个可用的fd  
  12.     int fd = get_unused_fd();  
  13.     int err;  
  14.     struct sync_pt *pt;  
  15.     struct sync_fence *fence;  
  16.     struct sw_sync_create_fence_data data;  
  17.   
  18.     if (fd < 0)  
  19.         return fd;  
  20.   
  21.     if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {  
  22.         err = -EFAULT;  
  23.         goto err;  
  24.     }  
  25.     //调用pt的create函数,将user space的data.value作为新pt的value  
  26.     pt = sw_sync_pt_create(obj, data.value);  
  27.     if (pt == NULL) {  
  28.         err = -ENOMEM;  
  29.         goto err;  
  30.     }  
  31.   
  32.     data.name[sizeof(data.name) - 1] = '\0';  
  33.     //以pt为参数构建一个fence,data.name作为fence的名字  
  34.     fence = sync_fence_create(data.name, pt);  
  35.     if (fence == NULL) {  
  36.         sync_pt_free(pt);  
  37.         err = -ENOMEM;  
  38.         goto err;  
  39.     }  
  40.     //将fd写回user space  
  41.     data.fence = fd;  
  42.     if (copy_to_user((void __user *)arg, &data, sizeof(data))) {  
  43.         sync_fence_put(fence);  
  44.         err = -EFAULT;  
  45.         goto err;  
  46.     }  
  47.     //如果一切顺利,再把fence install到fd中,这样所有对fd的操作都是对fence的操作了.  
  48.     sync_fence_install(fence, fd);  
  49.   
  50.     return 0;  
  51.   
  52. err:  
  53.     put_unused_fd(fd);  
  54.     return err;  
  55. }  
我们看到sw_sync提供的ioctl只能create timeline和inc timeline,也就是说只能singal fence.fence的用法一般是一方signal,另一方wait.那么user space要怎么做才能wait fence呢?我们注意到sw_sync_ioctl_create_fence把fence对应的fd送回了user space,既然我们拿到了fd,就可以对这个fd施展一些手段了.到sync.c中看下fence fd都支持哪些操作.
  1. static const struct file_operations sync_fence_fops = {  
  2.     .release = sync_fence_release,  
  3.     .poll = sync_fence_poll,  
  4.     .unlocked_ioctl = sync_fence_ioctl,  
  5. };  
  6.   
  7. static struct sync_fence *sync_fence_alloc(const char *name)  
  8. {  
  9.     ...  
  10.     struct sync_fence *fence;  
  11.     unsigned long flags;  
  12.   
  13.     fence = kzalloc(sizeof(struct sync_fence), GFP_KERNEL);  
  14.     if (fence == NULL)  
  15.         return NULL;  
  16.   
  17.     fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops,  
  18.                      fence, 0);  
  19.     ...  
  20. }  
从sync_fence_alloc函数中可以知道fence对应的file所支持的操作都列在sync_fence_fops中.


1.sync_fence_release
  1. static int sync_fence_release(struct inode *inode, struct file *file)  
  2. {  
  3.     struct sync_fence *fence = file->private_data;  
  4.     unsigned long flags;  
  5.   
  6.     /* 
  7.      * We need to remove all ways to access this fence before droping 
  8.      * our ref. 
  9.      * 
  10.      * start with its membership in the global fence list 
  11.      */  
  12.     spin_lock_irqsave(&sync_fence_list_lock, flags);  
  13.     list_del(&fence->sync_fence_list);  
  14.     spin_unlock_irqrestore(&sync_fence_list_lock, flags);  
  15.   
  16.     /* 
  17.      * remove its pts from their parents so that sync_timeline_signal() 
  18.      * can't reference the fence. 
  19.      */  
  20.     sync_fence_detach_pts(fence);  
  21.   
  22.     //减小fence的kref计数,如果计数归零,调用sync_fence_free释放资源.  
  23.     //sync_fence_free的时候会调用sync_fence_free_pts,继而调用sync_pt_free释放pt资源,都是简单的调用,不再展开  
  24.     kref_put(&fence->kref, sync_fence_free);  
  25.   
  26.     return 0;  
  27. }  
  28. //将fence中pt_list_head中多有的pt从timeline中移除.  
  29. static void sync_fence_detach_pts(struct sync_fence *fence)  
  30. {  
  31.     struct list_head *pos, *n;  
  32.   
  33.     list_for_each_safe(pos, n, &fence->pt_list_head) {  
  34.         struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);  
  35.         //从timeline的active_list和child_list中移除pt,code很简单这里不再展开.  
  36.         sync_timeline_remove_pt(pt);  
  37.     }      
  38. }  

2.sync_fence_poll
  1. static unsigned int sync_fence_poll(struct file *file, poll_table *wait)  
  2. {  
  3.     struct sync_fence *fence = file->private_data;  
  4.   
  5.     //加入到poll_table的waitqueue是fence->wq,和我们在sync_fence_signal_pt和sync_fence_wait中的fence->wq是同一个wait_queue_head_t  
  6.     //具体请参考<a target=_blank href="http://blog.csdn.net/ear5cm/article/details/45093807">Android fb driver中的fence机制</a>一文  
  7.     poll_wait(file, &fence->wq, wait);  
  8.   
  9.     /* 
  10.      * Make sure that reads to fence->status are ordered with the 
  11.      * wait queue event triggering 
  12.      */  
  13.     smp_rmb();  
  14.   
  15.     //status取值  
  16.     //0 active  
  17.     //1 signal  
  18.     //-1 error  
  19.     if (fence->status == 1)  
  20.         return POLLIN;  
  21.     else if (fence->status < 0)  
  22.         return POLLERR;  
  23.     else  
  24.         return 0;  
  25. }  

3.sync_fence_ioctl

  1. static long sync_fence_ioctl(struct file *file, unsigned int cmd,  
  2.                  unsigned long arg)  
  3. {  
  4.     struct sync_fence *fence = file->private_data;  
  5.     switch (cmd) {  
  6.     case SYNC_IOC_WAIT:  
  7.         return sync_fence_ioctl_wait(fence, arg);  
  8.   
  9.     case SYNC_IOC_MERGE:  
  10.         return sync_fence_ioctl_merge(fence, arg);  
  11.   
  12.     case SYNC_IOC_FENCE_INFO:  
  13.         return sync_fence_ioctl_fence_info(fence, arg);  
  14.   
  15.     default:  
  16.         return -ENOTTY;  
  17.     }  
  18. }  
fence支持的3个ioctl中,
sync_fence_ioctl_wait我们在 Android fb driver中的fence机制一文中已经分析过了.
sync_fence_ioctl_merge吃进的arg其实是sync_merge_data类型:
  1. /** 
  2.  * struct sync_merge_data - data passed to merge ioctl 
  3.  * @fd2:    file descriptor of second fence 
  4.  * @name:   name of new fence 
  5.  * @fence:  returns the fd of the new fence to userspace 
  6.  */  
  7. struct sync_merge_data {  
  8.     __s32   fd2; /* fd of second fence */  
  9.     char    name[32]; /* name of new fence */  
  10.     __s32   fence; /* fd on newly created fence */  
  11. };  
sync_fence_ioctl_merge的功能是将本身这个fence和fd2指向的fence中所有的pt合并到fence指向的新fence当中,新fence以name为名字.
sync_fence_ioctl_fence_info向user space写回fence和它所包含的pt的信息,送给user space的data类型其实是sync_fence_info_data.
  1. /** 
  2.  * struct sync_fence_info_data - data returned from fence info ioctl 
  3.  * @len:    ioctl caller writes the size of the buffer its passing in. 
  4.  *      ioctl returns length of sync_fence_data reutnred to userspace 
  5.  *      including pt_info. 
  6.  * @name:   name of fence 
  7.  * @status: status of fence. 1: signaled 0:active <0:error 
  8.  * @pt_info:    a sync_pt_info struct for every sync_pt in the fence 
  9.  */  
  10. struct sync_fence_info_data {  
  11.     __u32   len;  
  12.     char    name[32];  
  13.     __s32   status;  
  14.   
  15.     __u8    pt_info[0];  
  16. };  
其中pt_info是大小为0的数组,所以pt_info其实指向的是紧挨status后面的地址,pt_info的大小和fence中pt的个数和pt的具体类型有关,在 Android fb driver中的fence机制中提到的sync_timeline->ops->fill_driver_data会负责填写每个pt的信息.


大致总结一下,在user space:
1. 通过open /dev/sw_sync打开sw_sync设备,open函数sw_sync_open会为我们create一个sw_sync_timeline
2. 通过ioctl SW_SYNC_IOC_CREATE_FENCE创造新的fence,kernel中的sw_sync_ioctl_create_fence函数会先create一个sw_sync_pt的,并把它装进一个fence,并把fence的fd返回给user space.
3. 通过ioctl SW_SYNC_IOC_INC call到sw_sync_ioctl_inc,增加sw_sync_timeline的value,以singal那些pt->value值小于等于timeline->value的pt,进而signal pt所属的fence.
4. 通过fence fd的ioctl SYNC_IOC_WAIT,可以在user space等待fence在一定时间段内被signal.

5. 当fence fd在kernel中所对应的fence file不再被引用时,sync_fence_release会释放fence资源.


PS:其实Android framework就是用的上述这个方法来实现fence机制.具体可以参考aosp中libsync的实现,code在aosp: system/core/libsync.

之后会单独开一篇来分析Android framework中的fence实现.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值