Android fb driver中的fence机制

转自:http://blog.csdn.net/ear5cm/article/details/45093807

  Android HardwareComposer中的fence机制中讨论了hwc中的fence,hwc最终把layer的acqireFenceFd送进fb driver,再由fb drvier生成新的reitreFenceFd并return回user space.本篇文章我们来探讨下fb driver中的fence,看看S3CFB_WIN_CONFIG ioctl都做了些什么.

    kernel代码下载地址: 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

    exynos/drivers/video/s3c-fb.c 

    在讨论fb driver中的fence之前,先来简单介绍几个和fence相关的基本数据结构:

[cpp]   view plain  copy
  1. /** 
  2.  * struct sync_timeline - sync object 
  3.  * @kref:       reference count on fence. 
  4.  * @ops:        ops that define the implementaiton of the sync_timeline 
  5.  * @name:       name of the sync_timeline. Useful for debugging 
  6.  * @destoryed:      set when sync_timeline is destroyed 
  7.  * @child_list_head:    list of children sync_pts for this sync_timeline 
  8.  * @child_list_lock:    lock protecting @child_list_head, destroyed, and 
  9.  *            sync_pt.status 
  10.  * @active_list_head:   list of active (unsignaled/errored) sync_pts 
  11.  * @sync_timeline_list: membership in global sync_timeline_list 
  12.  */  
  13. struct sync_timeline {  
  14.     struct kref     kref;  
  15.     const struct sync_timeline_ops  *ops;  
  16.     char            name[32];  
  17.   
  18.     /* protected by child_list_lock */  
  19.     bool            destroyed;  
  20.   
  21.     struct list_head    child_list_head;  
  22.     spinlock_t      child_list_lock;  
  23.   
  24.     struct list_head    active_list_head;  
  25.     spinlock_t      active_list_lock;  
  26.   
  27.     struct list_head    sync_timeline_list;  
  28. };  

    sync_timeline中包含一个由list_head串起来的sync_pt双向链表child_list_head.

[cpp]   view plain  copy
  1. /** 
  2.  * struct sync_pt - sync point 
  3.  * @parent:     sync_timeline to which this sync_pt belongs 
  4.  * @child_list:     membership in sync_timeline.child_list_head 
  5.  * @active_list:    membership in sync_timeline.active_list_head 
  6.  * @signaled_list:  membership in temorary signaled_list on stack 
  7.  * @fence:      sync_fence to which the sync_pt belongs 
  8.  * @pt_list:        membership in sync_fence.pt_list_head 
  9.  * @status:     1: signaled, 0:active, <0: error 
  10.  * @timestamp:      time which sync_pt status transitioned from active to 
  11.  *            singaled or error. 
  12.  */  
  13. struct sync_pt {  
  14.     struct sync_timeline        *parent;  
  15.     struct list_head    child_list;  
  16.   
  17.     struct list_head    active_list;  
  18.     struct list_head    signaled_list;  
  19.   
  20.     struct sync_fence   *fence;  
  21.     struct list_head    pt_list;  
  22.   
  23.     /* protected by parent->active_list_lock */  
  24.     int         status;  
  25.   
  26.     ktime_t         timestamp;  
  27. };  

    sync_pt中parent指针指向了sync_pt所属的sync_timeline,child_list表示了sync_pt在sync_timeline.child_list_head中的位置.fence指针指向了sync_pt所属的fence,pt_list表示了sync_pt在fence.pt_list_head中的位置.

[cpp]   view plain  copy
  1. /** 
  2.  * struct sync_fence - sync fence 
  3.  * @file:       file representing this fence 
  4.  * @kref:       referenace count on fence. 
  5.  * @name:       name of sync_fence.  Useful for debugging 
  6.  * @pt_list_head:   list of sync_pts in ths fence.  immutable once fence 
  7.  *            is created 
  8.  * @waiter_list_head:   list of asynchronous waiters on this fence 
  9.  * @waiter_list_lock:   lock protecting @waiter_list_head and @status 
  10.  * @status:     1: signaled, 0:active, <0: error 
  11.  * 
  12.  * @wq:         wait queue for fence signaling 
  13.  * @sync_fence_list:    membership in global fence list 
  14.  */  
  15. struct sync_fence {  
  16.     struct file     *file;  
  17.     struct kref     kref;  
  18.     char            name[32];  
  19.   
  20.     /* this list is immutable once the fence is created */  
  21.     struct list_head    pt_list_head;  
  22.   
  23.     struct list_head    waiter_list_head;  
  24.     spinlock_t      waiter_list_lock; /* also protects status */  
  25.     int         status;  
  26.   
  27.     wait_queue_head_t   wq;  
  28.   
  29.     struct list_head    sync_fence_list;  
  30. };  

    file指针表示fence所对应的file,linux中一切皆是file.pt_list_head是一个由list_head串起来的sync_pt双向链表.

