123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- From: Felix Fietkau <[email protected]>
- Date: Sun, 24 Jan 2016 01:03:51 +0100
- Subject: [PATCH] MIPS: fix cache flushing for highmem pages
- Most cache flush ops were no-op for highmem pages. This led to nasty
- segfaults and (in the case of page_address(page) == NULL) kernel
- crashes.
- Fix this by always flushing highmem pages using kmap/kunmap_atomic
- around the actual cache flush. This might be a bit inefficient, but at
- least it's stable.
- Signed-off-by: Felix Fietkau <[email protected]>
- ---
- --- a/arch/mips/mm/cache.c
- +++ b/arch/mips/mm/cache.c
- @@ -14,6 +14,7 @@
- #include <linux/sched.h>
- #include <linux/syscalls.h>
- #include <linux/mm.h>
- +#include <linux/highmem.h>
-
- #include <asm/cacheflush.h>
- #include <asm/processor.h>
- @@ -78,18 +79,29 @@ SYSCALL_DEFINE3(cacheflush, unsigned lon
- return 0;
- }
-
- +static void
- +flush_highmem_page(struct page *page)
- +{
- + void *addr = kmap_atomic(page);
- + flush_data_cache_page((unsigned long)addr);
- + kunmap_atomic(addr);
- +}
- +
- void __flush_dcache_page(struct page *page)
- {
- struct address_space *mapping = page_mapping(page);
- unsigned long addr;
-
- - if (PageHighMem(page))
- - return;
- if (mapping && !mapping_mapped(mapping)) {
- SetPageDcacheDirty(page);
- return;
- }
-
- + if (PageHighMem(page)) {
- + flush_highmem_page(page);
- + return;
- + }
- +
- /*
- * We could delay the flush for the !page_mapping case too. But that
- * case is for exec env/arg pages and those are %99 certainly going to
- @@ -105,6 +117,11 @@ void __flush_anon_page(struct page *page
- {
- unsigned long addr = (unsigned long) page_address(page);
-
- + if (PageHighMem(page)) {
- + flush_highmem_page(page);
- + return;
- + }
- +
- if (pages_do_alias(addr, vmaddr)) {
- if (page_mapped(page) && !Page_dcache_dirty(page)) {
- void *kaddr;
- @@ -123,8 +140,10 @@ void __flush_icache_page(struct vm_area_
- {
- unsigned long addr;
-
- - if (PageHighMem(page))
- + if (PageHighMem(page)) {
- + flush_highmem_page(page);
- return;
- + }
-
- addr = (unsigned long) page_address(page);
- flush_data_cache_page(addr);
- @@ -142,12 +161,17 @@ void __update_cache(struct vm_area_struc
- if (unlikely(!pfn_valid(pfn)))
- return;
- page = pfn_to_page(pfn);
- - if (page_mapping(page) && Page_dcache_dirty(page)) {
- + if (!Page_dcache_dirty(page) || !page_mapping(page))
- + return;
- +
- + if (PageHighMem(page)) {
- + flush_highmem_page(page);
- + } else {
- addr = (unsigned long) page_address(page);
- if (exec || pages_do_alias(addr, address & PAGE_MASK))
- flush_data_cache_page(addr);
- - ClearPageDcacheDirty(page);
- }
- + ClearPageDcacheDirty(page);
- }
-
- unsigned long _page_cachable_default;
|