diff options
author | Marc Zyngier <marc.zyngier@arm.com> | 2015-03-12 14:16:51 -0400 |
---|---|---|
committer | Christoffer Dall <christoffer.dall@linaro.org> | 2015-03-12 17:34:43 -0400 |
commit | 35307b9a5f7ebcc8d8db41c73b69c131b48ace2b (patch) | |
tree | 42cf3a7b7177139fe59d764893e0556487b5ba72 /arch | |
parent | 1d2ebaccc741a299abfafb848414b01d190f4e33 (diff) |
arm/arm64: KVM: Implement Stage-2 page aging
Until now, KVM/arm didn't care much for page aging (who was swapping
anyway?), and simply provided empty hooks to the core KVM code. With
server-type systems now being available, things are quite different.
This patch implements very simple support for page aging, by clearing
the Access flag in the Stage-2 page tables. On access fault, the current
fault handling will write the PTE or PMD again, putting the Access flag
back on.
It should be possible to implement a much faster handling for Access
faults, but that's left for a later patch.
With this in place, performance in VMs is degraded much more gracefully.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Acked-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/include/asm/kvm_arm.h | 1 | ||||
-rw-r--r-- | arch/arm/include/asm/kvm_host.h | 13 | ||||
-rw-r--r-- | arch/arm/kvm/mmu.c | 65 | ||||
-rw-r--r-- | arch/arm/kvm/trace.h | 33 | ||||
-rw-r--r-- | arch/arm64/include/asm/esr.h | 1 | ||||
-rw-r--r-- | arch/arm64/include/asm/kvm_arm.h | 1 | ||||
-rw-r--r-- | arch/arm64/include/asm/kvm_host.h | 13 |
7 files changed, 104 insertions, 23 deletions
diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h index 816db0bf2dd8..d995821f1698 100644 --- a/arch/arm/include/asm/kvm_arm.h +++ b/arch/arm/include/asm/kvm_arm.h | |||
@@ -185,6 +185,7 @@ | |||
185 | #define HSR_COND (0xfU << HSR_COND_SHIFT) | 185 | #define HSR_COND (0xfU << HSR_COND_SHIFT) |
186 | 186 | ||
187 | #define FSC_FAULT (0x04) | 187 | #define FSC_FAULT (0x04) |
188 | #define FSC_ACCESS (0x08) | ||
188 | #define FSC_PERM (0x0c) | 189 | #define FSC_PERM (0x0c) |
189 | 190 | ||
190 | /* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */ | 191 | /* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */ |
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 902a7d1441ae..d71607c16601 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h | |||
@@ -167,19 +167,10 @@ void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); | |||
167 | 167 | ||
168 | unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu); | 168 | unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu); |
169 | int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices); | 169 | int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices); |
170 | int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end); | ||
171 | int kvm_test_age_hva(struct kvm *kvm, unsigned long hva); | ||
170 | 172 | ||
171 | /* We do not have shadow page tables, hence the empty hooks */ | 173 | /* We do not have shadow page tables, hence the empty hooks */ |
172 | static inline int kvm_age_hva(struct kvm *kvm, unsigned long start, | ||
173 | unsigned long end) | ||
174 | { | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static inline int kvm_test_age_hva(struct kvm *kvm, unsigned long hva) | ||
179 | { | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static inline void kvm_arch_mmu_notifier_invalidate_page(struct kvm *kvm, | 174 | static inline void kvm_arch_mmu_notifier_invalidate_page(struct kvm *kvm, |
184 | unsigned long address) | 175 | unsigned long address) |
185 | { | 176 | { |
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index ffa06e07eed2..1831aa26eef8 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c | |||
@@ -1299,6 +1299,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, | |||
1299 | 1299 | ||
1300 | out_unlock: | 1300 | out_unlock: |
1301 | spin_unlock(&kvm->mmu_lock); | 1301 | spin_unlock(&kvm->mmu_lock); |
1302 | kvm_set_pfn_accessed(pfn); | ||
1302 | kvm_release_pfn_clean(pfn); | 1303 | kvm_release_pfn_clean(pfn); |
1303 | return ret; | 1304 | return ret; |
1304 | } | 1305 | } |
@@ -1333,7 +1334,8 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run) | |||
1333 | 1334 | ||
1334 | /* Check the stage-2 fault is trans. fault or write fault */ | 1335 | /* Check the stage-2 fault is trans. fault or write fault */ |
1335 | fault_status = kvm_vcpu_trap_get_fault_type(vcpu); | 1336 | fault_status = kvm_vcpu_trap_get_fault_type(vcpu); |
1336 | if (fault_status != FSC_FAULT && fault_status != FSC_PERM) { | 1337 | if (fault_status != FSC_FAULT && fault_status != FSC_PERM && |
1338 | fault_status != FSC_ACCESS) { | ||
1337 | kvm_err("Unsupported FSC: EC=%#x xFSC=%#lx ESR_EL2=%#lx\n", | 1339 | kvm_err("Unsupported FSC: EC=%#x xFSC=%#lx ESR_EL2=%#lx\n", |
1338 | kvm_vcpu_trap_get_class(vcpu), | 1340 | kvm_vcpu_trap_get_class(vcpu), |
1339 | (unsigned long)kvm_vcpu_trap_get_fault(vcpu), | 1341 | (unsigned long)kvm_vcpu_trap_get_fault(vcpu), |
@@ -1475,6 +1477,67 @@ void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte) | |||
1475 | handle_hva_to_gpa(kvm, hva, end, &kvm_set_spte_handler, &stage2_pte); | 1477 | handle_hva_to_gpa(kvm, hva, end, &kvm_set_spte_handler, &stage2_pte); |
1476 | } | 1478 | } |
1477 | 1479 | ||
1480 | static int kvm_age_hva_handler(struct kvm *kvm, gpa_t gpa, void *data) | ||
1481 | { | ||
1482 | pmd_t *pmd; | ||
1483 | pte_t *pte; | ||
1484 | |||
1485 | pmd = stage2_get_pmd(kvm, NULL, gpa); | ||
1486 | if (!pmd || pmd_none(*pmd)) /* Nothing there */ | ||
1487 | return 0; | ||
1488 | |||
1489 | if (kvm_pmd_huge(*pmd)) { /* THP, HugeTLB */ | ||
1490 | if (pmd_young(*pmd)) { | ||
1491 | *pmd = pmd_mkold(*pmd); | ||
1492 | return 1; | ||
1493 | } | ||
1494 | |||
1495 | return 0; | ||
1496 | } | ||
1497 | |||
1498 | pte = pte_offset_kernel(pmd, gpa); | ||
1499 | if (pte_none(*pte)) | ||
1500 | return 0; | ||
1501 | |||
1502 | if (pte_young(*pte)) { | ||
1503 | *pte = pte_mkold(*pte); /* Just a page... */ | ||
1504 | return 1; | ||
1505 | } | ||
1506 | |||
1507 | return 0; | ||
1508 | } | ||
1509 | |||
1510 | static int kvm_test_age_hva_handler(struct kvm *kvm, gpa_t gpa, void *data) | ||
1511 | { | ||
1512 | pmd_t *pmd; | ||
1513 | pte_t *pte; | ||
1514 | |||
1515 | pmd = stage2_get_pmd(kvm, NULL, gpa); | ||
1516 | if (!pmd || pmd_none(*pmd)) /* Nothing there */ | ||
1517 | return 0; | ||
1518 | |||
1519 | if (kvm_pmd_huge(*pmd)) /* THP, HugeTLB */ | ||
1520 | return pmd_young(*pmd); | ||
1521 | |||
1522 | pte = pte_offset_kernel(pmd, gpa); | ||
1523 | if (!pte_none(*pte)) /* Just a page... */ | ||
1524 | return pte_young(*pte); | ||
1525 | |||
1526 | return 0; | ||
1527 | } | ||
1528 | |||
1529 | int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end) | ||
1530 | { | ||
1531 | trace_kvm_age_hva(start, end); | ||
1532 | return handle_hva_to_gpa(kvm, start, end, kvm_age_hva_handler, NULL); | ||
1533 | } | ||
1534 | |||
1535 | int kvm_test_age_hva(struct kvm *kvm, unsigned long hva) | ||
1536 | { | ||
1537 | trace_kvm_test_age_hva(hva); | ||
1538 | return handle_hva_to_gpa(kvm, hva, hva, kvm_test_age_hva_handler, NULL); | ||
1539 | } | ||
1540 | |||
1478 | void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu) | 1541 | void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu) |
1479 | { | 1542 | { |
1480 | mmu_free_memory_cache(&vcpu->arch.mmu_page_cache); | 1543 | mmu_free_memory_cache(&vcpu->arch.mmu_page_cache); |
diff --git a/arch/arm/kvm/trace.h b/arch/arm/kvm/trace.h index 6817664b46b8..c09f37faff01 100644 --- a/arch/arm/kvm/trace.h +++ b/arch/arm/kvm/trace.h | |||
@@ -210,6 +210,39 @@ TRACE_EVENT(kvm_set_spte_hva, | |||
210 | TP_printk("mmu notifier set pte hva: %#08lx", __entry->hva) | 210 | TP_printk("mmu notifier set pte hva: %#08lx", __entry->hva) |
211 | ); | 211 | ); |
212 | 212 | ||
213 | TRACE_EVENT(kvm_age_hva, | ||
214 | TP_PROTO(unsigned long start, unsigned long end), | ||
215 | TP_ARGS(start, end), | ||
216 | |||
217 | TP_STRUCT__entry( | ||
218 | __field( unsigned long, start ) | ||
219 | __field( unsigned long, end ) | ||
220 | ), | ||
221 | |||
222 | TP_fast_assign( | ||
223 | __entry->start = start; | ||
224 | __entry->end = end; | ||
225 | ), | ||
226 | |||
227 | TP_printk("mmu notifier age hva: %#08lx -- %#08lx", | ||
228 | __entry->start, __entry->end) | ||
229 | ); | ||
230 | |||
231 | TRACE_EVENT(kvm_test_age_hva, | ||
232 | TP_PROTO(unsigned long hva), | ||
233 | TP_ARGS(hva), | ||
234 | |||
235 | TP_STRUCT__entry( | ||
236 | __field( unsigned long, hva ) | ||
237 | ), | ||
238 | |||
239 | TP_fast_assign( | ||
240 | __entry->hva = hva; | ||
241 | ), | ||
242 | |||
243 | TP_printk("mmu notifier test age hva: %#08lx", __entry->hva) | ||
244 | ); | ||
245 | |||
213 | TRACE_EVENT(kvm_hvc, | 246 | TRACE_EVENT(kvm_hvc, |
214 | TP_PROTO(unsigned long vcpu_pc, unsigned long r0, unsigned long imm), | 247 | TP_PROTO(unsigned long vcpu_pc, unsigned long r0, unsigned long imm), |
215 | TP_ARGS(vcpu_pc, r0, imm), | 248 | TP_ARGS(vcpu_pc, r0, imm), |
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h index 92bbae381598..70522450ca23 100644 --- a/arch/arm64/include/asm/esr.h +++ b/arch/arm64/include/asm/esr.h | |||
@@ -90,6 +90,7 @@ | |||
90 | #define ESR_ELx_FSC (0x3F) | 90 | #define ESR_ELx_FSC (0x3F) |
91 | #define ESR_ELx_FSC_TYPE (0x3C) | 91 | #define ESR_ELx_FSC_TYPE (0x3C) |
92 | #define ESR_ELx_FSC_EXTABT (0x10) | 92 | #define ESR_ELx_FSC_EXTABT (0x10) |
93 | #define ESR_ELx_FSC_ACCESS (0x08) | ||
93 | #define ESR_ELx_FSC_FAULT (0x04) | 94 | #define ESR_ELx_FSC_FAULT (0x04) |
94 | #define ESR_ELx_FSC_PERM (0x0C) | 95 | #define ESR_ELx_FSC_PERM (0x0C) |
95 | #define ESR_ELx_CV (UL(1) << 24) | 96 | #define ESR_ELx_CV (UL(1) << 24) |
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h index 94674eb7e7bb..9e5543e08955 100644 --- a/arch/arm64/include/asm/kvm_arm.h +++ b/arch/arm64/include/asm/kvm_arm.h | |||
@@ -187,6 +187,7 @@ | |||
187 | 187 | ||
188 | /* For compatibility with fault code shared with 32-bit */ | 188 | /* For compatibility with fault code shared with 32-bit */ |
189 | #define FSC_FAULT ESR_ELx_FSC_FAULT | 189 | #define FSC_FAULT ESR_ELx_FSC_FAULT |
190 | #define FSC_ACCESS ESR_ELx_FSC_ACCESS | ||
190 | #define FSC_PERM ESR_ELx_FSC_PERM | 191 | #define FSC_PERM ESR_ELx_FSC_PERM |
191 | 192 | ||
192 | /* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */ | 193 | /* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */ |
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 967fb1cee300..f0f58c9beec0 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h | |||
@@ -179,19 +179,10 @@ int kvm_unmap_hva(struct kvm *kvm, unsigned long hva); | |||
179 | int kvm_unmap_hva_range(struct kvm *kvm, | 179 | int kvm_unmap_hva_range(struct kvm *kvm, |
180 | unsigned long start, unsigned long end); | 180 | unsigned long start, unsigned long end); |
181 | void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); | 181 | void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); |
182 | int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end); | ||
183 | int kvm_test_age_hva(struct kvm *kvm, unsigned long hva); | ||
182 | 184 | ||
183 | /* We do not have shadow page tables, hence the empty hooks */ | 185 | /* We do not have shadow page tables, hence the empty hooks */ |
184 | static inline int kvm_age_hva(struct kvm *kvm, unsigned long start, | ||
185 | unsigned long end) | ||
186 | { | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static inline int kvm_test_age_hva(struct kvm *kvm, unsigned long hva) | ||
191 | { | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static inline void kvm_arch_mmu_notifier_invalidate_page(struct kvm *kvm, | 186 | static inline void kvm_arch_mmu_notifier_invalidate_page(struct kvm *kvm, |
196 | unsigned long address) | 187 | unsigned long address) |
197 | { | 188 | { |