]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
mshv: Introduce new hypercall to map stats page for L1VH partitions
authorJinank Jain <jinankjain@linux.microsoft.com>
Fri, 10 Oct 2025 21:55:51 +0000 (14:55 -0700)
committerWei Liu <wei.liu@kernel.org>
Sat, 15 Nov 2025 06:18:16 +0000 (06:18 +0000)
Introduce HVCALL_MAP_STATS_PAGE2 which provides a map location (GPFN)
to map the stats to. This hypercall is required for L1VH partitions,
depending on the hypervisor version. This uses the same check as the
state page map location; mshv_use_overlay_gpfn().

Add mshv_map_vp_state_page() helpers to use this new hypercall or the
old one depending on availability.

For unmapping, the original HVCALL_UNMAP_STATS_PAGE works for both
cases.

Signed-off-by: Jinank Jain <jinankjain@linux.microsoft.com>
Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
Reviewed-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
Signed-off-by: Wei Liu <wei.liu@kernel.org>
drivers/hv/mshv_root.h
drivers/hv/mshv_root_hv_call.c
drivers/hv/mshv_root_main.c
include/hyperv/hvgdk_mini.h
include/hyperv/hvhdk_mini.h

index 5b57d894358a856863ba0f48500e5273704aea1d..3eb815011b46b21c56788ee88329be1f443b6903 100644 (file)
@@ -297,11 +297,11 @@ int hv_call_connect_port(u64 port_partition_id, union hv_port_id port_id,
 int hv_call_disconnect_port(u64 connection_partition_id,
                            union hv_connection_id connection_id);
 int hv_call_notify_port_ring_empty(u32 sint_index);
-int hv_call_map_stat_page(enum hv_stats_object_type type,
-                         const union hv_stats_object_identity *identity,
-                         void **addr);
-int hv_call_unmap_stat_page(enum hv_stats_object_type type,
-                           const union hv_stats_object_identity *identity);
+int hv_map_stats_page(enum hv_stats_object_type type,
+                     const union hv_stats_object_identity *identity,
+                     void **addr);
+int hv_unmap_stats_page(enum hv_stats_object_type type, void *page_addr,
+                       const union hv_stats_object_identity *identity);
 int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
                                   u64 page_struct_count, u32 host_access,
                                   u32 flags, u8 acquire);
index 6dac9fcc092c43c51e692cee415bb087b5a5c5c4..caf02cfa49c93a019afd0d402a78aa96cd160e80 100644 (file)
@@ -807,9 +807,51 @@ hv_call_notify_port_ring_empty(u32 sint_index)
        return hv_result_to_errno(status);
 }
 
-int hv_call_map_stat_page(enum hv_stats_object_type type,
-                         const union hv_stats_object_identity *identity,
-                         void **addr)
+static int hv_call_map_stats_page2(enum hv_stats_object_type type,
+                                  const union hv_stats_object_identity *identity,
+                                  u64 map_location)
+{
+       unsigned long flags;
+       struct hv_input_map_stats_page2 *input;
+       u64 status;
+       int ret;
+
+       if (!map_location || !mshv_use_overlay_gpfn())
+               return -EINVAL;
+
+       do {
+               local_irq_save(flags);
+               input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+
+               memset(input, 0, sizeof(*input));
+               input->type = type;
+               input->identity = *identity;
+               input->map_location = map_location;
+
+               status = hv_do_hypercall(HVCALL_MAP_STATS_PAGE2, input, NULL);
+
+               local_irq_restore(flags);
+
+               ret = hv_result_to_errno(status);
+
+               if (!ret)
+                       break;
+
+               if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
+                       hv_status_debug(status, "\n");
+                       break;
+               }
+
+               ret = hv_call_deposit_pages(NUMA_NO_NODE,
+                                           hv_current_partition_id, 1);
+       } while (!ret);
+
+       return ret;
+}
+
+static int hv_call_map_stats_page(enum hv_stats_object_type type,
+                                 const union hv_stats_object_identity *identity,
+                                 void **addr)
 {
        unsigned long flags;
        struct hv_input_map_stats_page *input;
@@ -848,8 +890,38 @@ int hv_call_map_stat_page(enum hv_stats_object_type type,
        return ret;
 }
 
-int hv_call_unmap_stat_page(enum hv_stats_object_type type,
-                           const union hv_stats_object_identity *identity)
+int hv_map_stats_page(enum hv_stats_object_type type,
+                     const union hv_stats_object_identity *identity,
+                     void **addr)
+{
+       int ret;
+       struct page *allocated_page = NULL;
+
+       if (!addr)
+               return -EINVAL;
+
+       if (mshv_use_overlay_gpfn()) {
+               allocated_page = alloc_page(GFP_KERNEL);
+               if (!allocated_page)
+                       return -ENOMEM;
+
+               ret = hv_call_map_stats_page2(type, identity,
+                                             page_to_pfn(allocated_page));
+               *addr = page_address(allocated_page);
+       } else {
+               ret = hv_call_map_stats_page(type, identity, addr);
+       }
+
+       if (ret && allocated_page) {
+               __free_page(allocated_page);
+               *addr = NULL;
+       }
+
+       return ret;
+}
+
+static int hv_call_unmap_stats_page(enum hv_stats_object_type type,
+                                   const union hv_stats_object_identity *identity)
 {
        unsigned long flags;
        struct hv_input_unmap_stats_page *input;
@@ -868,6 +940,19 @@ int hv_call_unmap_stat_page(enum hv_stats_object_type type,
        return hv_result_to_errno(status);
 }
 
+int hv_unmap_stats_page(enum hv_stats_object_type type, void *page_addr,
+                       const union hv_stats_object_identity *identity)
+{
+       int ret;
+
+       ret = hv_call_unmap_stats_page(type, identity);
+
+       if (mshv_use_overlay_gpfn() && page_addr)
+               __free_page(virt_to_page(page_addr));
+
+       return ret;
+}
+
 int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
                                   u64 page_struct_count, u32 host_access,
                                   u32 flags, u8 acquire)
index 288695a859f73e93d6726dd894c9de73e9f321a8..7684645ef00d2761a3dc0175d8a912a8dd08630e 100644 (file)
@@ -843,7 +843,8 @@ mshv_vp_release(struct inode *inode, struct file *filp)
        return 0;
 }
 
