diff options
author | Paul Mackerras <paulus@samba.org> | 2013-04-18 15:50:24 -0400 |
---|---|---|
committer | Alexander Graf <agraf@suse.de> | 2013-04-26 14:27:12 -0400 |
commit | a1b4a0f6064aacad0d708105e6f60a06e93fbf37 (patch) | |
tree | 7bbe9a95f283307775ec93dd95b2e02173a5487c /arch/powerpc | |
parent | d9ce6041b39cf0b48978db63713f9b7e25ef9792 (diff) |
KVM: PPC: Book3S HV: Make HPT reading code notice R/C bit changes
At present, the code that determines whether a HPT entry has changed,
and thus needs to be sent to userspace when it is copying the HPT,
doesn't consider a hardware update to the reference and change bits
(R and C) in the HPT entries to constitute a change that needs to
be sent to userspace. This adds code to check for changes in R and C
when we are scanning the HPT to find changed entries, and adds code
to set the changed flag for the HPTE when we update the R and C bits
in the guest view of the HPTE.
Since we now need to set the HPTE changed flag in book3s_64_mmu_hv.c
as well as book3s_hv_rm_mmu.c, we move the note_hpte_modification()
function into kvm_book3s_64.h.
Current Linux guest kernels don't use the hardware updates of R and C
in the HPT, so this change won't affect them. Linux (or other) kernels
might in future want to use the R and C bits and have them correctly
transferred across when a guest is migrated, so it is better to correct
this deficiency.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/include/asm/kvm_book3s_64.h | 13 | ||||
-rw-r--r-- | arch/powerpc/kvm/book3s_64_mmu_hv.c | 59 | ||||
-rw-r--r-- | arch/powerpc/kvm/book3s_hv_rm_mmu.c | 11 |
3 files changed, 63 insertions, 20 deletions
diff --git a/arch/powerpc/include/asm/kvm_book3s_64.h b/arch/powerpc/include/asm/kvm_book3s_64.h index 38bec1dc9928..9c1ff330c805 100644 --- a/arch/powerpc/include/asm/kvm_book3s_64.h +++ b/arch/powerpc/include/asm/kvm_book3s_64.h | |||
@@ -268,4 +268,17 @@ static inline int is_vrma_hpte(unsigned long hpte_v) | |||
268 | (HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16))); | 268 | (HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16))); |
269 | } | 269 | } |
270 | 270 | ||
271 | #ifdef CONFIG_KVM_BOOK3S_64_HV | ||
272 | /* | ||
273 | * Note modification of an HPTE; set the HPTE modified bit | ||
274 | * if anyone is interested. | ||
275 | */ | ||
276 | static inline void note_hpte_modification(struct kvm *kvm, | ||
277 | struct revmap_entry *rev) | ||
278 | { | ||
279 | if (atomic_read(&kvm->arch.hpte_mod_interest)) | ||
280 | rev->guest_rpte |= HPTE_GR_MODIFIED; | ||
281 | } | ||
282 | #endif /* CONFIG_KVM_BOOK3S_64_HV */ | ||
283 | |||
271 | #endif /* __ASM_KVM_BOOK3S_64_H__ */ | 284 | #endif /* __ASM_KVM_BOOK3S_64_H__ */ |
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index 8cc18abd6dde..d641a6634b02 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c | |||
@@ -893,7 +893,10 @@ static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp, | |||
893 | /* Harvest R and C */ | 893 | /* Harvest R and C */ |
894 | rcbits = hptep[1] & (HPTE_R_R | HPTE_R_C); | 894 | rcbits = hptep[1] & (HPTE_R_R | HPTE_R_C); |
895 | *rmapp |= rcbits << KVMPPC_RMAP_RC_SHIFT; | 895 | *rmapp |= rcbits << KVMPPC_RMAP_RC_SHIFT; |
896 | rev[i].guest_rpte = ptel | rcbits; | 896 | if (rcbits & ~rev[i].guest_rpte) { |
897 | rev[i].guest_rpte = ptel | rcbits; | ||
898 | note_hpte_modification(kvm, &rev[i]); | ||
899 | } | ||
897 | } | 900 | } |
898 | unlock_rmap(rmapp); | 901 | unlock_rmap(rmapp); |
899 | hptep[0] &= ~HPTE_V_HVLOCK; | 902 | hptep[0] &= ~HPTE_V_HVLOCK; |
@@ -976,7 +979,10 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp, | |||
976 | /* Now check and modify the HPTE */ | 979 | /* Now check and modify the HPTE */ |
977 | if ((hptep[0] & HPTE_V_VALID) && (hptep[1] & HPTE_R_R)) { | 980 | if ((hptep[0] & HPTE_V_VALID) && (hptep[1] & HPTE_R_R)) { |
978 | kvmppc_clear_ref_hpte(kvm, hptep, i); | 981 | kvmppc_clear_ref_hpte(kvm, hptep, i); |
979 | rev[i].guest_rpte |= HPTE_R_R; | 982 | if (!(rev[i].guest_rpte & HPTE_R_R)) { |
983 | rev[i].guest_rpte |= HPTE_R_R; | ||
984 | note_hpte_modification(kvm, &rev[i]); | ||
985 | } | ||
980 | ret = 1; | 986 | ret = 1; |
981 | } | 987 | } |
982 | hptep[0] &= ~HPTE_V_HVLOCK; | 988 | hptep[0] &= ~HPTE_V_HVLOCK; |
@@ -1080,7 +1086,10 @@ static int kvm_test_clear_dirty(struct kvm *kvm, unsigned long *rmapp) | |||
1080 | hptep[1] &= ~HPTE_R_C; | 1086 | hptep[1] &= ~HPTE_R_C; |
1081 | eieio(); | 1087 | eieio(); |
1082 | hptep[0] = (hptep[0] & ~HPTE_V_ABSENT) | HPTE_V_VALID; | 1088 | hptep[0] = (hptep[0] & ~HPTE_V_ABSENT) | HPTE_V_VALID; |
1083 | rev[i].guest_rpte |= HPTE_R_C; | 1089 | if (!(rev[i].guest_rpte & HPTE_R_C)) { |
1090 | rev[i].guest_rpte |= HPTE_R_C; | ||
1091 | note_hpte_modification(kvm, &rev[i]); | ||
1092 | } | ||
1084 | ret = 1; | 1093 | ret = 1; |
1085 | } | 1094 | } |
1086 | hptep[0] &= ~HPTE_V_HVLOCK; | 1095 | hptep[0] &= ~HPTE_V_HVLOCK; |
@@ -1193,16 +1202,36 @@ struct kvm_htab_ctx { | |||
1193 | 1202 | ||
1194 | #define HPTE_SIZE (2 * sizeof(unsigned long)) | 1203 | #define HPTE_SIZE (2 * sizeof(unsigned long)) |
1195 | 1204 | ||
1205 | /* | ||
1206 | * Returns 1 if this HPT entry has been modified or has pending | ||
1207 | * R/C bit changes. | ||
1208 | */ | ||
1209 | static int hpte_dirty(struct revmap_entry *revp, unsigned long *hptp) | ||
1210 | { | ||
1211 | unsigned long rcbits_unset; | ||
1212 | |||
1213 | if (revp->guest_rpte & HPTE_GR_MODIFIED) | ||
1214 | return 1; | ||
1215 | |||
1216 | /* Also need to consider changes in reference and changed bits */ | ||
1217 | rcbits_unset = ~revp->guest_rpte & (HPTE_R_R | HPTE_R_C); | ||
1218 | if ((hptp[0] & HPTE_V_VALID) && (hptp[1] & rcbits_unset)) | ||
1219 | return 1; | ||
1220 | |||
1221 | return 0; | ||
1222 | } | ||
1223 | |||
1196 | static long record_hpte(unsigned long flags, unsigned long *hptp, | 1224 | static long record_hpte(unsigned long flags, unsigned long *hptp, |
1197 | unsigned long *hpte, struct revmap_entry *revp, | 1225 | unsigned long *hpte, struct revmap_entry *revp, |
1198 | int want_valid, int first_pass) | 1226 | int want_valid, int first_pass) |
1199 | { | 1227 | { |
1200 | unsigned long v, r; | 1228 | unsigned long v, r; |
1229 | unsigned long rcbits_unset; | ||
1201 | int ok = 1; | 1230 | int ok = 1; |
1202 | int valid, dirty; | 1231 | int valid, dirty; |
1203 | 1232 | ||
1204 | /* Unmodified entries are uninteresting except on the first pass */ | 1233 | /* Unmodified entries are uninteresting except on the first pass */ |
1205 | dirty = !!(revp->guest_rpte & HPTE_GR_MODIFIED); | 1234 | dirty = hpte_dirty(revp, hptp); |
1206 | if (!first_pass && !dirty) | 1235 | if (!first_pass && !dirty) |
1207 | return 0; | 1236 | return 0; |
1208 | 1237 | ||
@@ -1223,16 +1252,28 @@ static long record_hpte(unsigned long flags, unsigned long *hptp, | |||
1223 | while (!try_lock_hpte(hptp, HPTE_V_HVLOCK)) | 1252 | while (!try_lock_hpte(hptp, HPTE_V_HVLOCK)) |
1224 | cpu_relax(); | 1253 | cpu_relax(); |
1225 | v = hptp[0]; | 1254 | v = hptp[0]; |
1255 | |||
1256 | /* re-evaluate valid and dirty from synchronized HPTE value */ | ||
1257 | valid = !!(v & HPTE_V_VALID); | ||
1258 | dirty = !!(revp->guest_rpte & HPTE_GR_MODIFIED); | ||
1259 | |||
1260 | /* Harvest R and C into guest view if necessary */ | ||
1261 | rcbits_unset = ~revp->guest_rpte & (HPTE_R_R | HPTE_R_C); | ||
1262 | if (valid && (rcbits_unset & hptp[1])) { | ||
1263 | revp->guest_rpte |= (hptp[1] & (HPTE_R_R | HPTE_R_C)) | | ||
1264 | HPTE_GR_MODIFIED; | ||
1265 | dirty = 1; | ||
1266 | } | ||
1267 | |||
1226 | if (v & HPTE_V_ABSENT) { | 1268 | if (v & HPTE_V_ABSENT) { |
1227 | v &= ~HPTE_V_ABSENT; | 1269 | v &= ~HPTE_V_ABSENT; |
1228 | v |= HPTE_V_VALID; | 1270 | v |= HPTE_V_VALID; |
1271 | valid = 1; | ||
1229 | } | 1272 | } |
1230 | /* re-evaluate valid and dirty from synchronized HPTE value */ | ||
1231 | valid = !!(v & HPTE_V_VALID); | ||
1232 | if ((flags & KVM_GET_HTAB_BOLTED_ONLY) && !(v & HPTE_V_BOLTED)) | 1273 | if ((flags & KVM_GET_HTAB_BOLTED_ONLY) && !(v & HPTE_V_BOLTED)) |
1233 | valid = 0; | 1274 | valid = 0; |
1234 | r = revp->guest_rpte | (hptp[1] & (HPTE_R_R | HPTE_R_C)); | 1275 | |
1235 | dirty = !!(revp->guest_rpte & HPTE_GR_MODIFIED); | 1276 | r = revp->guest_rpte; |
1236 | /* only clear modified if this is the right sort of entry */ | 1277 | /* only clear modified if this is the right sort of entry */ |
1237 | if (valid == want_valid && dirty) { | 1278 | if (valid == want_valid && dirty) { |
1238 | r &= ~HPTE_GR_MODIFIED; | 1279 | r &= ~HPTE_GR_MODIFIED; |
@@ -1288,7 +1329,7 @@ static ssize_t kvm_htab_read(struct file *file, char __user *buf, | |||
1288 | /* Skip uninteresting entries, i.e. clean on not-first pass */ | 1329 | /* Skip uninteresting entries, i.e. clean on not-first pass */ |
1289 | if (!first_pass) { | 1330 | if (!first_pass) { |
1290 | while (i < kvm->arch.hpt_npte && | 1331 | while (i < kvm->arch.hpt_npte && |
1291 | !(revp->guest_rpte & HPTE_GR_MODIFIED)) { | 1332 | !hpte_dirty(revp, hptp)) { |
1292 | ++i; | 1333 | ++i; |
1293 | hptp += 2; | 1334 | hptp += 2; |
1294 | ++revp; | 1335 | ++revp; |
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c index 19c93bae1aea..6dcbb49105a4 100644 --- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c +++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c | |||
@@ -97,17 +97,6 @@ void kvmppc_add_revmap_chain(struct kvm *kvm, struct revmap_entry *rev, | |||
97 | } | 97 | } |
98 | EXPORT_SYMBOL_GPL(kvmppc_add_revmap_chain); | 98 | EXPORT_SYMBOL_GPL(kvmppc_add_revmap_chain); |
99 | 99 | ||
100 | /* | ||
101 | * Note modification of an HPTE; set the HPTE modified bit | ||
102 | * if anyone is interested. | ||
103 | */ | ||
104 | static inline void note_hpte_modification(struct kvm *kvm, | ||
105 | struct revmap_entry *rev) | ||
106 | { | ||
107 | if (atomic_read(&kvm->arch.hpte_mod_interest)) | ||
108 | rev->guest_rpte |= HPTE_GR_MODIFIED; | ||
109 | } | ||
110 | |||
111 | /* Remove this HPTE from the chain for a real page */ | 100 | /* Remove this HPTE from the chain for a real page */ |
112 | static void remove_revmap_chain(struct kvm *kvm, long pte_index, | 101 | static void remove_revmap_chain(struct kvm *kvm, long pte_index, |
113 | struct revmap_entry *rev, | 102 | struct revmap_entry *rev, |