Commit de6c0b02 authored by David Gibson's avatar David Gibson Committed by Avi Kivity

KVM: PPC: Book3S HV: Fix refcounting of hugepages

The H_REGISTER_VPA hcall implementation in HV Power KVM needs to pin some
guest memory pages into host memory so that they can be safely accessed
from usermode.  It does this used get_user_pages_fast().  When the VPA is
unregistered, or the VCPUs are cleaned up, these pages are released using
put_page().

However, the get_user_pages() is invoked on the specific memory are of the
VPA which could lie within hugepages.  In case the pinned page is huge,
we explicitly find the head page of the compound page before calling
put_page() on it.

At least with the latest kernel, this is not correct.  put_page() already
handles finding the correct head page of a compound, and also deals with
various counts on the individual tail page which are important for
transparent huge pages.  We don't support transparent hugepages on Power,
but even so, bypassing this count maintenance can lead (when the VM ends)
to a hugepage being released back to the pool with a non-zero mapcount on
one of the tail pages.  This can then lead to a bad_page() when the page
is released from the hugepage pool.

This removes the explicit compound_head() call to correct this bug.
Signed-off-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Acked-by: default avatarAlexander Graf <agraf@suse.de>
Signed-off-by: default avatarAvi Kivity <avi@redhat.com>
parent 62c49cc9
...@@ -258,6 +258,8 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn, ...@@ -258,6 +258,8 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn,
!(memslot->userspace_addr & (s - 1))) { !(memslot->userspace_addr & (s - 1))) {
start &= ~(s - 1); start &= ~(s - 1);
pgsize = s; pgsize = s;
get_page(hpage);
put_page(page);
page = hpage; page = hpage;
} }
} }
...@@ -281,11 +283,8 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn, ...@@ -281,11 +283,8 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn,
err = 0; err = 0;
out: out:
if (got) { if (got)
if (PageHuge(page))
page = compound_head(page);
put_page(page); put_page(page);
}
return err; return err;
up_err: up_err:
...@@ -678,8 +677,15 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, ...@@ -678,8 +677,15 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
SetPageDirty(page); SetPageDirty(page);
out_put: out_put:
if (page) if (page) {
put_page(page); /*
* We drop pages[0] here, not page because page might
* have been set to the head page of a compound, but
* we have to drop the reference on the correct tail
* page to match the get inside gup()
*/
put_page(pages[0]);
}
return ret; return ret;
out_unlock: out_unlock:
...@@ -979,6 +985,7 @@ void *kvmppc_pin_guest_page(struct kvm *kvm, unsigned long gpa, ...@@ -979,6 +985,7 @@ void *kvmppc_pin_guest_page(struct kvm *kvm, unsigned long gpa,
pa = *physp; pa = *physp;
} }
page = pfn_to_page(pa >> PAGE_SHIFT); page = pfn_to_page(pa >> PAGE_SHIFT);
get_page(page);
} else { } else {
hva = gfn_to_hva_memslot(memslot, gfn); hva = gfn_to_hva_memslot(memslot, gfn);
npages = get_user_pages_fast(hva, 1, 1, pages); npages = get_user_pages_fast(hva, 1, 1, pages);
...@@ -991,8 +998,6 @@ void *kvmppc_pin_guest_page(struct kvm *kvm, unsigned long gpa, ...@@ -991,8 +998,6 @@ void *kvmppc_pin_guest_page(struct kvm *kvm, unsigned long gpa,
page = compound_head(page); page = compound_head(page);
psize <<= compound_order(page); psize <<= compound_order(page);
} }
if (!kvm->arch.using_mmu_notifiers)
get_page(page);
offset = gpa & (psize - 1); offset = gpa & (psize - 1);
if (nb_ret) if (nb_ret)
*nb_ret = psize - offset; *nb_ret = psize - offset;
...@@ -1003,7 +1008,6 @@ void kvmppc_unpin_guest_page(struct kvm *kvm, void *va) ...@@ -1003,7 +1008,6 @@ void kvmppc_unpin_guest_page(struct kvm *kvm, void *va)
{ {
struct page *page = virt_to_page(va); struct page *page = virt_to_page(va);
page = compound_head(page);
put_page(page); put_page(page);
} }
......
...@@ -1192,8 +1192,6 @@ static void unpin_slot(struct kvm *kvm, int slot_id) ...@@ -1192,8 +1192,6 @@ static void unpin_slot(struct kvm *kvm, int slot_id)
continue; continue;
pfn = physp[j] >> PAGE_SHIFT; pfn = physp[j] >> PAGE_SHIFT;
page = pfn_to_page(pfn); page = pfn_to_page(pfn);
if (PageHuge(page))
page = compound_head(page);
SetPageDirty(page); SetPageDirty(page);
put_page(page); put_page(page);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment