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.
- Always emit a 2M (PMD_ORDER) stride for the huge-page case. The
hypercall has no 1G stride, so 1G folios are processed as a
sequence of 2M chunks. Folios whose order is neither PMD_ORDER nor
PUD_ORDER (e.g. mTHP) fall back to single-page stride; mapping
them as 2M would fail in the hypervisor anyway.
Assisted-by: Copilot-CLI:claude-opus-4.7
Signed-off-by: Anirudh Rayabharam (Microsoft) <[email protected]>
---
Changes in v3:
- Fixed various corner cases reported by Sashiko.
- Link to v2:
https://lore.kernel.org/r/[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 | 32 +++++++++++++++-----------------
1 file changed, 15 insertions(+), 17 deletions(-)
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index fdffd4f002f6..1756b733968c 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -29,29 +29,28 @@
* Uses huge page stride if the backing page is huge and the guest mapping
* is properly aligned; otherwise falls back to single page stride.
*
- * Return: Stride in pages, or -EINVAL if page order is unsupported.
+ * Return: Stride in pages.
*/
-static int mshv_chunk_stride(struct page *page,
- u64 gfn, u64 page_count)
+static unsigned int mshv_chunk_stride(struct page *page, u64 gfn,
+ u64 page_count)
{
- unsigned int page_order;
+ unsigned int page_order = folio_order(page_folio(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 2M-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))
+ !IS_ALIGNED(page_count, PTRS_PER_PMD) ||
+ (page_order != PMD_ORDER && page_order != PUD_ORDER))
return 1;
- page_order = folio_order(page_folio(page));
- /* The hypervisor only supports 2M huge page */
- if (page_order != PMD_ORDER)
- return -EINVAL;
-
- return 1 << page_order;
+ /* Use 2M stride always i.e. process 1G folios as 2M chunks */
+ return 1 << PMD_ORDER;
}
/**
@@ -86,15 +85,14 @@ static long mshv_region_process_chunk(struct
mshv_mem_region *region,
u64 gfn = region->start_gfn + page_offset;
u64 count;
struct page *page;
- int stride, ret;
+ unsigned int stride;
+ int ret;
page = region->mreg_pages[page_offset];
if (!page)
return -EINVAL;
stride = mshv_chunk_stride(page, gfn, page_count);
- if (stride < 0)
- return stride;
/* Start at stride since the first stride is validated */
for (count = stride; count < page_count; count += stride) {
---
base-commit: cd9f2e7d6e5b1837ef40b96e300fa28b73ab5a77
change-id: 20260416-huge_1g-e44461393c8f
Best regards,
--
Anirudh Rayabharam (Microsoft) <[email protected]>