summaryrefslogtreecommitdiff
path: root/arch/mips/mm
diff options
context:
space:
mode:
authorLars Persson <lars.persson@axis.com>2015-02-26 16:16:03 +0300
committerRalf Baechle <ralf@linux-mips.org>2015-03-25 15:48:00 +0300
commit4d46a67a3eb827ccf1125959936fd51ba318dabc (patch)
tree6f2bee482b4325927a1f176a974a72c69c5b52b2 /arch/mips/mm
parent5b9593f3bccb9904f260f9ad7f184e1d2921bd1e (diff)
downloadlinux-4d46a67a3eb827ccf1125959936fd51ba318dabc.tar.xz
MIPS: Fix race condition in lazy cache flushing.
The lazy cache flushing implemented in the MIPS kernel suffers from a race condition that is exposed by do_set_pte() in mm/memory.c. A pre-condition is a file-system that writes to the page from the CPU in its readpage method and then calls flush_dcache_page(). One example is ubifs. Another pre-condition is that the dcache flush is postponed in __flush_dcache_page(). Upon a page fault for an executable mapping not existing in the page-cache, the following will happen: 1. Write to the page 2. flush_dcache_page 3. flush_icache_page 4. set_pte_at 5. update_mmu_cache (commits the flush of a dcache-dirty page) Between steps 4 and 5 another thread can hit the same page and it will encounter a valid pte. Because the data still is in the L1 dcache the CPU will fetch stale data from L2 into the icache and execute garbage. This fix moves the commit of the cache flush to step 3 to close the race window. It also reduces the amount of flushes on non-executable mappings because we never enter __flush_dcache_page() for non-aliasing CPUs. Regressions can occur in drivers that mistakenly relies on the flush_dcache_page() in get_user_pages() for DMA operations. [ralf@linux-mips.org: Folded in patch 9346 to fix highmem issue.] Signed-off-by: Lars Persson <larper@axis.com> Cc: linux-mips@linux-mips.org Cc: paul.burton@imgtec.com Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/9346/ Patchwork: https://patchwork.linux-mips.org/patch/9738/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/mm')
-rw-r--r--arch/mips/mm/cache.c12
1 files changed, 12 insertions, 0 deletions
diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
index f7b91d3a371d..77d96db8253c 100644
--- a/arch/mips/mm/cache.c
+++ b/arch/mips/mm/cache.c
@@ -119,6 +119,18 @@ void __flush_anon_page(struct page *page, unsigned long vmaddr)
EXPORT_SYMBOL(__flush_anon_page);
+void __flush_icache_page(struct vm_area_struct *vma, struct page *page)
+{
+ unsigned long addr;
+
+ if (PageHighMem(page))
+ return;
+
+ addr = (unsigned long) page_address(page);
+ flush_data_cache_page(addr);
+}
+EXPORT_SYMBOL_GPL(__flush_icache_page);
+
void __update_cache(struct vm_area_struct *vma, unsigned long address,
pte_t pte)
{