Skip to content

Commit c0e0812

Browse files
committed
[TEMP] KVM: guest_memfd: Update gmem_prepare hook to handle partially-allocated folios
TODO: move to bitmap-based tracking within gmem Marking the whole folio uptodate as part of kvm_gmem_populate() doesn't work as expected for THP because kvm_gmem_populate() might only update a subset of the pages, leaving the other pages still needing the gmem_prepare hooks to be called when they are faulted in later. Eventually this will be tracked by introducing a more granular flag than uptodate that can track whether any particular 4K page has been prepared or not. For now however, modify kvm_gmem_populate() to only set the uptodate flag if the entire 2M range is initialized, and leave it unset afterward so gmem_prepare hooks still run. The gmem_prepare() hooks themselves can already track page states at 4K granularity via the RMP table, so are fully capable of dealing with partially-initialized folios. As an optimization, also allow kvm_gmem_prepare() to set the uptodate flag if the whole folio is already in a private/initialized state to avoid needing to scan the RMP entries for each subpage. Effectively, this makes the uptodate flag an optimization to skip RMP checks within a fully-prepared folio, but in the absence of the uptodate flag the RMP table is ultimately the authority on preparation state of an individual sub-page within a folio. Signed-off-by: Michael Roth <michael.roth@amd.com>
1 parent 46cf7f3 commit c0e0812

2 files changed

Lines changed: 92 additions & 18 deletions

File tree

arch/x86/kvm/svm/sev.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,6 +2189,9 @@ static int sev_gmem_post_populate(struct kvm *kvm, gfn_t gfn_start, kvm_pfn_t pf
21892189
int npages = (1 << order);
21902190
gfn_t gfn;
21912191

2192+
pr_debug("%s: GFN start 0x%llx PFN start 0x%llx order %d\n",
2193+
__func__, gfn_start, pfn, order);
2194+
21922195
if (WARN_ON_ONCE(sev_populate_args->type != KVM_SEV_SNP_PAGE_TYPE_ZERO && !src))
21932196
return -EINVAL;
21942197

@@ -4745,6 +4748,33 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code)
47454748
kvm_release_page_unused(page);
47464749
}
47474750

4751+
static bool is_pfn_range_private(kvm_pfn_t start, kvm_pfn_t end)
4752+
{
4753+
kvm_pfn_t pfn = start;
4754+
4755+
while (pfn < end) {
4756+
int ret, rmp_level;
4757+
bool assigned;
4758+
4759+
ret = snp_lookup_rmpentry(pfn, &assigned, &rmp_level);
4760+
if (ret) {
4761+
pr_warn_ratelimited("SEV: Failed to retrieve RMP entry: PFN 0x%llx GFN start 0x%llx GFN end 0x%llx RMP level %d error %d\n",
4762+
pfn, start, end, rmp_level, ret);
4763+
return false;
4764+
}
4765+
4766+
if (!assigned) {
4767+
pr_debug("%s: overlap detected, PFN 0x%llx start 0x%llx end 0x%llx RMP level %d\n",
4768+
__func__, pfn, start, end, rmp_level);
4769+
return false;
4770+
}
4771+
4772+
pfn++;
4773+
}
4774+
4775+
return true;
4776+
}
4777+
47484778
static bool is_pfn_range_shared(kvm_pfn_t start, kvm_pfn_t end)
47494779
{
47504780
kvm_pfn_t pfn = start;
@@ -4800,6 +4830,7 @@ int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order)
48004830
{
48014831
struct kvm_sev_info *sev = to_kvm_sev_info(kvm);
48024832
kvm_pfn_t pfn_aligned;
4833+
struct folio *folio;
48034834
gfn_t gfn_aligned;
48044835
int level, rc;
48054836
bool assigned;
@@ -4830,6 +4861,16 @@ int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order)
48304861
gfn_aligned = gfn;
48314862
}
48324863

