天天看點

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. struct sync_timeline {  
  2.     struct kref     kref;  
  3.     const struct sync_timeline_ops  *ops;  
  4.     char            name[32];  
  5.     bool            destroyed;  
  6.     struct list_head    child_list_head;  
  7.     spinlock_t      child_list_lock;  
  8.     struct list_head    active_list_head;  
  9.     spinlock_t      active_list_lock;  
  10.     struct list_head    sync_timeline_list;  
  11. };  

    sync_timeline中包含一個由list_head串起來的sync_pt雙向連結清單child_list_head.

[cpp]   view plain  copy

  1. struct sync_pt {  
  2.     struct sync_timeline        *parent;  
  3.     struct list_head    child_list;  
  4.     struct list_head    active_list;  
  5.     struct list_head    signaled_list;  
  6.     struct sync_fence   *fence;  
  7.     struct list_head    pt_list;  
  8.     int         status;  
  9.     ktime_t         timestamp;  
  10. };  

    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. struct sync_fence {  
  2.     struct file     *file;  
  3.     struct kref     kref;  
  4.     char            name[32];  
  5.     struct list_head    pt_list_head;  
  6.     struct list_head    waiter_list_head;  
  7.     spinlock_t      waiter_list_lock;   
  8.     int         status;  
  9.     wait_queue_head_t   wq;  
  10.     struct list_head    sync_fence_list;  
  11. };  

    file指針表示fence所對應的file,linux中一切皆是file.pt_list_head是一個由list_head串起來的sync_pt雙向連結清單.

sync_timeline,sync_pt和sync_fence的關系可以用下面的圖來表示:

Android fb driver中的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.     u32         value;  
  4. };  
  5. struct sw_sync_pt {  
  6.     struct sync_pt      pt;  
  7.     u32         value;  
  8. };  

    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. 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.     struct sw_sync_timeline *timeline;  
  10.     int         timeline_max;  
  11.     ...  
  12. }  

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.     dev_err(dev, "failed to run update_regs thread\n");  
  13.     return err;  
  14.     }  
  15.     init_kthread_work(&sfb->update_regs_work, s3c_fb_update_regs_handler);  
  16.     sfb->timeline = sw_sync_timeline_create("s3c-fb");  
  17.     sfb->timeline_max = 1;  
  18.     ...  
  19. }  

    從上面這段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.     return obj;  
  8. }  

[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.         ret = s3c_fb_set_win_config(sfb, &p.win_data);  
  13.         if (ret)  
  14.             break;  
  15.         if (copy_to_user((struct s3c_fb_win_config_data __user *)arg,  
  16.                  &p.win_data,  
  17.                  sizeof(p.user_ion_client))) {  
  18.             ret = -EFAULT;  
  19.             break;  
  20.         }  
  21.         break;  
  22.     ...  
  23. }  

    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.     fd = get_unused_fd();  
  15.     if (fd < 0)  
  16.         return fd;  
  17.     mutex_lock(&sfb->output_lock);  
  18.     regs = kzalloc(sizeof(struct s3c_reg_data), GFP_KERNEL);  
  19.     ...  
  20.     ret = s3c_fb_set_win_buffer(sfb, win, config, regs);  
  21.     ...  
  22.     mutex_lock(&sfb->update_regs_list_lock);  
  23.     //timeline_max初始值為1  
  24.     sfb->timeline_max++;  
  25.     //以time_line_max為參數在sw_sync_timeline上建構了一個新的sw_sync_pt  
  26.     pt = sw_sync_pt_create(sfb->timeline, sfb->timeline_max);  
  27.     //以pt為參數建構一個fence  
  28.     fence = sync_fence_create("display", pt);  
  29.     //将fence install到file中,以fd表示這個file,之後對于fd的操作其實都是對fence的操作  
  30.     sync_fence_install(fence, fd);  
  31.     //把fd指派給win_data->fence,win_data->fence将寫回user space  
  32.     win_data->fence = fd;  
  33.     //buffer data的具體顯示工作交給kthread完成,  
  34.     list_add_tail(&regs->list, &sfb->update_regs_list);  
  35.     mutex_unlock(&sfb->update_regs_list_lock);  
  36.     queue_kthread_work(&sfb->update_regs_worker,  
  37.             &sfb->update_regs_work);  
  38.     mutex_unlock(&sfb->output_lock);  
  39.     return ret;  
  40. }  

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.     pt = (struct sw_sync_pt *)  
  5.         sync_pt_create(&obj->obj, sizeof(struct sw_sync_pt));  
  6.     pt->value = value;  
  7.     return (struct sync_pt *)pt;  
  8. }  

