Skip to content

Commit 9123c5f

Browse files
committed
Merge tag 'kvm-x86-gmem-6.20' of https://github.com/kvm-x86/linux into HEAD
KVM guest_memfd changes for 6.20 - Remove kvm_gmem_populate()'s preparation tracking and half-baked hugepage handling, and instead rely on SNP (the only user of the tracking) to do its own tracking via the RMP. - Retroactively document and enforce (for SNP) that KVM_SEV_SNP_LAUNCH_UPDATE and KVM_TDX_INIT_MEM_REGION require the source page to be 4KiB aligned, to avoid non-trivial complexity for a non-existent usecase (and because in-place conversion simply can't support unaligned sources). - When populating guest_memfd memory, GUP the source page in common code and pass the refcounted page to the vendor callback, instead of letting vendor code do the heavy lifting. Doing so avoids a looming deadlock bug with in-place due an AB-BA conflict betwee mmap_lock and guest_memfd's filemap invalidate lock.
2 parents 54f15eb + 2a62345 commit 9123c5f

File tree

6 files changed

+130
-141
lines changed

6 files changed

+130
-141
lines changed

Documentation/virt/kvm/x86/amd-memory-encryption.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ Returns: 0 on success, < 0 on error, -EAGAIN if caller should retry
523523

524524
struct kvm_sev_snp_launch_update {
525525
__u64 gfn_start; /* Guest page number to load/encrypt data into. */
526-
__u64 uaddr; /* Userspace address of data to be loaded/encrypted. */
526+
__u64 uaddr; /* 4k-aligned address of data to be loaded/encrypted. */
527527
__u64 len; /* 4k-aligned length in bytes to copy into guest memory.*/
528528
__u8 type; /* The type of the guest pages being initialized. */
529529
__u8 pad0;

Documentation/virt/kvm/x86/intel-tdx.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ KVM_TDX_INIT_MEM_REGION
156156
:Returns: 0 on success, <0 on error
157157

158158
Initialize @nr_pages TDX guest private memory starting from @gpa with userspace
159-
provided data from @source_addr.
159+
provided data from @source_addr. @source_addr must be PAGE_SIZE-aligned.
160160

161161
Note, before calling this sub command, memory attribute of the range
162162
[gpa, gpa + nr_pages] needs to be private. Userspace can use

arch/x86/kvm/svm/sev.c

Lines changed: 47 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2277,66 +2277,52 @@ struct sev_gmem_populate_args {
22772277
int fw_error;
22782278
};
22792279

2280-
static int sev_gmem_post_populate(struct kvm *kvm, gfn_t gfn_start, kvm_pfn_t pfn,
2281-
void __user *src, int order, void *opaque)
2280+
static int sev_gmem_post_populate(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
2281+
struct page *src_page, void *opaque)
22822282
{
22832283
struct sev_gmem_populate_args *sev_populate_args = opaque;
2284+
struct sev_data_snp_launch_update fw_args = {0};
22842285
struct kvm_sev_info *sev = to_kvm_sev_info(kvm);
2285-
int n_private = 0, ret, i;
2286-
int npages = (1 << order);
2287-
gfn_t gfn;
2286+
bool assigned = false;
2287+
int level;
2288+
int ret;
22882289

2289-
if (WARN_ON_ONCE(sev_populate_args->type != KVM_SEV_SNP_PAGE_TYPE_ZERO && !src))
2290+
if (WARN_ON_ONCE(sev_populate_args->type != KVM_SEV_SNP_PAGE_TYPE_ZERO && !src_page))
22902291
return -EINVAL;
22912292

2292-
for (gfn = gfn_start, i = 0; gfn < gfn_start + npages; gfn++, i++) {
2293-
struct sev_data_snp_launch_update fw_args = {0};
2294-
bool assigned = false;
2295-
int level;
2296-
2297-
ret = snp_lookup_rmpentry((u64)pfn + i, &assigned, &level);
2298-
if (ret || assigned) {
2299-
pr_debug("%s: Failed to ensure GFN 0x%llx RMP entry is initial shared state, ret: %d assigned: %d\n",
2300-
__func__, gfn, ret, assigned);
2301-
ret = ret ? -EINVAL : -EEXIST;
2302-
goto err;
2303-
}
2304-
2305-
if (src) {
2306-
void *vaddr = kmap_local_pfn(pfn + i);
2307-
2308-
if (copy_from_user(vaddr, src + i * PAGE_SIZE, PAGE_SIZE)) {
2309-
ret = -EFAULT;
2310-
goto err;
2311-
}
2312-
kunmap_local(vaddr);
2313-
}
2314-
2315-
ret = rmp_make_private(pfn + i, gfn << PAGE_SHIFT, PG_LEVEL_4K,
2316-
sev_get_asid(kvm), true);
2317-
if (ret)
2318-
goto err;
2293+
ret = snp_lookup_rmpentry((u64)pfn, &assigned, &level);
2294+
if (ret || assigned) {
2295+
pr_debug("%s: Failed to ensure GFN 0x%llx RMP entry is initial shared state, ret: %d assigned: %d\n",
2296+
__func__, gfn, ret, assigned);
2297+
ret = ret ? -EINVAL : -EEXIST;
2298+
goto out;
2299+
}
23192300

2320-
n_private++;
2301+
if (src_page) {
2302+
void *src_vaddr = kmap_local_page(src_page);
2303+
void *dst_vaddr = kmap_local_pfn(pfn);
23212304

2322-
fw_args.gctx_paddr = __psp_pa(sev->snp_context);
2323-
fw_args.address = __sme_set(pfn_to_hpa(pfn + i));
2324-
fw_args.page_size = PG_LEVEL_TO_RMP(PG_LEVEL_4K);
2325-
fw_args.page_type = sev_populate_args->type;
2305+
memcpy(dst_vaddr, src_vaddr, PAGE_SIZE);
23262306

2327-
ret = __sev_issue_cmd(sev_populate_args->sev_fd, SEV_CMD_SNP_LAUNCH_UPDATE,
2328-
&fw_args, &sev_populate_args->fw_error);
2329-
if (ret)
2330-
goto fw_err;
2307+
kunmap_local(src_vaddr);
2308+
kunmap_local(dst_vaddr);
23312309
}
23322310

2333-
return 0;
2311+
ret = rmp_make_private(pfn, gfn << PAGE_SHIFT, PG_LEVEL_4K,
2312+
sev_get_asid(kvm), true);
2313+
if (ret)
2314+
goto out;
23342315

2335-
fw_err:
2316+
fw_args.gctx_paddr = __psp_pa(sev->snp_context);
2317+
fw_args.address = __sme_set(pfn_to_hpa(pfn));
2318+
fw_args.page_size = PG_LEVEL_TO_RMP(PG_LEVEL_4K);
2319+
fw_args.page_type = sev_populate_args->type;
2320+
2321+
ret = __sev_issue_cmd(sev_populate_args->sev_fd, SEV_CMD_SNP_LAUNCH_UPDATE,
2322+
&fw_args, &sev_populate_args->fw_error);
23362323
/*
23372324
* If the firmware command failed handle the reclaim and cleanup of that
2338-
* PFN specially vs. prior pages which can be cleaned up below without
2339-
* needing to reclaim in advance.
2325+
* PFN before reporting an error.
23402326
*
23412327
* Additionally, when invalid CPUID function entries are detected,
23422328
* firmware writes the expected values into the page and leaves it
@@ -2346,26 +2332,22 @@ static int sev_gmem_post_populate(struct kvm *kvm, gfn_t gfn_start, kvm_pfn_t pf
23462332
* information to provide information on which CPUID leaves/fields
23472333
* failed CPUID validation.
23482334
*/
2349-
if (!snp_page_reclaim(kvm, pfn + i) &&
2335+
if (ret && !snp_page_reclaim(kvm, pfn) &&
23502336
sev_populate_args->type == KVM_SEV_SNP_PAGE_TYPE_CPUID &&
23512337
sev_populate_args->fw_error == SEV_RET_INVALID_PARAM) {
2352-
void *vaddr = kmap_local_pfn(pfn + i);
2338+
void *src_vaddr = kmap_local_page(src_page);
2339+
void *dst_vaddr = kmap_local_pfn(pfn);
23532340

2354-
if (copy_to_user(src + i * PAGE_SIZE, vaddr, PAGE_SIZE))
2355-
pr_debug("Failed to write CPUID page back to userspace\n");
2341+
memcpy(src_vaddr, dst_vaddr, PAGE_SIZE);
23562342

2357-
kunmap_local(vaddr);
2343+
kunmap_local(src_vaddr);
2344+
kunmap_local(dst_vaddr);
23582345
}
23592346

2360-
/* pfn + i is hypervisor-owned now, so skip below cleanup for it. */
2361-
n_private--;
2362-
2363-
err:
2364-
pr_debug("%s: exiting with error ret %d (fw_error %d), restoring %d gmem PFNs to shared.\n",
2365-
__func__, ret, sev_populate_args->fw_error, n_private);
2366-
for (i = 0; i < n_private; i++)
2367-
kvm_rmp_make_shared(kvm, pfn + i, PG_LEVEL_4K);
2368-
2347+
out:
2348+
if (ret)
2349+
pr_debug("%s: error updating GFN %llx, return code %d (fw_error %d)\n",
2350+
__func__, gfn, ret, sev_populate_args->fw_error);
23692351
return ret;
23702352
}
23712353

@@ -2396,6 +2378,11 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp)
23962378
params.type != KVM_SEV_SNP_PAGE_TYPE_CPUID))
23972379
return -EINVAL;
23982380

