aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorHugh Dickins <hugh@veritas.com>2005-10-29 21:16:36 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2005-10-30 00:40:42 -0400
commit92dc6fcc845d99e87d8168e0786796525832d130 (patch)
treeac36da7e3dc3e0ba62d4139443beb7eb7c25288d /include
parent69b0475456ff7ef520e16f69d7a15c0d68b74e64 (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>
Diffstat (limited to 'include')
-rw-r--r--include/asm-parisc/cacheflush.h35
1 files changed, 19 insertions, 16 deletions
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. */
103static inline pte_t *__translation_exists(struct mm_struct *mm, 103static 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