免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 11110 | 回复: 5
打印 上一主题 下一主题

[文件系统] flush_dcache_page [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-01-15 00:15 |只看该作者 |倒序浏览
http://bbs.chinaunix.net/thread-3774478-1-1.html
在我的一个文件写入的帖子,write_end方法中用到了flush_dcache_page()方法,当时不是很清楚,现在拿出来重新看一下。假设cpu是arm a9,v7架构。

经过研究,发现flush_dcache_page的一个直接的功能就是将page相关的data cache内容写回到内存,因为后面磁盘驱动的dma可能要开始搬运它了。这是必须的。
看一下这个功能怎么调用到的,大概流程如下(实际上还有更复杂的逻辑)
flush_dcache_page(struct page *page)
-》 __flush_dcache_page(mapping, page);
-》__cpuc_flush_dcache_area
-》cpu_cache.flush_kern_dcache_area
-》v7_flush_kern_dcache_area
       
在cache-v7.S中实现:
/*
*        v7_flush_kern_dcache_area(void *addr, size_t size)
*
*        Ensure that the data held in the page kaddr is written back
*        to the page in question.
*
*        - addr        - kernel address
*        - size        - region size
*/
ENTRY(v7_flush_kern_dcache_area)
        dcache_line_size r2, r3  //从ctr寄存器读取到data cache的line length,假设是32字节。
        add        r1, r0, r1 //计算出最大的要写回地址。
        sub        r3, r2, #1  //计算掩码,将r0掩码位清0.
        bic        r0, r0, r3
1:
        //写回r0地址对应的cache line,(D line 或者 唯一 line)
        mcr        p15, 0, r0, c7, c14, 1                @ clean & invalidate D line / unified line
        add        r0, r0, r2  //r0地址加上32(line length)
        cmp        r0, r1   //同结束地址进行比较,循环写回cache。
        blo        1b
        dsb
        mov        pc, lr
ENDPROC(v7_flush_kern_dcache_area)
很明显,调用了体系相关的指令写回了kaddr到kaddr+size这段范围内的cache。看到,32字节的line,一个page也要循环128次,所以还是有代价的,尤其是刷新整个cache,需要注意。

接着其实flush_dcache_page不仅仅这个刷新cache的功能,还有其他复杂的逻辑
先看一个文档:http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=1972593

通过这个文档,有几个要点:
data cache有几种类型:pipt,vipt-noaliasing,vipt-aliasing,vivt。
其中前两种cache没有aliasing问题,后两种有aliasing问题,由于前两种性能较低,所以现在用的较多的是后面两种,我的机器上就是vipt-aliasing的,存在alasing的问题,aliasing最普遍的情况就是一个物理page被内核,多个userspace同时映射了(比如mmap一个文件),由于虚拟地址不同,所以他们会使用到不同的cache line,这样假设内核修改了page对应的自己cache line的值,那么userspace是不知道的,还是使用userpace对应的cache line。解决办法就是,先写回内核的cache,然后invaliadate所有page关联的userspace地址对应的cache。那么userpace再读取page的时候,将会重新从memory获取到最新的值。

具体的逻辑还是比较复杂,匆忙写出来恐怕错误太多,还是先放在这大家研究一下。
/*
* Ensure cache coherency between kernel mapping and userspace mapping
* of this page.
*
* We have three cases to consider:
*  - VIPT non-aliasing cache: fully coherent so nothing required.
*  - VIVT: fully aliasing, so we need to handle every alias in our
*          current VM view.
*  - VIPT aliasing: need to handle one alias in our current VM view.
*
* If we need to handle aliasing:
*  If the page only exists in the page cache and there are no user
*  space mappings, we can be lazy and remember that we may have dirty
*  kernel cache lines for later.  Otherwise, we assume we have
*  aliasing mappings.
*
* Note that we disable the lazy flush for SMP configurations where
* the cache maintenance operations are not automatically broadcasted.
*/
void flush_dcache_page(struct page *page)
{
        struct address_space *mapping;

        /*
         * The zero page is never written to, so never has any dirty
         * cache lines, and therefore never needs to be flushed.
         */
        if (page == ZERO_PAGE(0))
                return;

        mapping = page_mapping(page);

        if (!cache_ops_need_broadcast() &&
            mapping && !mapping_mapped(mapping))
                clear_bit(PG_dcache_clean, &page->flags);
        else {
                __flush_dcache_page(mapping, page);
                if (mapping && cache_is_vivt())
                        __flush_dcache_aliases(mapping, page);
                else if (mapping)
                        __flush_icache_all();
                set_bit(PG_dcache_clean, &page->flags);
        }
}



论坛徽章:
16
2015亚冠之吉达阿赫利
日期:2015-08-17 11:21:462015年迎新春徽章
日期:2015-03-04 09:58:11酉鸡
日期:2014-12-07 09:06:19水瓶座
日期:2014-11-04 14:23:29天秤座
日期:2014-03-02 08:57:52双鱼座
日期:2014-02-22 13:07:56午马
日期:2014-02-14 11:08:18双鱼座
日期:2014-02-13 11:09:37卯兔
日期:2014-02-06 15:10:34子鼠
日期:2014-01-20 14:48:19戌狗
日期:2013-12-19 09:37:46射手座
日期:2013-12-19 09:33:47
2 [报告]
发表于 2013-01-15 09:24 |只看该作者
赞~

论坛徽章:
0
3 [报告]
发表于 2013-01-15 15:23 |只看该作者
跟了下调用,普通情况比如通过sys_write一个page,那么走的都是clear_bit这个流程。

        if (!cache_ops_need_broadcast() &&
            mapping && !mapping_mapped(mapping))
                clear_bit(PG_dcache_clean, &page->flags);


这个不会触发flush dcache的操作,而是通过lazy方式清除了dcache_clean标志,以后调用set_pte_at这个page的时候延迟flush dcache/icache.

那么,我就纠结了,flush dcache的动作到底在哪里做的?以后驱动dma都要搬运这块memory了,不flush怎么可以呢?


求指导。

论坛徽章:
0
4 [报告]
发表于 2013-01-15 17:08 |只看该作者
flush_dcache_page的作用是处理aliasing问题的。

flush dcache的动作是在驱动调用dma_map_sg的时候实现的。
dma_map_sg有两个作用:一个转换物理地址到dma地址,另外一个就是根据读,写对相应的page分别进行inv和clean操作。
/**
* dma_map_sg - map a set of SG buffers for streaming mode DMA
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
* @sg: list of buffers
* @nents: number of buffers to map
* @dir: DMA transfer direction
*
* Map a set of buffers described by scatterlist in streaming mode for DMA.
* This is the scatter-gather version of the dma_map_single interface.
* Here the scatter gather list elements are each tagged with the
* appropriate dma address and length.  They are obtained via
* sg_dma_{address,length}.
*
* Device ownership issues as mentioned for dma_map_single are the same
* here.
*/
int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
                enum dma_data_direction dir)
{
        struct scatterlist *s;
        int i, j;

