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 | |
| 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>
| -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 | { |
