From df40f32c87be64c96ee974573968592e147a4ded Mon Sep 17 00:00:00 2001 From: Nuno Das Neves Date: Wed, 28 Jan 2026 10:11:44 -0800 Subject: mshv: Update hv_stats_page definitions hv_stats_page belongs in hvhdk.h, move it there. It does not require a union to access the data for different counters, just use a single u64 array for simplicity and to match the Windows definitions. While at it, correct the ARM64 value for VpRootDispatchThreadBlocked. Signed-off-by: Nuno Das Neves Acked-by: Stanislav Kinsburskii Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- include/hyperv/hvhdk.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h index 08965970c17d..79d1f16a850a 100644 --- a/include/hyperv/hvhdk.h +++ b/include/hyperv/hvhdk.h @@ -10,6 +10,13 @@ #include "hvhdk_mini.h" #include "hvgdk.h" +/* + * Hypervisor statistics page format + */ +struct hv_stats_page { + u64 data[HV_HYP_PAGE_SIZE / sizeof(u64)]; +} __packed; + /* Bits for dirty mask of hv_vp_register_page */ #define HV_X64_REGISTER_CLASS_GENERAL 0 #define HV_X64_REGISTER_CLASS_IP 1 -- cgit v1.2.3 From 4bef6b28bab8697b4f9255c375da2b6b6943a969 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Wed, 18 Feb 2026 19:11:40 +0000 Subject: mshv: Add support for integrated scheduler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Query the hypervisor for integrated scheduler support and use it if configured. Microsoft Hypervisor originally provided two schedulers: root and core. The root scheduler allows the root partition to schedule guest vCPUs across physical cores, supporting both time slicing and CPU affinity (e.g., via cgroups). In contrast, the core scheduler delegates vCPU-to-physical-core scheduling entirely to the hypervisor. Direct virtualization introduces a new privileged guest partition type - L1 Virtual Host (L1VH) — which can create child partitions from its own resources. These child partitions are effectively siblings, scheduled by the hypervisor's core scheduler. This prevents the L1VH parent from setting affinity or time slicing for its own processes or guest VPs. While cgroups, CFS, and cpuset controllers can still be used, their effectiveness is unpredictable, as the core scheduler swaps vCPUs according to its own logic (typically round-robin across all allocated physical CPUs). As a result, the system may appear to "steal" time from the L1VH and its children. To address this, Microsoft Hypervisor introduces the integrated scheduler. This allows an L1VH partition to schedule its own vCPUs and those of its guests across its "physical" cores, effectively emulating root scheduler behavior within the L1VH, while retaining core scheduler behavior for the rest of the system. The integrated scheduler is controlled by the root partition and gated by the vmm_enable_integrated_scheduler capability bit. If set, the hypervisor supports the integrated scheduler. The L1VH partition must then check if it is enabled by querying the corresponding extended partition property. If this property is true, the L1VH partition must use the root scheduler logic; otherwise, it must use the core scheduler. This requirement makes reading VMM capabilities in L1VH partition a requirement too. Signed-off-by: Andreea Pintilie Signed-off-by: Stanislav Kinsburskii Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/mshv_root_main.c | 82 +++++++++++++++++++++++++++------------------ include/hyperv/hvhdk_mini.h | 7 +++- 2 files changed, 56 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 431aebf95bc7..c6ec88884728 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -2079,6 +2079,29 @@ static const char *scheduler_type_to_string(enum hv_scheduler_type type) }; } +static int __init l1vh_retrieve_scheduler_type(enum hv_scheduler_type *out) +{ + u64 integrated_sched_enabled; + int ret; + + *out = HV_SCHEDULER_TYPE_CORE_SMT; + + if (!mshv_root.vmm_caps.vmm_enable_integrated_scheduler) + return 0; + + ret = hv_call_get_partition_property_ex(HV_PARTITION_ID_SELF, + HV_PARTITION_PROPERTY_INTEGRATED_SCHEDULER_ENABLED, + 0, &integrated_sched_enabled, + sizeof(integrated_sched_enabled)); + if (ret) + return ret; + + if (integrated_sched_enabled) + *out = HV_SCHEDULER_TYPE_ROOT; + + return 0; +} + /* TODO move this to hv_common.c when needed outside */ static int __init hv_retrieve_scheduler_type(enum hv_scheduler_type *out) { @@ -2111,13 +2134,12 @@ static int __init hv_retrieve_scheduler_type(enum hv_scheduler_type *out) /* Retrieve and stash the supported scheduler type */ static int __init mshv_retrieve_scheduler_type(struct device *dev) { - int ret = 0; + int ret; if (hv_l1vh_partition()) - hv_scheduler_type = HV_SCHEDULER_TYPE_CORE_SMT; + ret = l1vh_retrieve_scheduler_type(&hv_scheduler_type); else ret = hv_retrieve_scheduler_type(&hv_scheduler_type); - if (ret) return ret; @@ -2237,42 +2259,29 @@ struct notifier_block mshv_reboot_nb = { static void mshv_root_partition_exit(void) { unregister_reboot_notifier(&mshv_reboot_nb); - root_scheduler_deinit(); } static int __init mshv_root_partition_init(struct device *dev) { - int err; - - err = root_scheduler_init(dev); - if (err) - return err; - - err = register_reboot_notifier(&mshv_reboot_nb); - if (err) - goto root_sched_deinit; - - return 0; - -root_sched_deinit: - root_scheduler_deinit(); - return err; + return register_reboot_notifier(&mshv_reboot_nb); } -static void mshv_init_vmm_caps(struct device *dev) +static int __init mshv_init_vmm_caps(struct device *dev) { - /* - * This can only fail here if HVCALL_GET_PARTITION_PROPERTY_EX or - * HV_PARTITION_PROPERTY_VMM_CAPABILITIES are not supported. In that - * case it's valid to proceed as if all vmm_caps are disabled (zero). - */ - if (hv_call_get_partition_property_ex(HV_PARTITION_ID_SELF, - HV_PARTITION_PROPERTY_VMM_CAPABILITIES, - 0, &mshv_root.vmm_caps, - sizeof(mshv_root.vmm_caps))) - dev_warn(dev, "Unable to get VMM capabilities\n"); + int ret; + + ret = hv_call_get_partition_property_ex(HV_PARTITION_ID_SELF, + HV_PARTITION_PROPERTY_VMM_CAPABILITIES, + 0, &mshv_root.vmm_caps, + sizeof(mshv_root.vmm_caps)); + if (ret && hv_l1vh_partition()) { + dev_err(dev, "Failed to get VMM capabilities: %d\n", ret); + return ret; + } dev_dbg(dev, "vmm_caps = %#llx\n", mshv_root.vmm_caps.as_uint64[0]); + + return 0; } static int __init mshv_parent_partition_init(void) @@ -2318,6 +2327,10 @@ static int __init mshv_parent_partition_init(void) mshv_cpuhp_online = ret; + ret = mshv_init_vmm_caps(dev); + if (ret) + goto remove_cpu_state; + ret = mshv_retrieve_scheduler_type(dev); if (ret) goto remove_cpu_state; @@ -2327,11 +2340,13 @@ static int __init mshv_parent_partition_init(void) if (ret) goto remove_cpu_state; - mshv_init_vmm_caps(dev); + ret = root_scheduler_init(dev); + if (ret) + goto exit_partition; ret = mshv_debugfs_init(); if (ret) - goto exit_partition; + goto deinit_root_scheduler; ret = mshv_irqfd_wq_init(); if (ret) @@ -2346,6 +2361,8 @@ static int __init mshv_parent_partition_init(void) exit_debugfs: mshv_debugfs_exit(); +deinit_root_scheduler: + root_scheduler_deinit(); exit_partition: if (hv_root_partition()) mshv_root_partition_exit(); @@ -2365,6 +2382,7 @@ static void __exit mshv_parent_partition_exit(void) mshv_debugfs_exit(); misc_deregister(&mshv_dev); mshv_irqfd_wq_cleanup(); + root_scheduler_deinit(); if (hv_root_partition()) mshv_root_partition_exit(); cpuhp_remove_state(mshv_cpuhp_online); diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h index 41a29bf8ec14..c0300910808b 100644 --- a/include/hyperv/hvhdk_mini.h +++ b/include/hyperv/hvhdk_mini.h @@ -87,6 +87,9 @@ enum hv_partition_property_code { HV_PARTITION_PROPERTY_PRIVILEGE_FLAGS = 0x00010000, HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES = 0x00010001, + /* Integrated scheduling properties */ + HV_PARTITION_PROPERTY_INTEGRATED_SCHEDULER_ENABLED = 0x00020005, + /* Resource properties */ HV_PARTITION_PROPERTY_GPA_PAGE_ACCESS_TRACKING = 0x00050005, HV_PARTITION_PROPERTY_UNIMPLEMENTED_MSR_ACTION = 0x00050017, @@ -102,7 +105,7 @@ enum hv_partition_property_code { }; #define HV_PARTITION_VMM_CAPABILITIES_BANK_COUNT 1 -#define HV_PARTITION_VMM_CAPABILITIES_RESERVED_BITFIELD_COUNT 59 +#define HV_PARTITION_VMM_CAPABILITIES_RESERVED_BITFIELD_COUNT 57 struct hv_partition_property_vmm_capabilities { u16 bank_count; @@ -119,6 +122,8 @@ struct hv_partition_property_vmm_capabilities { u64 reservedbit3: 1; #endif u64 assignable_synthetic_proc_features: 1; + u64 reservedbit5: 1; + u64 vmm_enable_integrated_scheduler : 1; u64 reserved0: HV_PARTITION_VMM_CAPABILITIES_RESERVED_BITFIELD_COUNT; } __packed; }; -- cgit v1.2.3 From 36d6cbb62133fc6eea28f380409e0fb190f3dfbe Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Wed, 18 Feb 2026 23:32:17 +0000 Subject: mshv: expose the scrub partition hypercall This hypercall needs to be exposed for VMMs to soft-reboot guests. It will reset APIC and synthetic interrupt controller state, among others. Signed-off-by: Magnus Kulke Signed-off-by: Wei Liu --- drivers/hv/mshv_root_main.c | 1 + include/hyperv/hvgdk_mini.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index c6ec88884728..e5d94398528e 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -120,6 +120,7 @@ static u16 mshv_passthru_hvcalls[] = { HVCALL_SET_VP_REGISTERS, HVCALL_TRANSLATE_VIRTUAL_ADDRESS, HVCALL_CLEAR_VIRTUAL_INTERRUPT, + HVCALL_SCRUB_PARTITION, HVCALL_REGISTER_INTERCEPT_RESULT, HVCALL_ASSERT_VIRTUAL_INTERRUPT, HVCALL_GET_GPA_PAGES_ACCESS_STATES, diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h index 30fbbde81c5c..d9aa5afb0a27 100644 --- a/include/hyperv/hvgdk_mini.h +++ b/include/hyperv/hvgdk_mini.h @@ -474,6 +474,7 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_ASSIST_PAGE */ #define HVCALL_NOTIFY_PARTITION_EVENT 0x0087 #define HVCALL_ENTER_SLEEP_STATE 0x0084 #define HVCALL_NOTIFY_PORT_RING_EMPTY 0x008b +#define HVCALL_SCRUB_PARTITION 0x008d #define HVCALL_REGISTER_INTERCEPT_RESULT 0x0091 #define HVCALL_ASSERT_VIRTUAL_INTERRUPT 0x0094 #define HVCALL_CREATE_PORT 0x0095 -- cgit v1.2.3 From a284dbc96a47891a7a595a1c81b1e2da4d309cf6 Mon Sep 17 00:00:00 2001 From: Muminul Islam Date: Wed, 18 Feb 2026 14:47:59 +0000 Subject: mshv: Add nested virtualization creation flag Introduce HV_PARTITION_CREATION_FLAG_NESTED_VIRTUALIZATION_CAPABLE to indicate support for nested virtualization during partition creation. This enables clearer configuration and capability checks for nested virtualization scenarios. Signed-off-by: Stanislav Kinsburskii Signed-off-by: Muminul Islam Signed-off-by: Wei Liu --- drivers/hv/mshv_root_main.c | 2 ++ include/hyperv/hvhdk.h | 1 + include/uapi/linux/mshv.h | 1 + 3 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index e5d94398528e..e490f8e5a8a5 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1947,6 +1947,8 @@ static long mshv_ioctl_process_pt_flags(void __user *user_arg, u64 *pt_flags, *pt_flags |= HV_PARTITION_CREATION_FLAG_X2APIC_CAPABLE; if (args.pt_flags & BIT_ULL(MSHV_PT_BIT_GPA_SUPER_PAGES)) *pt_flags |= HV_PARTITION_CREATION_FLAG_GPA_SUPER_PAGES_ENABLED; + if (args.pt_flags & BIT(MSHV_PT_BIT_NESTED_VIRTUALIZATION)) + *pt_flags |= HV_PARTITION_CREATION_FLAG_NESTED_VIRTUALIZATION_CAPABLE; isol_props->as_uint64 = 0; diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h index 79d1f16a850a..f139c7c5bb2d 100644 --- a/include/hyperv/hvhdk.h +++ b/include/hyperv/hvhdk.h @@ -335,6 +335,7 @@ union hv_partition_isolation_properties { #define HV_PARTITION_ISOLATION_HOST_TYPE_RESERVED 0x2 /* Note: Exo partition is enabled by default */ +#define HV_PARTITION_CREATION_FLAG_NESTED_VIRTUALIZATION_CAPABLE BIT(1) #define HV_PARTITION_CREATION_FLAG_GPA_SUPER_PAGES_ENABLED BIT(4) #define HV_PARTITION_CREATION_FLAG_EXO_PARTITION BIT(8) #define HV_PARTITION_CREATION_FLAG_LAPIC_ENABLED BIT(13) diff --git a/include/uapi/linux/mshv.h b/include/uapi/linux/mshv.h index dee3ece28ce5..7ef5dd67a232 100644 --- a/include/uapi/linux/mshv.h +++ b/include/uapi/linux/mshv.h @@ -27,6 +27,7 @@ enum { MSHV_PT_BIT_X2APIC, MSHV_PT_BIT_GPA_SUPER_PAGES, MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES, + MSHV_PT_BIT_NESTED_VIRTUALIZATION, MSHV_PT_BIT_COUNT, }; -- cgit v1.2.3 From 8927a108a7662eb83eb667bc0c5a0633397122b1 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Wed, 18 Feb 2026 14:48:02 +0000 Subject: mshv: Add SMT_ENABLED_GUEST partition creation flag Add support for HV_PARTITION_CREATION_FLAG_SMT_ENABLED_GUEST to allow userspace VMMs to enable SMT for guest partitions. Expose this via new MSHV_PT_BIT_SMT_ENABLED_GUEST flag in the UAPI. Without this flag, the hypervisor schedules guest VPs incorrectly, causing SMT unusable. Signed-off-by: Anatol Belski Signed-off-by: Wei Liu --- drivers/hv/mshv_root_main.c | 2 ++ include/hyperv/hvhdk.h | 1 + include/uapi/linux/mshv.h | 1 + 3 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index e490f8e5a8a5..192467a25f66 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1949,6 +1949,8 @@ static long mshv_ioctl_process_pt_flags(void __user *user_arg, u64 *pt_flags, *pt_flags |= HV_PARTITION_CREATION_FLAG_GPA_SUPER_PAGES_ENABLED; if (args.pt_flags & BIT(MSHV_PT_BIT_NESTED_VIRTUALIZATION)) *pt_flags |= HV_PARTITION_CREATION_FLAG_NESTED_VIRTUALIZATION_CAPABLE; + if (args.pt_flags & BIT(MSHV_PT_BIT_SMT_ENABLED_GUEST)) + *pt_flags |= HV_PARTITION_CREATION_FLAG_SMT_ENABLED_GUEST; isol_props->as_uint64 = 0; diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h index f139c7c5bb2d..245f3db53bf1 100644 --- a/include/hyperv/hvhdk.h +++ b/include/hyperv/hvhdk.h @@ -335,6 +335,7 @@ union hv_partition_isolation_properties { #define HV_PARTITION_ISOLATION_HOST_TYPE_RESERVED 0x2 /* Note: Exo partition is enabled by default */ +#define HV_PARTITION_CREATION_FLAG_SMT_ENABLED_GUEST BIT(0) #define HV_PARTITION_CREATION_FLAG_NESTED_VIRTUALIZATION_CAPABLE BIT(1) #define HV_PARTITION_CREATION_FLAG_GPA_SUPER_PAGES_ENABLED BIT(4) #define HV_PARTITION_CREATION_FLAG_EXO_PARTITION BIT(8) diff --git a/include/uapi/linux/mshv.h b/include/uapi/linux/mshv.h index 7ef5dd67a232..e0645a34b55b 100644 --- a/include/uapi/linux/mshv.h +++ b/include/uapi/linux/mshv.h @@ -28,6 +28,7 @@ enum { MSHV_PT_BIT_GPA_SUPER_PAGES, MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES, MSHV_PT_BIT_NESTED_VIRTUALIZATION, + MSHV_PT_BIT_SMT_ENABLED_GUEST, MSHV_PT_BIT_COUNT, }; -- cgit v1.2.3 From 7db44aa173de03c170d4dfa5864ae126678a5ad5 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 5 Feb 2026 18:42:10 +0000 Subject: mshv: Introduce hv_result_needs_memory() helper function Replace direct comparisons of hv_result(status) against HV_STATUS_INSUFFICIENT_MEMORY with a new hv_result_needs_memory() helper function. This improves code readability and provides a consistent and extendable interface for checking out-of-memory conditions in hypercall results. No functional changes intended. Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Reviewed-by: Mukesh R Signed-off-by: Wei Liu --- drivers/hv/hv_proc.c | 14 ++++++++++++-- drivers/hv/mshv_root_hv_call.c | 25 ++++++++++++------------- drivers/hv/mshv_root_main.c | 2 +- include/asm-generic/mshyperv.h | 3 +++ 4 files changed, 28 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/hv/hv_proc.c b/drivers/hv/hv_proc.c index fbb4eb3901bb..e53204b9e05d 100644 --- a/drivers/hv/hv_proc.c +++ b/drivers/hv/hv_proc.c @@ -110,6 +110,16 @@ free_buf: } EXPORT_SYMBOL_GPL(hv_call_deposit_pages); +bool hv_result_needs_memory(u64 status) +{ + switch (hv_result(status)) { + case HV_STATUS_INSUFFICIENT_MEMORY: + return true; + } + return false; +} +EXPORT_SYMBOL_GPL(hv_result_needs_memory); + int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id) { struct hv_input_add_logical_processor *input; @@ -137,7 +147,7 @@ int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id) input, output); local_irq_restore(flags); - if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { + if (!hv_result_needs_memory(status)) { if (!hv_result_success(status)) { hv_status_err(status, "cpu %u apic ID: %u\n", lp_index, apic_id); @@ -179,7 +189,7 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags) status = hv_do_hypercall(HVCALL_CREATE_VP, input, NULL); local_irq_restore(irq_flags); - if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { + if (!hv_result_needs_memory(status)) { if (!hv_result_success(status)) { hv_status_err(status, "vcpu: %u, lp: %u\n", vp_index, flags); diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c index daee036e48bc..1c4a2dbf49c0 100644 --- a/drivers/hv/mshv_root_hv_call.c +++ b/drivers/hv/mshv_root_hv_call.c @@ -115,7 +115,7 @@ int hv_call_create_partition(u64 flags, status = hv_do_hypercall(HVCALL_CREATE_PARTITION, input, output); - if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { + if (!hv_result_needs_memory(status)) { if (hv_result_success(status)) *partition_id = output->partition_id; local_irq_restore(irq_flags); @@ -147,7 +147,7 @@ int hv_call_initialize_partition(u64 partition_id) status = hv_do_fast_hypercall8(HVCALL_INITIALIZE_PARTITION, *(u64 *)&input); - if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { + if (!hv_result_needs_memory(status)) { ret = hv_result_to_errno(status); break; } @@ -239,7 +239,7 @@ static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_struct_count, completed = hv_repcomp(status); - if (hv_result(status) == HV_STATUS_INSUFFICIENT_MEMORY) { + if (hv_result_needs_memory(status)) { ret = hv_call_deposit_pages(NUMA_NO_NODE, partition_id, HV_MAP_GPA_DEPOSIT_PAGES); if (ret) @@ -455,7 +455,7 @@ int hv_call_get_vp_state(u32 vp_index, u64 partition_id, status = hv_do_hypercall(control, input, output); - if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { + if (!hv_result_needs_memory(status)) { if (hv_result_success(status) && ret_output) memcpy(ret_output, output, sizeof(*output)); @@ -518,7 +518,7 @@ int hv_call_set_vp_state(u32 vp_index, u64 partition_id, status = hv_do_hypercall(control, input, NULL); - if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { + if (!hv_result_needs_memory(status)) { local_irq_restore(flags); ret = hv_result_to_errno(status); break; @@ -563,7 +563,7 @@ static int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type, status = hv_do_hypercall(HVCALL_MAP_VP_STATE_PAGE, input, output); - if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { + if (!hv_result_needs_memory(status)) { if (hv_result_success(status)) *state_page = pfn_to_page(output->map_location); local_irq_restore(flags); @@ -718,7 +718,7 @@ hv_call_create_port(u64 port_partition_id, union hv_port_id port_id, if (hv_result_success(status)) break; - if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { + if (!hv_result_needs_memory(status)) { ret = hv_result_to_errno(status); break; } @@ -772,7 +772,7 @@ hv_call_connect_port(u64 port_partition_id, union hv_port_id port_id, if (hv_result_success(status)) break; - if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { + if (!hv_result_needs_memory(status)) { ret = hv_result_to_errno(status); break; } @@ -850,7 +850,7 @@ static int hv_call_map_stats_page2(enum hv_stats_object_type type, if (!ret) break; - if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { + if (!hv_result_needs_memory(status)) { hv_status_debug(status, "\n"); break; } @@ -899,7 +899,7 @@ hv_call_map_stats_page(enum hv_stats_object_type type, struct hv_input_map_stats_page *input; struct hv_output_map_stats_page *output; u64 status, pfn; - int hv_status, ret = 0; + int ret = 0; do { local_irq_save(flags); @@ -915,13 +915,12 @@ hv_call_map_stats_page(enum hv_stats_object_type type, local_irq_restore(flags); - hv_status = hv_result(status); - if (hv_status != HV_STATUS_INSUFFICIENT_MEMORY) { + if (!hv_result_needs_memory(status)) { if (hv_result_success(status)) break; if (hv_stats_get_area_type(type, identity) == HV_STATS_AREA_PARENT && - hv_status == HV_STATUS_INVALID_PARAMETER) { + hv_result(status) == HV_STATUS_INVALID_PARAMETER) { *addr = NULL; return 0; } diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 192467a25f66..17546f6f4e85 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -252,7 +252,7 @@ static int mshv_ioctl_passthru_hvcall(struct mshv_partition *partition, if (hv_result_success(status)) break; - if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) + if (!hv_result_needs_memory(status)) ret = hv_result_to_errno(status); else ret = hv_call_deposit_pages(NUMA_NO_NODE, diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index ecedab554c80..452426d5b2ab 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -342,6 +342,8 @@ static inline bool hv_parent_partition(void) { return hv_root_partition() || hv_l1vh_partition(); } + +bool hv_result_needs_memory(u64 status); int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages); int hv_call_add_logical_proc(int node, u32 lp_index, u32 acpi_id); int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags); @@ -350,6 +352,7 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags); static inline bool hv_root_partition(void) { return false; } static inline bool hv_l1vh_partition(void) { return false; } static inline bool hv_parent_partition(void) { return false; } +static inline bool hv_result_needs_memory(u64 status) { return false; } static inline int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages) { return -EOPNOTSUPP; -- cgit v1.2.3 From ede54383e646821b499873c1caf2dd97551da8eb Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 5 Feb 2026 18:42:15 +0000 Subject: mshv: Introduce hv_deposit_memory helper functions Introduce hv_deposit_memory_node() and hv_deposit_memory() helper functions to handle memory deposit with proper error handling. The new hv_deposit_memory_node() function takes the hypervisor status as a parameter and validates it before depositing pages. It checks for HV_STATUS_INSUFFICIENT_MEMORY specifically and returns an error for unexpected status codes. This is a precursor patch to new out-of-memory error codes support. No functional changes intended. Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Reviewed-by: Mukesh R Signed-off-by: Wei Liu --- drivers/hv/hv_proc.c | 21 +++++++++++++++++++-- drivers/hv/mshv_root_hv_call.c | 25 +++++++++---------------- drivers/hv/mshv_root_main.c | 3 +-- include/asm-generic/mshyperv.h | 10 ++++++++++ 4 files changed, 39 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/hv/hv_proc.c b/drivers/hv/hv_proc.c index e53204b9e05d..53622e5886b8 100644 --- a/drivers/hv/hv_proc.c +++ b/drivers/hv/hv_proc.c @@ -110,6 +110,22 @@ free_buf: } EXPORT_SYMBOL_GPL(hv_call_deposit_pages); +int hv_deposit_memory_node(int node, u64 partition_id, + u64 hv_status) +{ + u32 num_pages = 1; + + switch (hv_result(hv_status)) { + case HV_STATUS_INSUFFICIENT_MEMORY: + break; + default: + hv_status_err(hv_status, "Unexpected!\n"); + return -ENOMEM; + } + return hv_call_deposit_pages(node, partition_id, num_pages); +} +EXPORT_SYMBOL_GPL(hv_deposit_memory_node); + bool hv_result_needs_memory(u64 status) { switch (hv_result(status)) { @@ -155,7 +171,8 @@ int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id) } break; } - ret = hv_call_deposit_pages(node, hv_current_partition_id, 1); + ret = hv_deposit_memory_node(node, hv_current_partition_id, + status); } while (!ret); return ret; @@ -197,7 +214,7 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags) } break; } - ret = hv_call_deposit_pages(node, partition_id, 1); + ret = hv_deposit_memory_node(node, partition_id, status); } while (!ret); diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c index 1c4a2dbf49c0..7f91096f95a8 100644 --- a/drivers/hv/mshv_root_hv_call.c +++ b/drivers/hv/mshv_root_hv_call.c @@ -123,8 +123,7 @@ int hv_call_create_partition(u64 flags, break; } local_irq_restore(irq_flags); - ret = hv_call_deposit_pages(NUMA_NO_NODE, - hv_current_partition_id, 1); + ret = hv_deposit_memory(hv_current_partition_id, status); } while (!ret); return ret; @@ -151,7 +150,7 @@ int hv_call_initialize_partition(u64 partition_id) ret = hv_result_to_errno(status); break; } - ret = hv_call_deposit_pages(NUMA_NO_NODE, partition_id, 1); + ret = hv_deposit_memory(partition_id, status); } while (!ret); return ret; @@ -465,8 +464,7 @@ int hv_call_get_vp_state(u32 vp_index, u64 partition_id, } local_irq_restore(flags); - ret = hv_call_deposit_pages(NUMA_NO_NODE, - partition_id, 1); + ret = hv_deposit_memory(partition_id, status); } while (!ret); return ret; @@ -525,8 +523,7 @@ int hv_call_set_vp_state(u32 vp_index, u64 partition_id, } local_irq_restore(flags); - ret = hv_call_deposit_pages(NUMA_NO_NODE, - partition_id, 1); + ret = hv_deposit_memory(partition_id, status); } while (!ret); return ret; @@ -573,7 +570,7 @@ static int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type, local_irq_restore(flags); - ret = hv_call_deposit_pages(NUMA_NO_NODE, partition_id, 1); + ret = hv_deposit_memory(partition_id, status); } while (!ret); return ret; @@ -722,8 +719,7 @@ hv_call_create_port(u64 port_partition_id, union hv_port_id port_id, ret = hv_result_to_errno(status); break; } - ret = hv_call_deposit_pages(NUMA_NO_NODE, port_partition_id, 1); - + ret = hv_deposit_memory(port_partition_id, status); } while (!ret); return ret; @@ -776,8 +772,7 @@ hv_call_connect_port(u64 port_partition_id, union hv_port_id port_id, ret = hv_result_to_errno(status); break; } - ret = hv_call_deposit_pages(NUMA_NO_NODE, - connection_partition_id, 1); + ret = hv_deposit_memory(connection_partition_id, status); } while (!ret); return ret; @@ -855,8 +850,7 @@ static int hv_call_map_stats_page2(enum hv_stats_object_type type, break; } - ret = hv_call_deposit_pages(NUMA_NO_NODE, - hv_current_partition_id, 1); + ret = hv_deposit_memory(hv_current_partition_id, status); } while (!ret); return ret; @@ -929,8 +923,7 @@ hv_call_map_stats_page(enum hv_stats_object_type type, return hv_result_to_errno(status); } - ret = hv_call_deposit_pages(NUMA_NO_NODE, - hv_current_partition_id, 1); + ret = hv_deposit_memory(hv_current_partition_id, status); if (ret) return ret; } while (!ret); diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 17546f6f4e85..e6509c980763 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -255,8 +255,7 @@ static int mshv_ioctl_passthru_hvcall(struct mshv_partition *partition, if (!hv_result_needs_memory(status)) ret = hv_result_to_errno(status); else - ret = hv_call_deposit_pages(NUMA_NO_NODE, - pt_id, 1); + ret = hv_deposit_memory(pt_id, status); } while (!ret); args.status = hv_result(status); diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index 452426d5b2ab..d37b68238c97 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -344,6 +344,7 @@ static inline bool hv_parent_partition(void) } bool hv_result_needs_memory(u64 status); +int hv_deposit_memory_node(int node, u64 partition_id, u64 status); int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages); int hv_call_add_logical_proc(int node, u32 lp_index, u32 acpi_id); int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags); @@ -353,6 +354,10 @@ static inline bool hv_root_partition(void) { return false; } static inline bool hv_l1vh_partition(void) { return false; } static inline bool hv_parent_partition(void) { return false; } static inline bool hv_result_needs_memory(u64 status) { return false; } +static inline int hv_deposit_memory_node(int node, u64 partition_id, u64 status) +{ + return -EOPNOTSUPP; +} static inline int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages) { return -EOPNOTSUPP; @@ -367,6 +372,11 @@ static inline int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u3 } #endif /* CONFIG_MSHV_ROOT */ +static inline int hv_deposit_memory(u64 partition_id, u64 status) +{ + return hv_deposit_memory_node(NUMA_NO_NODE, partition_id, status); +} + #if IS_ENABLED(CONFIG_HYPERV_VTL_MODE) u8 __init get_vtl(void); #else -- cgit v1.2.3 From cf82dd5ea95815e6c0612b61118d2358ef5c05b0 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 5 Feb 2026 18:42:21 +0000 Subject: mshv: Handle insufficient contiguous memory hypervisor status The HV_STATUS_INSUFFICIENT_CONTIGUOUS_MEMORY status indicates that the hypervisor lacks sufficient contiguous memory for its internal allocations. When this status is encountered, allocate and deposit HV_MAX_CONTIGUOUS_ALLOCATION_PAGES contiguous pages to the hypervisor. HV_MAX_CONTIGUOUS_ALLOCATION_PAGES is defined in the hypervisor headers, a deposit of this size will always satisfy the hypervisor's requirements. Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Reviewed-by: Mukesh R Signed-off-by: Wei Liu --- drivers/hv/hv_common.c | 1 + drivers/hv/hv_proc.c | 4 ++++ include/hyperv/hvgdk_mini.h | 1 + include/hyperv/hvhdk_mini.h | 2 ++ 4 files changed, 8 insertions(+) (limited to 'include') diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index f1c17fb60dc1..f20596276662 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -793,6 +793,7 @@ static const struct hv_status_info hv_status_infos[] = { _STATUS_INFO(HV_STATUS_UNKNOWN_PROPERTY, -EIO), _STATUS_INFO(HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE, -EIO), _STATUS_INFO(HV_STATUS_INSUFFICIENT_MEMORY, -ENOMEM), + _STATUS_INFO(HV_STATUS_INSUFFICIENT_CONTIGUOUS_MEMORY, -ENOMEM), _STATUS_INFO(HV_STATUS_INVALID_PARTITION_ID, -EINVAL), _STATUS_INFO(HV_STATUS_INVALID_VP_INDEX, -EINVAL), _STATUS_INFO(HV_STATUS_NOT_FOUND, -EIO), diff --git a/drivers/hv/hv_proc.c b/drivers/hv/hv_proc.c index 53622e5886b8..181f6d02bce3 100644 --- a/drivers/hv/hv_proc.c +++ b/drivers/hv/hv_proc.c @@ -118,6 +118,9 @@ int hv_deposit_memory_node(int node, u64 partition_id, switch (hv_result(hv_status)) { case HV_STATUS_INSUFFICIENT_MEMORY: break; + case HV_STATUS_INSUFFICIENT_CONTIGUOUS_MEMORY: + num_pages = HV_MAX_CONTIGUOUS_ALLOCATION_PAGES; + break; default: hv_status_err(hv_status, "Unexpected!\n"); return -ENOMEM; @@ -130,6 +133,7 @@ bool hv_result_needs_memory(u64 status) { switch (hv_result(status)) { case HV_STATUS_INSUFFICIENT_MEMORY: + case HV_STATUS_INSUFFICIENT_CONTIGUOUS_MEMORY: return true; } return false; diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h index d9aa5afb0a27..fa2fb91a6470 100644 --- a/include/hyperv/hvgdk_mini.h +++ b/include/hyperv/hvgdk_mini.h @@ -38,6 +38,7 @@ struct hv_u128 { #define HV_STATUS_INVALID_LP_INDEX 0x41 #define HV_STATUS_INVALID_REGISTER_VALUE 0x50 #define HV_STATUS_OPERATION_FAILED 0x71 +#define HV_STATUS_INSUFFICIENT_CONTIGUOUS_MEMORY 0x75 #define HV_STATUS_TIME_OUT 0x78 #define HV_STATUS_CALL_PENDING 0x79 #define HV_STATUS_VTL_ALREADY_ENABLED 0x86 diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h index c0300910808b..091c03e26046 100644 --- a/include/hyperv/hvhdk_mini.h +++ b/include/hyperv/hvhdk_mini.h @@ -7,6 +7,8 @@ #include "hvgdk_mini.h" +#define HV_MAX_CONTIGUOUS_ALLOCATION_PAGES 8 + /* * Doorbell connection_info flags. */ -- cgit v1.2.3 From 158ebb578cd5f7881fdc7c4ecebddcf9463f91fd Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 5 Feb 2026 18:42:27 +0000 Subject: mshv: Handle insufficient root memory hypervisor statuses When creating guest partition objects, the hypervisor may fail to allocate root partition pages and return an insufficient memory status. In this case, deposit memory using the root partition ID instead. Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Reviewed-by: Mukesh R Signed-off-by: Wei Liu --- drivers/hv/hv_common.c | 2 ++ drivers/hv/hv_proc.c | 14 +++++++++++ include/hyperv/hvgdk_mini.h | 58 +++++++++++++++++++++++---------------------- 3 files changed, 46 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index f20596276662..6b67ac616789 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -794,6 +794,8 @@ static const struct hv_status_info hv_status_infos[] = { _STATUS_INFO(HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE, -EIO), _STATUS_INFO(HV_STATUS_INSUFFICIENT_MEMORY, -ENOMEM), _STATUS_INFO(HV_STATUS_INSUFFICIENT_CONTIGUOUS_MEMORY, -ENOMEM), + _STATUS_INFO(HV_STATUS_INSUFFICIENT_ROOT_MEMORY, -ENOMEM), + _STATUS_INFO(HV_STATUS_INSUFFICIENT_CONTIGUOUS_ROOT_MEMORY, -ENOMEM), _STATUS_INFO(HV_STATUS_INVALID_PARTITION_ID, -EINVAL), _STATUS_INFO(HV_STATUS_INVALID_VP_INDEX, -EINVAL), _STATUS_INFO(HV_STATUS_NOT_FOUND, -EIO), diff --git a/drivers/hv/hv_proc.c b/drivers/hv/hv_proc.c index 181f6d02bce3..5f4fd9c3231c 100644 --- a/drivers/hv/hv_proc.c +++ b/drivers/hv/hv_proc.c @@ -121,6 +121,18 @@ int hv_deposit_memory_node(int node, u64 partition_id, case HV_STATUS_INSUFFICIENT_CONTIGUOUS_MEMORY: num_pages = HV_MAX_CONTIGUOUS_ALLOCATION_PAGES; break; + + case HV_STATUS_INSUFFICIENT_CONTIGUOUS_ROOT_MEMORY: + num_pages = HV_MAX_CONTIGUOUS_ALLOCATION_PAGES; + fallthrough; + case HV_STATUS_INSUFFICIENT_ROOT_MEMORY: + if (!hv_root_partition()) { + hv_status_err(hv_status, "Unexpected root memory deposit\n"); + return -ENOMEM; + } + partition_id = HV_PARTITION_ID_SELF; + break; + default: hv_status_err(hv_status, "Unexpected!\n"); return -ENOMEM; @@ -134,6 +146,8 @@ bool hv_result_needs_memory(u64 status) switch (hv_result(status)) { case HV_STATUS_INSUFFICIENT_MEMORY: case HV_STATUS_INSUFFICIENT_CONTIGUOUS_MEMORY: + case HV_STATUS_INSUFFICIENT_ROOT_MEMORY: + case HV_STATUS_INSUFFICIENT_CONTIGUOUS_ROOT_MEMORY: return true; } return false; diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h index fa2fb91a6470..056ef7b6b360 100644 --- a/include/hyperv/hvgdk_mini.h +++ b/include/hyperv/hvgdk_mini.h @@ -14,34 +14,36 @@ struct hv_u128 { } __packed; /* NOTE: when adding below, update hv_result_to_string() */ -#define HV_STATUS_SUCCESS 0x0 -#define HV_STATUS_INVALID_HYPERCALL_CODE 0x2 -#define HV_STATUS_INVALID_HYPERCALL_INPUT 0x3 -#define HV_STATUS_INVALID_ALIGNMENT 0x4 -#define HV_STATUS_INVALID_PARAMETER 0x5 -#define HV_STATUS_ACCESS_DENIED 0x6 -#define HV_STATUS_INVALID_PARTITION_STATE 0x7 -#define HV_STATUS_OPERATION_DENIED 0x8 -#define HV_STATUS_UNKNOWN_PROPERTY 0x9 -#define HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE 0xA -#define HV_STATUS_INSUFFICIENT_MEMORY 0xB -#define HV_STATUS_INVALID_PARTITION_ID 0xD -#define HV_STATUS_INVALID_VP_INDEX 0xE -#define HV_STATUS_NOT_FOUND 0x10 -#define HV_STATUS_INVALID_PORT_ID 0x11 -#define HV_STATUS_INVALID_CONNECTION_ID 0x12 -#define HV_STATUS_INSUFFICIENT_BUFFERS 0x13 -#define HV_STATUS_NOT_ACKNOWLEDGED 0x14 -#define HV_STATUS_INVALID_VP_STATE 0x15 -#define HV_STATUS_NO_RESOURCES 0x1D -#define HV_STATUS_PROCESSOR_FEATURE_NOT_SUPPORTED 0x20 -#define HV_STATUS_INVALID_LP_INDEX 0x41 -#define HV_STATUS_INVALID_REGISTER_VALUE 0x50 -#define HV_STATUS_OPERATION_FAILED 0x71 -#define HV_STATUS_INSUFFICIENT_CONTIGUOUS_MEMORY 0x75 -#define HV_STATUS_TIME_OUT 0x78 -#define HV_STATUS_CALL_PENDING 0x79 -#define HV_STATUS_VTL_ALREADY_ENABLED 0x86 +#define HV_STATUS_SUCCESS 0x0 +#define HV_STATUS_INVALID_HYPERCALL_CODE 0x2 +#define HV_STATUS_INVALID_HYPERCALL_INPUT 0x3 +#define HV_STATUS_INVALID_ALIGNMENT 0x4 +#define HV_STATUS_INVALID_PARAMETER 0x5 +#define HV_STATUS_ACCESS_DENIED 0x6 +#define HV_STATUS_INVALID_PARTITION_STATE 0x7 +#define HV_STATUS_OPERATION_DENIED 0x8 +#define HV_STATUS_UNKNOWN_PROPERTY 0x9 +#define HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE 0xA +#define HV_STATUS_INSUFFICIENT_MEMORY 0xB +#define HV_STATUS_INVALID_PARTITION_ID 0xD +#define HV_STATUS_INVALID_VP_INDEX 0xE +#define HV_STATUS_NOT_FOUND 0x10 +#define HV_STATUS_INVALID_PORT_ID 0x11 +#define HV_STATUS_INVALID_CONNECTION_ID 0x12 +#define HV_STATUS_INSUFFICIENT_BUFFERS 0x13 +#define HV_STATUS_NOT_ACKNOWLEDGED 0x14 +#define HV_STATUS_INVALID_VP_STATE 0x15 +#define HV_STATUS_NO_RESOURCES 0x1D +#define HV_STATUS_PROCESSOR_FEATURE_NOT_SUPPORTED 0x20 +#define HV_STATUS_INVALID_LP_INDEX 0x41 +#define HV_STATUS_INVALID_REGISTER_VALUE 0x50 +#define HV_STATUS_OPERATION_FAILED 0x71 +#define HV_STATUS_INSUFFICIENT_ROOT_MEMORY 0x73 +#define HV_STATUS_INSUFFICIENT_CONTIGUOUS_MEMORY 0x75 +#define HV_STATUS_TIME_OUT 0x78 +#define HV_STATUS_CALL_PENDING 0x79 +#define HV_STATUS_INSUFFICIENT_CONTIGUOUS_ROOT_MEMORY 0x83 +#define HV_STATUS_VTL_ALREADY_ENABLED 0x86 /* * The Hyper-V TimeRefCount register and the TSC -- cgit v1.2.3