        BUG_ON(!valid_dma_direction(dir));

        for_each_sg(sg, s, nents, i) {
                s->dma_address = __dma_map_page(dev, sg_page(s), s->offset,
                                                s->length, dir);
                if (dma_mapping_error(dev, s->dma_address))
                        goto bad_mapping;
        }
        debug_dma_map_sg(dev, sg, nents, nents, dir);
        return nents;

bad_mapping:
        for_each_sg(sg, s, i, j)
                __dma_unmap_page(dev, sg_dma_address(s), sg_dma_len(s), dir);
        return 0;
}

__dma_map_page()
__dma_page_cpu_to_dev()
___dma_page_cpu_to_dev()
void ___dma_page_cpu_to_dev(struct page *page, unsigned long off,
        size_t size, enum dma_data_direction dir)
{
        unsigned long paddr;

        dma_cache_maint_page(page, off, size, dir, dmac_map_area);//这个地方inv或者clean每个page

               //下面跟l2c有关。不太清楚??
        paddr = page_to_phys(page) + off;
        if (dir == DMA_FROM_DEVICE) {
                outer_inv_range(paddr, paddr + size);
        } else {
                outer_clean_range(paddr, paddr + size);
        }
        /* FIXME: non-speculating: flush on bidirectional mappings? */
}



#define dmac_map_area                        cpu_cache.dma_map_area
跟上面一样终于跑到cache-v7.S里面来了。

/*
*        dma_map_area(start, size, dir)
*        - start        - kernel virtual start address
*        - size        - size of region
*        - dir        - DMA direction
*/
ENTRY(v7_dma_map_area)
        add        r1, r1, r0
        teq        r2, #DMA_FROM_DEVICE
        beq        v7_dma_inv_range
        b        v7_dma_clean_range
ENDPROC(v7_dma_map_area)
明显的,从磁盘读就是inv cache,写磁盘就是clean cache。



/*
*        v7_dma_inv_range(start,end)
*
*        Invalidate the data cache within the specified region; we will
*        be performing a DMA operation in this region and we want to
*        purge old data in the cache.
*
*        - start   - virtual start address of region
*        - end     - virtual end address of region
*/
v7_dma_inv_range:
        dcache_line_size r2, r3
        sub        r3, r2, #1
        tst        r0, r3
        bic        r0, r0, r3
#ifdef CONFIG_ARM_ERRATA_764369
        ALT_SMP(W(dsb))
        ALT_UP(W(nop))
#endif
        mcrne        p15, 0, r0, c7, c14, 1                @ clean & invalidate D / U line

        tst        r1, r3
        bic        r1, r1, r3
        mcrne        p15, 0, r1, c7, c14, 1                @ clean & invalidate D / U line
1:
        mcr        p15, 0, r0, c7, c6, 1                @ invalidate D / U line
        add        r0, r0, r2
        cmp        r0, r1
        blo        1b
        dsb
        mov        pc, lr
ENDPROC(v7_dma_inv_range)

/*
*        v7_dma_clean_range(start,end)
*        - start   - virtual start address of region
*        - end     - virtual end address of region
*/
v7_dma_clean_range:
        dcache_line_size r2, r3
        sub        r3, r2, #1
        bic        r0, r0, r3
#ifdef CONFIG_ARM_ERRATA_764369
        ALT_SMP(W(dsb))
        ALT_UP(W(nop))
#endif
1:
        mcr        p15, 0, r0, c7, c10, 1                @ clean D / U line
        add        r0, r0, r2
        cmp        r0, r1
        blo        1b
        dsb
        mov        pc, lr
ENDPROC(v7_dma_clean_range)

论坛徽章:
0
5 [报告]
发表于 2013-03-17 09:57 |只看该作者
不太同意“ flush_dcache_page的作用是处理aliasing问题的。”

在dma的应用中flush dcache 跟aliasing没有关系吧。因为,dma不走cache,所以在DMA control 某个page之前,都需要将cache中的对应数据 clean & invalidate掉。

论坛徽章:
2
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:53:17
6 [报告]
发表于 2014-03-10 14:11 |只看该作者
不明觉厉啊!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP