diff options
author | David Gibson <david@gibson.dropbear.id.au> | 2009-10-26 15:24:31 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2009-10-30 02:20:59 -0400 |
commit | 883a3e523672ebba2ec3969837ba02af4f70fae2 (patch) | |
tree | ff8000aab5bc56348bd1b16ac904fe3a9b660b4a /arch/powerpc/mm/hugetlbpage-hash64.c | |
parent | d1837cba5d5d5458c09f0a2849db2d3c203cb8e9 (diff) |
powerpc/mm: Split hash MMU specific hugepage code into a new file
This patch separates the parts of hugetlbpage.c which are inherently
specific to the hash MMU into a new hugelbpage-hash64.c file.
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/mm/hugetlbpage-hash64.c')
-rw-r--r-- | arch/powerpc/mm/hugetlbpage-hash64.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/arch/powerpc/mm/hugetlbpage-hash64.c b/arch/powerpc/mm/hugetlbpage-hash64.c new file mode 100644 index 000000000000..1508ffc1e1e1 --- /dev/null +++ b/arch/powerpc/mm/hugetlbpage-hash64.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /* | ||
2 | * PPC64 Huge TLB Page Support for hash based MMUs (POWER4 and later) | ||
3 | * | ||
4 | * Copyright (C) 2003 David Gibson, IBM Corporation. | ||
5 | * | ||
6 | * Based on the IA-32 version: | ||
7 | * Copyright (C) 2002, Rohit Seth <rohit.seth@intel.com> | ||
8 | */ | ||
9 | |||
10 | #include <linux/mm.h> | ||
11 | #include <linux/hugetlb.h> | ||
12 | #include <asm/pgtable.h> | ||
13 | #include <asm/pgalloc.h> | ||
14 | #include <asm/cacheflush.h> | ||
15 | #include <asm/machdep.h> | ||
16 | |||
17 | /* | ||
18 | * Called by asm hashtable.S for doing lazy icache flush | ||
19 | */ | ||
20 | static unsigned int hash_huge_page_do_lazy_icache(unsigned long rflags, | ||
21 | pte_t pte, int trap, unsigned long sz) | ||
22 | { | ||
23 | struct page *page; | ||
24 | int i; | ||
25 | |||
26 | if (!pfn_valid(pte_pfn(pte))) | ||
27 | return rflags; | ||
28 | |||
29 | page = pte_page(pte); | ||
30 | |||
31 | /* page is dirty */ | ||
32 | if (!test_bit(PG_arch_1, &page->flags) && !PageReserved(page)) { | ||
33 | if (trap == 0x400) { | ||
34 | for (i = 0; i < (sz / PAGE_SIZE); i++) | ||
35 | __flush_dcache_icache(page_address(page+i)); | ||
36 | set_bit(PG_arch_1, &page->flags); | ||
37 | } else { | ||
38 | rflags |= HPTE_R_N; | ||
39 | } | ||
40 | } | ||
41 | return rflags; | ||
42 | } | ||
43 | |||
44 | int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid, | ||
45 | pte_t *ptep, unsigned long trap, int local, int ssize, | ||
46 | unsigned int shift, unsigned int mmu_psize) | ||
47 | { | ||
48 | unsigned long old_pte, new_pte; | ||
49 | unsigned long va, rflags, pa, sz; | ||
50 | long slot; | ||
51 | int err = 1; | ||
52 | |||
53 | BUG_ON(shift != mmu_psize_defs[mmu_psize].shift); | ||
54 | |||
55 | /* Search the Linux page table for a match with va */ | ||
56 | va = hpt_va(ea, vsid, ssize); | ||
57 | |||
58 | /* | ||
59 | * Check the user's access rights to the page. If access should be | ||
60 | * prevented then send the problem up to do_page_fault. | ||
61 | */ | ||
62 | if (unlikely(access & ~pte_val(*ptep))) | ||
63 | goto out; | ||
64 | /* | ||
65 | * At this point, we have a pte (old_pte) which can be used to build | ||
66 | * or update an HPTE. There are 2 cases: | ||
67 | * | ||
68 | * 1. There is a valid (present) pte with no associated HPTE (this is | ||
69 | * the most common case) | ||
70 | * 2. There is a valid (present) pte with an associated HPTE. The | ||
71 | * current values of the pp bits in the HPTE prevent access | ||
72 | * because we are doing software DIRTY bit management and the | ||
73 | * page is currently not DIRTY. | ||
74 | */ | ||
75 | |||
76 | |||
77 | do { | ||
78 | old_pte = pte_val(*ptep); | ||
79 | if (old_pte & _PAGE_BUSY) | ||
80 | goto out; | ||
81 | new_pte = old_pte | _PAGE_BUSY | _PAGE_ACCESSED; | ||
82 | } while(old_pte != __cmpxchg_u64((unsigned long *)ptep, | ||
83 | old_pte, new_pte)); | ||
84 | |||
85 | rflags = 0x2 | (!(new_pte & _PAGE_RW)); | ||
86 | /* _PAGE_EXEC -> HW_NO_EXEC since it's inverted */ | ||
87 | rflags |= ((new_pte & _PAGE_EXEC) ? 0 : HPTE_R_N); | ||
88 | sz = ((1UL) << shift); | ||
89 | if (!cpu_has_feature(CPU_FTR_COHERENT_ICACHE)) | ||
90 | /* No CPU has hugepages but lacks no execute, so we | ||
91 | * don't need to worry about that case */ | ||
92 | rflags = hash_huge_page_do_lazy_icache(rflags, __pte(old_pte), | ||
93 | trap, sz); | ||
94 | |||
95 | /* Check if pte already has an hpte (case 2) */ | ||
96 | if (unlikely(old_pte & _PAGE_HASHPTE)) { | ||
97 | /* There MIGHT be an HPTE for this pte */ | ||
98 | unsigned long hash, slot; | ||
99 | |||
100 | hash = hpt_hash(va, shift, ssize); | ||
101 | if (old_pte & _PAGE_F_SECOND) | ||
102 | hash = ~hash; | ||
103 | slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; | ||
104 | slot += (old_pte & _PAGE_F_GIX) >> 12; | ||
105 | |||
106 | if (ppc_md.hpte_updatepp(slot, rflags, va, mmu_psize, | ||
107 | ssize, local) == -1) | ||
108 | old_pte &= ~_PAGE_HPTEFLAGS; | ||
109 | } | ||
110 | |||
111 | if (likely(!(old_pte & _PAGE_HASHPTE))) { | ||
112 | unsigned long hash = hpt_hash(va, shift, ssize); | ||
113 | unsigned long hpte_group; | ||
114 | |||
115 | pa = pte_pfn(__pte(old_pte)) << PAGE_SHIFT; | ||
116 | |||
117 | repeat: | ||
118 | hpte_group = ((hash & htab_hash_mask) * | ||
119 | HPTES_PER_GROUP) & ~0x7UL; | ||
120 | |||
121 | /* clear HPTE slot informations in new PTE */ | ||
122 | #ifdef CONFIG_PPC_64K_PAGES | ||
123 | new_pte = (new_pte & ~_PAGE_HPTEFLAGS) | _PAGE_HPTE_SUB0; | ||
124 | #else | ||
125 | new_pte = (new_pte & ~_PAGE_HPTEFLAGS) | _PAGE_HASHPTE; | ||
126 | #endif | ||
127 | /* Add in WIMG bits */ | ||
128 | rflags |= (new_pte & (_PAGE_WRITETHRU | _PAGE_NO_CACHE | | ||
129 | _PAGE_COHERENT | _PAGE_GUARDED)); | ||
130 | |||
131 | /* Insert into the hash table, primary slot */ | ||
132 | slot = ppc_md.hpte_insert(hpte_group, va, pa, rflags, 0, | ||
133 | mmu_psize, ssize); | ||
134 | |||
135 | /* Primary is full, try the secondary */ | ||
136 | if (unlikely(slot == -1)) { | ||
137 | hpte_group = ((~hash & htab_hash_mask) * | ||
138 | HPTES_PER_GROUP) & ~0x7UL; | ||
139 | slot = ppc_md.hpte_insert(hpte_group, va, pa, rflags, | ||
140 | HPTE_V_SECONDARY, | ||
141 | mmu_psize, ssize); | ||
142 | if (slot == -1) { | ||
143 | if (mftb() & 0x1) | ||
144 | hpte_group = ((hash & htab_hash_mask) * | ||
145 | HPTES_PER_GROUP)&~0x7UL; | ||
146 | |||
147 | ppc_md.hpte_remove(hpte_group); | ||
148 | goto repeat; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | if (unlikely(slot == -2)) | ||
153 | panic("hash_huge_page: pte_insert failed\n"); | ||
154 | |||
155 | new_pte |= (slot << 12) & (_PAGE_F_SECOND | _PAGE_F_GIX); | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * No need to use ldarx/stdcx here | ||
160 | */ | ||
161 | *ptep = __pte(new_pte & ~_PAGE_BUSY); | ||
162 | |||
163 | err = 0; | ||
164 | |||
165 | out: | ||
166 | return err; | ||
167 | } | ||