Skip to content

Commit a1b4a0f

Browse files
paulusmackagraf
authored andcommitted
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>
1 parent d9ce604 commit a1b4a0f

File tree

3 files changed

+63
-20
lines changed

3 files changed

+63
-20
lines changed

arch/powerpc/include/asm/kvm_book3s_64.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,4 +268,17 @@ static inline int is_vrma_hpte(unsigned long hpte_v)
268268
(HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16)));
269269
}
270270

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+
271284
#endif /* __ASM_KVM_BOOK3S_64_H__ */

arch/powerpc/kvm/book3s_64_mmu_hv.c

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,10 @@ static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp,
893893
/* Harvest R and C */
894894
rcbits = hptep[1] & (HPTE_R_R | HPTE_R_C);
895895
*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+
}
897900
}
898901
unlock_rmap(rmapp);
899902
hptep[0] &= ~HPTE_V_HVLOCK;
@@ -976,7 +979,10 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
976979
/* Now check and modify the HPTE */
977980
if ((hptep[0] & HPTE_V_VALID) && (hptep[1] & HPTE_R_R)) {
978981
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+
}
980986
ret = 1;
981987
}
982988
hptep[0] &= ~HPTE_V_HVLOCK;
@@ -1080,7 +1086,10 @@ static int kvm_test_clear_dirty(struct kvm *kvm, unsigned long *rmapp)
10801086
hptep[1] &= ~HPTE_R_C;
10811087
eieio();
10821088
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+
}
10841093
ret = 1;
10851094
}
10861095
hptep[0] &= ~HPTE_V_HVLOCK;
@@ -1193,16 +1202,36 @@ struct kvm_htab_ctx {
11931202

11941203
#define HPTE_SIZE (2 * sizeof(unsigned long))
11951204

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+
11961224
static long record_hpte(unsigned long flags, unsigned long *hptp,
11971225
unsigned long *hpte, struct revmap_entry *revp,
11981226
int want_valid, int first_pass)
11991227
{
12001228
unsigned long v, r;
1229+
unsigned long rcbits_unset;
12011230
int ok = 1;
12021231
int valid, dirty;
12031232

12041233
/* Unmodified entries are uninteresting except on the first pass */
1205-
dirty = !!(revp->guest_rpte & HPTE_GR_MODIFIED);
1234+
dirty = hpte_dirty(revp, hptp);
12061235
if (!first_pass && !dirty)
12071236
return 0;
12081237

@@ -1223,16 +1252,28 @@ static long record_hpte(unsigned long flags, unsigned long *hptp,
12231252
while (!try_lock_hpte(hptp, HPTE_V_HVLOCK))
12241253
cpu_relax();
12251254
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+
12261268
if (v & HPTE_V_ABSENT) {
12271269
v &= ~HPTE_V_ABSENT;
12281270
v |= HPTE_V_VALID;
1271+
valid = 1;
12291272
}
1230-
/* re-evaluate valid and dirty from synchronized HPTE value */
1231-
valid = !!(v & HPTE_V_VALID);
12321273
if ((flags & KVM_GET_HTAB_BOLTED_ONLY) && !(v & HPTE_V_BOLTED))
12331274
valid = 0;
1234-
r = revp->guest_rpte | (hptp[1] & (HPTE_R_R | HPTE_R_C));
1235-
dirty = !!(revp->guest_rpte & HPTE_GR_MODIFIED);
1275+
1276+
r = revp->guest_rpte;
12361277
/* only clear modified if this is the right sort of entry */
12371278
if (valid == want_valid && dirty) {
12381279
r &= ~HPTE_GR_MODIFIED;
@@ -1288,7 +1329,7 @@ static ssize_t kvm_htab_read(struct file *file, char __user *buf,
12881329
/* Skip uninteresting entries, i.e. clean on not-first pass */
12891330
if (!first_pass) {
12901331
while (i < kvm->arch.hpt_npte &&
1291-
!(revp->guest_rpte & HPTE_GR_MODIFIED)) {
1332+
!hpte_dirty(revp, hptp)) {
12921333
++i;
12931334
hptp += 2;
12941335
++revp;

arch/powerpc/kvm/book3s_hv_rm_mmu.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,6 @@ void kvmppc_add_revmap_chain(struct kvm *kvm, struct revmap_entry *rev,
9797
}
9898
EXPORT_SYMBOL_GPL(kvmppc_add_revmap_chain);
9999

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-
111100
/* Remove this HPTE from the chain for a real page */
112101
static void remove_revmap_chain(struct kvm *kvm, long pte_index,
113102
struct revmap_entry *rev,

0 commit comments

Comments
 (0)