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>
This commit is contained in:
parent
d9ce6041b3
commit
a1b4a0f606
|
@ -268,4 +268,17 @@ static inline int is_vrma_hpte(unsigned long hpte_v)
|
||||||
(HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16)));
|
(HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_BOOK3S_64_HV
|
||||||
|
/*
|
||||||
|
* Note modification of an HPTE; set the HPTE modified bit
|
||||||
|
* if anyone is interested.
|
||||||
|
*/
|
||||||
|
static inline void note_hpte_modification(struct kvm *kvm,
|
||||||
|
struct revmap_entry *rev)
|
||||||
|
{
|
||||||
|
if (atomic_read(&kvm->arch.hpte_mod_interest))
|
||||||
|
rev->guest_rpte |= HPTE_GR_MODIFIED;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_KVM_BOOK3S_64_HV */
|
||||||
|
|
||||||
#endif /* __ASM_KVM_BOOK3S_64_H__ */
|
#endif /* __ASM_KVM_BOOK3S_64_H__ */
|
||||||
|
|
|
@ -893,7 +893,10 @@ static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp,
|
||||||
/* Harvest R and C */
|
/* Harvest R and C */
|
||||||
rcbits = hptep[1] & (HPTE_R_R | HPTE_R_C);
|
rcbits = hptep[1] & (HPTE_R_R | HPTE_R_C);
|
||||||
*rmapp |= rcbits << KVMPPC_RMAP_RC_SHIFT;
|
*rmapp |= rcbits << KVMPPC_RMAP_RC_SHIFT;
|
||||||
|
if (rcbits & ~rev[i].guest_rpte) {
|
||||||
rev[i].guest_rpte = ptel | rcbits;
|
rev[i].guest_rpte = ptel | rcbits;
|
||||||
|
note_hpte_modification(kvm, &rev[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
unlock_rmap(rmapp);
|
unlock_rmap(rmapp);
|
||||||
hptep[0] &= ~HPTE_V_HVLOCK;
|
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 */
|
/* Now check and modify the HPTE */
|
||||||
if ((hptep[0] & HPTE_V_VALID) && (hptep[1] & HPTE_R_R)) {
|
if ((hptep[0] & HPTE_V_VALID) && (hptep[1] & HPTE_R_R)) {
|
||||||
kvmppc_clear_ref_hpte(kvm, hptep, i);
|
kvmppc_clear_ref_hpte(kvm, hptep, i);
|
||||||
|
if (!(rev[i].guest_rpte & HPTE_R_R)) {
|
||||||
rev[i].guest_rpte |= HPTE_R_R;
|
rev[i].guest_rpte |= HPTE_R_R;
|
||||||
|
note_hpte_modification(kvm, &rev[i]);
|
||||||
|
}
|
||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
hptep[0] &= ~HPTE_V_HVLOCK;
|
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;
|
hptep[1] &= ~HPTE_R_C;
|
||||||
eieio();
|
eieio();
|
||||||
hptep[0] = (hptep[0] & ~HPTE_V_ABSENT) | HPTE_V_VALID;
|
hptep[0] = (hptep[0] & ~HPTE_V_ABSENT) | HPTE_V_VALID;
|
||||||
|
if (!(rev[i].guest_rpte & HPTE_R_C)) {
|
||||||
rev[i].guest_rpte |= HPTE_R_C;
|
rev[i].guest_rpte |= HPTE_R_C;
|
||||||
|
note_hpte_modification(kvm, &rev[i]);
|
||||||
|
}
|
||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
hptep[0] &= ~HPTE_V_HVLOCK;
|
hptep[0] &= ~HPTE_V_HVLOCK;
|
||||||
|
@ -1193,16 +1202,36 @@ struct kvm_htab_ctx {
|
||||||
|
|
||||||
#define HPTE_SIZE (2 * sizeof(unsigned long))
|
#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,
|
static long record_hpte(unsigned long flags, unsigned long *hptp,
|
||||||
unsigned long *hpte, struct revmap_entry *revp,
|
unsigned long *hpte, struct revmap_entry *revp,
|
||||||
int want_valid, int first_pass)
|
int want_valid, int first_pass)
|
||||||
{
|
{
|
||||||
unsigned long v, r;
|
unsigned long v, r;
|
||||||
|
unsigned long rcbits_unset;
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
int valid, dirty;
|
int valid, dirty;
|
||||||
|
|
||||||
/* Unmodified entries are uninteresting except on the first pass */
|
/* 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)
|
if (!first_pass && !dirty)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -1223,16 +1252,28 @@ static long record_hpte(unsigned long flags, unsigned long *hptp,
|
||||||
while (!try_lock_hpte(hptp, HPTE_V_HVLOCK))
|
while (!try_lock_hpte(hptp, HPTE_V_HVLOCK))
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
v = hptp[0];
|
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) {
|
if (v & HPTE_V_ABSENT) {
|
||||||
v &= ~HPTE_V_ABSENT;
|
v &= ~HPTE_V_ABSENT;
|
||||||
v |= HPTE_V_VALID;
|
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))
|
if ((flags & KVM_GET_HTAB_BOLTED_ONLY) && !(v & HPTE_V_BOLTED))
|
||||||
valid = 0;
|
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 */
|
/* only clear modified if this is the right sort of entry */
|
||||||
if (valid == want_valid && dirty) {
|
if (valid == want_valid && dirty) {
|
||||||
r &= ~HPTE_GR_MODIFIED;
|
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 */
|
/* Skip uninteresting entries, i.e. clean on not-first pass */
|
||||||
if (!first_pass) {
|
if (!first_pass) {
|
||||||
while (i < kvm->arch.hpt_npte &&
|
while (i < kvm->arch.hpt_npte &&
|
||||||
!(revp->guest_rpte & HPTE_GR_MODIFIED)) {
|
!hpte_dirty(revp, hptp)) {
|
||||||
++i;
|
++i;
|
||||||
hptp += 2;
|
hptp += 2;
|
||||||
++revp;
|
++revp;
|
||||||
|
|
|
@ -97,17 +97,6 @@ void kvmppc_add_revmap_chain(struct kvm *kvm, struct revmap_entry *rev,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kvmppc_add_revmap_chain);
|
EXPORT_SYMBOL_GPL(kvmppc_add_revmap_chain);
|
||||||
|
|
||||||
/*
|
|
||||||
* Note modification of an HPTE; set the HPTE modified bit
|
|
||||||
* if anyone is interested.
|
|
||||||
*/
|
|
||||||
static inline void note_hpte_modification(struct kvm *kvm,
|
|
||||||
struct revmap_entry *rev)
|
|
||||||
{
|
|
||||||
if (atomic_read(&kvm->arch.hpte_mod_interest))
|
|
||||||
rev->guest_rpte |= HPTE_GR_MODIFIED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove this HPTE from the chain for a real page */
|
/* Remove this HPTE from the chain for a real page */
|
||||||
static void remove_revmap_chain(struct kvm *kvm, long pte_index,
|
static void remove_revmap_chain(struct kvm *kvm, long pte_index,
|
||||||
struct revmap_entry *rev,
|
struct revmap_entry *rev,
|
||||||
|
|
Loading…
Reference in New Issue