sync_timeline,sync_pt和sync_fence的关系可以用下面的图来表示:

    syc_timeline来管理所有在这条timeline上的sync_pt,可以决定sync_pt何时被signal.sync_fence中可以包含一个或者多个sync_pt,当sync_fence中所有的sync_pt被signal的时候,sync_fence被signal.

    不过sync_timeline sync_pt有点像一个virtual class,真正在使用的时候需要"继承"它,并实现它定义的sync_timeline_ops  *ops接口,s3c-fb.c 使用的是sw_sync_timeline和sw_sync_pt,定义如下:

[cpp]   view plain  copy
  1. struct sw_sync_timeline {  
  2.     struct  sync_timeline   obj;  
  3.   
  4.     u32         value;  
  5. };  
  6.   
  7. struct sw_sync_pt {  
  8.     struct sync_pt      pt;  
  9.   
  10.     u32         value;  
  11. };  

    sw_sync_timeline和sw_sync_pt相当简单,只不过是在原本的sync_timeline和sync_pt基础上多加了一个u32的value而已.另外sw_sync_timeline和sync_pt还开出了几个新的api.

[cpp]   view plain  copy
  1. struct sw_sync_timeline *sw_sync_timeline_create(const char *name);  
  2. void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc);  
  3.   
  4. struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value);  

    上面这三个api在s3c-fb.c 中都会用到,我们在分析到对应code的时候再来深入分析.


    接下来我们进入s3c-fb.c,具体看下sw_sync_timeline,sw_sync_pt和sync_fence是如何使用的.首先,s3c-fb定义了一些和处理fence相关的member

[cpp]   view plain  copy
  1. struct s3c_fb {  
  2.     ...  
  3.     struct fb_info      *fbinfo;  
  4.     struct list_head    update_regs_list;  
  5.     struct mutex        update_regs_list_lock;  
  6.     struct kthread_worker   update_regs_worker;  
  7.     struct task_struct  *update_regs_thread;  
  8.     struct kthread_work update_regs_work;  
  9.   
  10.     struct sw_sync_timeline *timeline;  
  11.     int         timeline_max;  
  12.     ...  
  13. }  

s3c-fb是把buffer的显示放在一个单独的kthread里面来做,   

    struct list_head    update_regs_list;
    struct mutex        update_regs_list_lock;
    struct kthread_worker   update_regs_worker;
    struct task_struct  *update_regs_thread;
    struct kthread_work update_regs_work;

这几个都是kthread相关的struct,在prob的时候会进行初始化.

[cpp]   view plain  copy
  1. static struct platform_driver s3c_fb_driver = {  
  2.     .probe      = s3c_fb_probe,  
  3.     .remove     = __devexit_p(s3c_fb_remove),  
  4.     .id_table   = s3c_fb_driver_ids,  
  5.     .driver     = {  
  6.         .name   = "s3c-fb",  
  7.         .owner  = THIS_MODULE,  
  8.         .pm = &s3cfb_pm_ops,  
  9.     },  
  10. };  
