6a6c581bc557a2e58bf9506dc16b3ed3ed52c74f
[openwrt.git] / target / linux / generic / patches-4.4 / 101-MIPS-fix-cache-flushing-for-highmem-pages.patch
1 From: Felix Fietkau <nbd@openwrt.org>
2 Date: Sun, 24 Jan 2016 01:03:51 +0100
3 Subject: [PATCH] MIPS: fix cache flushing for highmem pages
4
5 Most cache flush ops were no-op for highmem pages. This led to nasty
6 segfaults and (in the case of page_address(page) == NULL) kernel
7 crashes.
8
9 Fix this by always flushing highmem pages using kmap/kunmap_atomic
10 around the actual cache flush. This might be a bit inefficient, but at
11 least it's stable.
12
13 Signed-off-by: Felix Fietkau <nbd@openwrt.org>
14 ---
15
16 --- a/arch/mips/mm/cache.c
17 +++ b/arch/mips/mm/cache.c
18 @@ -14,6 +14,7 @@
19  #include <linux/sched.h>
20  #include <linux/syscalls.h>
21  #include <linux/mm.h>
22 +#include <linux/highmem.h>
23  
24  #include <asm/cacheflush.h>
25  #include <asm/processor.h>
26 @@ -78,18 +79,29 @@ SYSCALL_DEFINE3(cacheflush, unsigned lon
27         return 0;
28  }
29  
30 +static void
31 +flush_highmem_page(struct page *page)
32 +{
33 +       void *addr = kmap_atomic(page);
34 +       flush_data_cache_page((unsigned long)addr);
35 +       kunmap_atomic(addr);
36 +}
37 +
38  void __flush_dcache_page(struct page *page)
39  {
40         struct address_space *mapping = page_mapping(page);
41         unsigned long addr;
42  
43 -       if (PageHighMem(page))
44 -               return;
45         if (mapping && !mapping_mapped(mapping)) {
46                 SetPageDcacheDirty(page);
47                 return;
48         }
49  
50 +       if (PageHighMem(page)) {
51 +               flush_highmem_page(page);
52 +               return;
53 +       }
54 +
55         /*
56          * We could delay the flush for the !page_mapping case too.  But that
57          * case is for exec env/arg pages and those are %99 certainly going to
58 @@ -105,6 +117,11 @@ void __flush_anon_page(struct page *page
59  {
60         unsigned long addr = (unsigned long) page_address(page);
61  
62 +       if (PageHighMem(page)) {
63 +               flush_highmem_page(page);
64 +               return;
65 +       }
66 +
67         if (pages_do_alias(addr, vmaddr)) {
68                 if (page_mapped(page) && !Page_dcache_dirty(page)) {
69                         void *kaddr;
70 @@ -123,8 +140,10 @@ void __flush_icache_page(struct vm_area_
71  {
72         unsigned long addr;
73  
74 -       if (PageHighMem(page))
75 +       if (PageHighMem(page)) {
76 +               flush_highmem_page(page);
77                 return;
78 +       }
79  
80         addr = (unsigned long) page_address(page);
81         flush_data_cache_page(addr);
82 @@ -142,12 +161,17 @@ void __update_cache(struct vm_area_struc
83         if (unlikely(!pfn_valid(pfn)))
84                 return;
85         page = pfn_to_page(pfn);
86 -       if (page_mapping(page) && Page_dcache_dirty(page)) {
87 +       if (!Page_dcache_dirty(page) || !page_mapping(page))
88 +               return;
89 +
90 +       if (PageHighMem(page)) {
91 +               flush_highmem_page(page);
92 +       } else {
93                 addr = (unsigned long) page_address(page);
94                 if (exec || pages_do_alias(addr, address & PAGE_MASK))
95                         flush_data_cache_page(addr);
96 -               ClearPageDcacheDirty(page);
97         }
98 +       ClearPageDcacheDirty(page);
99  }
100  
101  unsigned long _page_cachable_default;