aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/mm
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2010-07-22 19:02:27 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2010-07-22 22:55:21 -0400
commit171aa2caaad16ed32b655d33565e112a12cb3537 (patch)
treefcaff760d9771e932fbfe831a3fc1ce4dc0bce00 /arch/powerpc/mm
parentca91e6c09d656c6deb1f2bc5d57186c718106aa5 (diff)
powerpc/mm: Fix bugs in huge page hashing
There's a couple of nasty bugs lurking in our huge page hashing code. First, we don't check the access permission atomically with setting the _PAGE_BUSY bit, which means that the PTE value we end up using for the hashing might be different than the one we have checked the access permissions for. We've seen cases where that leads us to try to use an invalidated PTE for hashing, causing all sort of "interesting" issues. Then, we also failed to set _PAGE_DIRTY on a write access. Finally, a minor tweak but we should return 0 when we find the PTE busy, in order to just re-execute the access, rather than 1 which means going to do_page_fault(). Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> ---
Diffstat (limited to 'arch/powerpc/mm')
-rw-r--r--arch/powerpc/mm/hugetlbpage-hash64.c31
1 files changed, 13 insertions, 18 deletions
diff --git a/arch/powerpc/mm/hugetlbpage-hash64.c b/arch/powerpc/mm/hugetlbpage-hash64.c
index c9acd7910eea..faae9ec4cb04 100644
--- a/arch/powerpc/mm/hugetlbpage-hash64.c
+++ b/arch/powerpc/mm/hugetlbpage-hash64.c
@@ -21,21 +21,13 @@ int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid,
21 unsigned long old_pte, new_pte; 21 unsigned long old_pte, new_pte;
22 unsigned long va, rflags, pa, sz; 22 unsigned long va, rflags, pa, sz;
23 long slot; 23 long slot;
24 int err = 1;
25 24
26 BUG_ON(shift != mmu_psize_defs[mmu_psize].shift); 25 BUG_ON(shift != mmu_psize_defs[mmu_psize].shift);
27 26
28 /* Search the Linux page table for a match with va */ 27 /* Search the Linux page table for a match with va */
29 va = hpt_va(ea, vsid, ssize); 28 va = hpt_va(ea, vsid, ssize);
30 29
31 /* 30 /* At this point, we have a pte (old_pte) which can be used to build
32 * Check the user's access rights to the page. If access should be
33 * prevented then send the problem up to do_page_fault.
34 */
35 if (unlikely(access & ~pte_val(*ptep)))
36 goto out;
37 /*
38 * At this point, we have a pte (old_pte) which can be used to build
39 * or update an HPTE. There are 2 cases: 31 * or update an HPTE. There are 2 cases:
40 * 32 *
41 * 1. There is a valid (present) pte with no associated HPTE (this is 33 * 1. There is a valid (present) pte with no associated HPTE (this is
@@ -49,9 +41,17 @@ int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid,
49 41
50 do { 42 do {
51 old_pte = pte_val(*ptep); 43 old_pte = pte_val(*ptep);
52 if (old_pte & _PAGE_BUSY) 44 /* If PTE busy, retry the access */
53 goto out; 45 if (unlikely(old_pte & _PAGE_BUSY))
46 return 0;
47 /* If PTE permissions don't match, take page fault */
48 if (unlikely(access & ~old_pte))
49 return 1;
50 /* Try to lock the PTE, add ACCESSED and DIRTY if it was
51 * a write access */
54 new_pte = old_pte | _PAGE_BUSY | _PAGE_ACCESSED; 52 new_pte = old_pte | _PAGE_BUSY | _PAGE_ACCESSED;
53 if (access & _PAGE_RW)
54 new_pte |= _PAGE_DIRTY;
55 } while(old_pte != __cmpxchg_u64((unsigned long *)ptep, 55 } while(old_pte != __cmpxchg_u64((unsigned long *)ptep,
56 old_pte, new_pte)); 56 old_pte, new_pte));
57 57
@@ -127,8 +127,7 @@ repeat:
127 */ 127 */
128 if (unlikely(slot == -2)) { 128 if (unlikely(slot == -2)) {
129 *ptep = __pte(old_pte); 129 *ptep = __pte(old_pte);
130 err = -1; 130 return -1;
131 goto out;
132 } 131 }
133 132
134 new_pte |= (slot << 12) & (_PAGE_F_SECOND | _PAGE_F_GIX); 133 new_pte |= (slot << 12) & (_PAGE_F_SECOND | _PAGE_F_GIX);
@@ -138,9 +137,5 @@ repeat:
138 * No need to use ldarx/stdcx here 137 * No need to use ldarx/stdcx here
139 */ 138 */
140 *ptep = __pte(new_pte & ~_PAGE_BUSY); 139 *ptep = __pte(new_pte & ~_PAGE_BUSY);
141 140 return 0;
142 err = 0;
143
144 out:
145 return err;
146} 141}