2016-12-31

1. flushdcachepage的实现

问题:flushdcachepage()到底是 invalid, 还是 clean and invalid, 还是仅清掉dirty bit

linux-3.10.86/arch/arm/mm/flush.c

flush_dcache_page -> __flush_dcache_page
{
    /*
     * Writeback ...
    */

}

是Writeback, 所以, 可以理解为 clean and invalid, 尽管实现上可能采用lazy什么的.

2. 读写中的使用

linux-3.10.86/mm/filemap.c

do_generic_file_read
{

page_ok:  /*page是uptodate状态*/

        ...
        /* If users can be writing to this page using arbitrary
         * virtual addresses, take care about potential aliasing
         * before reading the page on the kernel side.

        可能是应对这样的场景:
        1. 用户发起 读
        2. 磁盘驱动开始工作, 传送内容到page
        3. 用户写 page
        4. 本函数的flush_dcache_page
        5. actor()


         */
        if (mapping_writably_mapped(mapping))
            flush_dcache_page(page);
        ...
        ret = actor(desc, page, offset, nr);//比如file_read_actor()

}
generic_perform_write
{


        /*
        1. 问题:既然接下来要从用户态那边拷贝数据到page, 这里flush有啥意义?
        答:    存在cache alias的可能, 会导致后续不知道那个才是latest的, 故拷贝前先flush掉,
        这样就不会不知道用哪个cache line了.

        2. 场景:
        cache alias in mmap + write
        https://lkml.org/lkml/2010/1/20/60

        3.  可以考虑改写 2中的程序, 让数据的大小 大于 一个cache line, 这样就可以知道, 
        flush_dcache_page是不是clean and invalid了

        */
        if (mapping_writably_mapped(mapping))
            flush_dcache_page(page);

        iov_iter_copy_from_user_atomic(page, i, offset, bytes);
        ...
        /*
        问题:这里flush的原因是?
        答:iov_iter_copy_from_user_atomic()中对page的访问是使用kmap_atomic, 
        映射到内核地址, 这是一个值大于3G的地址, 

        而用户态对该page的访问, 是通过一个小于3G的地址 (map的话, 解决了权限问题), 
        这是两个值不同的虚拟地址, 对应同一个物理RAM, 好了, D-cache aliasing.

        场景是:
        1. 用户发起写
        2. 内核iov_iter_copy_from_user_atomic
        3. 用户写page
        4. flush_dcache_page

        */
        flush_dcache_page(page);

        a_ops->write_end(...); //比如ext2_write_end -> generic_write_end()


}
generic_write_end -> block_write_end
{
    /*
    如果调用路径是generic_write_end -> block_write_end的话,
    对于ext2, 大概是没有必要, 但其他文件系统是需要的.

    另外, 还有其他调用路径, 不单单被generic_write_end调用, 不分析了.
    都一个套路, 填page cache后, flush之.
    */
    flush_dcache_page(page);
}

3. 总结

大致就这样, 总结下:

linux-3.10.86/Documentation/cachetlb.txt

  void flush_dcache_page(struct page *page)
    Any time the kernel writes to a page cache page, _OR_
    the kernel is about to read from a page cache page and
    user space shared/writable mappings of this page potentially
    exist, this routine is called.
    NOTE: This routine need only be called for page cache pages which can potentially ever be mapped into the address
          space of a user process.  So for example, VFS layer code
          handling vfs symlinks in the page cache need not call
          this interface at all.
    The phrase "kernel writes to a page cache page" means,
    specifically, that the kernel executes store instructions
    that dirty data in that page at the page->virtual mapping
    of that page.  It is important to flush here to handle
    D-cache aliasing, to make sure these kernel stores are
    visible to user space mappings of that page.
    The corollary case is just as important, if there are users
    which have shared+writable mappings of this file, we must make
    sure that kernel reads of these pages will see the most recent
    stores done by the user. 

问题:是写page cache之前调用flushdcachepage, 还是写page cache之后调用?
答:上面有:

... to make sure these kernel stores are visible to user space mappings of that page.

所以, 是写page cache之后.

本文地址: https://awakening-fong.github.io/posts/mm/flush_dcache_page

转载请注明出处: https://awakening-fong.github.io


若无法评论, 请打开JavaScript, 并通过proxy.


blog comments powered by Disqus