[cpp]   view plain  copy
  1. static int __devinit s3c_fb_probe(struct platform_device *pdev)  
  2. {  
  3.     ...  
  4.     INIT_LIST_HEAD(&sfb->update_regs_list);  
  5.     mutex_init(&sfb->update_regs_list_lock);  
  6.     init_kthread_worker(&sfb->update_regs_worker);  
  7.     sfb->update_regs_thread = kthread_run(kthread_worker_fn,  
  8.         &sfb->update_regs_worker, "s3c-fb");  
  9.     if (IS_ERR(sfb->update_regs_thread)) {  
  10.     int err = PTR_ERR(sfb->update_regs_thread);  
  11.     sfb->update_regs_thread = NULL;  
  12.   
  13.     dev_err(dev, "failed to run update_regs thread\n");  
  14.     return err;  
  15.     }  
  16.     init_kthread_work(&sfb->update_regs_work, s3c_fb_update_regs_handler);  
  17.     sfb->timeline = sw_sync_timeline_create("s3c-fb");  
  18.     sfb->timeline_max = 1;  
  19.     ...  
  20. }  

    从上面这段code可以看出,最终kthread的工作会由s3c_fb_update_regs_handler来完成,有关kthread的具体细节这里就不详细讨论了.我们只来看timeline = sw_sync_timeline_create(),同时timeline_max被初始化为1.

[cpp]   view plain  copy
  1. struct sw_sync_timeline *sw_sync_timeline_create(const char *name)  
  2. {  
  3.     struct sw_sync_timeline *obj = (struct sw_sync_timeline *)  
  4.         sync_timeline_create(&sw_sync_timeline_ops,  
  5.                      sizeof(struct sw_sync_timeline),  
  6.                      name);  
  7.   
  8.     return obj;  
  9. }  
[cpp]   view plain  copy
  1. struct sync_timeline_ops sw_sync_timeline_ops = {  
  2.     .driver_name = "sw_sync",  
  3.     .dup = sw_sync_pt_dup,  
  4.     .has_signaled = sw_sync_pt_has_signaled,  
  5.     .compare = sw_sync_pt_compare,  
  6.     .fill_driver_data = sw_sync_fill_driver_data,  
  7.     .timeline_value_str = sw_sync_timeline_value_str,  
  8.     .pt_value_str = sw_sync_pt_value_str,  
  9. };  

    sw_sync_timeline_create以sw_sync_timeline_ops为参数,构造了一个"基类"sync_timeline_create的结构体,sw_sync_timeline_ops每个函数的作用我们在遇到的时候再来讨论.

    当hwc通过S3CFB_WIN_CONFIG ioctl把所有layer的信息送进fb driver时(详细过程请参考Android HardwareComposer中的fence机制):

[cpp]   view plain  copy
  1. static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd,  
  2.             unsigned long arg)  
  3. {  
  4.     ...  
  5.     case S3CFB_WIN_CONFIG:  
  6.         if (copy_from_user(&p.win_data,  
  7.                    (struct s3c_fb_win_config_data __user *)arg,  
  8.                    sizeof(p.win_data))) {  
  9.             ret = -EFAULT;  
  10.             break;  
  11.         }  
  12.   
  13.         ret = s3c_fb_set_win_config(sfb, &p.win_data);  
  14.         if (ret)  
  15.             break;  
  16.   
  17.         if (copy_to_user((struct s3c_fb_win_config_data __user *)arg,  
  18.                  &p.win_data,  
  19.                  sizeof(p.user_ion_client))) {  
  20.             ret = -EFAULT;  
  21.             break;  
  22.         }  
  23.   
  24.         break;  
  25.     ...  
  26. }  

    copy_from_user和copy_to_user读写的都是s3c_fb_win_config_data类型的data,只不过在copy_to_user时写回user space的data大小变了,因为user space感兴趣的只是一个fenceFd而已.

