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;
 



Reply via email to