Skip to content

Commit ac3cf3e

Browse files
authored
Merge pull request #141 from microsoft/user/namjain/6.18-warn-fix-v3
Drivers: hv: mshv_vtl: fix GUP into VTL0 device mappings
2 parents 4c1c682 + c79bbfd commit ac3cf3e

1 file changed

Lines changed: 148 additions & 10 deletions

File tree

drivers/hv/mshv_vtl_main.c

Lines changed: 148 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/eventfd.h>
2121
#include <linux/poll.h>
2222
#include <linux/file.h>
23+
#include <linux/pagemap.h>
2324
#include <linux/user-return-notifier.h>
2425
#include <linux/vmalloc.h>
2526
#include <asm/boot.h>
@@ -1154,6 +1155,15 @@ static int vtl_set_vp_register(struct hv_register_assoc *reg)
11541155

11551156
#define DECRYPTED_MASK (1ul << 51)
11561157

1158+
/*
1159+
* /dev/mshv_vtl_low address_space, captured on first open.
1160+
* Used by add_vtl0_mem() to zap stale 4K PTEs.
1161+
*/
1162+
static struct address_space *mshv_vtl_low_mapping;
1163+
1164+
/* Identity token tagged on every mshv_vtl pgmap; only its address matters. */
1165+
static const u8 mshv_vtl_pgmap_token;
1166+
11571167
static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg)
11581168
{
11591169
struct mshv_vtl_ram_disposition vtl0_mem;
@@ -1182,6 +1192,7 @@ static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg)
11821192
pgmap->ranges[0].end = PFN_PHYS(vtl0_mem.last_pfn) - 1;
11831193
pgmap->nr_range = 1;
11841194
pgmap->type = MEMORY_DEVICE_GENERIC;
1195+
pgmap->owner = (void *)&mshv_vtl_pgmap_token;
11851196
if (decrypted)
11861197
pgmap->flags = PGMAP_DECRYPTED;
11871198

@@ -1205,6 +1216,20 @@ static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg)
12051216
return PTR_ERR(addr);
12061217
}
12071218