[cpp]   view plain  copy
  1. static int s3c_fb_set_win_config(struct s3c_fb *sfb,  
  2.         struct s3c_fb_win_config_data *win_data)  
  3. {  
  4.     struct s3c_fb_win_config *win_config = win_data->config;  
  5.     int ret = 0;  
  6.     unsigned short i;  
  7.     struct s3c_reg_data *regs;  
  8.     //这个fence就是需要return给user space的retireFence  
  9.     struct sync_fence *fence;  
  10.     struct sync_pt *pt;  
  11.     //fb会和上面的fence绑定  
  12.     int fd;  
  13.     unsigned int bw = 0;  
  14.   
  15.     fd = get_unused_fd();  
  16.   
  17.     if (fd < 0)  
  18.         return fd;  
  19.   
  20.     mutex_lock(&sfb->output_lock);  
  21.     regs = kzalloc(sizeof(struct s3c_reg_data), GFP_KERNEL);  
  22.     ...  
  23.     ret = s3c_fb_set_win_buffer(sfb, win, config, regs);  
  24.     ...  
  25.     mutex_lock(&sfb->update_regs_list_lock);  
  26.     //timeline_max初始值为1  
  27.     sfb->timeline_max++;  
  28.     //以time_line_max为参数在sw_sync_timeline上构建了一个新的sw_sync_pt  
  29.     pt = sw_sync_pt_create(sfb->timeline, sfb->timeline_max);  
  30.     //以pt为参数构建一个fence  
  31.     fence = sync_fence_create("display", pt);  
  32.     //将fence install到file中,以fd表示这个file,之后对于fd的操作其实都是对fence的操作  
  33.     sync_fence_install(fence, fd);  
  34.     //把fd赋值给win_data->fence,win_data->fence将写回user space  
  35.     win_data->fence = fd;  
  36.   
  37.     //buffer data的具体显示工作交给kthread完成,  
  38.     list_add_tail(&regs->list, &sfb->update_regs_list);  
  39.     mutex_unlock(&sfb->update_regs_list_lock);  
  40.     queue_kthread_work(&sfb->update_regs_worker,  
  41.             &sfb->update_regs_work);  
  42.               
  43.     mutex_unlock(&sfb->output_lock);  
  44.     return ret;  
  45. }  

code中关键的地方都加了注释.s3c_fb_set_win_buffer和kthread工作的内容稍后再来展开,我们先来看将要return回user space的这个fence是怎么生成的.

[cpp]   view plain  copy
  1. struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value)  
  2. {  
  3.     struct sw_sync_pt *pt;  
  4.   
  5.     pt = (struct sw_sync_pt *)  
  6.         sync_pt_create(&obj->obj, sizeof(struct sw_sync_pt));  
  7.   
  8.     pt->value = value;  
  9.   
  10.     return (struct sync_pt *)pt;  
  11. }  
[cpp]   view plain  copy
  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.     kref_get(&parent->kref);  
  14.     sync_timeline_add_pt(parent, pt);  
  15.   
  16.     return pt;  
  17. }  

    sw_sync_pt把value,也就是timeline_max作为pt->value的值保存下来,接着call了"基类"sync_pt_create的"构造函数".在sync_pt_create中为pt分配空间,并把自己加入到timeline的child_list_head中去.这时候,新建立的sw_sync_pt->value == timeline_max.注意,因为pt的buffer是由kzalloc分配的,所以pt->status的值是0,status各个值的含义在之前的struct定义中可以看到1: signaled, 0:active, <0: error,所以现在pt的satus是 active状态.

[cpp]   view plain  copy
  1. struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt)  
  2. {  
  3.     struct sync_fence *fence;  
  4.   
  5.     if (pt->fence)  
  6.         return NULL;  
  7.     //sync_fence_alloc中通过anon_inode_getfile()为fence分配一个file,这个file在sync_fence_installde时候会用到  
  8.     fence = sync_fence_alloc(name);  
  9.     if (fence == NULL)  
  10.         return NULL;  
  11.   
  12.     //保存fence指针到pt->fence  
  13.     pt->fence = fence;  
  14.     //将pt加入到fence的pt_list_head链表中  
  15.     list_add(&pt->pt_list, &fence->pt_list_head);  
  16.     //将pt加入到timelien的active_list_head链表中  
  17.     sync_pt_activate(pt);  
  18.   
  19.     //马上判断一次pt是否已经处于signal的状态,如果fence中所有的pt都处于signal状态,fence就要被signal  
  20.     sync_fence_signal_pt(pt);  
  21.   
  22.     return fence;  
  23. }  

这里有两个关键点,sync_pt_activate和sync_fence_signal_pt,我们来各个击破,先来看sync_pt_activate.