[cpp]   view plain  copy

  1. struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size)  
  2. {  
  3.     struct sync_pt *pt;  
  4.     if (size < sizeof(struct sync_pt))  
  5.         return NULL;  
  6.     pt = kzalloc(size, GFP_KERNEL);  
  7.     if (pt == NULL)  
  8.         return NULL;  
  9.     INIT_LIST_HEAD(&pt->active_list);  
  10.     kref_get(&parent->kref);  
  11.     sync_timeline_add_pt(parent, pt);  
  12.     return pt;  
  13. }  

    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.     if (pt->fence)  
  5.         return NULL;  
  6.     //sync_fence_alloc中通過anon_inode_getfile()為fence配置設定一個file,這個file在sync_fence_installde時候會用到  
  7.     fence = sync_fence_alloc(name);  
  8.     if (fence == NULL)  
  9.         return NULL;  
  10.     //儲存fence指針到pt->fence  
  11.     pt->fence = fence;  
  12.     //将pt加入到fence的pt_list_head連結清單中  
  13.     list_add(&pt->pt_list, &fence->pt_list_head);  
  14.     //将pt加入到timelien的active_list_head連結清單中  
  15.     sync_pt_activate(pt);  
  16.     //馬上判斷一次pt是否已經處于signal的狀态,如果fence中所有的pt都處于signal狀态,fence就要被signal  
  17.     sync_fence_signal_pt(pt);  
  18.     return fence;  
  19. }  

這裡有兩個關鍵點,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.     spin_lock_irqsave(&obj->active_list_lock, flags);  
  7.     err = _sync_pt_has_signaled(pt);  
  8.     if (err != 0)  
  9.         goto out;  
  10.     list_add_tail(&pt->active_list, &obj->active_list_head);  
  11. out:  
  12.     spin_unlock_irqrestore(&obj->active_list_lock, flags);  
  13. }  

    在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.     //call到了parent->ops的has_signaled,也就是sw_sync_timeline->ops->has_signaled  
  5.     //我們還記得在sw_sync_timeline_create的時候使用的ops參數是sw_sync_timeline_ops  
  6.     //其中.has_signaled = sw_sync_pt_has_signaled,    
  7.     if (!pt->status)  
  8.         pt->status = pt->parent->ops->has_signaled(pt);  
  9.     if (!pt->status && pt->parent->destroyed)  
  10.         pt->status = -ENOENT;  
  11.     if (pt->status != old_status)  
  12.         pt->timestamp = ktime_get();  
  13.     return pt->status;  
  14. }  

[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.     return sw_sync_cmp(obj->value, pt->value) >= 0;  
  7. }  

[cpp]   view plain  copy

  1. static int sw_sync_cmp(u32 a, u32 b)  
  2. {  
  3.     if (a == b)  
  4.         return 0;  
  5.     return ((s32)a - (s32)b) < 0 ? -1 : 1;  
  6. }  

    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.     status = sync_fence_get_status(fence);  
  11.     spin_lock_irqsave(&fence->waiter_list_lock, flags);  
  12.     //如果status不為0,也就是fence處于signaled或者error狀态,那麼在spin_lock的保護下,  
  13.     //把fence的waiter_list_header中的sync_fence_waiter移動到signaled_waiters list中去  
  14.     if (status && !fence->status) {  
  15.         list_for_each_safe(pos, n, &fence->waiter_list_head)  
  16.             list_move(pos, &signaled_waiters);  
  17.         //更新fence的status  
  18.         fence->status = status;  
  19.     } else {  
  20.         status = 0;  
  21.     }  
  22.     spin_unlock_irqrestore(&fence->waiter_list_lock, flags);  
  23.     if (status) {  
  24.         //周遊signaled_waiters,把每個waiter移除list,并call他們的callback  
  25.         list_for_each_safe(pos, n, &signaled_waiters) {  
  26.             struct sync_fence_waiter *waiter =  
  27.                 container_of(pos, struct sync_fence_waiter,  
  28.                          waiter_list);  
  29.             list_del(pos);  
  30.             waiter->callback(fence, waiter);  
  31.         }  
  32.         //wake up wait_queue_head_t  
  33.         wake_up(&fence->wq);  
  34.     }  
  35. }  

