summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorNicolas Pitre <nico@cam.org>2009-09-01 23:01:27 +0200
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-09-02 12:33:24 +0200
commit13f96d8f4c5a3f6a6b5e578d08869d79d690e0b2 (patch)
treefe33eb0bce77b41d9e7a491b1241db6a70a93df2 /arch
parentautofs4 - fix missed case when changing to use struct path (diff)
downloadlinux-13f96d8f4c5a3f6a6b5e578d08869d79d690e0b2.tar.xz
linux-13f96d8f4c5a3f6a6b5e578d08869d79d690e0b2.zip
ARM: 5687/1: fix an oops with highmem
In xdr_partial_copy_from_skb() there is that sequence: kaddr = kmap_atomic(*ppage, KM_SKB_SUNRPC_DATA); [...] flush_dcache_page(*ppage); kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA); Mixing flush_dcache_page() and kmap_atomic() is a bit odd, especially since kunmap_atomic() must deal with cache issues already. OTOH the non-highmem case must use flush_dcache_page() as kunmap_atomic() becomes a no op with no cache maintenance. Problem is that with highmem the implementation of kmap_atomic() doesn't set page->virtual, and page_address(page) returns 0 in that case. Here flush_dcache_page() calls __flush_dcache_page() which calls __cpuc_flush_dcache_page(page_address(page)) resulting in a kernel oops. None of the kmap_atomic() implementations uses set_page_address(). Hence we can assume page_address() is always expected to return 0 in that case. Let's conditionally call __cpuc_flush_dcache_page() only when the page address is non zero, and perform that test only when highmem is configured. Signed-off-by: Nicolas Pitre <nico@marvell.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mm/flush.c9
1 files changed, 8 insertions, 1 deletions
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
index c07222eb5ce0..575f3ad722e7 100644
--- a/arch/arm/mm/flush.c
+++ b/arch/arm/mm/flush.c
@@ -144,7 +144,14 @@ void __flush_dcache_page(struct address_space *mapping, struct page *page)
* page. This ensures that data in the physical page is mutually
* coherent with the kernels mapping.
*/
- __cpuc_flush_dcache_page(page_address(page));
+#ifdef CONFIG_HIGHMEM
+ /*
+ * kmap_atomic() doesn't set the page virtual address, and
+ * kunmap_atomic() takes care of cache flushing already.
+ */
+ if (page_address(page))
+#endif
+ __cpuc_flush_dcache_page(page_address(page));
/*
* If this is a page cache page, and we have an aliasing VIPT cache,