4864+
folio = page_folio(pfn_to_page(pfn_aligned));
4865+
if (!folio_test_uptodate(folio)) {
4866+
unsigned long nr_pages = level == PG_LEVEL_4K ? 1 : 512;
4867+
int i;
4868+
4869+
pr_debug("%s: folio not up-to-date, clearing folio pages.\n", __func__);
4870+
for (i = 0; i < nr_pages; i++)
4871+
clear_highpage(pfn_to_page(pfn_aligned + i));
4872+
}
4873+
48334874
rc = rmp_make_private(pfn_aligned, gfn_to_gpa(gfn_aligned), level, sev->asid, false);
48344875
if (rc) {
48354876
pr_err_ratelimited("SEV: Failed to update RMP entry: GFN %llx PFN %llx level %d error %d\n",
@@ -4840,6 +4881,15 @@ int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order)
48404881
pr_debug("%s: updated: gfn %llx pfn %llx pfn_aligned %llx max_order %d level %d\n",
48414882
__func__, gfn, pfn, pfn_aligned, max_order, level);
48424883

4884+
if (pfn == pfn_aligned && folio_order(folio) == max_order) {
4885+
folio_mark_uptodate(folio);
4886+
pr_debug("%s: setting folio up-to-date (full update)\n", __func__);
4887+
} else if (is_pfn_range_private(pfn_aligned,
4888+
pfn_aligned + (1 << folio_order(folio)))) {
4889+
folio_mark_uptodate(folio);
4890+
pr_debug("%s: setting folio up-to-date (follow-up update)\n", __func__);
4891+
}
4892+
48434893
return 0;
48444894
}
48454895

