The hypervisor's map GPA hypercall coalesces contiguous 2M-aligned chunks
into 1G mappings when alignment permits, so the driver can support 1G
hugepages by feeding them in as 2M chunks. Note that this is the only
way to make 1G mappings. There is no way to directly map a 1G hugepage
using the hypercall.

Update mshv_chunk_stride() to:

  - Accept 2M-aligned tail pages of a larger folio. The previous
    PageHead() check rejected every page after the head of a 1G hugepage
    and fell back to 4K mappings for the remaining 1022 MB. Replace it
    with a PFN alignment check so any 2M-aligned page of a sufficiently
    large folio is acceptable.

  - Allow only PMD_ORDER and PUD_ORDER folios. Other compound orders
    (e.g. mTHP) remain unsupported and are rejected with -EINVAL plus a
    pr_err(); silently mapping them with HV_MAP_GPA_LARGE_PAGE would
    fail anyway.

Cap the stride returned by mshv_chunk_stride() against page_count in
mshv_region_process_chunk() to avoid overflow situations. For example,
The natural stride of a 1G folio (1 << PUD_ORDER = 262144 pages) exceeds
the per-fault batch (MSHV_MAP_FAULT_IN_PAGES = 512), which would otherwise
cause an out-of-bounds read past mreg_pages.

Assisted-by: Copilot-CLI:claude-opus-4.7
Signed-off-by: Anirudh Rayabharam (Microsoft) <[email protected]>
---
Changes in v2:
- Handled the case where we can have 2M aligned pages in the middle of a
  1G page
- Brought back the page order check but expanded it to include 1G
- Clamp stride to requested page count in mshv_region_process_chunk
- Link to v1: 
https://lore.kernel.org/r/[email protected]
---
 drivers/hv/mshv_regions.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index fdffd4f002f6..8a9d7fb558c4 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -29,6 +29,8 @@
  * Uses huge page stride if the backing page is huge and the guest mapping
  * is properly aligned; otherwise falls back to single page stride.
  *
+ * Only PMD_ORDER (2 MB) and PUD_ORDER (1 GB) folios are supported.
+ *
  * Return: Stride in pages, or -EINVAL if page order is unsupported.
  */
 static int mshv_chunk_stride(struct page *page,
@@ -38,18 +40,21 @@ static int mshv_chunk_stride(struct page *page,
 
        /*
         * Use single page stride by default. For huge page stride, the
-        * page must be compound and point to the head of the compound
-        * page, and both gfn and page_count must be huge-page aligned.
+        * page must be compound, the page's PFN must itself be 2 M-aligned
+        * (so that a 2M-aligned tail page of a larger folio is acceptable),
+        * and both gfn and page_count must be huge-page aligned.
         */
-       if (!PageCompound(page) || !PageHead(page) ||
+       if (!PageCompound(page) ||
+           !IS_ALIGNED(page_to_pfn(page), PTRS_PER_PMD) ||
            !IS_ALIGNED(gfn, PTRS_PER_PMD) ||
            !IS_ALIGNED(page_count, PTRS_PER_PMD))
                return 1;
 
        page_order = folio_order(page_folio(page));
-       /* The hypervisor only supports 2M huge page */
-       if (page_order != PMD_ORDER)
+       if (page_order != PMD_ORDER && page_order != PUD_ORDER) {
+               pr_err("mshv: unsupported folio order %u\n", page_order);
                return -EINVAL;
+       }
 
        return 1 << page_order;
 }
@@ -96,6 +101,8 @@ static long mshv_region_process_chunk(struct mshv_mem_region 
*region,
        if (stride < 0)
                return stride;
 
+       stride = min_t(u64, stride, page_count);
+
        /* Start at stride since the first stride is validated */
        for (count = stride; count < page_count; count += stride) {
                page = region->mreg_pages[page_offset + count];

---
base-commit: cd9f2e7d6e5b1837ef40b96e300fa28b73ab5a77
change-id: 20260416-huge_1g-e44461393c8f

Best regards,
-- 
Anirudh Rayabharam (Microsoft) <[email protected]>


Reply via email to