|
@@ -893,7 +893,10 @@ static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp,
|
|
|
/* Harvest R and C */
|
|
|
rcbits = hptep[1] & (HPTE_R_R | HPTE_R_C);
|
|
|
*rmapp |= rcbits << KVMPPC_RMAP_RC_SHIFT;
|
|
|
- rev[i].guest_rpte = ptel | rcbits;
|
|
|
+ if (rcbits & ~rev[i].guest_rpte) {
|
|
|
+ rev[i].guest_rpte = ptel | rcbits;
|
|
|
+ note_hpte_modification(kvm, &rev[i]);
|
|
|
+ }
|
|
|
}
|
|
|
unlock_rmap(rmapp);
|
|
|
hptep[0] &= ~HPTE_V_HVLOCK;
|
|
@@ -976,7 +979,10 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
|
|
|
/* Now check and modify the HPTE */
|
|
|
if ((hptep[0] & HPTE_V_VALID) && (hptep[1] & HPTE_R_R)) {
|
|
|
kvmppc_clear_ref_hpte(kvm, hptep, i);
|
|
|
- rev[i].guest_rpte |= HPTE_R_R;
|
|
|
+ if (!(rev[i].guest_rpte & HPTE_R_R)) {
|
|
|
+ rev[i].guest_rpte |= HPTE_R_R;
|
|
|
+ note_hpte_modification(kvm, &rev[i]);
|
|
|
+ }
|
|
|
ret = 1;
|
|
|
}
|
|
|
hptep[0] &= ~HPTE_V_HVLOCK;
|
|
@@ -1080,7 +1086,10 @@ static int kvm_test_clear_dirty(struct kvm *kvm, unsigned long *rmapp)
|
|
|
hptep[1] &= ~HPTE_R_C;
|
|
|
eieio();
|
|
|
hptep[0] = (hptep[0] & ~HPTE_V_ABSENT) | HPTE_V_VALID;
|
|
|
- rev[i].guest_rpte |= HPTE_R_C;
|
|
|
+ if (!(rev[i].guest_rpte & HPTE_R_C)) {
|
|
|
+ rev[i].guest_rpte |= HPTE_R_C;
|
|
|
+ note_hpte_modification(kvm, &rev[i]);
|
|
|
+ }
|
|
|
ret = 1;
|
|
|
}
|
|
|
hptep[0] &= ~HPTE_V_HVLOCK;
|
|
@@ -1193,16 +1202,36 @@ struct kvm_htab_ctx {
|
|
|
|
|
|
#define HPTE_SIZE (2 * sizeof(unsigned long))
|
|
|
|
|
|
+/*
|
|
|
+ * Returns 1 if this HPT entry has been modified or has pending
|
|
|
+ * R/C bit changes.
|
|
|
+ */
|
|
|
+static int hpte_dirty(struct revmap_entry *revp, unsigned long *hptp)
|
|
|
+{
|
|
|
+ unsigned long rcbits_unset;
|
|
|
+
|
|
|
+ if (revp->guest_rpte & HPTE_GR_MODIFIED)
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ /* Also need to consider changes in reference and changed bits */
|
|
|
+ rcbits_unset = ~revp->guest_rpte & (HPTE_R_R | HPTE_R_C);
|
|
|
+ if ((hptp[0] & HPTE_V_VALID) && (hptp[1] & rcbits_unset))
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static long record_hpte(unsigned long flags, unsigned long *hptp,
|
|
|
unsigned long *hpte, struct revmap_entry *revp,
|
|
|
int want_valid, int first_pass)
|
|
|
{
|
|
|
unsigned long v, r;
|
|
|
+ unsigned long rcbits_unset;
|
|
|
int ok = 1;
|
|
|
int valid, dirty;
|
|
|
|
|
|
/* Unmodified entries are uninteresting except on the first pass */
|
|
|
- dirty = !!(revp->guest_rpte & HPTE_GR_MODIFIED);
|
|
|
+ dirty = hpte_dirty(revp, hptp);
|
|
|
if (!first_pass && !dirty)
|
|
|
return 0;
|
|
|
|
|
@@ -1223,16 +1252,28 @@ static long record_hpte(unsigned long flags, unsigned long *hptp,
|
|
|
while (!try_lock_hpte(hptp, HPTE_V_HVLOCK))
|
|
|
cpu_relax();
|
|
|
v = hptp[0];
|
|
|
+
|
|
|
+ /* re-evaluate valid and dirty from synchronized HPTE value */
|
|
|
+ valid = !!(v & HPTE_V_VALID);
|
|
|
+ dirty = !!(revp->guest_rpte & HPTE_GR_MODIFIED);
|
|
|
+
|
|
|
+ /* Harvest R and C into guest view if necessary */
|
|
|
+ rcbits_unset = ~revp->guest_rpte & (HPTE_R_R | HPTE_R_C);
|
|
|
+ if (valid && (rcbits_unset & hptp[1])) {
|
|
|
+ revp->guest_rpte |= (hptp[1] & (HPTE_R_R | HPTE_R_C)) |
|
|
|
+ HPTE_GR_MODIFIED;
|
|
|
+ dirty = 1;
|
|
|
+ }
|
|
|
+
|
|
|
if (v & HPTE_V_ABSENT) {
|
|
|
v &= ~HPTE_V_ABSENT;
|
|
|
v |= HPTE_V_VALID;
|
|
|
+ valid = 1;
|
|
|
}
|
|
|
- /* re-evaluate valid and dirty from synchronized HPTE value */
|
|
|
- valid = !!(v & HPTE_V_VALID);
|
|
|
if ((flags & KVM_GET_HTAB_BOLTED_ONLY) && !(v & HPTE_V_BOLTED))
|
|
|
valid = 0;
|
|
|
- r = revp->guest_rpte | (hptp[1] & (HPTE_R_R | HPTE_R_C));
|
|
|
- dirty = !!(revp->guest_rpte & HPTE_GR_MODIFIED);
|
|
|
+
|
|
|
+ r = revp->guest_rpte;
|
|
|
/* only clear modified if this is the right sort of entry */
|
|
|
if (valid == want_valid && dirty) {
|
|
|
r &= ~HPTE_GR_MODIFIED;
|
|
@@ -1288,7 +1329,7 @@ static ssize_t kvm_htab_read(struct file *file, char __user *buf,
|
|
|
/* Skip uninteresting entries, i.e. clean on not-first pass */
|
|
|
if (!first_pass) {
|
|
|
while (i < kvm->arch.hpt_npte &&
|
|
|
- !(revp->guest_rpte & HPTE_GR_MODIFIED)) {
|
|
|
+ !hpte_dirty(revp, hptp)) {
|
|
|
++i;
|
|
|
hptp += 2;
|
|
|
++revp;
|