提交 687414be 编写于 作者: A Alexey Kardashevskiy 提交者: Alexander Graf

KVM: PPC: Book3S HV: Fix dirty map for hugepages

The dirty map that we construct for the KVM_GET_DIRTY_LOG ioctl has
one bit per system page (4K/64K).  Currently, we only set one bit in
the map for each HPT entry with the Change bit set, even if the HPT is
for a large page (e.g., 16MB).  Userspace then considers only the
first system page dirty, though in fact the guest may have modified
anywhere in the large page.

To fix this, we make kvm_test_clear_dirty() return the actual number
of pages that are dirty (and rename it to kvm_test_clear_dirty_npages()
to emphasize that that's what it returns).  In kvmppc_hv_get_dirty_log()
we then set that many bits in the dirty map.
Signed-off-by: NAlexey Kardashevskiy <aik@ozlabs.ru>
Signed-off-by: NPaul Mackerras <paulus@samba.org>
Signed-off-by: NAlexander Graf <agraf@suse.de>
上级 1066f772
...@@ -1060,22 +1060,27 @@ void kvm_set_spte_hva_hv(struct kvm *kvm, unsigned long hva, pte_t pte) ...@@ -1060,22 +1060,27 @@ void kvm_set_spte_hva_hv(struct kvm *kvm, unsigned long hva, pte_t pte)
kvm_handle_hva(kvm, hva, kvm_unmap_rmapp); kvm_handle_hva(kvm, hva, kvm_unmap_rmapp);
} }
static int kvm_test_clear_dirty(struct kvm *kvm, unsigned long *rmapp) /*
* Returns the number of system pages that are dirty.
* This can be more than 1 if we find a huge-page HPTE.
*/
static int kvm_test_clear_dirty_npages(struct kvm *kvm, unsigned long *rmapp)
{ {
struct revmap_entry *rev = kvm->arch.revmap; struct revmap_entry *rev = kvm->arch.revmap;
unsigned long head, i, j; unsigned long head, i, j;
unsigned long n;
unsigned long *hptep; unsigned long *hptep;
int ret = 0; int npages_dirty = 0;
retry: retry:
lock_rmap(rmapp); lock_rmap(rmapp);
if (*rmapp & KVMPPC_RMAP_CHANGED) { if (*rmapp & KVMPPC_RMAP_CHANGED) {
*rmapp &= ~KVMPPC_RMAP_CHANGED; *rmapp &= ~KVMPPC_RMAP_CHANGED;
ret = 1; npages_dirty = 1;
} }
if (!(*rmapp & KVMPPC_RMAP_PRESENT)) { if (!(*rmapp & KVMPPC_RMAP_PRESENT)) {
unlock_rmap(rmapp); unlock_rmap(rmapp);
return ret; return npages_dirty;
} }
i = head = *rmapp & KVMPPC_RMAP_INDEX; i = head = *rmapp & KVMPPC_RMAP_INDEX;
...@@ -1106,13 +1111,16 @@ static int kvm_test_clear_dirty(struct kvm *kvm, unsigned long *rmapp) ...@@ -1106,13 +1111,16 @@ static int kvm_test_clear_dirty(struct kvm *kvm, unsigned long *rmapp)
rev[i].guest_rpte |= HPTE_R_C; rev[i].guest_rpte |= HPTE_R_C;
note_hpte_modification(kvm, &rev[i]); note_hpte_modification(kvm, &rev[i]);
} }
ret = 1; n = hpte_page_size(hptep[0], hptep[1]);
n = (n + PAGE_SIZE - 1) >> PAGE_SHIFT;
if (n > npages_dirty)
npages_dirty = n;
} }
hptep[0] &= ~HPTE_V_HVLOCK; hptep[0] &= ~HPTE_V_HVLOCK;
} while ((i = j) != head); } while ((i = j) != head);
unlock_rmap(rmapp); unlock_rmap(rmapp);
return ret; return npages_dirty;
} }
static void harvest_vpa_dirty(struct kvmppc_vpa *vpa, static void harvest_vpa_dirty(struct kvmppc_vpa *vpa,
...@@ -1136,15 +1144,22 @@ static void harvest_vpa_dirty(struct kvmppc_vpa *vpa, ...@@ -1136,15 +1144,22 @@ static void harvest_vpa_dirty(struct kvmppc_vpa *vpa,
long kvmppc_hv_get_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot, long kvmppc_hv_get_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot,
unsigned long *map) unsigned long *map)
{ {
unsigned long i; unsigned long i, j;
unsigned long *rmapp; unsigned long *rmapp;
struct kvm_vcpu *vcpu; struct kvm_vcpu *vcpu;
preempt_disable(); preempt_disable();
rmapp = memslot->arch.rmap; rmapp = memslot->arch.rmap;
for (i = 0; i < memslot->npages; ++i) { for (i = 0; i < memslot->npages; ++i) {
if (kvm_test_clear_dirty(kvm, rmapp) && map) int npages = kvm_test_clear_dirty_npages(kvm, rmapp);
__set_bit_le(i, map); /*
* Note that if npages > 0 then i must be a multiple of npages,
* since we always put huge-page HPTEs in the rmap chain
* corresponding to their page base address.
*/
if (npages && map)
for (j = i; npages; ++j, --npages)
__set_bit_le(j, map);
++rmapp; ++rmapp;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册