aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2013-04-18 15:50:24 -0400
committerAlexander Graf <agraf@suse.de>2013-04-26 14:27:12 -0400
commita1b4a0f6064aacad0d708105e6f60a06e93fbf37 (patch)
tree7bbe9a95f283307775ec93dd95b2e02173a5487c /arch/powerpc
parentd9ce6041b39cf0b48978db63713f9b7e25ef9792 (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.h13
-rw-r--r--arch/powerpc/kvm/book3s_64_mmu_hv.c59
-rw-r--r--arch/powerpc/kvm/book3s_hv_rm_mmu.c11
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 */
276static 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 */
1209static 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
1196static long record_hpte(unsigned long flags, unsigned long *hptp, 1224static 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}
98EXPORT_SYMBOL_GPL(kvmppc_add_revmap_chain); 98EXPORT_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 */
104static 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 */
112static void remove_revmap_chain(struct kvm *kvm, long pte_index, 101static void remove_revmap_chain(struct kvm *kvm, long pte_index,
113 struct revmap_entry *rev, 102 struct revmap_entry *rev,