aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2013-06-20 05:00:19 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2013-06-21 02:01:55 -0400
commitdb7cb5b92409b36e4338355fbc3561b3f6801c7b (patch)
treee9e000cbae80bb0e92742d4bbe10f68352ffe2ff /arch
parent12bc9f6fc1d6582b4529ac522d2231bd2584a5f1 (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.h58
-rw-r--r--arch/powerpc/kvm/book3s_64_mmu_hv.c8
-rw-r--r--arch/powerpc/kvm/book3s_hv_rm_mmu.c12
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 */
165static inline pte_t kvmppc_read_update_linux_pte(pte_t *p, int writing) 166static 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 */
193static inline unsigned long hpte_cache_bits(unsigned long pte_val) 203static 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
158static inline void unlock_hpte(unsigned long *hpte, unsigned long hpte_v) 156static inline void unlock_hpte(unsigned long *hpte, unsigned long hpte_v)