ARM: force dcache flush if dcache_dirty bit set
authorNitin Gupta <ngupta@vflare.org>
Mon, 12 Oct 2009 08:50:23 +0000 (14:20 +0530)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Mon, 12 Oct 2009 16:52:26 +0000 (17:52 +0100)
commit787b2faadc4356b6c2c71feb42fb944fece9a12f
treee3acab624bb2de248a2e4f1e6293024200c8dc8c
parentedc72786d208e77db94f84dcb0d166c0d23d82f7
ARM: force dcache flush if dcache_dirty bit set

On ARM, update_mmu_cache() does dcache flush for a page only if
it has a kernel mapping (page_mapping(page) != NULL). The correct
behavior would be to force the flush based on dcache_dirty bit only.

One of the cases where present logic would be a problem is when
a RAM based block device[1] is used as a swap disk. In this case,
we would have in-memory data corruption as shown in steps below:

do_swap_page()
{
    - Allocate a new page (if not already in swap cache)
    - Issue read from swap disk
        - Block driver issues flush_dcache_page()
        - flush_dcache_page() simply sets PG_dcache_dirty bit and does not
          actually issue a flush since this page has no user space mapping yet.
    - Now, if swap disk is almost full, this newly read page is removed
      from swap cache and corrsponding swap slot is freed.
    - Map this page anonymously in user space.
    - update_mmu_cache()
        - Since this page does not have kernel mapping (its not in page/swap
          cache and is mapped anonymously), it does not issue dcache flush
          even if dcache_dirty bit is set by flush_dcache_page() above.

    <user now gets stale data since dcache was never flushed>
}

Same problem exists on mips too.

[1] example:
 - brd (RAM based block device)
 - ramzswap (RAM based compressed swap device)

Signed-off-by: Nitin Gupta <ngupta@vflare.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/mm/fault-armv.c