Consolidate memory region processing to handle both valid and invalid PFNs uniformly. This eliminates code duplication across remap, unmap, share, and unshare operations by using a common range processing interface.
Holes are now remapped with no-access permissions to enable hypervisor dirty page tracking for precopy live migration. This refactoring is a precursor to an upcoming change that will map present pages in movable regions upon region creation, requiring consistent handling of both mapped and unmapped ranges. Signed-off-by: Stanislav Kinsburskii <[email protected]> --- drivers/hv/mshv_regions.c | 108 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 95 insertions(+), 13 deletions(-) diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c index b1a707d16c07..ed9c55841140 100644 --- a/drivers/hv/mshv_regions.c +++ b/drivers/hv/mshv_regions.c @@ -119,6 +119,57 @@ static long mshv_region_process_pfns(struct mshv_mem_region *region, return count; } +/** + * mshv_region_process_hole - Handle a hole (invalid PFNs) in a memory + * region + * @region : Memory region containing the hole + * @flags : Flags to pass to the handler function + * @pfn_offset: Starting PFN offset within the region + * @pfn_count : Number of PFNs in the hole + * @handler : Callback function to invoke for the hole + * + * Invokes the handler function for a contiguous hole with the specified + * parameters. + * + * Return: Number of PFNs handled, or negative error code. + */ +static long mshv_region_process_hole(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)) +{ + long ret; + + ret = handler(region, flags, pfn_offset, pfn_count, 0); + if (ret) + return ret; + + return pfn_count; +} + +static long mshv_region_process_chunk(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)) +{ + if (pfn_valid(region->mreg_pfns[pfn_offset])) + return mshv_region_process_pfns(region, flags, + pfn_offset, pfn_count, + handler); + else + return mshv_region_process_hole(region, flags, + pfn_offset, pfn_count, + handler); +} + /** * mshv_region_process_range - Processes a range of PFNs in a region. * @region : Pointer to the memory region structure. @@ -146,33 +197,47 @@ static int mshv_region_process_range(struct mshv_mem_region *region, u64 pfn_count, bool huge_page)) { - u64 pfn_end; + u64 start, end; long ret; - if (check_add_overflow(pfn_offset, pfn_count, &pfn_end)) + if (!pfn_count) + return 0; + + if (check_add_overflow(pfn_offset, pfn_count, &end)) return -EOVERFLOW; - if (pfn_end > region->nr_pfns) + if (end > region->nr_pfns) return -EINVAL; - while (pfn_count) { - /* Skip non-present pages */ - if (!pfn_valid(region->mreg_pfns[pfn_offset])) { - pfn_offset++; - pfn_count--; + start = pfn_offset; + end = pfn_offset + 1; + + while (end < pfn_offset + pfn_count) { + /* + * Accumulate contiguous pfns with the same validity + * (valid or not). + */ + if (pfn_valid(region->mreg_pfns[start]) == + pfn_valid(region->mreg_pfns[end])) { + end++; continue; } - ret = mshv_region_process_pfns(region, flags, - pfn_offset, pfn_count, - handler); + ret = mshv_region_process_chunk(region, flags, + start, end - start, + handler); if (ret < 0) return ret; - pfn_offset += ret; - pfn_count -= ret; + start += ret; } + ret = mshv_region_process_chunk(region, flags, + start, end - start, + handler); + if (ret < 0) + return ret; + return 0; } @@ -208,6 +273,9 @@ static int mshv_region_chunk_share(struct mshv_mem_region *region, u64 pfn_offset, u64 pfn_count, bool huge_page) { + if (!pfn_valid(region->mreg_pfns[pfn_offset])) + return -EINVAL; + if (huge_page) flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE; @@ -233,6 +301,9 @@ static int mshv_region_chunk_unshare(struct mshv_mem_region *region, u64 pfn_offset, u64 pfn_count, bool huge_page) { + if (!pfn_valid(region->mreg_pfns[pfn_offset])) + return -EINVAL; + if (huge_page) flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE; @@ -256,6 +327,14 @@ static int mshv_region_chunk_remap(struct mshv_mem_region *region, u64 pfn_offset, u64 pfn_count, bool huge_page) { + /* + * Remap missing pages with no access to let the + * hypervisor track dirty pages, enabling precopy live + * migration. + */ + if (!pfn_valid(region->mreg_pfns[pfn_offset])) + flags = HV_MAP_GPA_NO_ACCESS; + if (huge_page) flags |= HV_MAP_GPA_LARGE_PAGE; @@ -357,6 +436,9 @@ static int mshv_region_chunk_unmap(struct mshv_mem_region *region, u64 pfn_offset, u64 pfn_count, bool huge_page) { + if (!pfn_valid(region->mreg_pfns[pfn_offset])) + return 0; + if (huge_page) flags |= HV_UNMAP_GPA_LARGE_PAGE;