[cpp]   view plain  copy
  1. static void sync_pt_activate(struct sync_pt *pt)  
  2. {  
  3.     struct sync_timeline *obj = pt->parent;  
  4.     unsigned long flags;  
  5.     int err;  
  6.   
  7.     spin_lock_irqsave(&obj->active_list_lock, flags);  
  8.   
  9.     err = _sync_pt_has_signaled(pt);  
  10.     if (err != 0)  
  11.         goto out;  
  12.   
  13.     list_add_tail(&pt->active_list, &obj->active_list_head);  
  14.   
  15. out:  
  16.     spin_unlock_irqrestore(&obj->active_list_lock, flags);  
  17. }  

    在spin_lock的保护下去call了_sync_pt_has_signaled,如果err==0,也就是pt的status是activate,就把pt加入到timeline的active_list_head中去.我们来看看_sync_pt_has_signaled里面是如何判断pt的status的.

[cpp]   view plain  copy
  1. static int _sync_pt_has_signaled(struct sync_pt *pt)  
  2. {  
  3.     int old_status = pt->status;  
  4.   
  5.     //call到了parent->ops的has_signaled,也就是sw_sync_timeline->ops->has_signaled  
  6.     //我们还记得在sw_sync_timeline_create的时候使用的ops参数是sw_sync_timeline_ops  
  7.     //其中.has_signaled = sw_sync_pt_has_signaled,    
  8.     if (!pt->status)  
  9.         pt->status = pt->parent->ops->has_signaled(pt);  
  10.   
  11.     if (!pt->status && pt->parent->destroyed)  
  12.         pt->status = -ENOENT;  
  13.   
  14.     if (pt->status != old_status)  
  15.         pt->timestamp = ktime_get();  
  16.   
  17.     return pt->status;  
  18. }  
[cpp]   view plain  copy
  1. static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt)  
  2. {  
  3.     struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;  
  4.     struct sw_sync_timeline *obj =  
  5.         (struct sw_sync_timeline *)sync_pt->parent;  
  6.   
  7.     return sw_sync_cmp(obj->value, pt->value) >= 0;  
  8. }  
[cpp]   view plain  copy
  1. static int sw_sync_cmp(u32 a, u32 b)  
  2. {  
  3.     if (a == b)  
  4.         return 0;  
  5.   
  6.     return ((s32)a - (s32)b) < 0 ? -1 : 1;  
  7. }  

    sw_sync_cmp比较sw_sync_timeline的value和sync_pt的value,我们之前分析过,sw_sync_timeline create的时候value为0,timeline_max初始值为1,在sw_sync_pt_create之前,先执行了timeline_max++,所以这个时候timeline_max的值是2,也就是sw_sync_pt的value为2.这里要注意的是,sw_sync_pt_has_signaled返回的不是sw_sync_cmp的返回值,而是它的返回值与0比较的结果 return sw_sync_cmp(obj->value, pt->value) >= 0; 

    如果两者相等,sw_sync_cmp return 0,sw_sync_tp_has_signaled返回1.

    如果timeline的value大于pt的value,sw_sync_cmp return 1,sw_sync_tp_has_signaled返回1.

    如果timeline的value小于pt的value,sw_sync_cmp return -1,sw_sync_tp_has_signaled返回0.

因为我们的timeline->value == 0 pt->value == 2,所以这里sw_sync_tp_has_signaled返回的是0,也就是说pt处于activate状态,不处于signaled状态,pt不需要加入到timeline的activate_list_head列表中去.

分析完第一个关键点sync_pt_activate,我们接下来分析第二个关键点sync_fence_signal_pt

[cpp]   view plain  copy
  1. static void sync_fence_signal_pt(struct sync_pt *pt)  
  2. {  
  3.     LIST_HEAD(signaled_waiters);  
  4.     struct sync_fence *fence = pt->fence;  
  5.     struct list_head *pos;  
  6.     struct list_head *n;  
  7.     unsigned long flags;  
  8.     int status;  
  9.   
  10.     //这个函数需要进去分析  
  11.     status = sync_fence_get_status(fence);  
  12.   
  13.     spin_lock_irqsave(&fence->waiter_list_lock, flags);  
  14.     /* 
  15.      * this should protect against two threads racing on the signaled 
  16.      * false -> true transition 
  17.      */  
  18.     //如果status不为0,也就是fence处于signaled或者error状态,那么在spin_lock的保护下,  
  19.     //把fence的waiter_list_header中的sync_fence_waiter移动到signaled_waiters list中去  
  20.     if (status && !fence->status) {  
  21.         list_for_each_safe(pos, n, &fence->waiter_list_head)  
  22.             list_move(pos, &signaled_waiters);  
  23.         //更新fence的status  
  24.         fence->status = status;  
  25.     } else {  
  26.         status = 0;  
  27.     }  
  28.     spin_unlock_irqrestore(&fence->waiter_list_lock, flags);  
  29.   
  30.     if (status) {  
  31.         //遍历signaled_waiters,把每个waiter移除list,并call他们的callback  
  32.         list_for_each_safe(pos, n, &signaled_waiters) {  
  33.             struct sync_fence_waiter *waiter =  
  34.                 container_of(pos, struct sync_fence_waiter,  
  35.                          waiter_list);  
  36.   
  37.             list_del(pos);  
  38.             waiter->callback(fence, waiter);  
  39.         }  
  40.         //wake up wait_queue_head_t  
  41.         wake_up(&fence->wq);  
  42.     }  
  43. }  