-static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index)
+static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index,
+                               void *stats_pages[])
 {
        union hv_stats_object_identity identity = {
                .vp.partition_id = partition_id,
@@ -851,10 +852,10 @@ static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index)
        };
 
        identity.vp.stats_area_type = HV_STATS_AREA_SELF;
-       hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
+       hv_unmap_stats_page(HV_STATS_OBJECT_VP, NULL, &identity);
 
        identity.vp.stats_area_type = HV_STATS_AREA_PARENT;
-       hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
+       hv_unmap_stats_page(HV_STATS_OBJECT_VP, NULL, &identity);
 }
 
 static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
@@ -867,14 +868,14 @@ static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
        int err;
 
        identity.vp.stats_area_type = HV_STATS_AREA_SELF;
-       err = hv_call_map_stat_page(HV_STATS_OBJECT_VP, &identity,
-                                   &stats_pages[HV_STATS_AREA_SELF]);
+       err = hv_map_stats_page(HV_STATS_OBJECT_VP, &identity,
+                               &stats_pages[HV_STATS_AREA_SELF]);
        if (err)
                return err;
 
        identity.vp.stats_area_type = HV_STATS_AREA_PARENT;
-       err = hv_call_map_stat_page(HV_STATS_OBJECT_VP, &identity,
-                                   &stats_pages[HV_STATS_AREA_PARENT]);
+       err = hv_map_stats_page(HV_STATS_OBJECT_VP, &identity,
+                               &stats_pages[HV_STATS_AREA_PARENT]);
        if (err)
                goto unmap_self;
 
@@ -882,7 +883,7 @@ static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
 
 unmap_self:
        identity.vp.stats_area_type = HV_STATS_AREA_SELF;
-       hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
+       hv_unmap_stats_page(HV_STATS_OBJECT_VP, NULL, &identity);
        return err;
 }
 
@@ -990,7 +991,7 @@ free_vp:
        kfree(vp);
 unmap_stats_pages:
        if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
-               mshv_vp_stats_unmap(partition->pt_id, args.vp_index);
+               mshv_vp_stats_unmap(partition->pt_id, args.vp_index, stats_pages);
 unmap_ghcb_page:
        if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available())
                hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
@@ -1742,7 +1743,8 @@ static void destroy_partition(struct mshv_partition *partition)
                                continue;
 
                        if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
-                               mshv_vp_stats_unmap(partition->pt_id, vp->vp_index);
+                               mshv_vp_stats_unmap(partition->pt_id, vp->vp_index,
+                                                   (void **)vp->vp_stats_pages);
 
                        if (vp->vp_register_page) {
                                (void)hv_unmap_vp_state_page(partition->pt_id,
index af85b1c36b6e46d3793b744b2ad13f22c3e950be..f6e31d1c3267f46f67c533e0e56ed2dead3e47b3 100644 (file)
@@ -494,6 +494,7 @@ union hv_vp_assist_msr_contents {    /* HV_REGISTER_VP_ASSIST_PAGE */
 #define HVCALL_GET_PARTITION_PROPERTY_EX               0x0101
 #define HVCALL_MMIO_READ                               0x0106
 #define HVCALL_MMIO_WRITE                              0x0107
+#define HVCALL_MAP_STATS_PAGE2                         0x0131
 
 /* HV_HYPERCALL_INPUT */
 #define HV_HYPERCALL_RESULT_MASK       GENMASK_ULL(15, 0)
index bf2ce27dfcc5c56525e704e89a23a289b67fa28f..064bf735cab6b95388095aea9f1841097a4f1199 100644 (file)
@@ -177,6 +177,13 @@ struct hv_input_map_stats_page {
        union hv_stats_object_identity identity;
 } __packed;
 
+struct hv_input_map_stats_page2 {
+       u32 type; /* enum hv_stats_object_type */
+       u32 padding;
+       union hv_stats_object_identity identity;
+       u64 map_location;
+} __packed;
+
 struct hv_output_map_stats_page {
        u64 map_location;
 } __packed;