mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 07:33:19 +02:00
mshv: Align huge page stride with guest mapping
Ensure that a stride larger than 1 (huge page) is only used when page
points to a head of a huge page and both the guest frame number (gfn) and
the operation size (page_count) are aligned to the huge page size
(PTRS_PER_PMD). This matches the hypervisor requirement that map/unmap
operations for huge pages must be guest-aligned and cover a full huge page.
Add mshv_chunk_stride() to encapsulate this alignment and page-order
validation, and plumb a huge_page flag into the region chunk handlers.
This prevents issuing large-page map/unmap/share operations that the
hypervisor would reject due to misaligned guest mappings.
Fixes: abceb4297b ("mshv: Fix huge page handling in memory region traversal")
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
Reviewed-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
This commit is contained in:
parent
49f49d47af
commit
259add0d98
|
|
@ -19,6 +19,41 @@
|
|||
|
||||
#define MSHV_MAP_FAULT_IN_PAGES PTRS_PER_PMD
|
||||
|
||||
/**
|
||||
* mshv_chunk_stride - Compute stride for mapping guest memory
|
||||
* @page : The page to check for huge page backing
|
||||
* @gfn : Guest frame number for the mapping
|
||||
* @page_count: Total number of pages in the mapping
|
||||
*
|
||||
* Determines the appropriate stride (in pages) for mapping guest memory.
|
||||
* 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.
|
||||
*/
|
||||
static int mshv_chunk_stride(struct page *page,
|
||||
u64 gfn, u64 page_count)
|
||||
{
|
||||
unsigned int page_order;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
if (!PageCompound(page) || !PageHead(page) ||
|
||||
!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)
|
||||
return -EINVAL;
|
||||
|
||||
return 1 << page_order;
|
||||
}
|
||||
|
||||
/**
|
||||
* mshv_region_process_chunk - Processes a contiguous chunk of memory pages
|
||||
* in a region.
|
||||
|
|
@ -45,25 +80,23 @@ static long mshv_region_process_chunk(struct mshv_mem_region *region,
|
|||
int (*handler)(struct mshv_mem_region *region,
|
||||
u32 flags,
|
||||
u64 page_offset,
|
||||
u64 page_count))
|
||||
u64 page_count,
|
||||
bool huge_page))
|
||||
{
|
||||
u64 count, stride;
|
||||
unsigned int page_order;
|
||||
u64 gfn = region->start_gfn + page_offset;
|
||||
u64 count;
|
||||
struct page *page;
|
||||
int ret;
|
||||
int stride, ret;
|
||||
|
||||
page = region->pages[page_offset];
|
||||
if (!page)
|
||||
return -EINVAL;
|
||||
|
||||
page_order = folio_order(page_folio(page));
|
||||
/* The hypervisor only supports 4K and 2M page sizes */
|
||||
if (page_order && page_order != PMD_ORDER)
|
||||
return -EINVAL;
|
||||
stride = mshv_chunk_stride(page, gfn, page_count);
|
||||
if (stride < 0)
|
||||
return stride;
|
||||
|
||||
stride = 1 << page_order;
|
||||
|
||||
/* Start at stride since the first page is validated */
|
||||
/* Start at stride since the first stride is validated */
|
||||
for (count = stride; count < page_count; count += stride) {
|
||||
page = region->pages[page_offset + count];
|
||||
|
||||
|
|
@ -71,12 +104,13 @@ static long mshv_region_process_chunk(struct mshv_mem_region *region,
|
|||
if (!page)
|
||||
break;
|
||||
|
||||
/* Break if page size changes */
|
||||
if (page_order != folio_order(page_folio(page)))
|
||||
/* Break if stride size changes */
|
||||
if (stride != mshv_chunk_stride(page, gfn + count,
|
||||
page_count - count))
|
||||
break;
|
||||
}
|
||||
|
||||
ret = handler(region, flags, page_offset, count);
|
||||
ret = handler(region, flags, page_offset, count, stride > 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -108,7 +142,8 @@ static int mshv_region_process_range(struct mshv_mem_region *region,
|
|||
int (*handler)(struct mshv_mem_region *region,
|
||||
u32 flags,
|
||||
u64 page_offset,
|
||||
u64 page_count))
|
||||
u64 page_count,
|
||||
bool huge_page))
|
||||
{
|
||||
long ret;
|
||||
|
||||
|
|
@ -162,11 +197,10 @@ struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,
|
|||
|
||||
static int mshv_region_chunk_share(struct mshv_mem_region *region,
|
||||
u32 flags,
|
||||
u64 page_offset, u64 page_count)
|
||||
u64 page_offset, u64 page_count,
|
||||
bool huge_page)
|
||||
{
|
||||
struct page *page = region->pages[page_offset];
|
||||
|
||||
if (PageHuge(page) || PageTransCompound(page))
|
||||
if (huge_page)
|
||||
flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
|
||||
|
||||
return hv_call_modify_spa_host_access(region->partition->pt_id,
|
||||
|
|
@ -188,11 +222,10 @@ int mshv_region_share(struct mshv_mem_region *region)
|
|||
|
||||
static int mshv_region_chunk_unshare(struct mshv_mem_region *region,
|
||||
u32 flags,
|
||||
u64 page_offset, u64 page_count)
|
||||
u64 page_offset, u64 page_count,
|
||||
bool huge_page)
|
||||
{
|
||||
struct page *page = region->pages[page_offset];
|
||||
|
||||
if (PageHuge(page) || PageTransCompound(page))
|
||||
if (huge_page)
|
||||
flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
|
||||
|
||||
return hv_call_modify_spa_host_access(region->partition->pt_id,
|
||||
|
|
@ -212,11 +245,10 @@ int mshv_region_unshare(struct mshv_mem_region *region)
|
|||
|
||||
static int mshv_region_chunk_remap(struct mshv_mem_region *region,
|
||||
u32 flags,
|
||||
u64 page_offset, u64 page_count)
|
||||
u64 page_offset, u64 page_count,
|
||||
bool huge_page)
|
||||
{
|
||||
struct page *page = region->pages[page_offset];
|
||||
|
||||
if (PageHuge(page) || PageTransCompound(page))
|
||||
if (huge_page)
|
||||
flags |= HV_MAP_GPA_LARGE_PAGE;
|
||||
|
||||
return hv_call_map_gpa_pages(region->partition->pt_id,
|
||||
|
|
@ -295,11 +327,10 @@ int mshv_region_pin(struct mshv_mem_region *region)
|
|||
|
||||
static int mshv_region_chunk_unmap(struct mshv_mem_region *region,
|
||||
u32 flags,
|
||||
u64 page_offset, u64 page_count)
|
||||
u64 page_offset, u64 page_count,
|
||||
bool huge_page)
|
||||
{
|
||||
struct page *page = region->pages[page_offset];
|
||||
|
||||
if (PageHuge(page) || PageTransCompound(page))
|
||||
if (huge_page)
|
||||
flags |= HV_UNMAP_GPA_LARGE_PAGE;
|
||||
|
||||
return hv_call_unmap_gpa_pages(region->partition->pt_id,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user