其他的部分code中已经有注释,我们来看sync_fence_get_status

[cpp]   view plain  copy
  1. static int sync_fence_get_status(struct sync_fence *fence)  
  2. {  
  3.     struct list_head *pos;  
  4.     //预设默认值是signaled  
  5.     int status = 1;  
  6.   
  7.     list_for_each(pos, &fence->pt_list_head) {  
  8.         struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);  
  9.         int pt_status = pt->status;  
  10.   
  11.         if (pt_status < 0) {  
  12.             //如果有一个pt的status是error,则整个fence的status就是error  
  13.             status = pt_status;  
  14.             break;  
  15.         } else if (status == 1) {  
  16.             //如果有一个pt的status是activate,则覆盖掉默认值,继续遍历直到遇到error或者到list尾部.  
  17.             status = pt_status;  
  18.         }  
  19.     }  
  20.   
  21.     return status;  
  22. }  

    因为我们的pt现在处于activate状态,而fence中只有这一个pt,所以fence也处于activate状态.sync_fence_create分析完毕,我们来看sync_fence_install的过程.

[cpp]   view plain  copy
  1. void sync_fence_install(struct sync_fence *fence, int fd)  
  2. {  
  3.     fd_install(fd, fence->file);  
  4. }  

    简单的通过fd_install把fence_file和fd关联起来.

    至此我们新建了一个sync_pt,并把pt加入到timeline,之后用这个pt新建了一个sync_fence,time_line中最新的pt->value = timeline->value+2.接下来fb driver把fence对应的fd写回user space,retireFence的处理也就告一段落了,至于retireFence何时被signal,在我们稍后分析kthread处理buffer data的时候就会揭晓.


    之前在分析s3c_fb_set_win_config的时候我们提到过:"s3c_fb_set_win_buffer和kthread工作的内容稍后再来展开",下面我们就来分析s3c_fb_set_win_buffer.

[cpp]   view plain  copy
  1. static int s3c_fb_set_win_config(struct s3c_fb *sfb,    
  2.         struct s3c_fb_win_config_data *win_data)   
  3. {  
  4.     ...  
  5.     ret = s3c_fb_set_win_buffer(sfb, win, config, regs);   
  6.     ...   
  7. }  
[cpp]   view plain  copy
  1. static int s3c_fb_set_win_buffer(struct s3c_fb *sfb, struct s3c_fb_win *win,  
  2.         struct s3c_fb_win_config *win_config, struct s3c_reg_data *regs)  
  3. {  
  4.     struct ion_handle *handle;  
  5.     struct fb_var_screeninfo prev_var = win->fbinfo->var;  
  6.     struct s3c_dma_buf_data dma_buf_data;  
  7.     if (win_config->fence_fd >= 0) {  
  8.         //如果从hwc传下来的win_config->fence_fd>=0,则通过sync_fence_fdget获取到它对应的sync_fence  
  9.         dma_buf_data.fence = sync_fence_fdget(win_config->fence_fd);  
  10.         if (!dma_buf_data.fence) {  
  11.             dev_err(sfb->dev, "failed to import fence fd\n");  
  12.             ret = -EINVAL;  
  13.             goto err_offset;  
  14.         }  
  15.     }  
  16.     //dma_buf_data连同刚刚获取的fence一起保存在regs中.  
  17.     regs->dma_buf_data[win_no] = dma_buf_data;  
  18.     return 0;  
  19. }  