virt/kvm/guest_memfd.c

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,9 @@ static inline void kvm_gmem_mark_prepared(struct folio *folio)
5656
static int kvm_gmem_prepare_folio(struct kvm *kvm, struct kvm_memory_slot *slot,
5757
gfn_t gfn, struct folio *folio)
5858
{
59-
unsigned long nr_pages, i;
6059
pgoff_t index;
6160
int r;
6261

63-
nr_pages = folio_nr_pages(folio);
64-
for (i = 0; i < nr_pages; i++)
65-
clear_highpage(folio_page(folio, i));
66-
6762
/*
6863
* Preparing huge folios should always be safe, since it should
6964
* be possible to split them later if needed.
@@ -77,12 +72,16 @@ static int kvm_gmem_prepare_folio(struct kvm *kvm, struct kvm_memory_slot *slot,
7772
* The order will be passed when creating the guest_memfd, and
7873
* checked when creating memslots.
7974
*/
80-
WARN_ON(!IS_ALIGNED(slot->gmem.pgoff, 1 << folio_order(folio)));
75+
if (!IS_ALIGNED(slot->gmem.pgoff, 1 << folio_order(folio)))
76+
pr_debug_ratelimited("%s: GFN %llx not aligned (slot gfn start %llx pgoff %lx)",
77+
__func__, gfn, slot->base_gfn, slot->gmem.pgoff);
8178
index = gfn - slot->base_gfn + slot->gmem.pgoff;
8279
index = ALIGN_DOWN(index, 1 << folio_order(folio));
8380
r = __kvm_gmem_prepare_folio(kvm, slot, index, folio);
84-
if (!r)
85-
kvm_gmem_mark_prepared(folio);
81+
if (!r) {
82+
pr_debug("%s: marking GFN %llx prepared\n", __func__, gfn);
83+
//kvm_gmem_mark_prepared(folio);
84+
}
8685

8786
return r;
8887
}
@@ -126,19 +125,36 @@ static struct folio *kvm_gmem_get_huge_folio(struct inode *inode, pgoff_t index,
126125
* Ignore accessed, referenced, and dirty flags. The memory is
127126
* unevictable and there is no storage to write back to.
128127
*/
129-
static struct folio *kvm_gmem_get_folio(struct inode *inode, pgoff_t index)
128+
static struct folio *__kvm_gmem_get_folio(struct inode *inode, pgoff_t index,
129+
bool allow_huge)
130130
{
131131
struct folio *folio = NULL;
132132

133-
if (gmem_2m_enabled)
133+
if (gmem_2m_enabled && allow_huge)
134134
folio = kvm_gmem_get_huge_folio(inode, index, PMD_ORDER);
135135

136136
if (!folio)
137137
folio = filemap_grab_folio(inode->i_mapping, index);
138138

139+
pr_debug("%s: allocate folio with PFN %lx order %d\n",
140+
__func__, folio_pfn(folio), folio_order(folio));
139141
return folio;
140142
}
141143

144+
/*
145+
* Returns a locked folio on success. The caller is responsible for
146+
* setting the up-to-date flag before the memory is mapped into the guest.
147+
* There is no backing storage for the memory, so the folio will remain
148+
* up-to-date until it's removed.
149+
*
150+
* Ignore accessed, referenced, and dirty flags. The memory is
151+
* unevictable and there is no storage to write back to.
152+
*/
153+
static struct folio *kvm_gmem_get_folio(struct inode *inode, pgoff_t index)
154+
{
155+
return __kvm_gmem_get_folio(inode, index, true);
156+
}
157+
142158
static void kvm_gmem_invalidate_begin(struct kvm_gmem *gmem, pgoff_t start,
143159
pgoff_t end)
144160
{
@@ -606,7 +622,8 @@ void kvm_gmem_unbind(struct kvm_memory_slot *slot)
606622
static struct folio *__kvm_gmem_get_pfn(struct file *file,
607623
struct kvm_memory_slot *slot,
608624
pgoff_t index, kvm_pfn_t *pfn,
609-
bool *is_prepared, int *max_order)
625+
bool *is_prepared, int *max_order,
626+
bool allow_huge)
610627
{
611628
struct file *gmem_file = READ_ONCE(slot->gmem.file);
612629
struct kvm_gmem *gmem = file->private_data;
@@ -624,7 +641,7 @@ static struct folio *__kvm_gmem_get_pfn(struct file *file,
624641
return ERR_PTR(-EIO);
625642
}
626643

627-
folio = kvm_gmem_get_folio(file_inode(file), index);
644+
folio = __kvm_gmem_get_folio(file_inode(file), index, allow_huge);
628645
if (IS_ERR(folio))
629646
return folio;
630647

@@ -636,11 +653,11 @@ static struct folio *__kvm_gmem_get_pfn(struct file *file,
636653

637654
*pfn = folio_file_pfn(folio, index);
638655
if (!max_order)
639-
goto success;
656+
return folio;
640657

641-
*max_order = compound_order(compound_head(page));
658+
*max_order = folio_order(folio);
642659
if (!*max_order)
643-
goto success;
660+
return folio;
644661

645662
/*
646663
* The folio can be mapped with a hugepage if and only if the folio is
@@ -670,7 +687,7 @@ int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot,
670687
if (!file)
671688
return -EFAULT;
672689

673-
folio = __kvm_gmem_get_pfn(file, slot, index, pfn, &is_prepared, max_order);
690+
folio = __kvm_gmem_get_pfn(file, slot, index, pfn, &is_prepared, max_order, true);
674691
if (IS_ERR(folio)) {
675692
r = PTR_ERR(folio);
676693
goto out;
@@ -730,12 +747,16 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, long
730747
break;
731748
}
732749

733-
folio = __kvm_gmem_get_pfn(file, slot, index, &pfn, &is_prepared, &max_order);
750+
folio = __kvm_gmem_get_pfn(file, slot, index, &pfn, &is_prepared, &max_order, false);
734751
if (IS_ERR(folio)) {
735752
ret = PTR_ERR(folio);
736753
break;
737754
}
738755

756+
pr_debug("%s: GFN start 0x%llx PFN start 0x%llx max_order %d folio_order %d uptodate %d\n",
757+
__func__, gfn, pfn, max_order, folio_order(folio),
758+
folio_test_uptodate(folio));
759+
739760
if (is_prepared) {
740761
folio_unlock(folio);
741762
folio_put(folio);
@@ -758,8 +779,11 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, long
758779

759780
p = src ? src + i * PAGE_SIZE : NULL;
760781
ret = post_populate(kvm, gfn, pfn, p, max_order, opaque);
761-
if (!ret)
782+
if (!ret && max_order == folio_order(folio)) {
783+
pr_debug("%s: GFN start 0x%llx PFN start 0x%llx max_order %d folio_order %d marking uptodate.\n",
784+
__func__, gfn, pfn, max_order, folio_order(folio));
762785
kvm_gmem_mark_prepared(folio);
786+
}
763787

764788
put_folio_and_exit:
765789
folio_put(folio);

0 commit comments

Comments
 (0)