Skip to content

Commit 775741a

Browse files
author
Naman Jain
committed
Drivers: hv: mshv_vtl: use folio-aware inserters for huge VTL0 mappings
Since v6.15 (aed877c, d3f7922), GUP no longer takes a pgmap reference for ZONE_DEVICE pages and walks huge entries through the unified folio path. With vmf_insert_pfn_{pmd,pud}() the mapping holds no folio reference, so a zap racing with pin_user_pages_fast() can briefly drop the folio refcount to 0 and trigger a WARN in try_grab_folio() with the I/O failing as -ENOMEM. Switch the PMD/PUD fault paths to vmf_insert_folio_{pmd,pud}(), mirroring drivers/dax/device.c. Each map takes folio_get(); the matching folio_put() in zap keeps the refcount above 0. Gate the huge inserters on pfn_valid() + ZONE_DEVICE + MEMORY_DEVICE_GENERIC via mshv_vtl_low_resolve_page(); fall back to VM_FAULT_FALLBACK when the folio order does not match PMD_ORDER/PUD_ORDER or the PFN is not yet pgmap-backed, so the core can retry at smaller order. Add VM_DONTEXPAND to the VMA to block mremap() growth past the pgmap. Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
1 parent 4c1c682 commit 775741a

1 file changed

Lines changed: 113 additions & 9 deletions

File tree

drivers/hv/mshv_vtl_main.c

Lines changed: 113 additions & 9 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,9 @@ static int vtl_set_vp_register(struct hv_register_assoc *reg)
11541155

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

1158+
/* Identity token tagged on every mshv_vtl pgmap; only its address matters. */
1159+
static const u8 mshv_vtl_pgmap_token;
1160+
11571161
static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg)
11581162
{
11591163
struct mshv_vtl_ram_disposition vtl0_mem;
@@ -1182,6 +1186,7 @@ static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg)
11821186
pgmap->ranges[0].end = PFN_PHYS(vtl0_mem.last_pfn) - 1;
11831187
pgmap->nr_range = 1;
11841188
pgmap->type = MEMORY_DEVICE_GENERIC;
1189+
pgmap->owner = (void *)&mshv_vtl_pgmap_token;
11851190
if (decrypted)
11861191
pgmap->flags = PGMAP_DECRYPTED;
11871192

@@ -3640,6 +3645,18 @@ static struct miscdevice mshv_vtl_hvcall_dev = {
36403645
.minor = MISC_DYNAMIC_MINOR,
36413646
};
36423647

3648+
/*
3649+
* Mirror drivers/dax/device.c: once the fault path publishes folio->mapping
3650+
* to this inode's address_space, writeback-side helpers (e.g.
3651+
* folio_mark_dirty() called from bio_set_pages_dirty() after direct I/O into
3652+
* a GUP'd VTL0 buffer) will dispatch through mapping->a_ops->dirty_folio.
3653+
* The default empty_aops leaves dirty_folio NULL, so install noop_dirty_folio
3654+
* to keep that dispatch safe; nothing here participates in real writeback.
3655+
*/
3656+
static const struct address_space_operations mshv_vtl_low_aops = {
3657+
.dirty_folio = noop_dirty_folio,
3658+
};
3659+
36433660
static int mshv_vtl_low_open(struct inode *inodep, struct file *filp)
36443661
{
36453662
pid_t pid = task_pid_vnr(current);
@@ -3650,6 +3667,7 @@ static int mshv_vtl_low_open(struct inode *inodep, struct file *filp)
36503667

36513668
if (capable(CAP_SYS_ADMIN)) {
36523669
filp->private_data = inodep;
3670+
inodep->i_mapping->a_ops = &mshv_vtl_low_aops;
36533671
} else {
36543672
pr_err("%s: VTL low open failed: CAP_SYS_ADMIN required. task group %d, uid %d",
36553673
__func__, pid, uid);
@@ -3678,26 +3696,102 @@ static bool can_fault(struct vm_fault *vmf, unsigned long size, unsigned long *p
36783696
return is_valid;
36793697
}
36803698

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

36863761
switch (order) {
36873762
case 0:
3688-
/* __pfn_to_pfn_t ? */
3763+
/* pte_special path; GUP bails before try_grab_folio() so the WARN cannot fire here. */
36893764
return vmf_insert_mixed(vmf->vma, vmf->address, pfn);
36903765

36913766
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;
3767+
if (!can_fault(vmf, PMD_SIZE, &pfn))
3768+
return VM_FAULT_FALLBACK;
3769+
page = mshv_vtl_low_resolve_page(pfn);
3770+
if (!page)
3771+
return VM_FAULT_FALLBACK;
3772+
folio = page_folio(page);
3773+
/*
3774+
* vmf_insert_folio_pmd() needs an exact-order folio; let core
3775+
* retry smaller on mismatch.
3776+
*/
3777+
if (folio_order(folio) != PMD_ORDER)
3778+
return VM_FAULT_FALLBACK;
3779+
mshv_vtl_low_set_mapping(vmf, folio, PMD_SIZE);
3780+
return vmf_insert_folio_pmd(vmf, folio, write);
36953781

36963782
#if defined(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD)
36973783
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;
3784+
if (!can_fault(vmf, PUD_SIZE, &pfn))
3785+
return VM_FAULT_FALLBACK;
3786+
page = mshv_vtl_low_resolve_page(pfn);
3787+
if (!page)
3788+
return VM_FAULT_FALLBACK;
3789+
folio = page_folio(page);
3790+
/* Same exact-order requirement as the PMD case above. */
3791+
if (folio_order(folio) != PUD_ORDER)
3792+
return VM_FAULT_FALLBACK;
3793+
mshv_vtl_low_set_mapping(vmf, folio, PUD_SIZE);
3794+
return vmf_insert_folio_pud(vmf, folio, write);
37013795
#endif
37023796

37033797
default:
@@ -3717,8 +3811,18 @@ static const struct vm_operations_struct mshv_vtl_low_vm_ops = {
37173811

37183812
static int mshv_vtl_low_mmap(struct file *filp, struct vm_area_struct *vma)
37193813
{
3814+
/*
3815+
* Reject MAP_PRIVATE: the fault path installs PTEs via
3816+
* vmf_insert_{page,folio}_{,pmd,pud}() and bypasses core-mm COW, so
3817+
* MAP_PRIVATE writes would land on the underlying VTL0/device page
3818+
* instead of a private copy. Mirror device-dax (drivers/dax/device.c).
3819+
*/
3820+
if ((vma->vm_flags & VM_MAYSHARE) != VM_MAYSHARE)
3821+
return -EINVAL;
3822+
37203823
vma->vm_ops = &mshv_vtl_low_vm_ops;
3721-
vm_flags_set(vma, VM_HUGEPAGE | VM_MIXEDMAP);
3824+
/* VM_MIXEDMAP for the 4K pte_special path; VM_DONTEXPAND pins size to the pgmap. */
3825+
vm_flags_set(vma, VM_HUGEPAGE | VM_MIXEDMAP | VM_DONTEXPAND);
37223826

37233827
if (vma->vm_pgoff & DECRYPTED_MASK)
37243828
vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);

0 commit comments

Comments
 (0)