2381+
src = params.type == KVM_SEV_SNP_PAGE_TYPE_ZERO ? NULL : u64_to_user_ptr(params.uaddr);
2382+
2383+
if (!PAGE_ALIGNED(src))
2384+
return -EINVAL;
2385+
23992386
npages = params.len / PAGE_SIZE;
24002387

24012388
/*
@@ -2427,7 +2414,6 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp)
24272414

24282415
sev_populate_args.sev_fd = argp->sev_fd;
24292416
sev_populate_args.type = params.type;
2430-
src = params.type == KVM_SEV_SNP_PAGE_TYPE_ZERO ? NULL : u64_to_user_ptr(params.uaddr);
24312417

24322418
count = kvm_gmem_populate(kvm, params.gfn_start, src, npages,
24332419
sev_gmem_post_populate, &sev_populate_args);

arch/x86/kvm/vmx/tdx.c

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3118,34 +3118,24 @@ struct tdx_gmem_post_populate_arg {
31183118
};
31193119

31203120
static int tdx_gmem_post_populate(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
3121-
void __user *src, int order, void *_arg)
3121+
struct page *src_page, void *_arg)
31223122
{
31233123
struct tdx_gmem_post_populate_arg *arg = _arg;
31243124
struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm);
31253125
u64 err, entry, level_state;
31263126
gpa_t gpa = gfn_to_gpa(gfn);
3127-
struct page *src_page;
31283127
int ret, i;
31293128

31303129
if (KVM_BUG_ON(kvm_tdx->page_add_src, kvm))
31313130
return -EIO;
31323131

3133-
/*
3134-
* Get the source page if it has been faulted in. Return failure if the
3135-
* source page has been swapped out or unmapped in primary memory.
3136-
*/
3137-
ret = get_user_pages_fast((unsigned long)src, 1, 0, &src_page);
3138-
if (ret < 0)
3139-
return ret;
3140-
if (ret != 1)
3141-
return -ENOMEM;
3132+
if (!src_page)
3133+
return -EOPNOTSUPP;
31423134

