[PATCH v3 04/11] mshv: Refactor region segmentation into a dedicated helper
From: Stanislav Kinsburskii
Date: Wed May 13 2026 - 14:59:14 EST
mshv_region_process_pfns() conflated three concerns: validating the first
PFN of a chunk, locating the longest contiguous run of same-stride PFNs
starting from there, and dispatching the chunk to the handler. The
locate-and-dispatch interleaving made the partial-consume case (4K-to-2M
stride transition inside a same-validity run) emergent rather than
explicit, and required process_range to handle a return value that was
simultaneously a count and an error code.
Split the locate step out into mshv_region_chunk_size(). The new helper
takes a starting offset and an upper bound, returns the length of the
same-stride run, and reports whether that run is huge-page-backed via an
out-parameter. mshv_region_process_pfns() goes away;
mshv_region_process_range() now drives the loop directly, calling
chunk_size() for the next segment length and dispatching the handler with
the precomputed huge_page hint.
mshv_chunk_stride() additionally takes a PFN instead of a struct page * and
validates it internally, so each call site no longer needs its own
mshv_pfn_valid() check before pfn_to_page().
No functional change; the per-handler dispatch shape, segmentation
boundaries, and lock context are all preserved.
Signed-off-by: Stanislav Kinsburskii <skinsburskii@xxxxxxxxxxxxxxxxxxx>
---
drivers/hv/mshv_regions.c | 103 ++++++++++++++++++++++-----------------------
1 file changed, 50 insertions(+), 53 deletions(-)
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index 77fc94733cb20..090c4052f0f4d 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -41,7 +41,7 @@ void mshv_region_init_pfns(struct mshv_mem_region *region)
/**
* mshv_chunk_stride - Compute stride for mapping guest memory
- * @page : The page to check for huge page backing
+ * @pfn : The PFN to check for huge page backing
* @gfn : Guest frame number for the mapping
* @pfn_count: Total number of pages in the mapping
*
@@ -51,11 +51,16 @@ void mshv_region_init_pfns(struct mshv_mem_region *region)
*
* Return: Stride in pages, or -EINVAL if page order is unsupported.
*/
-static int mshv_chunk_stride(struct page *page,
- u64 gfn, u64 pfn_count)
+static int mshv_chunk_stride(unsigned long pfn, u64 gfn, u64 pfn_count)
{
+ struct page *page;
unsigned int page_order;
+ if (!mshv_pfn_valid(pfn))
+ return -EINVAL;
+
+ page = pfn_to_page(pfn);
+
/*
* Use single page stride by default. For huge page stride, the
* page must be compound and point to the head of the compound
@@ -76,65 +81,51 @@ static int mshv_chunk_stride(struct page *page,
}
/**
- * mshv_region_process_chunk - Processes a contiguous chunk of memory pages
- * in a region.
- * @region : Pointer to the memory region structure.
- * @flags : Flags to pass to the handler.
- * @pfn_offset: Offset into the region's PFNs array to start processing.
- * @pfn_count : Number of PFNs to process.
- * @handler : Callback function to handle the chunk.
+ * mshv_region_chunk_size - Length of the next same-stride PFN run.
+ * @region : Memory region whose PFN array is being walked.
+ * @pfn_offset: Offset into region->mreg_pfns at which to start; the
+ * PFN at this offset must be valid.
+ * @pfn_count : Upper bound on the run length (not necessarily the
+ * region's total length; typically the residual passed
+ * from mshv_region_process_range()).
+ * @huge_page : Out-parameter set to true if the run is backed by
+ * PMD-order folios and may be dispatched as 2 MiB
+ * chunks; false for 4 KiB-stride dispatch.
*
- * This function scans the region's PFNs starting from @pfn_offset,
- * checking for contiguous valid PFNs backed by pages of the same size
- * (normal or huge). It invokes @handler for the chunk of contiguous valid
- * PFNs found. Returns the number of PFNs handled, or a negative error code
- * if the first PFN is invalid or the handler fails.
+ * Walks the PFN array starting at @pfn_offset and returns the length
+ * of the longest contiguous run that shares the stride classification
+ * (4 KiB vs 2 MiB) of the first PFN. An invalid PFN inside the run
+ * terminates it. The run is bounded above by @pfn_count.
*
- * Note: The @handler callback must be able to handle valid PFNs backed by
- * both normal and huge pages.
+ * The caller may then dispatch [pfn_offset, pfn_offset + return) to a
+ * handler with @huge_page indicating which stride applies. After the
+ * dispatch the caller advances by the returned length and re-invokes
+ * this function for the next run.
*
- * Return: Number of pages handled, or negative error code.
+ * Return: Length of the run in PFNs, or a negative errno from
+ * mshv_chunk_stride() if the starting PFN is invalid or its
+ * backing folio order is unsupported.
*/
-static long mshv_region_process_pfns(struct mshv_mem_region *region,
- u32 flags,
- u64 pfn_offset, u64 pfn_count,
- int (*handler)(struct mshv_mem_region *region,
- u32 flags,
- u64 pfn_offset,
- u64 pfn_count,
- bool huge_page))
+static long mshv_region_chunk_size(struct mshv_mem_region *region,
+ u64 pfn_offset, u64 pfn_count,
+ bool *huge_page)
{
+ unsigned long *pfns = region->mreg_pfns + pfn_offset;
u64 gfn = region->start_gfn + pfn_offset;
- u64 count;
- unsigned long pfn;
- int stride, ret;
+ u64 count = 0, stride;
- pfn = region->mreg_pfns[pfn_offset];
- if (!mshv_pfn_valid(pfn))
- return -EINVAL;
-
- stride = mshv_chunk_stride(pfn_to_page(pfn), gfn, pfn_count);
+ stride = mshv_chunk_stride(pfns[0], gfn, pfn_count);
if (stride < 0)
return stride;
- /* Start at stride since the first stride is validated */
- for (count = stride; count < pfn_count ; count += stride) {
- pfn = region->mreg_pfns[pfn_offset + count];
-
- /* Break if current pfn is invalid */
- if (!mshv_pfn_valid(pfn))
- break;
-
- /* Break if stride size changes */
- if (stride != mshv_chunk_stride(pfn_to_page(pfn),
+ for (count = stride; count < pfn_count; count += stride) {
+ if (stride != mshv_chunk_stride(pfns[count],
gfn + count,
pfn_count - count))
break;
}
- ret = handler(region, flags, pfn_offset, count, stride > 1);
- if (ret)
- return ret;
+ *huge_page = stride > 1;
return count;
}
@@ -150,7 +141,7 @@ static long mshv_region_process_pfns(struct mshv_mem_region *region,
*
* Iterates over the specified range of PFNs in @region, skipping
* invalid PFNs. For each contiguous chunk of valid PFNS, invokes
- * @handler via mshv_region_process_pfns.
+ * @handler.
*
* Note: The @handler callback must be able to handle PFNs backed by both
* normal and huge pages.
@@ -176,6 +167,9 @@ static int mshv_region_process_range(struct mshv_mem_region *region,
return -EINVAL;
while (pfn_count) {
+ bool huge_page;
+ long count;
+
/* Skip non-present pages */
if (!mshv_pfn_valid(region->mreg_pfns[pfn_offset])) {
pfn_offset++;
@@ -183,14 +177,17 @@ static int mshv_region_process_range(struct mshv_mem_region *region,
continue;
}
- ret = mshv_region_process_pfns(region, flags,
- pfn_offset, pfn_count,
- handler);
+ count = mshv_region_chunk_size(region, pfn_offset, pfn_count,
+ &huge_page);
+ if (count < 0)
+ return count;
+
+ ret = handler(region, flags, pfn_offset, count, huge_page);
if (ret < 0)
return ret;
- pfn_offset += ret;
- pfn_count -= ret;
+ pfn_offset += count;
+ pfn_count -= count;
}
return 0;