zoukankan      html  css  js  c++  java
  • Linux framebuffer deferred io机制

    一、总体框架

      deferred io机制主要用于驱动没有实现自刷新同时应用层又不想调用FBIOPAN_DISPLAY的一个折中方案,  使用ioctrl FBIOPAN_DISPLAY好处是节能, 驱动不用盲目的刷数据(尤其是一静态帧数据), 数据的更新是由应用程序操作的,

    所以应用程序当然知道何时刷数据, 最理想的情况是应用程序一更新数据立马调用FBIOPAN_DISPLAY, 但也有缺点, 一是要应用层显示调用FBIOPAN_DISPLAY,二是画面更新频率高的话, FBIOPAN_DISPLAY带来的系统调用开支也不小;

    使用驱动自刷新当然解放应用, 应用不用关心数据显示问题, 直接操作显存, 所写即所见。

    二、源码分析

      代码具体在linux/drivers/video/fb_defio.c, 如下演示刷图穿插该框架的实现代码:

      1. fb_defio 自己实现一个mmap(), 没有将用户空间虚拟地址和物理帧缓存进行页表映射, 倒是提供了缺页异常处理函数

    void fb_deferred_io_init(struct fb_info *info)
    {
        struct fb_deferred_io *fbdefio = info->fbdefio;
    
        BUG_ON(!fbdefio);
        mutex_init(&fbdefio->lock);
        info->fbops->fb_mmap = fb_deferred_io_mmap;
        INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
        INIT_LIST_HEAD(&fbdefio->pagelist);
        if (fbdefio->delay == 0) /* set a default of 1 s */
            fbdefio->delay = HZ;
    }
    EXPORT_SYMBOL_GPL(fb_deferred_io_init);
    
    static int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
    {
    vma->vm_ops = &fb_deferred_io_vm_ops;
    vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
    if (!(info->flags & FBINFO_VIRTFB))
    vma->vm_flags |= VM_IO;
    vma->vm_private_data = info;
    return 0;
    }
    
    static const struct vm_operations_struct fb_deferred_io_vm_ops = {
    .fault    = fb_deferred_io_fault,
    .page_mkwrite    = fb_deferred_io_mkwrite,
    };

      2. 应用程序通过mmap(), 获得一块帧缓存的虚拟地址, 但没有对应的实际物理内存

     

      3. 应用程序操作内存(write), 由于页表没有对应物理内存导致缺页异常

    do_page_fault()
        -> __do_page_fault()
            -> handle_mm_fault()
                -> __handle_mm_fault()
                    -> handle_pte_fault():
                                            if (vma->vm_ops) {
                                                if (likely(vma->vm_ops->fault))
                                                    return do_linear_fault(mm, vma, address, pte, pmd, flags, entry);
                                            }
                                            -> __do_fault():
                                                            vma->vm_ops->fault(vma, &vmf);
                                                            vma->vm_ops->page_mkwrite(vma, &vmf);

      从上面流程可以看出, 当vm_ops且fault有效时, 会走自定义的fault实现, 同时如果操作时write行为, 还会调用page_mkwrite(有效的话)

      4. fb_defio提供了缺页异常的处理函数fb_deferred_io_fault(), 分配物理页跟虚拟地址对应起来, 并把该物理页挂到fbdefio->pagelist(即将被刷新数据), 然后启动工作队列延迟delay后执行这个工作项fb_deferred_io_work()

    static int fb_deferred_io_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
    {
        unsigned long offset;
        struct page *page;
        struct fb_info *info = vma->vm_private_data;
    
        offset = vmf->pgoff << PAGE_SHIFT;
        if (offset >= info->fix.smem_len)
            return VM_FAULT_SIGBUS;
    
        page = fb_deferred_io_page(info, offset);
        if (!page)
            return VM_FAULT_SIGBUS;
    
        get_page(page);
    
        if (vma->vm_file)
            page->mapping = vma->vm_file->f_mapping;
        else
            printk(KERN_ERR "no mapping available
    ");
    
        BUG_ON(!page->mapping);
        page->index = vmf->pgoff;
    
        vmf->page = page;
        return 0;
    }
    
    static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs)
    {
        void *screen_base = (void __force *) info->screen_base;
        struct page *page;
    
        if (is_vmalloc_addr(screen_base + offs))
            page = vmalloc_to_page(screen_base + offs);
        else
            page = pfn_to_page((info->fix.smem_start + offs) >> PAGE_SHIFT);
    
        return page;
    }
    
    /* 需要注意的是帧缓存是一开始fb驱动就应该分配好的, 同时赋值给fb_info->screen_base, fb_info->fix.smem_start
     * 而fb_deferred_io_fault()-> fb_deferred_io_page()分配内存只是找出并返回此次缺页异常页虚拟地址对应的物理地址,
     * 好填充页表, 这样应用程序就可以正常write数据到缓存, 整个过程对应用程序是透明的。
    */                                     

      5. fb_deferred_io_work() 最核心就是调用函数指针fbdefio->deferred_io(驱动要实现的刷数据函数), 并调用page_mkclean()将之前虚拟地址和帧物理页的映射清除, 使得下次操作这块虚拟地址又能重新触发缺页机制

      

    二、fb驱动示例

    static void lcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
    {
        struct page *cur;
        struct fb_deferred_io *fbdefio = info->fbdefio;
    
        /* pagelist存放的都是被更新的脏页, 由于我驱动用SPI DMA搬数据,会存在cache不一致, 所以要把cache的缓存刷回内存 */
        list_for_each_entry(cur, &fbdefio->pagelist, lru) {
            flush_dcache_page(cur);
        }
        
        /* 虽然pagelist存放都是脏页,我懒得对这些页在帧的位置进行排布分析, 直接一整帧都刷
           也即哪怕应用程序改动一个page, 驱动都会整帧刷*/
        info->fbops->fb_pan_display(&info->var , info);
    }
    
    static struct fb_deferred_io gen_lcd_fb_defio = {
        .delay        = HZ / 8,
        .deferred_io    = lcd_fb_deferred_io,
    };
    
    static void lcd_defio_init(struct fb_info *info, struct fb_deferred_io *fbdefio)
    {
        info->fbdefio = fbdefio;
        fb_deferred_io_init(info);
    }
    
    static void lcd_defio_cleanup(struct fb_info *info)
    {
        if (info->fbdefio != NULL) {
            fb_deferred_io_cleanup(info);
            info->fbdefio = NULL;
        }
    }
    
    ==========================================
    lcd_defio_init(fb_dev, gen_lcd_fb_defio)
    lcd_defio_cleanup(fb_dev)

    三、其他

      1. 要使能deferred io机制, 要打开CONFIG_FB_DEFERRED_IO配置

      2. 这里没有帧率说法, 只跟应用刷图有关

      3. 缺页异常会被调用多次, 但fb_deferred_io_work()只会被最后页调用一次, 比如应用要刷,2个page, 第一个page导致fb_deferred_io_mkwrite()添加到pagelist, 然后调用schedule_delayed_work()启动延迟1/8s的工作项fb_deferred_io_work(),

       接着引发第二页缺页异常会被继续添加到pagelist, schedule_delayed_work再次被执行, 但只是重新更新延迟时间为1/8s

      4. 我感觉page_mkclean() 这里有个bug, 它是把物理页所有的映射都清除, 包括kernel空间的虚拟地址, 那下次缺页异常时fb_deferred_io_page() -> vmalloc_to_page(screen_base + offs), 就会有问题, 因为页表被清除掉了, 所以该框架目前应该只支持连续的帧缓存

  • 相关阅读:
    集成方法-概念理解
    k-近邻算法-手写识别系统
    k-近邻算法-优化约会网站的配对效果
    支持向量机-手写识别问题
    支持向量机-在复杂数据上应用核函数
    支持向量机-完整Platt-SMO算法加速优化
    支持向量机-SMO算法简化版
    支持向量机-引入及原理
    hdu4123-Bob’s Race(树形dp+rmq+尺取)
    hdu4436-str2int(后缀数组 or 后缀自动机)
  • 原文地址:https://www.cnblogs.com/vedic/p/10722514.html
Copyright © 2011-2022 走看看