diff options
author | Hugh Dickins <hugh@veritas.com> | 2005-10-29 21:16:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-10-30 00:40:42 -0400 |
commit | 92dc6fcc845d99e87d8168e0786796525832d130 (patch) | |
tree | ac36da7e3dc3e0ba62d4139443beb7eb7c25288d | |
parent | 69b0475456ff7ef520e16f69d7a15c0d68b74e64 (diff) |
[PATCH] mm: parisc pte atomicity
There's a worrying function translation_exists in parisc cacheflush.h,
unaffected by split ptlock since flush_dcache_page is using it on some other
mm, without any relevant lock. Oh well, make it a slightly more robust by
factoring the pfn check within it. And it looked liable to confuse a
camouflaged swap or file entry with a good pte: fix that too.
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | arch/parisc/kernel/cache.c | 24 | ||||
-rw-r--r-- | include/asm-parisc/cacheflush.h | 35 |
2 files changed, 28 insertions, 31 deletions
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c index e15f09eaed12..a065349aee37 100644 --- a/arch/parisc/kernel/cache.c +++ b/arch/parisc/kernel/cache.c | |||
@@ -270,7 +270,6 @@ void flush_dcache_page(struct page *page) | |||
270 | unsigned long offset; | 270 | unsigned long offset; |
271 | unsigned long addr; | 271 | unsigned long addr; |
272 | pgoff_t pgoff; | 272 | pgoff_t pgoff; |
273 | pte_t *pte; | ||
274 | unsigned long pfn = page_to_pfn(page); | 273 | unsigned long pfn = page_to_pfn(page); |
275 | 274 | ||
276 | 275 | ||
@@ -301,21 +300,16 @@ void flush_dcache_page(struct page *page) | |||
301 | * taking a page fault if the pte doesn't exist. | 300 | * taking a page fault if the pte doesn't exist. |
302 | * This is just for speed. If the page translation | 301 | * This is just for speed. If the page translation |
303 | * isn't there, there's no point exciting the | 302 | * isn't there, there's no point exciting the |
304 | * nadtlb handler into a nullification frenzy */ | 303 | * nadtlb handler into a nullification frenzy. |
305 | 304 | * | |
306 | 305 | * Make sure we really have this page: the private | |
307 | if(!(pte = translation_exists(mpnt, addr))) | ||
308 | continue; | ||
309 | |||
310 | /* make sure we really have this page: the private | ||
311 | * mappings may cover this area but have COW'd this | 306 | * mappings may cover this area but have COW'd this |
312 | * particular page */ | 307 | * particular page. |
313 | if(pte_pfn(*pte) != pfn) | 308 | */ |
314 | continue; | 309 | if (translation_exists(mpnt, addr, pfn)) { |
315 | 310 | __flush_cache_page(mpnt, addr); | |
316 | __flush_cache_page(mpnt, addr); | 311 | break; |
317 | 312 | } | |
318 | break; | ||
319 | } | 313 | } |
320 | flush_dcache_mmap_unlock(mapping); | 314 | flush_dcache_mmap_unlock(mapping); |
321 | } | 315 | } |
diff --git a/include/asm-parisc/cacheflush.h b/include/asm-parisc/cacheflush.h index aa592d8c0e39..1bc3c83ee74b 100644 --- a/include/asm-parisc/cacheflush.h +++ b/include/asm-parisc/cacheflush.h | |||
@@ -100,30 +100,34 @@ static inline void flush_cache_range(struct vm_area_struct *vma, | |||
100 | 100 | ||
101 | /* Simple function to work out if we have an existing address translation | 101 | /* Simple function to work out if we have an existing address translation |
102 | * for a user space vma. */ | 102 | * for a user space vma. */ |
103 | static inline pte_t *__translation_exists(struct mm_struct *mm, | 103 | static inline int translation_exists(struct vm_area_struct *vma, |
104 | unsigned long addr) | 104 | unsigned long addr, unsigned long pfn) |
105 | { | 105 | { |
106 | pgd_t *pgd = pgd_offset(mm, addr); | 106 | pgd_t *pgd = pgd_offset(vma->vm_mm, addr); |
107 | pmd_t *pmd; | 107 | pmd_t *pmd; |
108 | pte_t *pte; | 108 | pte_t pte; |
109 | 109 | ||
110 | if(pgd_none(*pgd)) | 110 | if(pgd_none(*pgd)) |
111 | return NULL; | 111 | return 0; |
112 | 112 | ||
113 | pmd = pmd_offset(pgd, addr); | 113 | pmd = pmd_offset(pgd, addr); |
114 | if(pmd_none(*pmd) || pmd_bad(*pmd)) | 114 | if(pmd_none(*pmd) || pmd_bad(*pmd)) |
115 | return NULL; | 115 | return 0; |
116 | 116 | ||
117 | pte = pte_offset_map(pmd, addr); | 117 | /* We cannot take the pte lock here: flush_cache_page is usually |
118 | * called with pte lock already held. Whereas flush_dcache_page | ||
119 | * takes flush_dcache_mmap_lock, which is lower in the hierarchy: | ||
120 | * the vma itself is secure, but the pte might come or go racily. | ||
121 | */ | ||
122 | pte = *pte_offset_map(pmd, addr); | ||
123 | /* But pte_unmap() does nothing on this architecture */ | ||
118 | 124 | ||
119 | /* The PA flush mappings show up as pte_none, but they're | 125 | /* Filter out coincidental file entries and swap entries */ |
120 | * valid none the less */ | 126 | if (!(pte_val(pte) & (_PAGE_FLUSH|_PAGE_PRESENT))) |
121 | if(pte_none(*pte) && ((pte_val(*pte) & _PAGE_FLUSH) == 0)) | 127 | return 0; |
122 | return NULL; | ||
123 | return pte; | ||
124 | } | ||
125 | #define translation_exists(vma, addr) __translation_exists((vma)->vm_mm, addr) | ||
126 | 128 | ||
129 | return pte_pfn(pte) == pfn; | ||
130 | } | ||
127 | 131 | ||
128 | /* Private function to flush a page from the cache of a non-current | 132 | /* Private function to flush a page from the cache of a non-current |
129 | * process. cr25 contains the Page Directory of the current user | 133 | * process. cr25 contains the Page Directory of the current user |
@@ -175,9 +179,8 @@ flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long | |||
175 | { | 179 | { |
176 | BUG_ON(!vma->vm_mm->context); | 180 | BUG_ON(!vma->vm_mm->context); |
177 | 181 | ||
178 | if(likely(translation_exists(vma, vmaddr))) | 182 | if (likely(translation_exists(vma, vmaddr, pfn))) |
179 | __flush_cache_page(vma, vmaddr); | 183 | __flush_cache_page(vma, vmaddr); |
180 | 184 | ||
181 | } | 185 | } |
182 | #endif | 186 | #endif |
183 | |||