31433135
kvm_tdx->page_add_src = src_page;
31443136
ret = kvm_tdp_mmu_map_private_pfn(arg->vcpu, gfn, pfn);
31453137
kvm_tdx->page_add_src = NULL;
31463138

3147-
put_page(src_page);
3148-
31493139
if (ret || !(arg->flags & KVM_TDX_MEASURE_MEMORY_REGION))
31503140
return ret;
31513141

include/linux/kvm_host.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2566,7 +2566,7 @@ int kvm_arch_gmem_prepare(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, int max_ord
25662566
* @gfn: starting GFN to be populated
25672567
* @src: userspace-provided buffer containing data to copy into GFN range
25682568
* (passed to @post_populate, and incremented on each iteration
2569-
* if not NULL)
2569+
* if not NULL). Must be page-aligned.
25702570
* @npages: number of pages to copy from userspace-buffer
25712571
* @post_populate: callback to issue for each gmem page that backs the GPA
25722572
* range
@@ -2581,7 +2581,7 @@ int kvm_arch_gmem_prepare(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, int max_ord
25812581
* Returns the number of pages that were populated.
25822582
*/
25832583
typedef int (*kvm_gmem_populate_cb)(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
2584-
void __user *src, int order, void *opaque);
2584+
struct page *page, void *opaque);
25852585

25862586
long kvm_gmem_populate(struct kvm *kvm, gfn_t gfn, void __user *src, long npages,
25872587
kvm_gmem_populate_cb post_populate, void *opaque);

0 commit comments

Comments
 (0)