[cpp]   view plain  copy
  1. struct sync_fence *sync_fence_fdget(int fd)  
  2. {  
  3.     struct file *file = fget(fd);  
  4.   
  5.     if (file == NULL)  
  6.         return NULL;  
  7.   
  8.     if (file->f_op != &sync_fence_fops)  
  9.         goto err;  
  10.   
  11.     return file->private_data;  
  12.   
  13. err:  
  14.     fput(file);  
  15.     return NULL;  
  16. }  

regs会保存到update_regs_list中,最终由kthread在s3c_fb_update_regs_handler函数中处理.其中regs->dma_buf_data[i].fence就是hwc中的acquireFence.

[cpp]   view plain  copy
  1. static void s3c_fb_update_regs_handler(struct kthread_work *work)  
  2. {  
  3.     struct s3c_fb *sfb =  
  4.             container_of(work, struct s3c_fb, update_regs_work);  
  5.     struct s3c_reg_data *data, *next;  
  6.     struct list_head saved_list;  
  7.   
  8.     mutex_lock(&sfb->update_regs_list_lock);  
  9.     saved_list = sfb->update_regs_list;  
  10.     list_replace_init(&sfb->update_regs_list, &saved_list);  
  11.     mutex_unlock(&sfb->update_regs_list_lock);  
  12.   
  13.     list_for_each_entry_safe(data, next, &saved_list, list) {  
  14.         //每处理一个update_regs_list中的regs,就把它从list中移除  
  15.         s3c_fb_update_regs(sfb, data);  
  16.         list_del(&data->list);  
  17.         kfree(data);  
  18.     }  
  19. }  
[cpp]   view plain  copy
  1. static void s3c_fb_update_regs(struct s3c_fb *sfb, struct s3c_reg_data *regs)  
  2. {  
  3.     for (i = 0; i < sfb->variant.nr_windows; i++) {  
  4.         old_dma_bufs[i] = sfb->windows[i]->dma_buf_data;  
  5.   
  6.         //这里会等待acquireFence被signal,需要进去看下  
  7.         if (regs->dma_buf_data[i].fence)  
  8.             s3c_fd_fence_wait(sfb, regs->dma_buf_data[i].fence);  
  9.     }  
  10.     //具体显示相关,就不展开了  
  11.     __s3c_fb_update_regs(sfb, regs);  
  12.     //这里很重要,也需要进去看下  
  13.     sw_sync_timeline_inc(sfb->timeline, 1);  
  14.   
  15.     //释放上一个cycle分配的buffer,里面会call到sync_fence_put(dma->fence),也需要看一下.  
  16.     for (i = 0; i < sfb->variant.nr_windows; i++)  
  17.         s3c_fb_free_dma_buf(sfb, &old_dma_bufs[i]);  
  18. }  

注释中标出了3个需要注意的地方

1. s3c_fd_fence_wait

2. sw_sync_timeline_inc

3. sync_fence_put


我们先看1. s3c_fd_fence_wait

[cpp]   view plain  copy
  1. static void s3c_fd_fence_wait(struct s3c_fb *sfb, struct sync_fence *fence)  
  2. {  
  3.     int err = sync_fence_wait(fence, 1000);  
  4.     if (err >= 0)  
  5.         return;  
  6.   
  7.     if (err == -ETIME)  
  8.         err = sync_fence_wait(fence, 10 * MSEC_PER_SEC);  
  9.   
  10.     if (err < 0)  
  11.         dev_warn(sfb->dev, "error waiting on fence: %d\n", err);  
  12. }  

两次call到sync_fence_wait,只是参数不同而已,这里可以看出如果wait不到,buffer data的处理还是要继续的,只是报了个warn.

[cpp]   view plain  copy
  1. int sync_fence_wait(struct sync_fence *fence, long timeout)  
  2. {  
  3.     int err = 0;  
  4.       
  5.     if (timeout > 0) {  
  6.         timeout = msecs_to_jiffies(timeout);  
  7.         //等待sync_fence_check的结果  
  8.         err = wait_event_interruptible_timeout(fence->wq,  
  9.                                sync_fence_check(fence),  
  10.                                timeout);  
  11.     } else if (timeout < 0) {  
  12.         err = wait_event_interruptible(fence->wq,  
  13.                            sync_fence_check(fence));  
  14.     }  
  15.     return 0;  
  16. }  