1219+
/*
1220+
* Zap stale pte_special PTEs the 4K fallback installed before this
1221+
* range had a pgmap, so the next access re-faults into the folio path.
1222+
* Both encrypted (pfn) and decrypted (pfn | DECRYPTED_MASK) aliases.
1223+
*/
1224+
if (READ_ONCE(mshv_vtl_low_mapping)) {
1225+
pgoff_t start = vtl0_mem.start_pfn;
1226+
pgoff_t nr = vtl0_mem.last_pfn - vtl0_mem.start_pfn;
1227+
1228+
unmap_mapping_pages(mshv_vtl_low_mapping, start, nr, true);
1229+
unmap_mapping_pages(mshv_vtl_low_mapping,
1230+
start | DECRYPTED_MASK, nr, true);
1231+
}
1232+
12081233
/* Don't free pgmap, since it has to stick around until the memory
12091234
* is unmapped, which will never happen as there is no scenario
12101235
* where VTL0 can be released/shutdown without bringing down VTL2.
@@ -3640,6 +3665,18 @@ static struct miscdevice mshv_vtl_hvcall_dev = {
36403665
.minor = MISC_DYNAMIC_MINOR,
36413666
};
36423667

3668+
/*
3669+
* Mirror drivers/dax/device.c: once the fault path publishes folio->mapping
3670+
* to this inode's address_space, writeback-side helpers (e.g.
3671+
* folio_mark_dirty() called from bio_set_pages_dirty() after direct I/O into
3672+
* a GUP'd VTL0 buffer) will dispatch through mapping->a_ops->dirty_folio.
3673+
* The default empty_aops leaves dirty_folio NULL, so install noop_dirty_folio
3674+
* to keep that dispatch safe; nothing here participates in real writeback.
3675+
*/
3676+
static const struct address_space_operations mshv_vtl_low_aops = {
3677+
.dirty_folio = noop_dirty_folio,
3678+
};
3679+
36433680
static int mshv_vtl_low_open(struct inode *inodep, struct file *filp)
36443681
{
36453682
pid_t pid = task_pid_vnr(current);
@@ -3650,6 +3687,10 @@ static int mshv_vtl_low_open(struct inode *inodep, struct file *filp)
36503687

36513688
if (capable(CAP_SYS_ADMIN)) {
36523689
filp->private_data = inodep;
3690+
/* All opens share one inode; first one publishes the address_space. */
3691+
if (!READ_ONCE(mshv_vtl_low_mapping))
3692+
cmpxchg(&mshv_vtl_low_mapping, NULL, inodep->i_mapping);
3693+
inodep->i_mapping->a_ops = &mshv_vtl_low_aops;
36533694
} else {
36543695
pr_err("%s: VTL low open failed: CAP_SYS_ADMIN required. task group %d, uid %d",
36553696
__func__, pid, uid);
@@ -3678,26 +3719,113 @@ static bool can_fault(struct vm_fault *vmf, unsigned long size, unsigned long *p
36783719
return is_valid;
36793720
}
36803721

3722+
/*
3723+
* Resolve a user-supplied PFN to a page owned by an mshv_vtl pgmap, or NULL.
3724+
* Look up the pgmap via get_dev_pagemap() rather than page_pgmap(): the pgmap
3725+
* is published in pgmap_array before per-page state is initialized, so a
3726+
* concurrent MSHV_ADD_VTL0_MEMORY can leave folio->pgmap unset while pfn_valid
3727+
* and is_zone_device_page already return true. The owner check additionally
3728+
* rejects foreign MEMORY_DEVICE_GENERIC pgmaps (e.g. DAX).
3729+
*/
3730+
static struct page *mshv_vtl_low_resolve_page(unsigned long pfn)
3731+
{
3732+
struct dev_pagemap *pgmap;
3733+
struct page *page;
3734+
3735+
pgmap = get_dev_pagemap(pfn);
3736+
if (!pgmap)
3737+
return NULL;
3738+
page = NULL;
3739+
if (pgmap->type == MEMORY_DEVICE_GENERIC &&
3740+
pgmap->owner == &mshv_vtl_pgmap_token)
3741+
page = pfn_to_page(pfn);
3742+
/* Safe to drop here: mshv_vtl pgmaps are never released for the life of the module. */
3743+
put_dev_pagemap(pgmap);
3744+
return page;
3745+
}
3746+
3747+
/*
3748+
* Mirror dax_set_mapping(): rmap walkers locate a file-rmapped folio via
3749+
* folio->mapping/index. ZONE_DEVICE init only fills ->pgmap, so set the
3750+
* file-mapping fields here before each insert that adds file rmap.
3751+
* Idempotent: only the head folio carries mapping/index, and once set the
3752+
* fields persist for the lifetime of the (never-released) pgmap.
3753+
*/
3754+
static void mshv_vtl_low_set_mapping(struct vm_fault *vmf, struct folio *folio,
3755+
unsigned long fault_size)
3756+
{
3757+
if (folio->mapping)
3758+
return;
3759+
3760+
folio->mapping = vmf->vma->vm_file->f_mapping;
3761+
folio->index = linear_page_index(vmf->vma,
3762+
ALIGN_DOWN(vmf->address, fault_size));
3763+
}
3764+
3765+
/*
3766+
* Note on rmap/RSS accounting for huge VTL0 mappings:
3767+
* vmf_insert_folio_{pmd,pud}() takes a folio reference, adds a file rmap,
3768+
* and bumps mm RSS, but the matching teardown is skipped at zap/split time
3769+
* because vma_is_special_huge() is true (VM_MIXEDMAP) while vma_is_dax() is
3770+
* false (CONFIG_FS_DAX is not set in OHCL). The drift is theoretical for
3771+
* OpenVMM/OpenHCL: VTL0 memory is mapped once per partition and held for
3772+
* its lifetime - there is no map/unmap cycling, no partial munmap, and the
3773+
* driver is not unloaded. Stale refs land on ZONE_DEVICE folios whose
3774+
* pgmap is intentionally never released, no real bytes are leaked, and the
3775+
* mm's inflated RSS is discarded with the mm at process exit.
3776+
*/
36813777
static vm_fault_t mshv_vtl_low_huge_fault(struct vm_fault *vmf, unsigned int order)
36823778
{
36833779
unsigned long pfn = vmf->pgoff & ~DECRYPTED_MASK;
3684-
vm_fault_t ret = VM_FAULT_FALLBACK;
3780+
bool write = vmf->flags & FAULT_FLAG_WRITE;
3781+
struct page *page;
3782+
struct folio *folio;
36853783

36863784
switch (order) {
36873785
case 0:
3688-
/* __pfn_to_pfn_t ? */
3689-
return vmf_insert_mixed(vmf->vma, vmf->address, pfn);
3786+
page = mshv_vtl_low_resolve_page(pfn);
3787+
if (!page) {
3788+
/*
3789+
* No pgmap yet: install pte_special so CPU access succeeds.
3790+
* The unmap_mapping_range() in add_vtl0_mem() invalidates this
3791+
* PTE on registration so a later GUP-bound access re-faults
3792+
* into the folio path below.
3793+
*/
3794+
return vmf_insert_mixed(vmf->vma, vmf->address, pfn);
3795+
}
3796+
/* Inserter operates on the compound-head folio per PTE; refcounts stay balanced. */
3797+
mshv_vtl_low_set_mapping(vmf, page_folio(page), PAGE_SIZE);
3798+
return vmf_insert_page_mkwrite(vmf, page, write);
36903799

36913800
case PMD_ORDER:
3692-
if (can_fault(vmf, PMD_SIZE, &pfn))
3693-
ret = vmf_insert_pfn_pmd(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE);
3694-
return ret;
3801+
if (!can_fault(vmf, PMD_SIZE, &pfn))
3802+
return VM_FAULT_FALLBACK;
3803+
page = mshv_vtl_low_resolve_page(pfn);
3804+
if (!page)
3805+
return VM_FAULT_FALLBACK;
3806+
folio = page_folio(page);
3807+
/*
3808+
* vmf_insert_folio_pmd() needs an exact-order folio; let core
3809+
* retry smaller on mismatch.
3810+
*/
3811+
if (folio_order(folio) != PMD_ORDER)
3812+
return VM_FAULT_FALLBACK;
3813+
mshv_vtl_low_set_mapping(vmf, folio, PMD_SIZE);
3814+
return vmf_insert_folio_pmd(vmf, folio, write);
36953815

36963816
#if defined(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD)
36973817
case PUD_ORDER:
3698-
if (can_fault(vmf, PUD_SIZE, &pfn))
3699-
ret = vmf_insert_pfn_pud(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE);
3700-
return ret;
3818+
if (!can_fault(vmf, PUD_SIZE, &pfn))
3819+
return VM_FAULT_FALLBACK;
3820+
page = mshv_vtl_low_resolve_page(pfn);
3821+
if (!page)
3822+
return VM_FAULT_FALLBACK;
3823+
folio = page_folio(page);
3824+
/* Same exact-order requirement as the PMD case above. */
3825+
if (folio_order(folio) != PUD_ORDER)
3826+
return VM_FAULT_FALLBACK;
3827+
mshv_vtl_low_set_mapping(vmf, folio, PUD_SIZE);
3828+
return vmf_insert_folio_pud(vmf, folio, write);
37013829
#endif
37023830

37033831
default:
@@ -3717,8 +3845,18 @@ static const struct vm_operations_struct mshv_vtl_low_vm_ops = {
37173845

37183846
static int mshv_vtl_low_mmap(struct file *filp, struct vm_area_struct *vma)
37193847
{
3848+
/*
3849+
* Reject MAP_PRIVATE: the fault path installs PTEs via
3850+
* vmf_insert_{page,folio}_{,pmd,pud}() and bypasses core-mm COW, so
3851+
* MAP_PRIVATE writes would land on the underlying VTL0/device page
3852+
* instead of a private copy. Mirror device-dax (drivers/dax/device.c).
3853+
*/
3854+
if ((vma->vm_flags & VM_MAYSHARE) != VM_MAYSHARE)
3855+
return -EINVAL;
3856+
37203857
vma->vm_ops = &mshv_vtl_low_vm_ops;
3721-
vm_flags_set(vma, VM_HUGEPAGE | VM_MIXEDMAP);
3858+
/* VM_MIXEDMAP for pte_special 4K fallback; VM_DONTEXPAND pins size to pgmap. */
3859+
vm_flags_set(vma, VM_HUGEPAGE | VM_MIXEDMAP | VM_DONTEXPAND);
37223860

37233861
if (vma->vm_pgoff & DECRYPTED_MASK)
37243862
vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);

0 commit comments

Comments
 (0)