diff options
author | Andres Lagar-Cavilla <andreslc@google.com> | 2014-09-22 17:54:42 -0400 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2014-09-24 08:07:58 -0400 |
commit | 57128468080a8b6ea452223036d3e417f748af55 (patch) | |
tree | e89cfc349a9c39710cfab4e387119365a0d64958 /arch | |
parent | 8a9522d2fe6a1b643d3aef5ab7f097f73c601e7a (diff) |
kvm: Fix page ageing bugs
1. We were calling clear_flush_young_notify in unmap_one, but we are
within an mmu notifier invalidate range scope. The spte exists no more
(due to range_start) and the accessed bit info has already been
propagated (due to kvm_pfn_set_accessed). Simply call
clear_flush_young.
2. We clear_flush_young on a primary MMU PMD, but this may be mapped
as a collection of PTEs by the secondary MMU (e.g. during log-dirty).
This required expanding the interface of the clear_flush_young mmu
notifier, so a lot of code has been trivially touched.
3. In the absence of shadow_accessed_mask (e.g. EPT A bit), we emulate
the access bit by blowing the spte. This requires proper synchronizing
with MMU notifier consumers, like every other removal of spte's does.
Signed-off-by: Andres Lagar-Cavilla <andreslc@google.com>
Acked-by: Rik van Riel <riel@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/include/asm/kvm_host.h | 3 | ||||
-rw-r--r-- | arch/arm64/include/asm/kvm_host.h | 3 | ||||
-rw-r--r-- | arch/powerpc/include/asm/kvm_host.h | 2 | ||||
-rw-r--r-- | arch/powerpc/include/asm/kvm_ppc.h | 2 | ||||
-rw-r--r-- | arch/powerpc/kvm/book3s.c | 4 | ||||
-rw-r--r-- | arch/powerpc/kvm/book3s.h | 3 | ||||
-rw-r--r-- | arch/powerpc/kvm/book3s_64_mmu_hv.c | 4 | ||||
-rw-r--r-- | arch/powerpc/kvm/book3s_pr.c | 3 | ||||
-rw-r--r-- | arch/powerpc/kvm/e500_mmu_host.c | 2 | ||||
-rw-r--r-- | arch/x86/include/asm/kvm_host.h | 2 | ||||
-rw-r--r-- | arch/x86/kvm/mmu.c | 38 |
11 files changed, 39 insertions, 27 deletions
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 032a8538318a..8c3f7eb62b54 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h | |||
@@ -170,7 +170,8 @@ unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu); | |||
170 | int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices); | 170 | int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices); |
171 | 171 | ||
172 | /* We do not have shadow page tables, hence the empty hooks */ | 172 | /* We do not have shadow page tables, hence the empty hooks */ |
173 | static inline int kvm_age_hva(struct kvm *kvm, unsigned long hva) | 173 | static inline int kvm_age_hva(struct kvm *kvm, unsigned long start, |
174 | unsigned long end) | ||
174 | { | 175 | { |
175 | return 0; | 176 | return 0; |
176 | } | 177 | } |
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index be9970a59497..a3c671b3acc9 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h | |||
@@ -180,7 +180,8 @@ int kvm_unmap_hva_range(struct kvm *kvm, | |||
180 | void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); | 180 | void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); |
181 | 181 | ||
182 | /* We do not have shadow page tables, hence the empty hooks */ | 182 | /* We do not have shadow page tables, hence the empty hooks */ |
183 | static inline int kvm_age_hva(struct kvm *kvm, unsigned long hva) | 183 | static inline int kvm_age_hva(struct kvm *kvm, unsigned long start, |
184 | unsigned long end) | ||
184 | { | 185 | { |
185 | return 0; | 186 | return 0; |
186 | } | 187 | } |
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 604000882352..d329bc5543a2 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h | |||
@@ -56,7 +56,7 @@ | |||
56 | extern int kvm_unmap_hva(struct kvm *kvm, unsigned long hva); | 56 | extern int kvm_unmap_hva(struct kvm *kvm, unsigned long hva); |
57 | extern int kvm_unmap_hva_range(struct kvm *kvm, | 57 | extern int kvm_unmap_hva_range(struct kvm *kvm, |
58 | unsigned long start, unsigned long end); | 58 | unsigned long start, unsigned long end); |
59 | extern int kvm_age_hva(struct kvm *kvm, unsigned long hva); | 59 | extern int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end); |
60 | extern int kvm_test_age_hva(struct kvm *kvm, unsigned long hva); | 60 | extern int kvm_test_age_hva(struct kvm *kvm, unsigned long hva); |
61 | extern void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); | 61 | extern void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); |
62 | 62 | ||
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index fb86a2299d8a..d4a92d7cea6a 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h | |||
@@ -243,7 +243,7 @@ struct kvmppc_ops { | |||
243 | int (*unmap_hva)(struct kvm *kvm, unsigned long hva); | 243 | int (*unmap_hva)(struct kvm *kvm, unsigned long hva); |
244 | int (*unmap_hva_range)(struct kvm *kvm, unsigned long start, | 244 | int (*unmap_hva_range)(struct kvm *kvm, unsigned long start, |
245 | unsigned long end); | 245 | unsigned long end); |
246 | int (*age_hva)(struct kvm *kvm, unsigned long hva); | 246 | int (*age_hva)(struct kvm *kvm, unsigned long start, unsigned long end); |
247 | int (*test_age_hva)(struct kvm *kvm, unsigned long hva); | 247 | int (*test_age_hva)(struct kvm *kvm, unsigned long hva); |
248 | void (*set_spte_hva)(struct kvm *kvm, unsigned long hva, pte_t pte); | 248 | void (*set_spte_hva)(struct kvm *kvm, unsigned long hva, pte_t pte); |
249 | void (*mmu_destroy)(struct kvm_vcpu *vcpu); | 249 | void (*mmu_destroy)(struct kvm_vcpu *vcpu); |
diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c index dd03f6b299ba..c16cfbfeb781 100644 --- a/arch/powerpc/kvm/book3s.c +++ b/arch/powerpc/kvm/book3s.c | |||
@@ -851,9 +851,9 @@ int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end) | |||
851 | return kvm->arch.kvm_ops->unmap_hva_range(kvm, start, end); | 851 | return kvm->arch.kvm_ops->unmap_hva_range(kvm, start, end); |
852 | } | 852 | } |
853 | 853 | ||
854 | int kvm_age_hva(struct kvm *kvm, unsigned long hva) | 854 | int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end) |
855 | { | 855 | { |
856 | return kvm->arch.kvm_ops->age_hva(kvm, hva); | 856 | return kvm->arch.kvm_ops->age_hva(kvm, start, end); |
857 | } | 857 | } |
858 | 858 | ||
859 | int kvm_test_age_hva(struct kvm *kvm, unsigned long hva) | 859 | int kvm_test_age_hva(struct kvm *kvm, unsigned long hva) |
diff --git a/arch/powerpc/kvm/book3s.h b/arch/powerpc/kvm/book3s.h index 4bf956cf94d6..d2b3ec088b8c 100644 --- a/arch/powerpc/kvm/book3s.h +++ b/arch/powerpc/kvm/book3s.h | |||
@@ -17,7 +17,8 @@ extern void kvmppc_core_flush_memslot_hv(struct kvm *kvm, | |||
17 | extern int kvm_unmap_hva_hv(struct kvm *kvm, unsigned long hva); | 17 | extern int kvm_unmap_hva_hv(struct kvm *kvm, unsigned long hva); |
18 | extern int kvm_unmap_hva_range_hv(struct kvm *kvm, unsigned long start, | 18 | extern int kvm_unmap_hva_range_hv(struct kvm *kvm, unsigned long start, |
19 | unsigned long end); | 19 | unsigned long end); |
20 | extern int kvm_age_hva_hv(struct kvm *kvm, unsigned long hva); | 20 | extern int kvm_age_hva_hv(struct kvm *kvm, unsigned long start, |
21 | unsigned long end); | ||
21 | extern int kvm_test_age_hva_hv(struct kvm *kvm, unsigned long hva); | 22 | extern int kvm_test_age_hva_hv(struct kvm *kvm, unsigned long hva); |
22 | extern void kvm_set_spte_hva_hv(struct kvm *kvm, unsigned long hva, pte_t pte); | 23 | extern void kvm_set_spte_hva_hv(struct kvm *kvm, unsigned long hva, pte_t pte); |
23 | 24 | ||
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index 72c20bb16d26..81460c5359c0 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c | |||
@@ -1002,11 +1002,11 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp, | |||
1002 | return ret; | 1002 | return ret; |
1003 | } | 1003 | } |
1004 | 1004 | ||
1005 | int kvm_age_hva_hv(struct kvm *kvm, unsigned long hva) | 1005 | int kvm_age_hva_hv(struct kvm *kvm, unsigned long start, unsigned long end) |
1006 | { | 1006 | { |
1007 | if (!kvm->arch.using_mmu_notifiers) | 1007 | if (!kvm->arch.using_mmu_notifiers) |
1008 | return 0; | 1008 | return 0; |
1009 | return kvm_handle_hva(kvm, hva, kvm_age_rmapp); | 1009 | return kvm_handle_hva_range(kvm, start, end, kvm_age_rmapp); |
1010 | } | 1010 | } |
1011 | 1011 | ||
1012 | static int kvm_test_age_rmapp(struct kvm *kvm, unsigned long *rmapp, | 1012 | static int kvm_test_age_rmapp(struct kvm *kvm, unsigned long *rmapp, |
diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index faffb27badd9..852fcd8951c4 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c | |||
@@ -295,7 +295,8 @@ static int kvm_unmap_hva_range_pr(struct kvm *kvm, unsigned long start, | |||
295 | return 0; | 295 | return 0; |
296 | } | 296 | } |
297 | 297 | ||
298 | static int kvm_age_hva_pr(struct kvm *kvm, unsigned long hva) | 298 | static int kvm_age_hva_pr(struct kvm *kvm, unsigned long start, |
299 | unsigned long end) | ||
299 | { | 300 | { |
300 | /* XXX could be more clever ;) */ | 301 | /* XXX could be more clever ;) */ |
301 | return 0; | 302 | return 0; |
diff --git a/arch/powerpc/kvm/e500_mmu_host.c b/arch/powerpc/kvm/e500_mmu_host.c index 08f14bb57897..b1f3f630315e 100644 --- a/arch/powerpc/kvm/e500_mmu_host.c +++ b/arch/powerpc/kvm/e500_mmu_host.c | |||
@@ -732,7 +732,7 @@ int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end) | |||
732 | return 0; | 732 | return 0; |
733 | } | 733 | } |
734 | 734 | ||
735 | int kvm_age_hva(struct kvm *kvm, unsigned long hva) | 735 | int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end) |
736 | { | 736 | { |
737 | /* XXX could be more clever ;) */ | 737 | /* XXX could be more clever ;) */ |
738 | return 0; | 738 | return 0; |
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index eeeb573fcf6f..763d273cab1d 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h | |||
@@ -1035,7 +1035,7 @@ asmlinkage void kvm_spurious_fault(void); | |||
1035 | #define KVM_ARCH_WANT_MMU_NOTIFIER | 1035 | #define KVM_ARCH_WANT_MMU_NOTIFIER |
1036 | int kvm_unmap_hva(struct kvm *kvm, unsigned long hva); | 1036 | int kvm_unmap_hva(struct kvm *kvm, unsigned long hva); |
1037 | int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end); | 1037 | int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end); |
1038 | int kvm_age_hva(struct kvm *kvm, unsigned long hva); | 1038 | int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end); |
1039 | int kvm_test_age_hva(struct kvm *kvm, unsigned long hva); | 1039 | int kvm_test_age_hva(struct kvm *kvm, unsigned long hva); |
1040 | void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); | 1040 | void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); |
1041 | int cpuid_maxphyaddr(struct kvm_vcpu *vcpu); | 1041 | int cpuid_maxphyaddr(struct kvm_vcpu *vcpu); |
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 47d534066325..3201e93ebd07 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c | |||
@@ -1417,18 +1417,7 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp, | |||
1417 | struct rmap_iterator uninitialized_var(iter); | 1417 | struct rmap_iterator uninitialized_var(iter); |
1418 | int young = 0; | 1418 | int young = 0; |
1419 | 1419 | ||
1420 | /* | 1420 | BUG_ON(!shadow_accessed_mask); |
1421 | * In case of absence of EPT Access and Dirty Bits supports, | ||
1422 | * emulate the accessed bit for EPT, by checking if this page has | ||
1423 | * an EPT mapping, and clearing it if it does. On the next access, | ||
1424 | * a new EPT mapping will be established. | ||
1425 | * This has some overhead, but not as much as the cost of swapping | ||
1426 | * out actively used pages or breaking up actively used hugepages. | ||
1427 | */ | ||
1428 | if (!shadow_accessed_mask) { | ||
1429 | young = kvm_unmap_rmapp(kvm, rmapp, slot, gfn, level, data); | ||
1430 | goto out; | ||
1431 | } | ||
1432 | 1421 | ||
1433 | for (sptep = rmap_get_first(*rmapp, &iter); sptep; | 1422 | for (sptep = rmap_get_first(*rmapp, &iter); sptep; |
1434 | sptep = rmap_get_next(&iter)) { | 1423 | sptep = rmap_get_next(&iter)) { |
@@ -1440,7 +1429,6 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp, | |||
1440 | (unsigned long *)sptep); | 1429 | (unsigned long *)sptep); |
1441 | } | 1430 | } |
1442 | } | 1431 | } |
1443 | out: | ||
1444 | trace_kvm_age_page(gfn, level, slot, young); | 1432 | trace_kvm_age_page(gfn, level, slot, young); |
1445 | return young; | 1433 | return young; |
1446 | } | 1434 | } |
@@ -1489,9 +1477,29 @@ static void rmap_recycle(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn) | |||
1489 | kvm_flush_remote_tlbs(vcpu->kvm); | 1477 | kvm_flush_remote_tlbs(vcpu->kvm); |
1490 | } | 1478 | } |
1491 | 1479 | ||
1492 | int kvm_age_hva(struct kvm *kvm, unsigned long hva) | 1480 | int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end) |
1493 | { | 1481 | { |
1494 | return kvm_handle_hva(kvm, hva, 0, kvm_age_rmapp); | 1482 | /* |
1483 | * In case of absence of EPT Access and Dirty Bits supports, | ||
1484 | * emulate the accessed bit for EPT, by checking if this page has | ||
1485 | * an EPT mapping, and clearing it if it does. On the next access, | ||
1486 | * a new EPT mapping will be established. | ||
1487 | * This has some overhead, but not as much as the cost of swapping | ||
1488 | * out actively used pages or breaking up actively used hugepages. | ||
1489 | */ | ||
1490 | if (!shadow_accessed_mask) { | ||
1491 | /* | ||
1492 | * We are holding the kvm->mmu_lock, and we are blowing up | ||
1493 | * shadow PTEs. MMU notifier consumers need to be kept at bay. | ||
1494 | * This is correct as long as we don't decouple the mmu_lock | ||
1495 | * protected regions (like invalidate_range_start|end does). | ||
1496 | */ | ||
1497 | kvm->mmu_notifier_seq++; | ||
1498 | return kvm_handle_hva_range(kvm, start, end, 0, | ||
1499 | kvm_unmap_rmapp); | ||
1500 | } | ||
1501 | |||
1502 | return kvm_handle_hva_range(kvm, start, end, 0, kvm_age_rmapp); | ||
1495 | } | 1503 | } |
1496 | 1504 | ||
1497 | int kvm_test_age_hva(struct kvm *kvm, unsigned long hva) | 1505 | int kvm_test_age_hva(struct kvm *kvm, unsigned long hva) |