[cpp]   view plain  copy
  1. static bool sync_fence_check(struct sync_fence *fence)  
  2. {  
  3.     /* 
  4.      * Make sure that reads to fence->status are ordered with the 
  5.      * wait queue event triggering 
  6.      */  
  7.     //对status的读取放在了read barrier内存屏障之后  
  8.     smp_rmb();  
  9.     return fence->status != 0;  
  10. }  

从这里看出,s3c_fd_fence_wait只是在等待fence->status状态变成非0,1是signaled,-1是error.


我们接着来看2.sw_sync_timeline_inc

[cpp]   view plain  copy
  1. void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc)  
  2. {  
  3.     obj->value += inc;  
  4.   
  5.     sync_timeline_signal(&obj->obj);  
  6. }  

sw_sync_timeline的value增加了inc,我们的情况是+1,如果是第一次进来,timeline中只有一个pt,它的value是2,timeline的value是1.之后call到sync_timeline_signal.经过n个cycle后,value最大的pt的value是n+1,timeline的value是n.

[cpp]   view plain  copy
  1. void sync_timeline_signal(struct sync_timeline *obj)  
  2. {  
  3.     unsigned long flags;  
  4.     LIST_HEAD(signaled_pts);  
  5.     struct list_head *pos, *n;  
  6.   
  7.     spin_lock_irqsave(&obj->active_list_lock, flags);  
  8.   
  9.     //在spin_lock的保护下,把被判断为处于signaled状态的pt从activa_list_header中移除  
  10.     //添加到signaled_list中去  
  11.     list_for_each_safe(pos, n, &obj->active_list_head) {  
  12.         struct sync_pt *pt =  
  13.             container_of(pos, struct sync_pt, active_list);  
  14.   
  15.         if (_sync_pt_has_signaled(pt)) {  
  16.             list_del_init(pos);  
  17.             list_add(&pt->signaled_list, &signaled_pts);  
  18.             kref_get(&pt->fence->kref);  
  19.         }  
  20.     }  
  21.   
  22.     spin_unlock_irqrestore(&obj->active_list_lock, flags);  
  23.   
  24.     list_for_each_safe(pos, n, &signaled_pts) {  
  25.         struct sync_pt *pt =  
  26.             container_of(pos, struct sync_pt, signaled_list);  
  27.   
  28.         list_del_init(pos);  
  29.         //每个处于signaled状态的pt都要call一次sync_fence_signal_pt,  
  30.         //来判断它所属的fence是否需要被signal.  
  31.         sync_fence_signal_pt(pt);  
  32.         kref_put(&pt->fence->kref, sync_fence_free);  
  33.     }  
  34. }  

_sync_pt_has_signaled之前已经分析过了,在第n个cycle的时候,value值是n-1的pt被signal.


    分析完sw_sync_timeline_inc,我们接着来看 3. sync_fence_put

[cpp]   view plain  copy
  1. void sync_fence_put(struct sync_fence *fence)  
  2. {  
  3.     fput(fence->file);  
  4. }  

只有一句话,非常简单!


    到这里,fb driver中fence的机制就分析完了,总结一下:

1. fb driver构建一个sw_sync_timeline.sw_sync_timeline中的每个pt都有一个value,通过比较timeline->value和pt->value判断哪个pt需要被signal

2. 每次需要构建一个fence的时候,先在timeline上构建一个sw_sync_pt,这个pt的value值是递增的.再由sw_sync_pt构建sync_fence.

3. 当sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc)的时候,timeline->增加inc,马上判断timeline中哪些pt被singal,接着判断这些pt所属的fence是否被signal.

4. 当fence使用完毕时,通过sync_fence_put释放fence.

另外,sw_sync还可以通过open dev, ioctrl的方式来操作fence.

假如我们的hwc没有现成的ioctl可以用,又没有办法改到driver的code,hwc可以打开/dev/sw_sync设备,通过一系列的ioctl来监控和控制fence.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值