其他的部分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.     list_for_each(pos, &fence->pt_list_head) {  
  7.         struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list);  
  8.         int pt_status = pt->status;  
  9.         if (pt_status < 0) {  
  10.             //如果有一個pt的status是error,則整個fence的status就是error  
  11.             status = pt_status;  
  12.             break;  
  13.         } else if (status == 1) {  
  14.             //如果有一個pt的status是activate,則覆寫掉預設值,繼續周遊直到遇到error或者到list尾部.  
  15.             status = pt_status;  
  16.         }  
  17.     }  
  18.     return status;  
  19. }  

    因為我們的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.     if (file == NULL)  
  5.         return NULL;  
  6.     if (file->f_op != &sync_fence_fops)  
  7.         goto err;  
  8.     return file->private_data;  
  9. err:  
  10.     fput(file);  
  11.     return NULL;  
  12. }  

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.     mutex_lock(&sfb->update_regs_list_lock);  
  8.     saved_list = sfb->update_regs_list;  
  9.     list_replace_init(&sfb->update_regs_list, &saved_list);  
  10.     mutex_unlock(&sfb->update_regs_list_lock);  
  11.     list_for_each_entry_safe(data, next, &saved_list, list) {  
  12.         //每處理一個update_regs_list中的regs,就把它從list中移除  
  13.         s3c_fb_update_regs(sfb, data);  
  14.         list_del(&data->list);  
  15.         kfree(data);  
  16.     }  
  17. }  

[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.         //這裡會等待acquireFence被signal,需要進去看下  
  6.         if (regs->dma_buf_data[i].fence)  
  7.             s3c_fd_fence_wait(sfb, regs->dma_buf_data[i].fence);  
  8.     }  
  9.     //具體顯示相關,就不展開了  
  10.     __s3c_fb_update_regs(sfb, regs);  
  11.     //這裡很重要,也需要進去看下  
  12.     sw_sync_timeline_inc(sfb->timeline, 1);  
  13.     //釋放上一個cycle配置設定的buffer,裡面會call到sync_fence_put(dma->fence),也需要看一下.  
  14.     for (i = 0; i < sfb->variant.nr_windows; i++)  
  15.         s3c_fb_free_dma_buf(sfb, &old_dma_bufs[i]);  
  16. }  

注釋中标出了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.     if (err == -ETIME)  
  7.         err = sync_fence_wait(fence, 10 * MSEC_PER_SEC);  
  8.     if (err < 0)  
  9.         dev_warn(sfb->dev, "error waiting on fence: %d\n", err);  
  10. }  

兩次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.     if (timeout > 0) {  
  5.         timeout = msecs_to_jiffies(timeout);  
  6.         //等待sync_fence_check的結果  
  7.         err = wait_event_interruptible_timeout(fence->wq,  
  8.                                sync_fence_check(fence),  
  9.                                timeout);  
  10.     } else if (timeout < 0) {  
  11.         err = wait_event_interruptible(fence->wq,  
  12.                            sync_fence_check(fence));  
  13.     }  
  14.     return 0;  
  15. }  

[cpp]   view plain  copy

  1. static bool sync_fence_check(struct sync_fence *fence)  
  2. {  
  3.     //對status的讀取放在了read barrier記憶體屏障之後  
  4.     smp_rmb();  
  5.     return fence->status != 0;  
  6. }  

從這裡看出,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.     sync_timeline_signal(&obj->obj);  
  5. }  

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.     spin_lock_irqsave(&obj->active_list_lock, flags);  
  7.     //在spin_lock的保護下,把被判斷為處于signaled狀态的pt從activa_list_header中移除  
  8.     //添加到signaled_list中去  
  9.     list_for_each_safe(pos, n, &obj->active_list_head) {  
  10.         struct sync_pt *pt =  
  11.             container_of(pos, struct sync_pt, active_list);  
  12.         if (_sync_pt_has_signaled(pt)) {  
  13.             list_del_init(pos);  
  14.             list_add(&pt->signaled_list, &signaled_pts);  
  15.             kref_get(&pt->fence->kref);  
  16.         }  
  17.     }  
  18.     spin_unlock_irqrestore(&obj->active_list_lock, flags);  
  19.     list_for_each_safe(pos, n, &signaled_pts) {  
  20.         struct sync_pt *pt =  
  21.             container_of(pos, struct sync_pt, signaled_list);  
  22.         list_del_init(pos);  
  23.         //每個處于signaled狀态的pt都要call一次sync_fence_signal_pt,  
  24.         //來判斷它所屬的fence是否需要被signal.  
  25.         sync_fence_signal_pt(pt);  
  26.         kref_put(&pt->fence->kref, sync_fence_free);  
  27.     }  
  28. }  

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



繼續閱讀