Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit 9cfc946

Browse files
author
Quentin Perret
committed
ANDROID: KVM: arm64: Pre-alloctate mtree nodes in pkvm_mem_abort()
The call to mtree_insert_range() from insert_ppage() may end up doing GFP_KERNEL allocations if the mtree's kmem_cache is empty. Sadly this is all being done from an mmu_lock write critical section, where we can't sleep. Fix this by inserting dummy entries via mtree_insert_range() outside the critical section which can then be overwritten without requiring memory allocation. This is safe to do as we're using the builtin mtree lock, but does require checking the validity of the ppage found in the tree from all observers. Bug: 278749606 Bug: 278011447 Change-Id: Ifa4a092e6280db4d02f5943a4eab94d958b431ee Signed-off-by: Quentin Perret <[email protected]>
1 parent d53a549 commit 9cfc946

File tree

3 files changed

+47
-31
lines changed

3 files changed

+47
-31
lines changed

arch/arm64/include/asm/kvm_host.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ struct kvm_pinned_page {
232232
u16 pins;
233233
};
234234

235+
#define KVM_DUMMY_PPAGE ((struct kvm_pinned_page *)-1)
236+
235237
typedef unsigned int pkvm_handle_t;
236238

237239
struct kvm_protected_vm {

arch/arm64/kvm/mmu.c

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ static int pkvm_unmap_range(struct kvm *kvm, u64 start, u64 end)
326326

327327
mt_for_each(&kvm->arch.pkvm.pinned_pages, entry, index, end - 1) {
328328
struct kvm_pinned_page *ppage = entry;
329+
330+
if (ppage == KVM_DUMMY_PPAGE)
331+
continue;
329332
ret = pkvm_unmap_guest(kvm, ppage);
330333
if (ret)
331334
break;
@@ -425,6 +428,8 @@ static void pkvm_stage2_flush(struct kvm *kvm)
425428
mt_for_each(&kvm->arch.pkvm.pinned_pages, entry, index, ULONG_MAX) {
426429
struct kvm_pinned_page *ppage = entry;
427430

431+
if (ppage == KVM_DUMMY_PPAGE)
432+
continue;
428433
__clean_dcache_guest_page(page_address(ppage->page), PAGE_SIZE);
429434
cond_resched_rwlock_write(&kvm->mmu_lock);
430435
}
@@ -1284,7 +1289,11 @@ static int pkvm_wp_range(struct kvm *kvm, u64 start, u64 end)
12841289

12851290
mt_for_each(&kvm->arch.pkvm.pinned_pages, entry, index, end - 1) {
12861291
struct kvm_pinned_page *ppage = entry;
1287-
int ret = pkvm_call_hyp_nvhe_ppage(ppage, __pkvm_wrprotect_call,
1292+
int ret;
1293+
1294+
if (ppage == KVM_DUMMY_PPAGE)
1295+
continue;
1296+
ret = pkvm_call_hyp_nvhe_ppage(ppage, __pkvm_wrprotect_call,
12881297
kvm, false);
12891298

12901299
if (ret)
@@ -1618,27 +1627,22 @@ find_ppage_or_above(struct kvm *kvm, phys_addr_t ipa)
16181627
unsigned long index = ipa;
16191628
void *entry;
16201629

1621-
mt_for_each(&kvm->arch.pkvm.pinned_pages, entry, index, ULONG_MAX)
1630+
mt_for_each(&kvm->arch.pkvm.pinned_pages, entry, index, ULONG_MAX) {
1631+
if (entry == KVM_DUMMY_PPAGE)
1632+
continue;
16221633
return entry;
1634+
}
16231635

16241636
return NULL;
16251637
}
16261638

1627-
static int insert_ppage(struct kvm *kvm, struct kvm_pinned_page *ppage)
1628-
{
1629-
size_t size = PAGE_SIZE << ppage->order;
1630-
unsigned long start = ppage->ipa;
1631-
unsigned long end = start + size - 1;
1632-
1633-
return mtree_insert_range(&kvm->arch.pkvm.pinned_pages, start, end,
1634-
ppage, GFP_KERNEL);
1635-
}
1636-
16371639
static struct kvm_pinned_page *find_ppage(struct kvm *kvm, u64 ipa)
16381640
{
1641+
struct kvm_pinned_page *ppage;
16391642
unsigned long index = ipa;
16401643

1641-
return mt_find(&kvm->arch.pkvm.pinned_pages, &index, ipa + PAGE_SIZE - 1);
1644+
ppage = mt_find(&kvm->arch.pkvm.pinned_pages, &index, ipa + PAGE_SIZE - 1);
1645+
return ppage == KVM_DUMMY_PPAGE ? NULL : ppage;
16421646
}
16431647

16441648
static int __pkvm_relax_perms_call(u64 pfn, u64 gfn, u8 order, void *args)
@@ -1690,10 +1694,11 @@ static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t *fault_ipa,
16901694
{
16911695
unsigned int flags = FOLL_HWPOISON | FOLL_LONGTERM | FOLL_WRITE;
16921696
struct kvm_hyp_memcache *hyp_memcache = &vcpu->arch.stage2_mc;
1693-
unsigned long index, pmd_offset, page_size;
1697+
unsigned long index, pmd_offset, page_size, end;
16941698
struct mm_struct *mm = current->mm;
16951699
struct kvm_pinned_page *ppage;
16961700
struct kvm *kvm = vcpu->kvm;
1701+
struct maple_tree *mt = &kvm->arch.pkvm.pinned_pages;
16971702
int ret, nr_pages;
16981703
struct page *page;
16991704
u64 pfn;
@@ -1756,16 +1761,19 @@ static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t *fault_ipa,
17561761
if (ret)
17571762
goto unpin;
17581763

1759-
write_lock(&kvm->mmu_lock);
1764+
index = *fault_ipa;
1765+
end = index + page_size - 1;
1766+
ppage->page = page;
1767+
ppage->ipa = *fault_ipa;
1768+
ppage->order = get_order(page_size);
1769+
ppage->pins = 1 << ppage->order;
1770+
17601771
/*
17611772
* If we already have a mapping in the middle of the THP, we have no
17621773
* other choice than enforcing PAGE_SIZE for pkvm_host_map_guest() to
17631774
* succeed.
17641775
*/
1765-
index = *fault_ipa;
1766-
if (page_size > PAGE_SIZE &&
1767-
mt_find(&kvm->arch.pkvm.pinned_pages, &index, index + page_size - 1)) {
1768-
write_unlock(&kvm->mmu_lock);
1776+
if (page_size > PAGE_SIZE && mt_find(mt, &index, end)) {
17691777
*fault_ipa += pmd_offset;
17701778
pfn += pmd_offset >> PAGE_SHIFT;
17711779
page = pfn_to_page(pfn);
@@ -1774,27 +1782,31 @@ static int pkvm_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t *fault_ipa,
17741782
goto retry;
17751783
}
17761784

1777-
ret = pkvm_host_map_guest(pfn, *fault_ipa >> PAGE_SHIFT,
1778-
page_size >> PAGE_SHIFT, KVM_PGTABLE_PROT_R);
1785+
/* Reserve space in the mtree */
1786+
ret = mtree_insert_range(mt, index, end, KVM_DUMMY_PPAGE, GFP_KERNEL);
17791787
if (ret) {
1780-
if (ret == -EAGAIN)
1788+
if (ret == -EEXIST)
17811789
ret = 0;
1782-
17831790
goto dec_account;
17841791
}
17851792

1786-
ppage->page = page;
1787-
ppage->ipa = *fault_ipa;
1788-
ppage->order = get_order(page_size);
1789-
ppage->pins = 1 << ppage->order;
1790-
WARN_ON(insert_ppage(kvm, ppage));
1793+
write_lock(&kvm->mmu_lock);
1794+
ret = pkvm_host_map_guest(pfn, *fault_ipa >> PAGE_SHIFT,
1795+
page_size >> PAGE_SHIFT, KVM_PGTABLE_PROT_R);
1796+
if (ret) {
1797+
if (WARN_ON(ret == -EAGAIN))
1798+
ret = 0;
17911799

1800+
goto err_unlock;
1801+
}
1802+
WARN_ON(mtree_store_range(mt, index, end, ppage, GFP_ATOMIC));
17921803
write_unlock(&kvm->mmu_lock);
17931804

17941805
return 0;
17951806

1796-
dec_account:
1807+
err_unlock:
17971808
write_unlock(&kvm->mmu_lock);
1809+
dec_account:
17981810
account_locked_vm(mm, page_size >> PAGE_SHIFT, false);
17991811
unpin:
18001812
unpin_user_pages(&page, 1);
@@ -1825,7 +1837,7 @@ int pkvm_mem_abort_range(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, size_t si
18251837
ppage = find_ppage_or_above(vcpu->kvm, fault_ipa);
18261838

18271839
while (fault_ipa < ipa_end) {
1828-
if (ppage && ppage->ipa == fault_ipa) {
1840+
if (ppage && ppage != KVM_DUMMY_PPAGE && ppage->ipa == fault_ipa) {
18291841
page_size = PAGE_SIZE << ppage->order;
18301842
ppage = mt_next(&vcpu->kvm->arch.pkvm.pinned_pages,
18311843
ppage->ipa, ULONG_MAX);

arch/arm64/kvm/pkvm.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ static void __pkvm_destroy_hyp_vm(struct kvm *host_kvm)
330330
WARN_ON(kvm_call_hyp_nvhe(__pkvm_start_teardown_vm, host_kvm->arch.pkvm.handle));
331331

332332
mt_for_each(&host_kvm->arch.pkvm.pinned_pages, ppage, ipa, ULONG_MAX) {
333+
if (WARN_ON(ppage == KVM_DUMMY_PPAGE))
334+
continue;
333335
WARN_ON(pkvm_call_hyp_nvhe_ppage(ppage,
334336
__reclaim_dying_guest_page_call,
335337
host_kvm, true));
@@ -539,7 +541,7 @@ void pkvm_host_reclaim_page(struct kvm *host_kvm, phys_addr_t ipa)
539541
write_lock(&host_kvm->mmu_lock);
540542
ppage = mt_find(&host_kvm->arch.pkvm.pinned_pages, &index,
541543
index + PAGE_SIZE - 1);
542-
if (ppage) {
544+
if (ppage && ppage != KVM_DUMMY_PPAGE) {
543545
if (ppage->pins)
544546
ppage->pins--;
545547
else

0 commit comments

Comments
 (0)