diff options
author | Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> | 2013-06-20 05:00:19 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-06-21 02:01:55 -0400 |
commit | db7cb5b92409b36e4338355fbc3561b3f6801c7b (patch) | |
tree | e9e000cbae80bb0e92742d4bbe10f68352ffe2ff /arch | |
parent | 12bc9f6fc1d6582b4529ac522d2231bd2584a5f1 (diff) |
powerpc/kvm: Handle transparent hugepage in KVM
We can find pte that are splitting while walking page tables. Return
None pte in that case.
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/include/asm/kvm_book3s_64.h | 58 | ||||
-rw-r--r-- | arch/powerpc/kvm/book3s_64_mmu_hv.c | 8 | ||||
-rw-r--r-- | arch/powerpc/kvm/book3s_hv_rm_mmu.c | 12 |
3 files changed, 44 insertions, 34 deletions
diff --git a/arch/powerpc/include/asm/kvm_book3s_64.h b/arch/powerpc/include/asm/kvm_book3s_64.h index 9c1ff330c805..a1ecb14e4442 100644 --- a/arch/powerpc/include/asm/kvm_book3s_64.h +++ b/arch/powerpc/include/asm/kvm_book3s_64.h | |||
@@ -159,36 +159,46 @@ static inline int hpte_cache_flags_ok(unsigned long ptel, unsigned long io_type) | |||
159 | } | 159 | } |
160 | 160 | ||
161 | /* | 161 | /* |
162 | * Lock and read a linux PTE. If it's present and writable, atomically | 162 | * If it's present and writable, atomically set dirty and referenced bits and |
163 | * set dirty and referenced bits and return the PTE, otherwise return 0. | 163 | * return the PTE, otherwise return 0. If we find a transparent hugepage |
164 | * and if it is marked splitting we return 0; | ||
164 | */ | 165 | */ |
165 | static inline pte_t kvmppc_read_update_linux_pte(pte_t *p, int writing) | 166 | static inline pte_t kvmppc_read_update_linux_pte(pte_t *ptep, int writing, |
167 | unsigned int hugepage) | ||
166 | { | 168 | { |
167 | pte_t pte, tmp; | 169 | pte_t old_pte, new_pte = __pte(0); |
168 | 170 | ||
169 | /* wait until _PAGE_BUSY is clear then set it atomically */ | 171 | while (1) { |
170 | __asm__ __volatile__ ( | 172 | old_pte = pte_val(*ptep); |
171 | "1: ldarx %0,0,%3\n" | 173 | /* |
172 | " andi. %1,%0,%4\n" | 174 | * wait until _PAGE_BUSY is clear then set it atomically |
173 | " bne- 1b\n" | 175 | */ |
174 | " ori %1,%0,%4\n" | 176 | if (unlikely(old_pte & _PAGE_BUSY)) { |
175 | " stdcx. %1,0,%3\n" | 177 | cpu_relax(); |
176 | " bne- 1b" | 178 | continue; |
177 | : "=&r" (pte), "=&r" (tmp), "=m" (*p) | 179 | } |
178 | : "r" (p), "i" (_PAGE_BUSY) | 180 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
179 | : "cc"); | 181 | /* If hugepage and is trans splitting return None */ |
180 | 182 | if (unlikely(hugepage && | |
181 | if (pte_present(pte)) { | 183 | pmd_trans_splitting(pte_pmd(old_pte)))) |
182 | pte = pte_mkyoung(pte); | 184 | return __pte(0); |
183 | if (writing && pte_write(pte)) | 185 | #endif |
184 | pte = pte_mkdirty(pte); | 186 | /* If pte is not present return None */ |
185 | } | 187 | if (unlikely(!(old_pte & _PAGE_PRESENT))) |
188 | return __pte(0); | ||
186 | 189 | ||
187 | *p = pte; /* clears _PAGE_BUSY */ | 190 | new_pte = pte_mkyoung(old_pte); |
191 | if (writing && pte_write(old_pte)) | ||
192 | new_pte = pte_mkdirty(new_pte); | ||
188 | 193 | ||
189 | return pte; | 194 | if (old_pte == __cmpxchg_u64((unsigned long *)ptep, old_pte, |
195 | new_pte)) | ||
196 | break; | ||
197 | } | ||
198 | return new_pte; | ||
190 | } | 199 | } |
191 | 200 | ||
201 | |||
192 | /* Return HPTE cache control bits corresponding to Linux pte bits */ | 202 | /* Return HPTE cache control bits corresponding to Linux pte bits */ |
193 | static inline unsigned long hpte_cache_bits(unsigned long pte_val) | 203 | static inline unsigned long hpte_cache_bits(unsigned long pte_val) |
194 | { | 204 | { |
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index 5880dfb31074..710d31317d81 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c | |||
@@ -675,6 +675,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, | |||
675 | } | 675 | } |
676 | /* if the guest wants write access, see if that is OK */ | 676 | /* if the guest wants write access, see if that is OK */ |
677 | if (!writing && hpte_is_writable(r)) { | 677 | if (!writing && hpte_is_writable(r)) { |
678 | unsigned int hugepage_shift; | ||
678 | pte_t *ptep, pte; | 679 | pte_t *ptep, pte; |
679 | 680 | ||
680 | /* | 681 | /* |
@@ -683,9 +684,10 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, | |||
683 | */ | 684 | */ |
684 | rcu_read_lock_sched(); | 685 | rcu_read_lock_sched(); |
685 | ptep = find_linux_pte_or_hugepte(current->mm->pgd, | 686 | ptep = find_linux_pte_or_hugepte(current->mm->pgd, |
686 | hva, NULL); | 687 | hva, &hugepage_shift); |
687 | if (ptep && pte_present(*ptep)) { | 688 | if (ptep) { |
688 | pte = kvmppc_read_update_linux_pte(ptep, 1); | 689 | pte = kvmppc_read_update_linux_pte(ptep, 1, |
690 | hugepage_shift); | ||
689 | if (pte_write(pte)) | 691 | if (pte_write(pte)) |
690 | write_ok = 1; | 692 | write_ok = 1; |
691 | } | 693 | } |
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c index dcf892d25a56..fc25689a9f35 100644 --- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c +++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c | |||
@@ -139,20 +139,18 @@ static pte_t lookup_linux_pte(pgd_t *pgdir, unsigned long hva, | |||
139 | { | 139 | { |
140 | pte_t *ptep; | 140 | pte_t *ptep; |
141 | unsigned long ps = *pte_sizep; | 141 | unsigned long ps = *pte_sizep; |
142 | unsigned int shift; | 142 | unsigned int hugepage_shift; |
143 | 143 | ||
144 | ptep = find_linux_pte_or_hugepte(pgdir, hva, &shift); | 144 | ptep = find_linux_pte_or_hugepte(pgdir, hva, &hugepage_shift); |
145 | if (!ptep) | 145 | if (!ptep) |
146 | return __pte(0); | 146 | return __pte(0); |
147 | if (shift) | 147 | if (hugepage_shift) |
148 | *pte_sizep = 1ul << shift; | 148 | *pte_sizep = 1ul << hugepage_shift; |
149 | else | 149 | else |
150 | *pte_sizep = PAGE_SIZE; | 150 | *pte_sizep = PAGE_SIZE; |
151 | if (ps > *pte_sizep) | 151 | if (ps > *pte_sizep) |
152 | return __pte(0); | 152 | return __pte(0); |
153 | if (!pte_present(*ptep)) | 153 | return kvmppc_read_update_linux_pte(ptep, writing, hugepage_shift); |
154 | return __pte(0); | ||
155 | return kvmppc_read_update_linux_pte(ptep, writing); | ||
156 | } | 154 | } |
157 | 155 | ||
158 | static inline void unlock_hpte(unsigned long *hpte, unsigned long hpte_v) | 156 | static inline void unlock_hpte(unsigned long *hpte, unsigned long hpte_v) |