diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/include/asm/kvm_book3s_64.h | 25 | ||||
-rw-r--r-- | arch/powerpc/kvm/book3s_hv_rm_mmu.c | 63 |
2 files changed, 59 insertions, 29 deletions
diff --git a/arch/powerpc/include/asm/kvm_book3s_64.h b/arch/powerpc/include/asm/kvm_book3s_64.h index fa3dc79af702..300ec04a8381 100644 --- a/arch/powerpc/include/asm/kvm_book3s_64.h +++ b/arch/powerpc/include/asm/kvm_book3s_64.h | |||
@@ -43,6 +43,31 @@ static inline void svcpu_put(struct kvmppc_book3s_shadow_vcpu *svcpu) | |||
43 | #define HPT_HASH_MASK (HPT_NPTEG - 1) | 43 | #define HPT_HASH_MASK (HPT_NPTEG - 1) |
44 | #endif | 44 | #endif |
45 | 45 | ||
46 | /* | ||
47 | * We use a lock bit in HPTE dword 0 to synchronize updates and | ||
48 | * accesses to each HPTE, and another bit to indicate non-present | ||
49 | * HPTEs. | ||
50 | */ | ||
51 | #define HPTE_V_HVLOCK 0x40UL | ||
52 | |||
53 | static inline long try_lock_hpte(unsigned long *hpte, unsigned long bits) | ||
54 | { | ||
55 | unsigned long tmp, old; | ||
56 | |||
57 | asm volatile(" ldarx %0,0,%2\n" | ||
58 | " and. %1,%0,%3\n" | ||
59 | " bne 2f\n" | ||
60 | " ori %0,%0,%4\n" | ||
61 | " stdcx. %0,0,%2\n" | ||
62 | " beq+ 2f\n" | ||
63 | " li %1,%3\n" | ||
64 | "2: isync" | ||
65 | : "=&r" (tmp), "=&r" (old) | ||
66 | : "r" (hpte), "r" (bits), "i" (HPTE_V_HVLOCK) | ||
67 | : "cc", "memory"); | ||
68 | return old == 0; | ||
69 | } | ||
70 | |||
46 | static inline unsigned long compute_tlbie_rb(unsigned long v, unsigned long r, | 71 | static inline unsigned long compute_tlbie_rb(unsigned long v, unsigned long r, |
47 | unsigned long pte_index) | 72 | unsigned long pte_index) |
48 | { | 73 | { |
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c index 84dae821b230..a28a6030ec90 100644 --- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c +++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c | |||
@@ -53,26 +53,6 @@ static void *real_vmalloc_addr(void *x) | |||
53 | return __va(addr); | 53 | return __va(addr); |
54 | } | 54 | } |
55 | 55 | ||
56 | #define HPTE_V_HVLOCK 0x40UL | ||
57 | |||
58 | static inline long lock_hpte(unsigned long *hpte, unsigned long bits) | ||
59 | { | ||
60 | unsigned long tmp, old; | ||
61 | |||
62 | asm volatile(" ldarx %0,0,%2\n" | ||
63 | " and. %1,%0,%3\n" | ||
64 | " bne 2f\n" | ||
65 | " ori %0,%0,%4\n" | ||
66 | " stdcx. %0,0,%2\n" | ||
67 | " beq+ 2f\n" | ||
68 | " li %1,%3\n" | ||
69 | "2: isync" | ||
70 | : "=&r" (tmp), "=&r" (old) | ||
71 | : "r" (hpte), "r" (bits), "i" (HPTE_V_HVLOCK) | ||
72 | : "cc", "memory"); | ||
73 | return old == 0; | ||
74 | } | ||
75 | |||
76 | long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags, | 56 | long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags, |
77 | long pte_index, unsigned long pteh, unsigned long ptel) | 57 | long pte_index, unsigned long pteh, unsigned long ptel) |
78 | { | 58 | { |
@@ -126,24 +106,49 @@ long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags, | |||
126 | pteh &= ~0x60UL; | 106 | pteh &= ~0x60UL; |
127 | ptel &= ~(HPTE_R_PP0 - kvm->arch.ram_psize); | 107 | ptel &= ~(HPTE_R_PP0 - kvm->arch.ram_psize); |
128 | ptel |= pa; | 108 | ptel |= pa; |
109 | |||
129 | if (pte_index >= HPT_NPTE) | 110 | if (pte_index >= HPT_NPTE) |
130 | return H_PARAMETER; | 111 | return H_PARAMETER; |
131 | if (likely((flags & H_EXACT) == 0)) { | 112 | if (likely((flags & H_EXACT) == 0)) { |
132 | pte_index &= ~7UL; | 113 | pte_index &= ~7UL; |
133 | hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); | 114 | hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); |
134 | for (i = 0; ; ++i) { | 115 | for (i = 0; i < 8; ++i) { |
135 | if (i == 8) | ||
136 | return H_PTEG_FULL; | ||
137 | if ((*hpte & HPTE_V_VALID) == 0 && | 116 | if ((*hpte & HPTE_V_VALID) == 0 && |
138 | lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID)) | 117 | try_lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID)) |
139 | break; | 118 | break; |
140 | hpte += 2; | 119 | hpte += 2; |
141 | } | 120 | } |
121 | if (i == 8) { | ||
122 | /* | ||
123 | * Since try_lock_hpte doesn't retry (not even stdcx. | ||
124 | * failures), it could be that there is a free slot | ||
125 | * but we transiently failed to lock it. Try again, | ||
126 | * actually locking each slot and checking it. | ||
127 | */ | ||
128 | hpte -= 16; | ||
129 | for (i = 0; i < 8; ++i) { | ||
130 | while (!try_lock_hpte(hpte, HPTE_V_HVLOCK)) | ||
131 | cpu_relax(); | ||
132 | if ((*hpte & HPTE_V_VALID) == 0) | ||
133 | break; | ||
134 | *hpte &= ~HPTE_V_HVLOCK; | ||
135 | hpte += 2; | ||
136 | } | ||
137 | if (i == 8) | ||
138 | return H_PTEG_FULL; | ||
139 | } | ||
142 | pte_index += i; | 140 | pte_index += i; |
143 | } else { | 141 | } else { |
144 | hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); | 142 | hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); |
145 | if (!lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID)) | 143 | if (!try_lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID)) { |
146 | return H_PTEG_FULL; | 144 | /* Lock the slot and check again */ |
145 | while (!try_lock_hpte(hpte, HPTE_V_HVLOCK)) | ||
146 | cpu_relax(); | ||
147 | if (*hpte & HPTE_V_VALID) { | ||
148 | *hpte &= ~HPTE_V_HVLOCK; | ||
149 | return H_PTEG_FULL; | ||
150 | } | ||
151 | } | ||
147 | } | 152 | } |
148 | 153 | ||
149 | /* Save away the guest's idea of the second HPTE dword */ | 154 | /* Save away the guest's idea of the second HPTE dword */ |
@@ -189,7 +194,7 @@ long kvmppc_h_remove(struct kvm_vcpu *vcpu, unsigned long flags, | |||
189 | if (pte_index >= HPT_NPTE) | 194 | if (pte_index >= HPT_NPTE) |
190 | return H_PARAMETER; | 195 | return H_PARAMETER; |
191 | hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); | 196 | hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); |
192 | while (!lock_hpte(hpte, HPTE_V_HVLOCK)) | 197 | while (!try_lock_hpte(hpte, HPTE_V_HVLOCK)) |
193 | cpu_relax(); | 198 | cpu_relax(); |
194 | if ((hpte[0] & HPTE_V_VALID) == 0 || | 199 | if ((hpte[0] & HPTE_V_VALID) == 0 || |
195 | ((flags & H_AVPN) && (hpte[0] & ~0x7fUL) != avpn) || | 200 | ((flags & H_AVPN) && (hpte[0] & ~0x7fUL) != avpn) || |
@@ -248,7 +253,7 @@ long kvmppc_h_bulk_remove(struct kvm_vcpu *vcpu) | |||
248 | break; | 253 | break; |
249 | } | 254 | } |
250 | hp = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); | 255 | hp = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); |
251 | while (!lock_hpte(hp, HPTE_V_HVLOCK)) | 256 | while (!try_lock_hpte(hp, HPTE_V_HVLOCK)) |
252 | cpu_relax(); | 257 | cpu_relax(); |
253 | found = 0; | 258 | found = 0; |
254 | if (hp[0] & HPTE_V_VALID) { | 259 | if (hp[0] & HPTE_V_VALID) { |
@@ -310,7 +315,7 @@ long kvmppc_h_protect(struct kvm_vcpu *vcpu, unsigned long flags, | |||
310 | if (pte_index >= HPT_NPTE) | 315 | if (pte_index >= HPT_NPTE) |
311 | return H_PARAMETER; | 316 | return H_PARAMETER; |
312 | hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); | 317 | hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); |
313 | while (!lock_hpte(hpte, HPTE_V_HVLOCK)) | 318 | while (!try_lock_hpte(hpte, HPTE_V_HVLOCK)) |
314 | cpu_relax(); | 319 | cpu_relax(); |
315 | if ((hpte[0] & HPTE_V_VALID) == 0 || | 320 | if ((hpte[0] & HPTE_V_VALID) == 0 || |
316 | ((flags & H_AVPN) && (hpte[0] & ~0x7fUL) != avpn)) { | 321 | ((flags & H_AVPN) && (hpte[0] & ~0x7fUL) != avpn)) { |