* Re: [PATCH v6 3/3] hyperv: Cleanly shutdown root partition with MSHV
From: Praveen K Paladugu @ 2025-12-05 19:59 UTC (permalink / raw)
To: Stanislav Kinsburskii, Praveen K Paladugu
Cc: kys, haiyangz, wei.liu, decui, tglx, mingo, linux-hyperv,
linux-kernel, bp, dave.hansen, x86, hpa, arnd, anbelski,
easwar.hariharan, nunodasneves
In-Reply-To: <aS3K898vD31Qi8mE@skinsburskii.localdomain>
On 12/1/2025 11:05 AM, Stanislav Kinsburskii wrote:
> On Wed, Nov 26, 2025 at 03:49:53PM -0600, Praveen K Paladugu wrote:
>> When a root partition running on MSHV is powered off, the default
>> behavior is to write ACPI registers to power-off. However, this ACPI
>> write is intercepted by MSHV and will result in a Machine Check
>> Exception(MCE).
>>
>> The root partition eventually panics with a trace similar to:
>>
>> [ 81.306348] reboot: Power down
>> [ 81.314709] mce: [Hardware Error]: CPU 0: Machine Check Exception: 4 Bank 0: b2000000c0060001
>> [ 81.314711] mce: [Hardware Error]: TSC 3b8cb60a66 PPIN 11d98332458e4ea9
>> [ 81.314713] mce: [Hardware Error]: PROCESSOR 0:606a6 TIME 1759339405 SOCKET 0 APIC 0 microcode ffffffff
>> [ 81.314715] mce: [Hardware Error]: Run the above through 'mcelog --ascii'
>> [ 81.314716] mce: [Hardware Error]: Machine check: Processor context corrupt
>> [ 81.314717] Kernel panic - not syncing: Fatal machine check
>>
>> To correctly shutdown a root partition running on MSHV hypervisor, sleep
>> state information must be configured within the hypervsior. Later, the
>> HVCALL_ENTER_SLEEP_STATE hypercall should be invoked as the last step in
>> the shutdown sequence.
>>
>> The previous patch configures the sleep state information and this patch
>> invokes HVCALL_ENTER_SLEEP_STATE hypercall to cleanly shutdown the root
>> partition.
>>
>> Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
>> Co-developed-by: Anatol Belski <anbelski@linux.microsoft.com>
>> Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
>> Reviewed-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
>> ---
>> arch/x86/include/asm/mshyperv.h | 2 ++
>> arch/x86/kernel/cpu/mshyperv.c | 2 ++
>> drivers/hv/mshv_common.c | 18 ++++++++++++++++++
>> 3 files changed, 22 insertions(+)
>>
>> diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h
>> index 166053df0484..4c22f3257368 100644
>> --- a/arch/x86/include/asm/mshyperv.h
>> +++ b/arch/x86/include/asm/mshyperv.h
>> @@ -183,9 +183,11 @@ void hv_apic_init(void);
>> void __init hv_init_spinlocks(void);
>> bool hv_vcpu_is_preempted(int vcpu);
>> void hv_sleep_notifiers_register(void);
>> +void hv_machine_power_off(void);
>> #else
>> static inline void hv_apic_init(void) {}
>> static inline void hv_sleep_notifiers_register(void) {};
>> +static inline void hv_machine_power_off(void) {};
>> #endif
>>
>> struct irq_domain *hv_create_pci_msi_domain(void);
>> diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
>> index fac9953a72ef..579fb2c64cfd 100644
>> --- a/arch/x86/kernel/cpu/mshyperv.c
>> +++ b/arch/x86/kernel/cpu/mshyperv.c
>> @@ -621,6 +621,8 @@ static void __init ms_hyperv_init_platform(void)
>> #endif
>>
>> #if IS_ENABLED(CONFIG_HYPERV)
>> + if (hv_root_partition())
>> + machine_ops.power_off = hv_machine_power_off;
>> #if defined(CONFIG_KEXEC_CORE)
>> machine_ops.shutdown = hv_machine_shutdown;
>> #endif
>> diff --git a/drivers/hv/mshv_common.c b/drivers/hv/mshv_common.c
>> index f1d4e81107ee..28905e3ed9c0 100644
>> --- a/drivers/hv/mshv_common.c
>> +++ b/drivers/hv/mshv_common.c
>> @@ -217,4 +217,22 @@ void hv_sleep_notifiers_register(void)
>> pr_err("%s: cannot register reboot notifier %d\n", __func__,
>> ret);
>> }
>> +
>> +/*
>> + * Power off the machine by entering S5 sleep state via Hyper-V hypercall.
>> + * This call does not return if successful.
>> + */
>> +void hv_machine_power_off(void)
>> +{
>> + unsigned long flags;
>> + struct hv_input_enter_sleep_state *in;
>> +
>> + local_irq_save(flags);
>> + in = *this_cpu_ptr(hyperv_pcpu_input_arg);
>> + in->sleep_state = HV_SLEEP_STATE_S5;
>> +
>> + (void)hv_do_hypercall(HVCALL_ENTER_SLEEP_STATE, in, NULL);
>
> Should this the error be printed?
>
> Acked-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
I will add a BUG() here, as the hypercall is not supposed to return.
>> + local_irq_restore(flags);
>> +
>> +}
>> #endif
>> --
>> 2.51.0
>
--
Regards,
Praveen K Paladugu
^ permalink raw reply
* [PATCH v7 0/4] Add support for clean shutdown with MSHV
From: Praveen K Paladugu @ 2025-12-05 20:17 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, tglx, mingo, linux-hyperv,
linux-kernel, bp, dave.hansen, x86, hpa, arnd
Cc: anbelski, prapal, easwar.hariharan, nunodasneves, skinsburskii
Add support for clean shutdown of the root partition when running on
MSHV Hypervisor.
v7:
- Fix Makefile condition for building mshv_common.c
- Drop the now unnecessary CONFIG_X86_64 guards
v6:
- Fixed build errors, by adding CONFIG_X86_64 guard
- Moved machine_ops hook definition to ms_hyperv_init_platform
- Addressed review comments in v5
v5:
- Fixed build errors
- Padded struct hv_input_set_system_property for alignment
- Dropped CONFIG_ACPI stub
v4:
- Adopted machine_ops to order invoking HV_ENTER_SLEEP_STATE as the
last step in shutdown sequence.
- This ensures rest of the cleanups are done before powering off
v3:
- Dropped acpi_sleep handlers as they are not used on mshv
- Applied ordering for hv_reboot_notifier
- Fixed build issues on i386, arm64 architectures
v2:
- Addressed review comments from v1.
- Moved all sleep state handling methods under CONFIG_ACPI stub
- - This fixes build issues on non-x86 architectures.
Praveen K Paladugu (4):
fixup! Drivers: hv: Introduce mshv_vtl driver
hyperv: Add definitions for MSHV sleep state configuration
hyperv: Use reboot notifier to configure sleep state
hyperv: Cleanly shutdown root partition with MSHV
arch/x86/hyperv/hv_init.c | 1 +
arch/x86/include/asm/mshyperv.h | 2 +
arch/x86/kernel/cpu/mshyperv.c | 2 +
drivers/hv/Makefile | 2 +-
drivers/hv/mshv_common.c | 99 +++++++++++++++++++++++++++++++++
include/hyperv/hvgdk_mini.h | 4 +-
include/hyperv/hvhdk_mini.h | 40 +++++++++++++
7 files changed, 148 insertions(+), 2 deletions(-)
--
2.51.0
^ permalink raw reply
* [PATCH v7 1/4] fixup! Drivers: hv: Introduce mshv_vtl driver
From: Praveen K Paladugu @ 2025-12-05 20:17 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, tglx, mingo, linux-hyperv,
linux-kernel, bp, dave.hansen, x86, hpa, arnd
Cc: anbelski, prapal, easwar.hariharan, nunodasneves, skinsburskii
In-Reply-To: <20251205201721.7253-1-prapal@linux.microsoft.com>
Drop the spurios "space" character in Makefile condition check
that causes mshv_common.o to be built regardless of the CONFIG settings.
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
Suggested-by: Michael Kelley <mhklinux@outlook.com>
---
drivers/hv/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
index 58b8d07639f3..6d929fb0e13d 100644
--- a/drivers/hv/Makefile
+++ b/drivers/hv/Makefile
@@ -20,6 +20,6 @@ mshv_vtl-y := mshv_vtl_main.o
# Code that must be built-in
obj-$(CONFIG_HYPERV) += hv_common.o
obj-$(subst m,y,$(CONFIG_MSHV_ROOT)) += hv_proc.o
-ifneq ($(CONFIG_MSHV_ROOT) $(CONFIG_MSHV_VTL),)
+ifneq ($(CONFIG_MSHV_ROOT)$(CONFIG_MSHV_VTL),)
obj-y += mshv_common.o
endif
--
2.51.0
^ permalink raw reply related
* [PATCH v7 2/4] hyperv: Add definitions for MSHV sleep state configuration
From: Praveen K Paladugu @ 2025-12-05 20:17 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, tglx, mingo, linux-hyperv,
linux-kernel, bp, dave.hansen, x86, hpa, arnd
Cc: anbelski, prapal, easwar.hariharan, nunodasneves, skinsburskii
In-Reply-To: <20251205201721.7253-1-prapal@linux.microsoft.com>
Add the definitions required to configure sleep states in mshv hypervsior.
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
Co-developed-by: Anatol Belski <anbelski@linux.microsoft.com>
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Reviewed-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
Reviewed-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
Acked-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
include/hyperv/hvgdk_mini.h | 4 +++-
include/hyperv/hvhdk_mini.h | 40 +++++++++++++++++++++++++++++++++++++
2 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h
index 1d5ce11be8b6..04b18d0e37af 100644
--- a/include/hyperv/hvgdk_mini.h
+++ b/include/hyperv/hvgdk_mini.h
@@ -465,19 +465,21 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_ASSIST_PAGE */
#define HVCALL_RESET_DEBUG_SESSION 0x006b
#define HVCALL_MAP_STATS_PAGE 0x006c
#define HVCALL_UNMAP_STATS_PAGE 0x006d
+#define HVCALL_SET_SYSTEM_PROPERTY 0x006f
#define HVCALL_ADD_LOGICAL_PROCESSOR 0x0076
#define HVCALL_GET_SYSTEM_PROPERTY 0x007b
#define HVCALL_MAP_DEVICE_INTERRUPT 0x007c
#define HVCALL_UNMAP_DEVICE_INTERRUPT 0x007d
#define HVCALL_RETARGET_INTERRUPT 0x007e
#define HVCALL_NOTIFY_PARTITION_EVENT 0x0087
+#define HVCALL_ENTER_SLEEP_STATE 0x0084
#define HVCALL_NOTIFY_PORT_RING_EMPTY 0x008b
#define HVCALL_REGISTER_INTERCEPT_RESULT 0x0091
#define HVCALL_ASSERT_VIRTUAL_INTERRUPT 0x0094
#define HVCALL_CREATE_PORT 0x0095
#define HVCALL_CONNECT_PORT 0x0096
#define HVCALL_START_VP 0x0099
-#define HVCALL_GET_VP_INDEX_FROM_APIC_ID 0x009a
+#define HVCALL_GET_VP_INDEX_FROM_APIC_ID 0x009a
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
#define HVCALL_SIGNAL_EVENT_DIRECT 0x00c0
diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h
index f2d7b50de7a4..41a29bf8ec14 100644
--- a/include/hyperv/hvhdk_mini.h
+++ b/include/hyperv/hvhdk_mini.h
@@ -140,6 +140,7 @@ enum hv_snp_status {
enum hv_system_property {
/* Add more values when needed */
+ HV_SYSTEM_PROPERTY_SLEEP_STATE = 3,
HV_SYSTEM_PROPERTY_SCHEDULER_TYPE = 15,
HV_DYNAMIC_PROCESSOR_FEATURE_PROPERTY = 21,
HV_SYSTEM_PROPERTY_CRASHDUMPAREA = 47,
@@ -155,6 +156,19 @@ union hv_pfn_range { /* HV_SPA_PAGE_RANGE */
} __packed;
};
+enum hv_sleep_state {
+ HV_SLEEP_STATE_S1 = 1,
+ HV_SLEEP_STATE_S2 = 2,
+ HV_SLEEP_STATE_S3 = 3,
+ HV_SLEEP_STATE_S4 = 4,
+ HV_SLEEP_STATE_S5 = 5,
+ /*
+ * After hypervisor has received this, any follow up sleep
+ * state registration requests will be rejected.
+ */
+ HV_SLEEP_STATE_LOCK = 6
+};
+
enum hv_dynamic_processor_feature_property {
/* Add more values when needed */
HV_X64_DYNAMIC_PROCESSOR_FEATURE_MAX_ENCRYPTED_PARTITIONS = 13,
@@ -184,6 +198,32 @@ struct hv_output_get_system_property {
};
} __packed;
+struct hv_sleep_state_info {
+ u32 sleep_state; /* enum hv_sleep_state */
+ u8 pm1a_slp_typ;
+ u8 pm1b_slp_typ;
+} __packed;
+
+struct hv_input_set_system_property {
+ u32 property_id; /* enum hv_system_property */
+ u32 reserved;
+ union {
+ /* More fields to be filled in when needed */
+ struct hv_sleep_state_info set_sleep_state_info;
+
+ /*
+ * Add a reserved field to ensure the union is 8-byte aligned as
+ * existing members may not be. This is a temporary measure
+ * until all remaining members are added.
+ */
+ u64 reserved0[8];
+ };
+} __packed;
+
+struct hv_input_enter_sleep_state { /* HV_INPUT_ENTER_SLEEP_STATE */
+ u32 sleep_state; /* enum hv_sleep_state */
+} __packed;
+
struct hv_input_map_stats_page {
u32 type; /* enum hv_stats_object_type */
u32 padding;
--
2.51.0
^ permalink raw reply related
* [PATCH v7 3/4] hyperv: Use reboot notifier to configure sleep state
From: Praveen K Paladugu @ 2025-12-05 20:17 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, tglx, mingo, linux-hyperv,
linux-kernel, bp, dave.hansen, x86, hpa, arnd
Cc: anbelski, prapal, easwar.hariharan, nunodasneves, skinsburskii
In-Reply-To: <20251205201721.7253-1-prapal@linux.microsoft.com>
Configure sleep state information from ACPI in MSHV hypervisor using
a reboot notifier. This data allows the hypervisor to correctly power
off the host during shutdown.
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
Co-developed-by: Anatol Belski <anbelski@linux.microsoft.com>
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Reviewed-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
Reviewed-by: Stansialv Kinsburskii <skinsburskii@linux.miscrosoft.com>
Acked-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
---
arch/x86/hyperv/hv_init.c | 1 +
arch/x86/include/asm/mshyperv.h | 1 +
drivers/hv/mshv_common.c | 78 +++++++++++++++++++++++++++++++++
3 files changed, 80 insertions(+)
diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c
index e28737ec7054..daf97a984b78 100644
--- a/arch/x86/hyperv/hv_init.c
+++ b/arch/x86/hyperv/hv_init.c
@@ -555,6 +555,7 @@ void __init hyperv_init(void)
hv_remap_tsc_clocksource();
hv_root_crash_init();
+ hv_sleep_notifiers_register();
} else {
hypercall_msr.guest_physical_address = vmalloc_to_pfn(hv_hypercall_pg);
wrmsrq(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h
index 10037125099a..62a89debfbce 100644
--- a/arch/x86/include/asm/mshyperv.h
+++ b/arch/x86/include/asm/mshyperv.h
@@ -177,6 +177,7 @@ int hyperv_flush_guest_mapping_range(u64 as,
int hyperv_fill_flush_guest_mapping_list(
struct hv_guest_mapping_flush_list *flush,
u64 start_gfn, u64 end_gfn);
+void hv_sleep_notifiers_register(void);
#ifdef CONFIG_X86_64
void hv_apic_init(void);
diff --git a/drivers/hv/mshv_common.c b/drivers/hv/mshv_common.c
index aa2be51979fd..ee733ba1575e 100644
--- a/drivers/hv/mshv_common.c
+++ b/drivers/hv/mshv_common.c
@@ -14,6 +14,9 @@
#include <asm/mshyperv.h>
#include <linux/resume_user_mode.h>
#include <linux/export.h>
+#include <linux/acpi.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
#include "mshv.h"
@@ -138,3 +141,78 @@ int hv_call_get_partition_property(u64 partition_id,
return 0;
}
EXPORT_SYMBOL_GPL(hv_call_get_partition_property);
+
+/*
+ * Corresponding sleep states have to be initialized in order for a subsequent
+ * HVCALL_ENTER_SLEEP_STATE call to succeed. Currently only S5 state as per
+ * ACPI 6.4 chapter 7.4.2 is relevant, while S1, S2 and S3 can be supported.
+ *
+ * In order to pass proper PM values to mshv, ACPI should be initialized and
+ * should support S5 sleep state when this method is invoked.
+ */
+static int hv_initialize_sleep_states(void)
+{
+ u64 status;
+ unsigned long flags;
+ struct hv_input_set_system_property *in;
+ acpi_status acpi_status;
+ u8 sleep_type_a, sleep_type_b;
+
+ if (!acpi_sleep_state_supported(ACPI_STATE_S5)) {
+ pr_err("%s: S5 sleep state not supported.\n", __func__);
+ return -ENODEV;
+ }
+
+ acpi_status = acpi_get_sleep_type_data(ACPI_STATE_S5, &sleep_type_a,
+ &sleep_type_b);
+ if (ACPI_FAILURE(acpi_status))
+ return -ENODEV;
+
+ local_irq_save(flags);
+ in = *this_cpu_ptr(hyperv_pcpu_input_arg);
+ memset(in, 0, sizeof(*in));
+
+ in->property_id = HV_SYSTEM_PROPERTY_SLEEP_STATE;
+ in->set_sleep_state_info.sleep_state = HV_SLEEP_STATE_S5;
+ in->set_sleep_state_info.pm1a_slp_typ = sleep_type_a;
+ in->set_sleep_state_info.pm1b_slp_typ = sleep_type_b;
+
+ status = hv_do_hypercall(HVCALL_SET_SYSTEM_PROPERTY, in, NULL);
+ local_irq_restore(flags);
+
+ if (!hv_result_success(status)) {
+ hv_status_err(status, "\n");
+ return hv_result_to_errno(status);
+ }
+
+ return 0;
+}
+
+/*
+ * This notifier initializes sleep states in mshv hypervisor which will be
+ * used during power off.
+ */
+static int hv_reboot_notifier_handler(struct notifier_block *this,
+ unsigned long code, void *another)
+{
+ int ret = 0;
+
+ if (code == SYS_HALT || code == SYS_POWER_OFF)
+ ret = hv_initialize_sleep_states();
+
+ return ret ? NOTIFY_DONE : NOTIFY_OK;
+}
+
+static struct notifier_block hv_reboot_notifier = {
+ .notifier_call = hv_reboot_notifier_handler,
+};
+
+void hv_sleep_notifiers_register(void)
+{
+ int ret;
+
+ ret = register_reboot_notifier(&hv_reboot_notifier);
+ if (ret)
+ pr_err("%s: cannot register reboot notifier %d\n", __func__,
+ ret);
+}
--
2.51.0
^ permalink raw reply related
* [PATCH v7 4/4] hyperv: Cleanly shutdown root partition with MSHV
From: Praveen K Paladugu @ 2025-12-05 20:17 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, tglx, mingo, linux-hyperv,
linux-kernel, bp, dave.hansen, x86, hpa, arnd
Cc: anbelski, prapal, easwar.hariharan, nunodasneves, skinsburskii
In-Reply-To: <20251205201721.7253-1-prapal@linux.microsoft.com>
Root partitions running on MSHV currently attempt ACPI power-off, which
MSHV intercepts and triggers a Machine Check Exception (MCE), leading
to a kernel panic.
Root partitions panic with a trace similar to:
[ 81.306348] reboot: Power down
[ 81.314709] mce: [Hardware Error]: CPU 0: Machine Check Exception: 4 Bank 0: b2000000c0060001
[ 81.314711] mce: [Hardware Error]: TSC 3b8cb60a66 PPIN 11d98332458e4ea9
[ 81.314713] mce: [Hardware Error]: PROCESSOR 0:606a6 TIME 1759339405 SOCKET 0 APIC 0 microcode ffffffff
[ 81.314715] mce: [Hardware Error]: Run the above through 'mcelog --ascii'
[ 81.314716] mce: [Hardware Error]: Machine check: Processor context corrupt
[ 81.314717] Kernel panic - not syncing: Fatal machine check
To avoid this, configure the sleep state in the hypervisor and invoke
the HVCALL_ENTER_SLEEP_STATE hypercall as the final step in the shutdown
sequence. This ensures a clean and safe shutdown of the root partition.
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
Co-developed-by: Anatol Belski <anbelski@linux.microsoft.com>
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
Reviewed-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
Acked-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
arch/x86/include/asm/mshyperv.h | 1 +
arch/x86/kernel/cpu/mshyperv.c | 2 ++
drivers/hv/mshv_common.c | 21 +++++++++++++++++++++
3 files changed, 24 insertions(+)
diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h
index 62a89debfbce..eef4c3a5ba28 100644
--- a/arch/x86/include/asm/mshyperv.h
+++ b/arch/x86/include/asm/mshyperv.h
@@ -178,6 +178,7 @@ int hyperv_fill_flush_guest_mapping_list(
struct hv_guest_mapping_flush_list *flush,
u64 start_gfn, u64 end_gfn);
void hv_sleep_notifiers_register(void);
+void hv_machine_power_off(void);
#ifdef CONFIG_X86_64
void hv_apic_init(void);
diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
index fac9953a72ef..579fb2c64cfd 100644
--- a/arch/x86/kernel/cpu/mshyperv.c
+++ b/arch/x86/kernel/cpu/mshyperv.c
@@ -621,6 +621,8 @@ static void __init ms_hyperv_init_platform(void)
#endif
#if IS_ENABLED(CONFIG_HYPERV)
+ if (hv_root_partition())
+ machine_ops.power_off = hv_machine_power_off;
#if defined(CONFIG_KEXEC_CORE)
machine_ops.shutdown = hv_machine_shutdown;
#endif
diff --git a/drivers/hv/mshv_common.c b/drivers/hv/mshv_common.c
index ee733ba1575e..58027b23c206 100644
--- a/drivers/hv/mshv_common.c
+++ b/drivers/hv/mshv_common.c
@@ -216,3 +216,24 @@ void hv_sleep_notifiers_register(void)
pr_err("%s: cannot register reboot notifier %d\n", __func__,
ret);
}
+
+/*
+ * Power off the machine by entering S5 sleep state via Hyper-V hypercall.
+ * This call does not return if successful.
+ */
+void hv_machine_power_off(void)
+{
+ unsigned long flags;
+ struct hv_input_enter_sleep_state *in;
+
+ local_irq_save(flags);
+ in = *this_cpu_ptr(hyperv_pcpu_input_arg);
+ in->sleep_state = HV_SLEEP_STATE_S5;
+
+ (void)hv_do_hypercall(HVCALL_ENTER_SLEEP_STATE, in, NULL);
+ local_irq_restore(flags);
+
+ /* should never reach here */
+ BUG();
+
+}
--
2.51.0
^ permalink raw reply related
* Re: [PATCH v2 1/3] mshv: Ignore second stats page map result failure
From: Stanislav Kinsburskii @ 2025-12-05 22:50 UTC (permalink / raw)
To: Nuno Das Neves
Cc: linux-hyperv, linux-kernel, kys, haiyangz, wei.liu, decui, longli,
mhklinux, prapal, mrathor, paekkaladevi
In-Reply-To: <1764961122-31679-2-git-send-email-nunodasneves@linux.microsoft.com>
On Fri, Dec 05, 2025 at 10:58:40AM -0800, Nuno Das Neves wrote:
> From: Purna Pavan Chandra Aekkaladevi <paekkaladevi@linux.microsoft.com>
>
> Older versions of the hypervisor do not support HV_STATS_AREA_PARENT
> and return HV_STATUS_INVALID_PARAMETER for the second stats page
> mapping request.
>
> This results a failure in module init. Instead of failing, gracefully
> fall back to populating stats_pages[HV_STATS_AREA_PARENT] with the
> already-mapped stats_pages[HV_STATS_AREA_SELF].
>
Reviewed-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> Signed-off-by: Purna Pavan Chandra Aekkaladevi <paekkaladevi@linux.microsoft.com>
> Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
> Reviewed-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> ---
> drivers/hv/mshv_root_hv_call.c | 41 ++++++++++++++++++++++++++++++----
> drivers/hv/mshv_root_main.c | 3 +++
> 2 files changed, 40 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
> index 598eaff4ff29..b1770c7b500c 100644
> --- a/drivers/hv/mshv_root_hv_call.c
> +++ b/drivers/hv/mshv_root_hv_call.c
> @@ -855,6 +855,24 @@ static int hv_call_map_stats_page2(enum hv_stats_object_type type,
> return ret;
> }
>
> +static int
> +hv_stats_get_area_type(enum hv_stats_object_type type,
> + const union hv_stats_object_identity *identity)
> +{
> + switch (type) {
> + case HV_STATS_OBJECT_HYPERVISOR:
> + return identity->hv.stats_area_type;
> + case HV_STATS_OBJECT_LOGICAL_PROCESSOR:
> + return identity->lp.stats_area_type;
> + case HV_STATS_OBJECT_PARTITION:
> + return identity->partition.stats_area_type;
> + case HV_STATS_OBJECT_VP:
> + return identity->vp.stats_area_type;
> + }
> +
> + return -EINVAL;
> +}
> +
> static int hv_call_map_stats_page(enum hv_stats_object_type type,
> const union hv_stats_object_identity *identity,
> void **addr)
> @@ -863,7 +881,7 @@ static int 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 ret = 0;
> + int hv_status, ret = 0;
>
> do {
> local_irq_save(flags);
> @@ -878,11 +896,26 @@ static int hv_call_map_stats_page(enum hv_stats_object_type type,
> pfn = output->map_location;
>
> local_irq_restore(flags);
> - if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
> - ret = hv_result_to_errno(status);
> +
> + hv_status = hv_result(status);
> + if (hv_status != HV_STATUS_INSUFFICIENT_MEMORY) {
> if (hv_result_success(status))
> break;
> - return ret;
> +
> + /*
> + * Older versions of the hypervisor do not support the
> + * PARENT stats area. In this case return "success" but
> + * set the page to NULL. The caller should check for
> + * this case and instead just use the SELF area.
> + */
> + if (hv_stats_get_area_type(type, identity) == HV_STATS_AREA_PARENT &&
> + hv_status == HV_STATUS_INVALID_PARAMETER) {
> + *addr = NULL;
> + return 0;
> + }
> +
> + hv_status_debug(status, "\n");
> + return hv_result_to_errno(status);
> }
>
> ret = hv_call_deposit_pages(NUMA_NO_NODE,
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index bc15d6f6922f..f59a4ab47685 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -905,6 +905,9 @@ static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
> if (err)
> goto unmap_self;
>
> + if (!stats_pages[HV_STATS_AREA_PARENT])
> + stats_pages[HV_STATS_AREA_PARENT] = stats_pages[HV_STATS_AREA_SELF];
> +
> return 0;
>
> unmap_self:
> --
> 2.34.1
>
^ permalink raw reply
* Re: [PATCH v2 2/3] mshv: Add definitions for stats pages
From: Stanislav Kinsburskii @ 2025-12-05 22:51 UTC (permalink / raw)
To: Nuno Das Neves
Cc: linux-hyperv, linux-kernel, kys, haiyangz, wei.liu, decui, longli,
mhklinux, prapal, mrathor, paekkaladevi
In-Reply-To: <1764961122-31679-3-git-send-email-nunodasneves@linux.microsoft.com>
On Fri, Dec 05, 2025 at 10:58:41AM -0800, Nuno Das Neves wrote:
> Add the definitions for hypervisor, logical processor, and partition
> stats pages.
>
> Move the definition for the VP stats page to its rightful place in
> hvhdk.h, and add the missing members.
>
> These enum members retain their CamelCase style, since they are imported
> directly from the hypervisor code They will be stringified when printing
> the stats out, and retain more readability in this form.
>
Acked-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
> ---
> drivers/hv/mshv_root_main.c | 17 --
> include/hyperv/hvhdk.h | 437 ++++++++++++++++++++++++++++++++++++
> 2 files changed, 437 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index f59a4ab47685..19006b788e85 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -38,23 +38,6 @@ MODULE_AUTHOR("Microsoft");
> MODULE_LICENSE("GPL");
> MODULE_DESCRIPTION("Microsoft Hyper-V root partition VMM interface /dev/mshv");
>
> -/* TODO move this to another file when debugfs code is added */
> -enum hv_stats_vp_counters { /* HV_THREAD_COUNTER */
> -#if defined(CONFIG_X86)
> - VpRootDispatchThreadBlocked = 202,
> -#elif defined(CONFIG_ARM64)
> - VpRootDispatchThreadBlocked = 94,
> -#endif
> - VpStatsMaxCounter
> -};
> -
> -struct hv_stats_page {
> - union {
> - u64 vp_cntrs[VpStatsMaxCounter]; /* VP counters */
> - u8 data[HV_HYP_PAGE_SIZE];
> - };
> -} __packed;
> -
> struct mshv_root mshv_root;
>
> enum hv_scheduler_type hv_scheduler_type;
> diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h
> index 469186df7826..51abbcd0ec37 100644
> --- a/include/hyperv/hvhdk.h
> +++ b/include/hyperv/hvhdk.h
> @@ -10,6 +10,443 @@
> #include "hvhdk_mini.h"
> #include "hvgdk.h"
>
> +enum hv_stats_hypervisor_counters { /* HV_HYPERVISOR_COUNTER */
> + HvLogicalProcessors = 1,
> + HvPartitions = 2,
> + HvTotalPages = 3,
> + HvVirtualProcessors = 4,
> + HvMonitoredNotifications = 5,
> + HvModernStandbyEntries = 6,
> + HvPlatformIdleTransitions = 7,
> + HvHypervisorStartupCost = 8,
> + HvIOSpacePages = 10,
> + HvNonEssentialPagesForDump = 11,
> + HvSubsumedPages = 12,
> + HvStatsMaxCounter
> +};
> +
> +enum hv_stats_partition_counters { /* HV_PROCESS_COUNTER */
> + PartitionVirtualProcessors = 1,
> + PartitionTlbSize = 3,
> + PartitionAddressSpaces = 4,
> + PartitionDepositedPages = 5,
> + PartitionGpaPages = 6,
> + PartitionGpaSpaceModifications = 7,
> + PartitionVirtualTlbFlushEntires = 8,
> + PartitionRecommendedTlbSize = 9,
> + PartitionGpaPages4K = 10,
> + PartitionGpaPages2M = 11,
> + PartitionGpaPages1G = 12,
> + PartitionGpaPages512G = 13,
> + PartitionDevicePages4K = 14,
> + PartitionDevicePages2M = 15,
> + PartitionDevicePages1G = 16,
> + PartitionDevicePages512G = 17,
> + PartitionAttachedDevices = 18,
> + PartitionDeviceInterruptMappings = 19,
> + PartitionIoTlbFlushes = 20,
> + PartitionIoTlbFlushCost = 21,
> + PartitionDeviceInterruptErrors = 22,
> + PartitionDeviceDmaErrors = 23,
> + PartitionDeviceInterruptThrottleEvents = 24,
> + PartitionSkippedTimerTicks = 25,
> + PartitionPartitionId = 26,
> +#if IS_ENABLED(CONFIG_X86_64)
> + PartitionNestedTlbSize = 27,
> + PartitionRecommendedNestedTlbSize = 28,
> + PartitionNestedTlbFreeListSize = 29,
> + PartitionNestedTlbTrimmedPages = 30,
> + PartitionPagesShattered = 31,
> + PartitionPagesRecombined = 32,
> + PartitionHwpRequestValue = 33,
> +#elif IS_ENABLED(CONFIG_ARM64)
> + PartitionHwpRequestValue = 27,
> +#endif
> + PartitionStatsMaxCounter
> +};
> +
> +enum hv_stats_vp_counters { /* HV_THREAD_COUNTER */
> + VpTotalRunTime = 1,
> + VpHypervisorRunTime = 2,
> + VpRemoteNodeRunTime = 3,
> + VpNormalizedRunTime = 4,
> + VpIdealCpu = 5,
> + VpHypercallsCount = 7,
> + VpHypercallsTime = 8,
> +#if IS_ENABLED(CONFIG_X86_64)
> + VpPageInvalidationsCount = 9,
> + VpPageInvalidationsTime = 10,
> + VpControlRegisterAccessesCount = 11,
> + VpControlRegisterAccessesTime = 12,
> + VpIoInstructionsCount = 13,
> + VpIoInstructionsTime = 14,
> + VpHltInstructionsCount = 15,
> + VpHltInstructionsTime = 16,
> + VpMwaitInstructionsCount = 17,
> + VpMwaitInstructionsTime = 18,
> + VpCpuidInstructionsCount = 19,
> + VpCpuidInstructionsTime = 20,
> + VpMsrAccessesCount = 21,
> + VpMsrAccessesTime = 22,
> + VpOtherInterceptsCount = 23,
> + VpOtherInterceptsTime = 24,
> + VpExternalInterruptsCount = 25,
> + VpExternalInterruptsTime = 26,
> + VpPendingInterruptsCount = 27,
> + VpPendingInterruptsTime = 28,
> + VpEmulatedInstructionsCount = 29,
> + VpEmulatedInstructionsTime = 30,
> + VpDebugRegisterAccessesCount = 31,
> + VpDebugRegisterAccessesTime = 32,
> + VpPageFaultInterceptsCount = 33,
> + VpPageFaultInterceptsTime = 34,
> + VpGuestPageTableMaps = 35,
> + VpLargePageTlbFills = 36,
> + VpSmallPageTlbFills = 37,
> + VpReflectedGuestPageFaults = 38,
> + VpApicMmioAccesses = 39,
> + VpIoInterceptMessages = 40,
> + VpMemoryInterceptMessages = 41,
> + VpApicEoiAccesses = 42,
> + VpOtherMessages = 43,
> + VpPageTableAllocations = 44,
> + VpLogicalProcessorMigrations = 45,
> + VpAddressSpaceEvictions = 46,
> + VpAddressSpaceSwitches = 47,
> + VpAddressDomainFlushes = 48,
> + VpAddressSpaceFlushes = 49,
> + VpGlobalGvaRangeFlushes = 50,
> + VpLocalGvaRangeFlushes = 51,
> + VpPageTableEvictions = 52,
> + VpPageTableReclamations = 53,
> + VpPageTableResets = 54,
> + VpPageTableValidations = 55,
> + VpApicTprAccesses = 56,
> + VpPageTableWriteIntercepts = 57,
> + VpSyntheticInterrupts = 58,
> + VpVirtualInterrupts = 59,
> + VpApicIpisSent = 60,
> + VpApicSelfIpisSent = 61,
> + VpGpaSpaceHypercalls = 62,
> + VpLogicalProcessorHypercalls = 63,
> + VpLongSpinWaitHypercalls = 64,
> + VpOtherHypercalls = 65,
> + VpSyntheticInterruptHypercalls = 66,
> + VpVirtualInterruptHypercalls = 67,
> + VpVirtualMmuHypercalls = 68,
> + VpVirtualProcessorHypercalls = 69,
> + VpHardwareInterrupts = 70,
> + VpNestedPageFaultInterceptsCount = 71,
> + VpNestedPageFaultInterceptsTime = 72,
> + VpPageScans = 73,
> + VpLogicalProcessorDispatches = 74,
> + VpWaitingForCpuTime = 75,
> + VpExtendedHypercalls = 76,
> + VpExtendedHypercallInterceptMessages = 77,
> + VpMbecNestedPageTableSwitches = 78,
> + VpOtherReflectedGuestExceptions = 79,
> + VpGlobalIoTlbFlushes = 80,
> + VpGlobalIoTlbFlushCost = 81,
> + VpLocalIoTlbFlushes = 82,
> + VpLocalIoTlbFlushCost = 83,
> + VpHypercallsForwardedCount = 84,
> + VpHypercallsForwardingTime = 85,
> + VpPageInvalidationsForwardedCount = 86,
> + VpPageInvalidationsForwardingTime = 87,
> + VpControlRegisterAccessesForwardedCount = 88,
> + VpControlRegisterAccessesForwardingTime = 89,
> + VpIoInstructionsForwardedCount = 90,
> + VpIoInstructionsForwardingTime = 91,
> + VpHltInstructionsForwardedCount = 92,
> + VpHltInstructionsForwardingTime = 93,
> + VpMwaitInstructionsForwardedCount = 94,
> + VpMwaitInstructionsForwardingTime = 95,
> + VpCpuidInstructionsForwardedCount = 96,
> + VpCpuidInstructionsForwardingTime = 97,
> + VpMsrAccessesForwardedCount = 98,
> + VpMsrAccessesForwardingTime = 99,
> + VpOtherInterceptsForwardedCount = 100,
> + VpOtherInterceptsForwardingTime = 101,
> + VpExternalInterruptsForwardedCount = 102,
> + VpExternalInterruptsForwardingTime = 103,
> + VpPendingInterruptsForwardedCount = 104,
> + VpPendingInterruptsForwardingTime = 105,
> + VpEmulatedInstructionsForwardedCount = 106,
> + VpEmulatedInstructionsForwardingTime = 107,
> + VpDebugRegisterAccessesForwardedCount = 108,
> + VpDebugRegisterAccessesForwardingTime = 109,
> + VpPageFaultInterceptsForwardedCount = 110,
> + VpPageFaultInterceptsForwardingTime = 111,
> + VpVmclearEmulationCount = 112,
> + VpVmclearEmulationTime = 113,
> + VpVmptrldEmulationCount = 114,
> + VpVmptrldEmulationTime = 115,
> + VpVmptrstEmulationCount = 116,
> + VpVmptrstEmulationTime = 117,
> + VpVmreadEmulationCount = 118,
> + VpVmreadEmulationTime = 119,
> + VpVmwriteEmulationCount = 120,
> + VpVmwriteEmulationTime = 121,
> + VpVmxoffEmulationCount = 122,
> + VpVmxoffEmulationTime = 123,
> + VpVmxonEmulationCount = 124,
> + VpVmxonEmulationTime = 125,
> + VpNestedVMEntriesCount = 126,
> + VpNestedVMEntriesTime = 127,
> + VpNestedSLATSoftPageFaultsCount = 128,
> + VpNestedSLATSoftPageFaultsTime = 129,
> + VpNestedSLATHardPageFaultsCount = 130,
> + VpNestedSLATHardPageFaultsTime = 131,
> + VpInvEptAllContextEmulationCount = 132,
> + VpInvEptAllContextEmulationTime = 133,
> + VpInvEptSingleContextEmulationCount = 134,
> + VpInvEptSingleContextEmulationTime = 135,
> + VpInvVpidAllContextEmulationCount = 136,
> + VpInvVpidAllContextEmulationTime = 137,
> + VpInvVpidSingleContextEmulationCount = 138,
> + VpInvVpidSingleContextEmulationTime = 139,
> + VpInvVpidSingleAddressEmulationCount = 140,
> + VpInvVpidSingleAddressEmulationTime = 141,
> + VpNestedTlbPageTableReclamations = 142,
> + VpNestedTlbPageTableEvictions = 143,
> + VpFlushGuestPhysicalAddressSpaceHypercalls = 144,
> + VpFlushGuestPhysicalAddressListHypercalls = 145,
> + VpPostedInterruptNotifications = 146,
> + VpPostedInterruptScans = 147,
> + VpTotalCoreRunTime = 148,
> + VpMaximumRunTime = 149,
> + VpHwpRequestContextSwitches = 150,
> + VpWaitingForCpuTimeBucket0 = 151,
> + VpWaitingForCpuTimeBucket1 = 152,
> + VpWaitingForCpuTimeBucket2 = 153,
> + VpWaitingForCpuTimeBucket3 = 154,
> + VpWaitingForCpuTimeBucket4 = 155,
> + VpWaitingForCpuTimeBucket5 = 156,
> + VpWaitingForCpuTimeBucket6 = 157,
> + VpVmloadEmulationCount = 158,
> + VpVmloadEmulationTime = 159,
> + VpVmsaveEmulationCount = 160,
> + VpVmsaveEmulationTime = 161,
> + VpGifInstructionEmulationCount = 162,
> + VpGifInstructionEmulationTime = 163,
> + VpEmulatedErrataSvmInstructions = 164,
> + VpPlaceholder1 = 165,
> + VpPlaceholder2 = 166,
> + VpPlaceholder3 = 167,
> + VpPlaceholder4 = 168,
> + VpPlaceholder5 = 169,
> + VpPlaceholder6 = 170,
> + VpPlaceholder7 = 171,
> + VpPlaceholder8 = 172,
> + VpPlaceholder9 = 173,
> + VpPlaceholder10 = 174,
> + VpSchedulingPriority = 175,
> + VpRdpmcInstructionsCount = 176,
> + VpRdpmcInstructionsTime = 177,
> + VpPerfmonPmuMsrAccessesCount = 178,
> + VpPerfmonLbrMsrAccessesCount = 179,
> + VpPerfmonIptMsrAccessesCount = 180,
> + VpPerfmonInterruptCount = 181,
> + VpVtl1DispatchCount = 182,
> + VpVtl2DispatchCount = 183,
> + VpVtl2DispatchBucket0 = 184,
> + VpVtl2DispatchBucket1 = 185,
> + VpVtl2DispatchBucket2 = 186,
> + VpVtl2DispatchBucket3 = 187,
> + VpVtl2DispatchBucket4 = 188,
> + VpVtl2DispatchBucket5 = 189,
> + VpVtl2DispatchBucket6 = 190,
> + VpVtl1RunTime = 191,
> + VpVtl2RunTime = 192,
> + VpIommuHypercalls = 193,
> + VpCpuGroupHypercalls = 194,
> + VpVsmHypercalls = 195,
> + VpEventLogHypercalls = 196,
> + VpDeviceDomainHypercalls = 197,
> + VpDepositHypercalls = 198,
> + VpSvmHypercalls = 199,
> + VpBusLockAcquisitionCount = 200,
> + VpUnused = 201,
> + VpRootDispatchThreadBlocked = 202,
> +#elif IS_ENABLED(CONFIG_ARM64)
> + VpSysRegAccessesCount = 9,
> + VpSysRegAccessesTime = 10,
> + VpSmcInstructionsCount = 11,
> + VpSmcInstructionsTime = 12,
> + VpOtherInterceptsCount = 13,
> + VpOtherInterceptsTime = 14,
> + VpExternalInterruptsCount = 15,
> + VpExternalInterruptsTime = 16,
> + VpPendingInterruptsCount = 17,
> + VpPendingInterruptsTime = 18,
> + VpGuestPageTableMaps = 19,
> + VpLargePageTlbFills = 20,
> + VpSmallPageTlbFills = 21,
> + VpReflectedGuestPageFaults = 22,
> + VpMemoryInterceptMessages = 23,
> + VpOtherMessages = 24,
> + VpLogicalProcessorMigrations = 25,
> + VpAddressDomainFlushes = 26,
> + VpAddressSpaceFlushes = 27,
> + VpSyntheticInterrupts = 28,
> + VpVirtualInterrupts = 29,
> + VpApicSelfIpisSent = 30,
> + VpGpaSpaceHypercalls = 31,
> + VpLogicalProcessorHypercalls = 32,
> + VpLongSpinWaitHypercalls = 33,
> + VpOtherHypercalls = 34,
> + VpSyntheticInterruptHypercalls = 35,
> + VpVirtualInterruptHypercalls = 36,
> + VpVirtualMmuHypercalls = 37,
> + VpVirtualProcessorHypercalls = 38,
> + VpHardwareInterrupts = 39,
> + VpNestedPageFaultInterceptsCount = 40,
> + VpNestedPageFaultInterceptsTime = 41,
> + VpLogicalProcessorDispatches = 42,
> + VpWaitingForCpuTime = 43,
> + VpExtendedHypercalls = 44,
> + VpExtendedHypercallInterceptMessages = 45,
> + VpMbecNestedPageTableSwitches = 46,
> + VpOtherReflectedGuestExceptions = 47,
> + VpGlobalIoTlbFlushes = 48,
> + VpGlobalIoTlbFlushCost = 49,
> + VpLocalIoTlbFlushes = 50,
> + VpLocalIoTlbFlushCost = 51,
> + VpFlushGuestPhysicalAddressSpaceHypercalls = 52,
> + VpFlushGuestPhysicalAddressListHypercalls = 53,
> + VpPostedInterruptNotifications = 54,
> + VpPostedInterruptScans = 55,
> + VpTotalCoreRunTime = 56,
> + VpMaximumRunTime = 57,
> + VpWaitingForCpuTimeBucket0 = 58,
> + VpWaitingForCpuTimeBucket1 = 59,
> + VpWaitingForCpuTimeBucket2 = 60,
> + VpWaitingForCpuTimeBucket3 = 61,
> + VpWaitingForCpuTimeBucket4 = 62,
> + VpWaitingForCpuTimeBucket5 = 63,
> + VpWaitingForCpuTimeBucket6 = 64,
> + VpHwpRequestContextSwitches = 65,
> + VpPlaceholder2 = 66,
> + VpPlaceholder3 = 67,
> + VpPlaceholder4 = 68,
> + VpPlaceholder5 = 69,
> + VpPlaceholder6 = 70,
> + VpPlaceholder7 = 71,
> + VpPlaceholder8 = 72,
> + VpContentionTime = 73,
> + VpWakeUpTime = 74,
> + VpSchedulingPriority = 75,
> + VpVtl1DispatchCount = 76,
> + VpVtl2DispatchCount = 77,
> + VpVtl2DispatchBucket0 = 78,
> + VpVtl2DispatchBucket1 = 79,
> + VpVtl2DispatchBucket2 = 80,
> + VpVtl2DispatchBucket3 = 81,
> + VpVtl2DispatchBucket4 = 82,
> + VpVtl2DispatchBucket5 = 83,
> + VpVtl2DispatchBucket6 = 84,
> + VpVtl1RunTime = 85,
> + VpVtl2RunTime = 86,
> + VpIommuHypercalls = 87,
> + VpCpuGroupHypercalls = 88,
> + VpVsmHypercalls = 89,
> + VpEventLogHypercalls = 90,
> + VpDeviceDomainHypercalls = 91,
> + VpDepositHypercalls = 92,
> + VpSvmHypercalls = 93,
> + VpLoadAvg = 94,
> + VpRootDispatchThreadBlocked = 95,
> +#endif
> + VpStatsMaxCounter
> +};
> +
> +enum hv_stats_lp_counters { /* HV_CPU_COUNTER */
> + LpGlobalTime = 1,
> + LpTotalRunTime = 2,
> + LpHypervisorRunTime = 3,
> + LpHardwareInterrupts = 4,
> + LpContextSwitches = 5,
> + LpInterProcessorInterrupts = 6,
> + LpSchedulerInterrupts = 7,
> + LpTimerInterrupts = 8,
> + LpInterProcessorInterruptsSent = 9,
> + LpProcessorHalts = 10,
> + LpMonitorTransitionCost = 11,
> + LpContextSwitchTime = 12,
> + LpC1TransitionsCount = 13,
> + LpC1RunTime = 14,
> + LpC2TransitionsCount = 15,
> + LpC2RunTime = 16,
> + LpC3TransitionsCount = 17,
> + LpC3RunTime = 18,
> + LpRootVpIndex = 19,
> + LpIdleSequenceNumber = 20,
> + LpGlobalTscCount = 21,
> + LpActiveTscCount = 22,
> + LpIdleAccumulation = 23,
> + LpReferenceCycleCount0 = 24,
> + LpActualCycleCount0 = 25,
> + LpReferenceCycleCount1 = 26,
> + LpActualCycleCount1 = 27,
> + LpProximityDomainId = 28,
> + LpPostedInterruptNotifications = 29,
> + LpBranchPredictorFlushes = 30,
> +#if IS_ENABLED(CONFIG_X86_64)
> + LpL1DataCacheFlushes = 31,
> + LpImmediateL1DataCacheFlushes = 32,
> + LpMbFlushes = 33,
> + LpCounterRefreshSequenceNumber = 34,
> + LpCounterRefreshReferenceTime = 35,
> + LpIdleAccumulationSnapshot = 36,
> + LpActiveTscCountSnapshot = 37,
> + LpHwpRequestContextSwitches = 38,
> + LpPlaceholder1 = 39,
> + LpPlaceholder2 = 40,
> + LpPlaceholder3 = 41,
> + LpPlaceholder4 = 42,
> + LpPlaceholder5 = 43,
> + LpPlaceholder6 = 44,
> + LpPlaceholder7 = 45,
> + LpPlaceholder8 = 46,
> + LpPlaceholder9 = 47,
> + LpPlaceholder10 = 48,
> + LpReserveGroupId = 49,
> + LpRunningPriority = 50,
> + LpPerfmonInterruptCount = 51,
> +#elif IS_ENABLED(CONFIG_ARM64)
> + LpCounterRefreshSequenceNumber = 31,
> + LpCounterRefreshReferenceTime = 32,
> + LpIdleAccumulationSnapshot = 33,
> + LpActiveTscCountSnapshot = 34,
> + LpHwpRequestContextSwitches = 35,
> + LpPlaceholder2 = 36,
> + LpPlaceholder3 = 37,
> + LpPlaceholder4 = 38,
> + LpPlaceholder5 = 39,
> + LpPlaceholder6 = 40,
> + LpPlaceholder7 = 41,
> + LpPlaceholder8 = 42,
> + LpPlaceholder9 = 43,
> + LpSchLocalRunListSize = 44,
> + LpReserveGroupId = 45,
> + LpRunningPriority = 46,
> +#endif
> + LpStatsMaxCounter
> +};
> +
> +/*
> + * Hypervisor statsitics page format
> + */
> +struct hv_stats_page {
> + union {
> + u64 hv_cntrs[HvStatsMaxCounter]; /* Hypervisor counters */
> + u64 pt_cntrs[PartitionStatsMaxCounter]; /* Partition counters */
> + u64 vp_cntrs[VpStatsMaxCounter]; /* VP counters */
> + u64 lp_cntrs[LpStatsMaxCounter]; /* LP counters */
> + u8 data[HV_HYP_PAGE_SIZE];
> + };
> +} __packed;
> +
> /* Bits for dirty mask of hv_vp_register_page */
> #define HV_X64_REGISTER_CLASS_GENERAL 0
> #define HV_X64_REGISTER_CLASS_IP 1
> --
> 2.34.1
^ permalink raw reply
* Re: [PATCH v7 1/4] fixup! Drivers: hv: Introduce mshv_vtl driver
From: Wei Liu @ 2025-12-05 23:06 UTC (permalink / raw)
To: Praveen K Paladugu
Cc: kys, haiyangz, wei.liu, decui, tglx, mingo, linux-hyperv,
linux-kernel, bp, dave.hansen, x86, hpa, arnd, anbelski,
easwar.hariharan, nunodasneves, skinsburskii
In-Reply-To: <20251205201721.7253-2-prapal@linux.microsoft.com>
Please provide a proper commit subject.
On Fri, Dec 05, 2025 at 02:17:05PM -0600, Praveen K Paladugu wrote:
> Drop the spurios "space" character in Makefile condition check
> that causes mshv_common.o to be built regardless of the CONFIG settings.
>
> Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
> Suggested-by: Michael Kelley <mhklinux@outlook.com>
This should come with a Fixes: tag.
> ---
> drivers/hv/Makefile | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
> index 58b8d07639f3..6d929fb0e13d 100644
> --- a/drivers/hv/Makefile
> +++ b/drivers/hv/Makefile
> @@ -20,6 +20,6 @@ mshv_vtl-y := mshv_vtl_main.o
> # Code that must be built-in
> obj-$(CONFIG_HYPERV) += hv_common.o
> obj-$(subst m,y,$(CONFIG_MSHV_ROOT)) += hv_proc.o
> -ifneq ($(CONFIG_MSHV_ROOT) $(CONFIG_MSHV_VTL),)
> +ifneq ($(CONFIG_MSHV_ROOT)$(CONFIG_MSHV_VTL),)
> obj-y += mshv_common.o
> endif
> --
> 2.51.0
>
^ permalink raw reply
* Re: [PATCH v2 3/3] mshv: Add debugfs to view hypervisor statistics
From: Stanislav Kinsburskii @ 2025-12-05 23:06 UTC (permalink / raw)
To: Nuno Das Neves
Cc: linux-hyperv, linux-kernel, kys, haiyangz, wei.liu, decui, longli,
mhklinux, prapal, mrathor, paekkaladevi, Jinank Jain
In-Reply-To: <1764961122-31679-4-git-send-email-nunodasneves@linux.microsoft.com>
On Fri, Dec 05, 2025 at 10:58:42AM -0800, Nuno Das Neves wrote:
> Introduce a debugfs interface to expose root and child partition stats
> when running with mshv_root.
>
> Create a debugfs directory "mshv" containing 'stats' files organized by
> type and id. A stats file contains a number of counters depending on
> its type. e.g. an excerpt from a VP stats file:
>
> TotalRunTime : 1997602722
> HypervisorRunTime : 649671371
> RemoteNodeRunTime : 0
> NormalizedRunTime : 1997602721
> IdealCpu : 0
> HypercallsCount : 1708169
> HypercallsTime : 111914774
> PageInvalidationsCount : 0
> PageInvalidationsTime : 0
>
> On a root partition with some active child partitions, the entire
> directory structure may look like:
>
> mshv/
> stats # hypervisor stats
> lp/ # logical processors
> 0/ # LP id
> stats # LP 0 stats
> 1/
> 2/
> 3/
> partition/ # partition stats
> 1/ # root partition id
> stats # root partition stats
> vp/ # root virtual processors
> 0/ # root VP id
> stats # root VP 0 stats
> 1/
> 2/
> 3/
> 42/ # child partition id
> stats # child partition stats
> vp/ # child VPs
> 0/ # child VP id
> stats # child VP 0 stats
> 1/
> 43/
> 55/
>
> On L1VH, some stats are not present as it does not own the hardware
> like the root partition does:
> - The hypervisor and lp stats are not present
> - L1VH's partition directory is named "self" because it can't get its
> own id
> - Some of L1VH's partition and VP stats fields are not populated, because
> it can't map its own HV_STATS_AREA_PARENT page.
>
<snip>
> +static void __init *lp_debugfs_stats_create(u32 lp_index, struct dentry *parent)
It would be better to return struct hv_stats_page from this (and other
functions).
> +{
> + struct dentry *dentry;
> + void *stats;
> +
> + stats = mshv_lp_stats_map(lp_index);
> + if (IS_ERR(stats))
> + return stats;
> +
> + dentry = debugfs_create_file("stats", 0400, parent,
> + stats, &lp_stats_fops);
> + if (IS_ERR(dentry)) {
> + mshv_lp_stats_unmap(lp_index, stats);
> + return dentry;
This is sloppy as it returns struct dentry instead of struct
hv_stats_page and using void here simply sweeps the problem under the
carpet which otherwise the compiler would catch.
How about using ERR_CAST instead as it will make this behavior explicit?
> + }
> + return stats;
> +}
> +
> +static int __init lp_debugfs_create(u32 lp_index, struct dentry *parent)
> +{
> + struct dentry *idx;
> + char lp_idx_str[U32_BUF_SZ];
> + void *stats;
> + int err;
> +
> + sprintf(lp_idx_str, "%u", lp_index);
> +
> + idx = debugfs_create_dir(lp_idx_str, parent);
> + if (IS_ERR(idx))
> + return PTR_ERR(idx);
> +
> + stats = lp_debugfs_stats_create(lp_index, idx);
> + if (IS_ERR(stats)) {
> + err = PTR_ERR(stats);
> + goto remove_debugfs_lp_idx;
> + }
> +
> + return 0;
> +
> +remove_debugfs_lp_idx:
> + debugfs_remove_recursive(idx);
> + return err;
> +}
> +
> +static void mshv_debugfs_lp_remove(void)
> +{
> + int lp_index;
> +
> + debugfs_remove_recursive(mshv_debugfs_lp);
> +
> + for (lp_index = 0; lp_index < mshv_lps_count; lp_index++)
> + mshv_lp_stats_unmap(lp_index, NULL);
> +}
> +
> +static int __init mshv_debugfs_lp_create(struct dentry *parent)
> +{
> + struct dentry *lp_dir;
> + int err, lp_index;
> +
> + lp_dir = debugfs_create_dir("lp", parent);
> + if (IS_ERR(lp_dir))
> + return PTR_ERR(lp_dir);
> +
> + for (lp_index = 0; lp_index < mshv_lps_count; lp_index++) {
> + err = lp_debugfs_create(lp_index, lp_dir);
> + if (err)
> + goto remove_debugfs_lps;
> + }
> +
> + mshv_debugfs_lp = lp_dir;
> +
> + return 0;
> +
> +remove_debugfs_lps:
> + for (lp_index -= 1; lp_index >= 0; lp_index--)
> + mshv_lp_stats_unmap(lp_index, NULL);
> + debugfs_remove_recursive(lp_dir);
> + return err;
> +}
> +
> +static int vp_stats_show(struct seq_file *m, void *v)
> +{
> + const struct hv_stats_page **pstats = m->private;
> +
> +#define VP_SEQ_PRINTF(cnt) \
> +do { \
> + if (pstats[HV_STATS_AREA_SELF]->vp_cntrs[Vp##cnt]) \
> + seq_printf(m, "%-30s: %llu\n", __stringify(cnt), \
> + pstats[HV_STATS_AREA_SELF]->vp_cntrs[Vp##cnt]); \
> + else \
> + seq_printf(m, "%-30s: %llu\n", __stringify(cnt), \
> + pstats[HV_STATS_AREA_PARENT]->vp_cntrs[Vp##cnt]); \
> +} while (0)
> +
> + VP_SEQ_PRINTF(TotalRunTime);
> + VP_SEQ_PRINTF(HypervisorRunTime);
> + VP_SEQ_PRINTF(RemoteNodeRunTime);
> + VP_SEQ_PRINTF(NormalizedRunTime);
> + VP_SEQ_PRINTF(IdealCpu);
> + VP_SEQ_PRINTF(HypercallsCount);
> + VP_SEQ_PRINTF(HypercallsTime);
> +#if IS_ENABLED(CONFIG_X86_64)
> + VP_SEQ_PRINTF(PageInvalidationsCount);
> + VP_SEQ_PRINTF(PageInvalidationsTime);
> + VP_SEQ_PRINTF(ControlRegisterAccessesCount);
> + VP_SEQ_PRINTF(ControlRegisterAccessesTime);
> + VP_SEQ_PRINTF(IoInstructionsCount);
> + VP_SEQ_PRINTF(IoInstructionsTime);
> + VP_SEQ_PRINTF(HltInstructionsCount);
> + VP_SEQ_PRINTF(HltInstructionsTime);
> + VP_SEQ_PRINTF(MwaitInstructionsCount);
> + VP_SEQ_PRINTF(MwaitInstructionsTime);
> + VP_SEQ_PRINTF(CpuidInstructionsCount);
> + VP_SEQ_PRINTF(CpuidInstructionsTime);
> + VP_SEQ_PRINTF(MsrAccessesCount);
> + VP_SEQ_PRINTF(MsrAccessesTime);
> + VP_SEQ_PRINTF(OtherInterceptsCount);
> + VP_SEQ_PRINTF(OtherInterceptsTime);
> + VP_SEQ_PRINTF(ExternalInterruptsCount);
> + VP_SEQ_PRINTF(ExternalInterruptsTime);
> + VP_SEQ_PRINTF(PendingInterruptsCount);
> + VP_SEQ_PRINTF(PendingInterruptsTime);
> + VP_SEQ_PRINTF(EmulatedInstructionsCount);
> + VP_SEQ_PRINTF(EmulatedInstructionsTime);
> + VP_SEQ_PRINTF(DebugRegisterAccessesCount);
> + VP_SEQ_PRINTF(DebugRegisterAccessesTime);
> + VP_SEQ_PRINTF(PageFaultInterceptsCount);
> + VP_SEQ_PRINTF(PageFaultInterceptsTime);
> + VP_SEQ_PRINTF(GuestPageTableMaps);
> + VP_SEQ_PRINTF(LargePageTlbFills);
> + VP_SEQ_PRINTF(SmallPageTlbFills);
> + VP_SEQ_PRINTF(ReflectedGuestPageFaults);
> + VP_SEQ_PRINTF(ApicMmioAccesses);
> + VP_SEQ_PRINTF(IoInterceptMessages);
> + VP_SEQ_PRINTF(MemoryInterceptMessages);
> + VP_SEQ_PRINTF(ApicEoiAccesses);
> + VP_SEQ_PRINTF(OtherMessages);
> + VP_SEQ_PRINTF(PageTableAllocations);
> + VP_SEQ_PRINTF(LogicalProcessorMigrations);
> + VP_SEQ_PRINTF(AddressSpaceEvictions);
> + VP_SEQ_PRINTF(AddressSpaceSwitches);
> + VP_SEQ_PRINTF(AddressDomainFlushes);
> + VP_SEQ_PRINTF(AddressSpaceFlushes);
> + VP_SEQ_PRINTF(GlobalGvaRangeFlushes);
> + VP_SEQ_PRINTF(LocalGvaRangeFlushes);
> + VP_SEQ_PRINTF(PageTableEvictions);
> + VP_SEQ_PRINTF(PageTableReclamations);
> + VP_SEQ_PRINTF(PageTableResets);
> + VP_SEQ_PRINTF(PageTableValidations);
> + VP_SEQ_PRINTF(ApicTprAccesses);
> + VP_SEQ_PRINTF(PageTableWriteIntercepts);
> + VP_SEQ_PRINTF(SyntheticInterrupts);
> + VP_SEQ_PRINTF(VirtualInterrupts);
> + VP_SEQ_PRINTF(ApicIpisSent);
> + VP_SEQ_PRINTF(ApicSelfIpisSent);
> + VP_SEQ_PRINTF(GpaSpaceHypercalls);
> + VP_SEQ_PRINTF(LogicalProcessorHypercalls);
> + VP_SEQ_PRINTF(LongSpinWaitHypercalls);
> + VP_SEQ_PRINTF(OtherHypercalls);
> + VP_SEQ_PRINTF(SyntheticInterruptHypercalls);
> + VP_SEQ_PRINTF(VirtualInterruptHypercalls);
> + VP_SEQ_PRINTF(VirtualMmuHypercalls);
> + VP_SEQ_PRINTF(VirtualProcessorHypercalls);
> + VP_SEQ_PRINTF(HardwareInterrupts);
> + VP_SEQ_PRINTF(NestedPageFaultInterceptsCount);
> + VP_SEQ_PRINTF(NestedPageFaultInterceptsTime);
> + VP_SEQ_PRINTF(PageScans);
> + VP_SEQ_PRINTF(LogicalProcessorDispatches);
> + VP_SEQ_PRINTF(WaitingForCpuTime);
> + VP_SEQ_PRINTF(ExtendedHypercalls);
> + VP_SEQ_PRINTF(ExtendedHypercallInterceptMessages);
> + VP_SEQ_PRINTF(MbecNestedPageTableSwitches);
> + VP_SEQ_PRINTF(OtherReflectedGuestExceptions);
> + VP_SEQ_PRINTF(GlobalIoTlbFlushes);
> + VP_SEQ_PRINTF(GlobalIoTlbFlushCost);
> + VP_SEQ_PRINTF(LocalIoTlbFlushes);
> + VP_SEQ_PRINTF(LocalIoTlbFlushCost);
> + VP_SEQ_PRINTF(HypercallsForwardedCount);
> + VP_SEQ_PRINTF(HypercallsForwardingTime);
> + VP_SEQ_PRINTF(PageInvalidationsForwardedCount);
> + VP_SEQ_PRINTF(PageInvalidationsForwardingTime);
> + VP_SEQ_PRINTF(ControlRegisterAccessesForwardedCount);
> + VP_SEQ_PRINTF(ControlRegisterAccessesForwardingTime);
> + VP_SEQ_PRINTF(IoInstructionsForwardedCount);
> + VP_SEQ_PRINTF(IoInstructionsForwardingTime);
> + VP_SEQ_PRINTF(HltInstructionsForwardedCount);
> + VP_SEQ_PRINTF(HltInstructionsForwardingTime);
> + VP_SEQ_PRINTF(MwaitInstructionsForwardedCount);
> + VP_SEQ_PRINTF(MwaitInstructionsForwardingTime);
> + VP_SEQ_PRINTF(CpuidInstructionsForwardedCount);
> + VP_SEQ_PRINTF(CpuidInstructionsForwardingTime);
> + VP_SEQ_PRINTF(MsrAccessesForwardedCount);
> + VP_SEQ_PRINTF(MsrAccessesForwardingTime);
> + VP_SEQ_PRINTF(OtherInterceptsForwardedCount);
> + VP_SEQ_PRINTF(OtherInterceptsForwardingTime);
> + VP_SEQ_PRINTF(ExternalInterruptsForwardedCount);
> + VP_SEQ_PRINTF(ExternalInterruptsForwardingTime);
> + VP_SEQ_PRINTF(PendingInterruptsForwardedCount);
> + VP_SEQ_PRINTF(PendingInterruptsForwardingTime);
> + VP_SEQ_PRINTF(EmulatedInstructionsForwardedCount);
> + VP_SEQ_PRINTF(EmulatedInstructionsForwardingTime);
> + VP_SEQ_PRINTF(DebugRegisterAccessesForwardedCount);
> + VP_SEQ_PRINTF(DebugRegisterAccessesForwardingTime);
> + VP_SEQ_PRINTF(PageFaultInterceptsForwardedCount);
> + VP_SEQ_PRINTF(PageFaultInterceptsForwardingTime);
> + VP_SEQ_PRINTF(VmclearEmulationCount);
> + VP_SEQ_PRINTF(VmclearEmulationTime);
> + VP_SEQ_PRINTF(VmptrldEmulationCount);
> + VP_SEQ_PRINTF(VmptrldEmulationTime);
> + VP_SEQ_PRINTF(VmptrstEmulationCount);
> + VP_SEQ_PRINTF(VmptrstEmulationTime);
> + VP_SEQ_PRINTF(VmreadEmulationCount);
> + VP_SEQ_PRINTF(VmreadEmulationTime);
> + VP_SEQ_PRINTF(VmwriteEmulationCount);
> + VP_SEQ_PRINTF(VmwriteEmulationTime);
> + VP_SEQ_PRINTF(VmxoffEmulationCount);
> + VP_SEQ_PRINTF(VmxoffEmulationTime);
> + VP_SEQ_PRINTF(VmxonEmulationCount);
> + VP_SEQ_PRINTF(VmxonEmulationTime);
> + VP_SEQ_PRINTF(NestedVMEntriesCount);
> + VP_SEQ_PRINTF(NestedVMEntriesTime);
> + VP_SEQ_PRINTF(NestedSLATSoftPageFaultsCount);
> + VP_SEQ_PRINTF(NestedSLATSoftPageFaultsTime);
> + VP_SEQ_PRINTF(NestedSLATHardPageFaultsCount);
> + VP_SEQ_PRINTF(NestedSLATHardPageFaultsTime);
> + VP_SEQ_PRINTF(InvEptAllContextEmulationCount);
> + VP_SEQ_PRINTF(InvEptAllContextEmulationTime);
> + VP_SEQ_PRINTF(InvEptSingleContextEmulationCount);
> + VP_SEQ_PRINTF(InvEptSingleContextEmulationTime);
> + VP_SEQ_PRINTF(InvVpidAllContextEmulationCount);
> + VP_SEQ_PRINTF(InvVpidAllContextEmulationTime);
> + VP_SEQ_PRINTF(InvVpidSingleContextEmulationCount);
> + VP_SEQ_PRINTF(InvVpidSingleContextEmulationTime);
> + VP_SEQ_PRINTF(InvVpidSingleAddressEmulationCount);
> + VP_SEQ_PRINTF(InvVpidSingleAddressEmulationTime);
> + VP_SEQ_PRINTF(NestedTlbPageTableReclamations);
> + VP_SEQ_PRINTF(NestedTlbPageTableEvictions);
> + VP_SEQ_PRINTF(FlushGuestPhysicalAddressSpaceHypercalls);
> + VP_SEQ_PRINTF(FlushGuestPhysicalAddressListHypercalls);
> + VP_SEQ_PRINTF(PostedInterruptNotifications);
> + VP_SEQ_PRINTF(PostedInterruptScans);
> + VP_SEQ_PRINTF(TotalCoreRunTime);
> + VP_SEQ_PRINTF(MaximumRunTime);
> + VP_SEQ_PRINTF(HwpRequestContextSwitches);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket0);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket1);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket2);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket3);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket4);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket5);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket6);
> + VP_SEQ_PRINTF(VmloadEmulationCount);
> + VP_SEQ_PRINTF(VmloadEmulationTime);
> + VP_SEQ_PRINTF(VmsaveEmulationCount);
> + VP_SEQ_PRINTF(VmsaveEmulationTime);
> + VP_SEQ_PRINTF(GifInstructionEmulationCount);
> + VP_SEQ_PRINTF(GifInstructionEmulationTime);
> + VP_SEQ_PRINTF(EmulatedErrataSvmInstructions);
> + VP_SEQ_PRINTF(Placeholder1);
> + VP_SEQ_PRINTF(Placeholder2);
> + VP_SEQ_PRINTF(Placeholder3);
> + VP_SEQ_PRINTF(Placeholder4);
> + VP_SEQ_PRINTF(Placeholder5);
> + VP_SEQ_PRINTF(Placeholder6);
> + VP_SEQ_PRINTF(Placeholder7);
> + VP_SEQ_PRINTF(Placeholder8);
> + VP_SEQ_PRINTF(Placeholder9);
> + VP_SEQ_PRINTF(Placeholder10);
> + VP_SEQ_PRINTF(SchedulingPriority);
> + VP_SEQ_PRINTF(RdpmcInstructionsCount);
> + VP_SEQ_PRINTF(RdpmcInstructionsTime);
> + VP_SEQ_PRINTF(PerfmonPmuMsrAccessesCount);
> + VP_SEQ_PRINTF(PerfmonLbrMsrAccessesCount);
> + VP_SEQ_PRINTF(PerfmonIptMsrAccessesCount);
> + VP_SEQ_PRINTF(PerfmonInterruptCount);
> + VP_SEQ_PRINTF(Vtl1DispatchCount);
> + VP_SEQ_PRINTF(Vtl2DispatchCount);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket0);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket1);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket2);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket3);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket4);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket5);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket6);
> + VP_SEQ_PRINTF(Vtl1RunTime);
> + VP_SEQ_PRINTF(Vtl2RunTime);
> + VP_SEQ_PRINTF(IommuHypercalls);
> + VP_SEQ_PRINTF(CpuGroupHypercalls);
> + VP_SEQ_PRINTF(VsmHypercalls);
> + VP_SEQ_PRINTF(EventLogHypercalls);
> + VP_SEQ_PRINTF(DeviceDomainHypercalls);
> + VP_SEQ_PRINTF(DepositHypercalls);
> + VP_SEQ_PRINTF(SvmHypercalls);
> + VP_SEQ_PRINTF(BusLockAcquisitionCount);
> +#elif IS_ENABLED(CONFIG_ARM64)
> + VP_SEQ_PRINTF(SysRegAccessesCount);
> + VP_SEQ_PRINTF(SysRegAccessesTime);
> + VP_SEQ_PRINTF(SmcInstructionsCount);
> + VP_SEQ_PRINTF(SmcInstructionsTime);
> + VP_SEQ_PRINTF(OtherInterceptsCount);
> + VP_SEQ_PRINTF(OtherInterceptsTime);
> + VP_SEQ_PRINTF(ExternalInterruptsCount);
> + VP_SEQ_PRINTF(ExternalInterruptsTime);
> + VP_SEQ_PRINTF(PendingInterruptsCount);
> + VP_SEQ_PRINTF(PendingInterruptsTime);
> + VP_SEQ_PRINTF(GuestPageTableMaps);
> + VP_SEQ_PRINTF(LargePageTlbFills);
> + VP_SEQ_PRINTF(SmallPageTlbFills);
> + VP_SEQ_PRINTF(ReflectedGuestPageFaults);
> + VP_SEQ_PRINTF(MemoryInterceptMessages);
> + VP_SEQ_PRINTF(OtherMessages);
> + VP_SEQ_PRINTF(LogicalProcessorMigrations);
> + VP_SEQ_PRINTF(AddressDomainFlushes);
> + VP_SEQ_PRINTF(AddressSpaceFlushes);
> + VP_SEQ_PRINTF(SyntheticInterrupts);
> + VP_SEQ_PRINTF(VirtualInterrupts);
> + VP_SEQ_PRINTF(ApicSelfIpisSent);
> + VP_SEQ_PRINTF(GpaSpaceHypercalls);
> + VP_SEQ_PRINTF(LogicalProcessorHypercalls);
> + VP_SEQ_PRINTF(LongSpinWaitHypercalls);
> + VP_SEQ_PRINTF(OtherHypercalls);
> + VP_SEQ_PRINTF(SyntheticInterruptHypercalls);
> + VP_SEQ_PRINTF(VirtualInterruptHypercalls);
> + VP_SEQ_PRINTF(VirtualMmuHypercalls);
> + VP_SEQ_PRINTF(VirtualProcessorHypercalls);
> + VP_SEQ_PRINTF(HardwareInterrupts);
> + VP_SEQ_PRINTF(NestedPageFaultInterceptsCount);
> + VP_SEQ_PRINTF(NestedPageFaultInterceptsTime);
> + VP_SEQ_PRINTF(LogicalProcessorDispatches);
> + VP_SEQ_PRINTF(WaitingForCpuTime);
> + VP_SEQ_PRINTF(ExtendedHypercalls);
> + VP_SEQ_PRINTF(ExtendedHypercallInterceptMessages);
> + VP_SEQ_PRINTF(MbecNestedPageTableSwitches);
> + VP_SEQ_PRINTF(OtherReflectedGuestExceptions);
> + VP_SEQ_PRINTF(GlobalIoTlbFlushes);
> + VP_SEQ_PRINTF(GlobalIoTlbFlushCost);
> + VP_SEQ_PRINTF(LocalIoTlbFlushes);
> + VP_SEQ_PRINTF(LocalIoTlbFlushCost);
> + VP_SEQ_PRINTF(FlushGuestPhysicalAddressSpaceHypercalls);
> + VP_SEQ_PRINTF(FlushGuestPhysicalAddressListHypercalls);
> + VP_SEQ_PRINTF(PostedInterruptNotifications);
> + VP_SEQ_PRINTF(PostedInterruptScans);
> + VP_SEQ_PRINTF(TotalCoreRunTime);
> + VP_SEQ_PRINTF(MaximumRunTime);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket0);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket1);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket2);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket3);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket4);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket5);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket6);
> + VP_SEQ_PRINTF(HwpRequestContextSwitches);
> + VP_SEQ_PRINTF(Placeholder2);
> + VP_SEQ_PRINTF(Placeholder3);
> + VP_SEQ_PRINTF(Placeholder4);
> + VP_SEQ_PRINTF(Placeholder5);
> + VP_SEQ_PRINTF(Placeholder6);
> + VP_SEQ_PRINTF(Placeholder7);
> + VP_SEQ_PRINTF(Placeholder8);
> + VP_SEQ_PRINTF(ContentionTime);
> + VP_SEQ_PRINTF(WakeUpTime);
> + VP_SEQ_PRINTF(SchedulingPriority);
> + VP_SEQ_PRINTF(Vtl1DispatchCount);
> + VP_SEQ_PRINTF(Vtl2DispatchCount);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket0);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket1);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket2);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket3);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket4);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket5);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket6);
> + VP_SEQ_PRINTF(Vtl1RunTime);
> + VP_SEQ_PRINTF(Vtl2RunTime);
> + VP_SEQ_PRINTF(IommuHypercalls);
> + VP_SEQ_PRINTF(CpuGroupHypercalls);
> + VP_SEQ_PRINTF(VsmHypercalls);
> + VP_SEQ_PRINTF(EventLogHypercalls);
> + VP_SEQ_PRINTF(DeviceDomainHypercalls);
> + VP_SEQ_PRINTF(DepositHypercalls);
> + VP_SEQ_PRINTF(SvmHypercalls);
> +#endif
> +
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(vp_stats);
> +
> +static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index, void *stats_page_addr,
> + enum hv_stats_area_type stats_area_type)
> +{
> + union hv_stats_object_identity identity = {
> + .vp.partition_id = partition_id,
> + .vp.vp_index = vp_index,
> + .vp.stats_area_type = stats_area_type,
> + };
> + int err;
> +
> + err = hv_unmap_stats_page(HV_STATS_OBJECT_VP, stats_page_addr, &identity);
> + if (err)
> + pr_err("%s: failed to unmap partition %llu vp %u %s stats, err: %d\n",
> + __func__, partition_id, vp_index,
> + (stats_area_type == HV_STATS_AREA_SELF) ? "self" : "parent",
> + err);
> +}
> +
> +static void *mshv_vp_stats_map(u64 partition_id, u32 vp_index,
> + enum hv_stats_area_type stats_area_type)
> +{
> + union hv_stats_object_identity identity = {
> + .vp.partition_id = partition_id,
> + .vp.vp_index = vp_index,
> + .vp.stats_area_type = stats_area_type,
> + };
> + void *stats;
> + int err;
> +
> + err = hv_map_stats_page(HV_STATS_OBJECT_VP, &identity, &stats);
> + if (err) {
> + pr_err("%s: failed to map partition %llu vp %u %s stats, err: %d\n",
> + __func__, partition_id, vp_index,
> + (stats_area_type == HV_STATS_AREA_SELF) ? "self" : "parent",
> + err);
> + return ERR_PTR(err);
> + }
> + return stats;
> +}
> +
> +static int vp_debugfs_stats_create(u64 partition_id, u32 vp_index,
> + struct dentry **vp_stats_ptr,
> + struct dentry *parent)
> +{
> + struct dentry *dentry;
> + struct hv_stats_page **pstats;
> + int err;
> +
> + pstats = kcalloc(2, sizeof(struct hv_stats_page *), GFP_KERNEL_ACCOUNT);
> + if (!pstats)
> + return -ENOMEM;
> +
> + pstats[HV_STATS_AREA_SELF] = mshv_vp_stats_map(partition_id, vp_index,
> + HV_STATS_AREA_SELF);
> + if (IS_ERR(pstats[HV_STATS_AREA_SELF])) {
> + err = PTR_ERR(pstats[HV_STATS_AREA_SELF]);
> + goto cleanup;
> + }
> +
> + /*
> + * L1VH partition cannot access its vp stats in parent area.
> + */
> + if (is_l1vh_parent(partition_id)) {
> + pstats[HV_STATS_AREA_PARENT] = pstats[HV_STATS_AREA_SELF];
> + } else {
> + pstats[HV_STATS_AREA_PARENT] = mshv_vp_stats_map(
> + partition_id, vp_index, HV_STATS_AREA_PARENT);
> + if (IS_ERR(pstats[HV_STATS_AREA_PARENT])) {
> + err = PTR_ERR(pstats[HV_STATS_AREA_PARENT]);
> + goto unmap_self;
> + }
> + if (!pstats[HV_STATS_AREA_PARENT])
> + pstats[HV_STATS_AREA_PARENT] = pstats[HV_STATS_AREA_SELF];
> + }
> +
> + dentry = debugfs_create_file("stats", 0400, parent,
> + pstats, &vp_stats_fops);
> + if (IS_ERR(dentry)) {
> + err = PTR_ERR(dentry);
> + goto unmap_vp_stats;
> + }
> +
> + *vp_stats_ptr = dentry;
> + return 0;
> +
> +unmap_vp_stats:
> + if (pstats[HV_STATS_AREA_PARENT] != pstats[HV_STATS_AREA_SELF])
> + mshv_vp_stats_unmap(partition_id, vp_index, pstats[HV_STATS_AREA_PARENT],
> + HV_STATS_AREA_PARENT);
> +unmap_self:
> + mshv_vp_stats_unmap(partition_id, vp_index, pstats[HV_STATS_AREA_SELF],
> + HV_STATS_AREA_SELF);
> +cleanup:
> + kfree(pstats);
> + return err;
> +}
> +
> +static void vp_debugfs_remove(u64 partition_id, u32 vp_index,
> + struct dentry *vp_stats)
> +{
> + struct hv_stats_page **pstats = NULL;
> + void *stats;
> +
> + pstats = vp_stats->d_inode->i_private;
> + debugfs_remove_recursive(vp_stats->d_parent);
> + if (pstats[HV_STATS_AREA_PARENT] != pstats[HV_STATS_AREA_SELF]) {
> + stats = pstats[HV_STATS_AREA_PARENT];
> + mshv_vp_stats_unmap(partition_id, vp_index, stats,
> + HV_STATS_AREA_PARENT);
> + }
> +
> + stats = pstats[HV_STATS_AREA_SELF];
> + mshv_vp_stats_unmap(partition_id, vp_index, stats, HV_STATS_AREA_SELF);
> +
> + kfree(pstats);
> +}
> +
> +static int vp_debugfs_create(u64 partition_id, u32 vp_index,
> + struct dentry **vp_stats_ptr,
> + struct dentry *parent)
> +{
> + struct dentry *vp_idx_dir;
> + char vp_idx_str[U32_BUF_SZ];
> + int err;
> +
> + sprintf(vp_idx_str, "%u", vp_index);
> +
> + vp_idx_dir = debugfs_create_dir(vp_idx_str, parent);
> + if (IS_ERR(vp_idx_dir))
> + return PTR_ERR(vp_idx_dir);
> +
> + err = vp_debugfs_stats_create(partition_id, vp_index, vp_stats_ptr,
> + vp_idx_dir);
> + if (err)
> + goto remove_debugfs_vp_idx;
> +
> + return 0;
> +
> +remove_debugfs_vp_idx:
> + debugfs_remove_recursive(vp_idx_dir);
> + return err;
> +}
> +
> +static int partition_stats_show(struct seq_file *m, void *v)
> +{
> + const struct hv_stats_page **pstats = m->private;
> +
> +#define PARTITION_SEQ_PRINTF(cnt) \
> +do { \
> + if (pstats[HV_STATS_AREA_SELF]->pt_cntrs[Partition##cnt]) \
> + seq_printf(m, "%-30s: %llu\n", __stringify(cnt), \
> + pstats[HV_STATS_AREA_SELF]->pt_cntrs[Partition##cnt]); \
> + else \
> + seq_printf(m, "%-30s: %llu\n", __stringify(cnt), \
> + pstats[HV_STATS_AREA_PARENT]->pt_cntrs[Partition##cnt]); \
> +} while (0)
> +
> + PARTITION_SEQ_PRINTF(VirtualProcessors);
> + PARTITION_SEQ_PRINTF(TlbSize);
> + PARTITION_SEQ_PRINTF(AddressSpaces);
> + PARTITION_SEQ_PRINTF(DepositedPages);
> + PARTITION_SEQ_PRINTF(GpaPages);
> + PARTITION_SEQ_PRINTF(GpaSpaceModifications);
> + PARTITION_SEQ_PRINTF(VirtualTlbFlushEntires);
> + PARTITION_SEQ_PRINTF(RecommendedTlbSize);
> + PARTITION_SEQ_PRINTF(GpaPages4K);
> + PARTITION_SEQ_PRINTF(GpaPages2M);
> + PARTITION_SEQ_PRINTF(GpaPages1G);
> + PARTITION_SEQ_PRINTF(GpaPages512G);
> + PARTITION_SEQ_PRINTF(DevicePages4K);
> + PARTITION_SEQ_PRINTF(DevicePages2M);
> + PARTITION_SEQ_PRINTF(DevicePages1G);
> + PARTITION_SEQ_PRINTF(DevicePages512G);
> + PARTITION_SEQ_PRINTF(AttachedDevices);
> + PARTITION_SEQ_PRINTF(DeviceInterruptMappings);
> + PARTITION_SEQ_PRINTF(IoTlbFlushes);
> + PARTITION_SEQ_PRINTF(IoTlbFlushCost);
> + PARTITION_SEQ_PRINTF(DeviceInterruptErrors);
> + PARTITION_SEQ_PRINTF(DeviceDmaErrors);
> + PARTITION_SEQ_PRINTF(DeviceInterruptThrottleEvents);
> + PARTITION_SEQ_PRINTF(SkippedTimerTicks);
> + PARTITION_SEQ_PRINTF(PartitionId);
> +#if IS_ENABLED(CONFIG_X86_64)
> + PARTITION_SEQ_PRINTF(NestedTlbSize);
> + PARTITION_SEQ_PRINTF(RecommendedNestedTlbSize);
> + PARTITION_SEQ_PRINTF(NestedTlbFreeListSize);
> + PARTITION_SEQ_PRINTF(NestedTlbTrimmedPages);
> + PARTITION_SEQ_PRINTF(PagesShattered);
> + PARTITION_SEQ_PRINTF(PagesRecombined);
> + PARTITION_SEQ_PRINTF(HwpRequestValue);
> +#elif IS_ENABLED(CONFIG_ARM64)
> + PARTITION_SEQ_PRINTF(HwpRequestValue);
> +#endif
> +
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(partition_stats);
> +
> +static void mshv_partition_stats_unmap(u64 partition_id, void *stats_page_addr,
> + enum hv_stats_area_type stats_area_type)
> +{
> + union hv_stats_object_identity identity = {
> + .partition.partition_id = partition_id,
> + .partition.stats_area_type = stats_area_type,
> + };
> + int err;
> +
> + err = hv_unmap_stats_page(HV_STATS_OBJECT_PARTITION, stats_page_addr,
> + &identity);
> + if (err) {
nit: redundant curly brackets
> + pr_err("%s: failed to unmap partition %lld %s stats, err: %d\n",
> + __func__, partition_id,
> + (stats_area_type == HV_STATS_AREA_SELF) ? "self" : "parent",
> + err);
> + }
> +}
> +
> +static void *mshv_partition_stats_map(u64 partition_id,
> + enum hv_stats_area_type stats_area_type)
> +{
> + union hv_stats_object_identity identity = {
> + .partition.partition_id = partition_id,
> + .partition.stats_area_type = stats_area_type,
> + };
> + void *stats;
> + int err;
> +
> + err = hv_map_stats_page(HV_STATS_OBJECT_PARTITION, &identity, &stats);
> + if (err) {
> + pr_err("%s: failed to map partition %lld %s stats, err: %d\n",
> + __func__, partition_id,
> + (stats_area_type == HV_STATS_AREA_SELF) ? "self" : "parent",
> + err);
> + return ERR_PTR(err);
> + }
> + return stats;
> +}
> +
> +static int mshv_debugfs_partition_stats_create(u64 partition_id,
> + struct dentry **partition_stats_ptr,
> + struct dentry *parent)
> +{
> + struct dentry *dentry;
> + struct hv_stats_page **pstats;
> + int err;
> +
> + pstats = kcalloc(2, sizeof(struct hv_stats_page *), GFP_KERNEL_ACCOUNT);
> + if (!pstats)
> + return -ENOMEM;
> +
> + pstats[HV_STATS_AREA_SELF] = mshv_partition_stats_map(partition_id,
> + HV_STATS_AREA_SELF);
> + if (IS_ERR(pstats[HV_STATS_AREA_SELF])) {
> + err = PTR_ERR(pstats[HV_STATS_AREA_SELF]);
> + goto cleanup;
> + }
> +
> + /*
> + * L1VH partition cannot access its partition stats in parent area.
> + */
> + if (is_l1vh_parent(partition_id)) {
> + pstats[HV_STATS_AREA_PARENT] = pstats[HV_STATS_AREA_SELF];
> + } else {
> + pstats[HV_STATS_AREA_PARENT] = mshv_partition_stats_map(partition_id,
> + HV_STATS_AREA_PARENT);
> + if (IS_ERR(pstats[HV_STATS_AREA_PARENT])) {
> + err = PTR_ERR(pstats[HV_STATS_AREA_PARENT]);
> + goto unmap_self;
> + }
> + if (!pstats[HV_STATS_AREA_PARENT])
> + pstats[HV_STATS_AREA_PARENT] = pstats[HV_STATS_AREA_SELF];
> + }
> +
> + dentry = debugfs_create_file("stats", 0400, parent,
> + pstats, &partition_stats_fops);
> + if (IS_ERR(dentry)) {
> + err = PTR_ERR(dentry);
> + goto unmap_partition_stats;
> + }
> +
> + *partition_stats_ptr = dentry;
> + return 0;
> +
> +unmap_partition_stats:
> + if (pstats[HV_STATS_AREA_PARENT] != pstats[HV_STATS_AREA_SELF])
> + mshv_partition_stats_unmap(partition_id, pstats[HV_STATS_AREA_PARENT],
> + HV_STATS_AREA_PARENT);
> +unmap_self:
> + mshv_partition_stats_unmap(partition_id, pstats[HV_STATS_AREA_SELF],
> + HV_STATS_AREA_SELF);
> +cleanup:
> + kfree(pstats);
> + return err;
> +}
> +
> +static void partition_debugfs_remove(u64 partition_id, struct dentry *dentry)
> +{
> + struct hv_stats_page **pstats = NULL;
> + void *stats;
nit: stats variable looks redundant
> +
> + pstats = dentry->d_inode->i_private;
> +
> + debugfs_remove_recursive(dentry->d_parent);
> +
> + if (pstats[HV_STATS_AREA_PARENT] != pstats[HV_STATS_AREA_SELF]) {
> + stats = pstats[HV_STATS_AREA_PARENT];
> + mshv_partition_stats_unmap(partition_id, stats, HV_STATS_AREA_PARENT);
> + }
> +
> + stats = pstats[HV_STATS_AREA_SELF];
> + mshv_partition_stats_unmap(partition_id, stats, HV_STATS_AREA_SELF);
> +
> + kfree(pstats);
> +}
> +
> +static int partition_debugfs_create(u64 partition_id,
> + struct dentry **vp_dir_ptr,
> + struct dentry **partition_stats_ptr,
> + struct dentry *parent)
> +{
> + char part_id_str[U64_BUF_SZ];
> + struct dentry *part_id_dir, *vp_dir;
> + int err;
> +
> + if (is_l1vh_parent(partition_id))
> + sprintf(part_id_str, "self");
> + else
> + sprintf(part_id_str, "%llu", partition_id);
> +
> + part_id_dir = debugfs_create_dir(part_id_str, parent);
> + if (IS_ERR(part_id_dir))
> + return PTR_ERR(part_id_dir);
> +
> + vp_dir = debugfs_create_dir("vp", part_id_dir);
> + if (IS_ERR(vp_dir)) {
> + err = PTR_ERR(vp_dir);
> + goto remove_debugfs_partition_id;
> + }
> +
> + err = mshv_debugfs_partition_stats_create(partition_id,
> + partition_stats_ptr,
> + part_id_dir);
> + if (err)
> + goto remove_debugfs_partition_id;
> +
> + *vp_dir_ptr = vp_dir;
> +
> + return 0;
> +
> +remove_debugfs_partition_id:
> + debugfs_remove_recursive(part_id_dir);
> + return err;
> +}
> +
> +static void mshv_debugfs_parent_partition_remove(void)
> +{
> + int idx;
> +
> + for_each_online_cpu(idx)
> + vp_debugfs_remove(hv_current_partition_id, idx, NULL);
> +
> + partition_debugfs_remove(hv_current_partition_id, NULL);
> +}
> +
> +static int __init mshv_debugfs_parent_partition_create(void)
> +{
> + struct dentry *partition_stats, *vp_dir;
> + int err, idx, i;
> +
> + mshv_debugfs_partition = debugfs_create_dir("partition",
> + mshv_debugfs);
> + if (IS_ERR(mshv_debugfs_partition))
> + return PTR_ERR(mshv_debugfs_partition);
> +
> + err = partition_debugfs_create(hv_current_partition_id,
> + &vp_dir,
> + &partition_stats,
> + mshv_debugfs_partition);
> + if (err)
> + goto remove_debugfs_partition;
> +
> + for_each_online_cpu(idx) {
> + struct dentry *vp_stats;
> +
> + err = vp_debugfs_create(hv_current_partition_id,
> + hv_vp_index[idx],
> + &vp_stats,
> + vp_dir);
> + if (err)
> + goto remove_debugfs_partition_vp;
> + }
> +
> + return 0;
> +
> +remove_debugfs_partition_vp:
> + for_each_online_cpu(i) {
> + if (i >= idx)
> + break;
> + vp_debugfs_remove(hv_current_partition_id, i, NULL);
> + }
> + partition_debugfs_remove(hv_current_partition_id, NULL);
> +remove_debugfs_partition:
> + debugfs_remove_recursive(mshv_debugfs_partition);
> + return err;
> +}
> +
> +static int hv_stats_show(struct seq_file *m, void *v)
> +{
> + const struct hv_stats_page *stats = m->private;
> +
> +#define HV_SEQ_PRINTF(cnt) \
> + seq_printf(m, "%-25s: %llu\n", __stringify(cnt), stats->hv_cntrs[Hv##cnt])
> +
> + HV_SEQ_PRINTF(LogicalProcessors);
> + HV_SEQ_PRINTF(Partitions);
> + HV_SEQ_PRINTF(TotalPages);
> + HV_SEQ_PRINTF(VirtualProcessors);
> + HV_SEQ_PRINTF(MonitoredNotifications);
> + HV_SEQ_PRINTF(ModernStandbyEntries);
> + HV_SEQ_PRINTF(PlatformIdleTransitions);
> + HV_SEQ_PRINTF(HypervisorStartupCost);
> + HV_SEQ_PRINTF(IOSpacePages);
> + HV_SEQ_PRINTF(NonEssentialPagesForDump);
> + HV_SEQ_PRINTF(SubsumedPages);
> +
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(hv_stats);
> +
> +static void mshv_hv_stats_unmap(void)
> +{
> + union hv_stats_object_identity identity = {
> + .hv.stats_area_type = HV_STATS_AREA_SELF,
> + };
> + int err;
> +
> + err = hv_unmap_stats_page(HV_STATS_OBJECT_HYPERVISOR, NULL, &identity);
> + if (err)
> + pr_err("%s: failed to unmap hypervisor stats: %d\n",
> + __func__, err);
> +}
> +
> +static void * __init mshv_hv_stats_map(void)
> +{
> + union hv_stats_object_identity identity = {
> + .hv.stats_area_type = HV_STATS_AREA_SELF,
> + };
> + void *stats;
> + int err;
> +
> + err = hv_map_stats_page(HV_STATS_OBJECT_HYPERVISOR, &identity, &stats);
> + if (err) {
> + pr_err("%s: failed to map hypervisor stats: %d\n",
> + __func__, err);
> + return ERR_PTR(err);
> + }
> + return stats;
> +}
> +
> +static int __init mshv_debugfs_hv_stats_create(struct dentry *parent)
> +{
> + struct dentry *dentry;
> + u64 *stats;
> + int err;
> +
> + stats = mshv_hv_stats_map();
> + if (IS_ERR(stats))
> + return PTR_ERR(stats);
> +
> + dentry = debugfs_create_file("stats", 0400, parent,
> + stats, &hv_stats_fops);
> + if (IS_ERR(dentry)) {
> + err = PTR_ERR(dentry);
> + pr_err("%s: failed to create hypervisor stats dentry: %d\n",
> + __func__, err);
> + goto unmap_hv_stats;
> + }
> +
> + mshv_lps_count = stats[HvLogicalProcessors];
> +
> + return 0;
> +
> +unmap_hv_stats:
> + mshv_hv_stats_unmap();
> + return err;
> +}
> +
> +int mshv_debugfs_vp_create(struct mshv_vp *vp)
> +{
> + struct mshv_partition *p = vp->vp_partition;
> + int err;
nit: redundant variable
> +
> + if (!mshv_debugfs)
> + return 0;
> +
> + err = vp_debugfs_create(p->pt_id, vp->vp_index,
> + &vp->vp_debugfs_stats_dentry,
> + p->pt_debugfs_vp_dentry);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> +void mshv_debugfs_vp_remove(struct mshv_vp *vp)
> +{
> + if (!mshv_debugfs)
> + return;
> +
> + vp_debugfs_remove(vp->vp_partition->pt_id, vp->vp_index,
> + vp->vp_debugfs_stats_dentry);
> +}
> +
> +int mshv_debugfs_partition_create(struct mshv_partition *partition)
> +{
> + int err;
> +
> + if (!mshv_debugfs)
> + return 0;
> +
> + err = partition_debugfs_create(partition->pt_id,
> + &partition->pt_debugfs_vp_dentry,
> + &partition->pt_debugfs_stats_dentry,
> + mshv_debugfs_partition);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> +void mshv_debugfs_partition_remove(struct mshv_partition *partition)
> +{
> + if (!mshv_debugfs)
> + return;
> +
> + partition_debugfs_remove(partition->pt_id,
> + partition->pt_debugfs_stats_dentry);
> +}
> +
> +int __init mshv_debugfs_init(void)
> +{
> + int err;
> +
> + mshv_debugfs = debugfs_create_dir("mshv", NULL);
> + if (IS_ERR(mshv_debugfs)) {
> + pr_err("%s: failed to create debugfs directory\n", __func__);
> + return PTR_ERR(mshv_debugfs);
> + }
> +
> + if (hv_root_partition()) {
> + err = mshv_debugfs_hv_stats_create(mshv_debugfs);
> + if (err)
> + goto remove_mshv_dir;
> +
> + err = mshv_debugfs_lp_create(mshv_debugfs);
> + if (err)
> + goto unmap_hv_stats;
> + }
> +
> + err = mshv_debugfs_parent_partition_create();
> + if (err)
> + goto unmap_lp_stats;
> +
> + return 0;
> +
> +unmap_lp_stats:
> + if (hv_root_partition())
> + mshv_debugfs_lp_remove();
> +unmap_hv_stats:
> + if (hv_root_partition())
> + mshv_hv_stats_unmap();
> +remove_mshv_dir:
> + debugfs_remove_recursive(mshv_debugfs);
> + return err;
> +}
> +
> +void mshv_debugfs_exit(void)
> +{
> + mshv_debugfs_parent_partition_remove();
> +
> + if (hv_root_partition()) {
> + mshv_debugfs_lp_remove();
> + mshv_hv_stats_unmap();
> + }
> +
> + debugfs_remove_recursive(mshv_debugfs);
> +}
> diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
> index 3eb815011b46..1f1b1984449b 100644
> --- a/drivers/hv/mshv_root.h
> +++ b/drivers/hv/mshv_root.h
> @@ -51,6 +51,9 @@ struct mshv_vp {
> unsigned int kicked_by_hv;
> wait_queue_head_t vp_suspend_queue;
> } run;
> +#if IS_ENABLED(CONFIG_DEBUG_FS)
> + struct dentry *vp_debugfs_stats_dentry;
nit: the name could be shorter like vp_stats_dentry, for example
> +#endif
> };
>
> #define vp_fmt(fmt) "p%lluvp%u: " fmt
> @@ -128,6 +131,10 @@ struct mshv_partition {
> u64 isolation_type;
> bool import_completed;
> bool pt_initialized;
> +#if IS_ENABLED(CONFIG_DEBUG_FS)
> + struct dentry *pt_debugfs_stats_dentry;
> + struct dentry *pt_debugfs_vp_dentry;
same here
> +#endif
> };
>
> #define pt_fmt(fmt) "p%llu: " fmt
> @@ -308,6 +315,33 @@ int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
> int hv_call_get_partition_property_ex(u64 partition_id, u64 property_code, u64 arg,
> void *property_value, size_t property_value_sz);
>
> +#if IS_ENABLED(CONFIG_DEBUG_FS)
> +int __init mshv_debugfs_init(void);
> +void mshv_debugfs_exit(void);
> +
> +int mshv_debugfs_partition_create(struct mshv_partition *partition);
> +void mshv_debugfs_partition_remove(struct mshv_partition *partition);
> +int mshv_debugfs_vp_create(struct mshv_vp *vp);
> +void mshv_debugfs_vp_remove(struct mshv_vp *vp);
> +#else
> +static inline int __init mshv_debugfs_init(void)
> +{
> + return 0;
> +}
> +static inline void mshv_debugfs_exit(void) { }
> +
> +static inline int mshv_debugfs_partition_create(struct mshv_partition *partition)
> +{
> + return 0;
> +}
> +static inline void mshv_debugfs_partition_remove(struct mshv_partition *partition) { }
> +static inline int mshv_debugfs_vp_create(struct mshv_vp *vp)
> +{
> + return 0;
> +}
> +static inline void mshv_debugfs_vp_remove(struct mshv_vp *vp) { }
> +#endif
> +
> extern struct mshv_root mshv_root;
> extern enum hv_scheduler_type hv_scheduler_type;
> extern u8 * __percpu *hv_synic_eventring_tail;
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index 19006b788e85..152fcd9b45e6 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -982,6 +982,10 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
> if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
> memcpy(vp->vp_stats_pages, stats_pages, sizeof(stats_pages));
>
> + ret = mshv_debugfs_vp_create(vp);
> + if (ret)
> + goto put_partition;
> +
> /*
> * Keep anon_inode_getfd last: it installs fd in the file struct and
> * thus makes the state accessible in user space.
> @@ -989,7 +993,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
> ret = anon_inode_getfd("mshv_vp", &mshv_vp_fops, vp,
> O_RDWR | O_CLOEXEC);
> if (ret < 0)
> - goto put_partition;
> + goto remove_debugfs_vp;
>
> /* already exclusive with the partition mutex for all ioctls */
> partition->pt_vp_count++;
> @@ -997,6 +1001,8 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
>
> return ret;
>
> +remove_debugfs_vp:
> + mshv_debugfs_vp_remove(vp);
> put_partition:
> mshv_partition_put(partition);
> free_vp:
> @@ -1556,13 +1562,18 @@ mshv_partition_ioctl_initialize(struct mshv_partition *partition)
>
> ret = hv_call_initialize_partition(partition->pt_id);
> if (ret)
> - goto withdraw_mem;
Looks like the behavior changed here. Could you explain?
> + return ret;
> +
> + ret = mshv_debugfs_partition_create(partition);
> + if (ret)
> + goto finalize_partition;
>
> partition->pt_initialized = true;
>
> return 0;
>
> -withdraw_mem:
> +finalize_partition:
> + hv_call_finalize_partition(partition->pt_id);
> hv_call_withdraw_memory(U64_MAX, NUMA_NO_NODE, partition->pt_id);
>
> return ret;
> @@ -1741,6 +1752,8 @@ static void destroy_partition(struct mshv_partition *partition)
> if (!vp)
> continue;
>
> + mshv_debugfs_vp_remove(vp);
> +
> if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
> mshv_vp_stats_unmap(partition->pt_id, vp->vp_index,
> (void **)vp->vp_stats_pages);
> @@ -1775,6 +1788,8 @@ static void destroy_partition(struct mshv_partition *partition)
> partition->pt_vp_array[i] = NULL;
> }
>
> + mshv_debugfs_partition_remove(partition);
> +
> /* Deallocates and unmaps everything including vcpus, GPA mappings etc */
> hv_call_finalize_partition(partition->pt_id);
>
> @@ -2351,10 +2366,14 @@ static int __init mshv_parent_partition_init(void)
>
> mshv_init_vmm_caps(dev);
>
> - ret = mshv_irqfd_wq_init();
> + ret = mshv_debugfs_init();
> if (ret)
> goto exit_partition;
>
> + ret = mshv_irqfd_wq_init();
> + if (ret)
> + goto exit_debugfs;
> +
> spin_lock_init(&mshv_root.pt_ht_lock);
> hash_init(mshv_root.pt_htable);
>
> @@ -2362,6 +2381,10 @@ static int __init mshv_parent_partition_init(void)
>
> return 0;
>
> +destroy_irqds_wq:
Where is this label used?
Thanks,
Stanislav
> + mshv_irqfd_wq_cleanup();
> +exit_debugfs:
> + mshv_debugfs_exit();
> exit_partition:
> if (hv_root_partition())
> mshv_root_partition_exit();
> @@ -2378,6 +2401,7 @@ static void __exit mshv_parent_partition_exit(void)
> {
> hv_setup_mshv_handler(NULL);
> mshv_port_table_fini();
> + mshv_debugfs_exit();
> misc_deregister(&mshv_dev);
> mshv_irqfd_wq_cleanup();
> if (hv_root_partition())
> --
> 2.34.1
^ permalink raw reply
* Re: [PATCH v7 1/4] fixup! Drivers: hv: Introduce mshv_vtl driver
From: Wei Liu @ 2025-12-05 23:08 UTC (permalink / raw)
To: Praveen K Paladugu
Cc: kys, haiyangz, wei.liu, decui, tglx, mingo, linux-hyperv,
linux-kernel, bp, dave.hansen, x86, hpa, arnd, anbelski,
easwar.hariharan, nunodasneves, skinsburskii
In-Reply-To: <20251205230624.GA1942423@liuwe-devbox-debian-v2.local>
On Fri, Dec 05, 2025 at 11:06:24PM +0000, Wei Liu wrote:
> Please provide a proper commit subject.
>
> On Fri, Dec 05, 2025 at 02:17:05PM -0600, Praveen K Paladugu wrote:
> > Drop the spurios "space" character in Makefile condition check
> > that causes mshv_common.o to be built regardless of the CONFIG settings.
> >
> > Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
> > Suggested-by: Michael Kelley <mhklinux@outlook.com>
>
> This should come with a Fixes: tag.
Since the bug is not in any released kernel, so it is not needed.
I don't want to squash this though. So the request to have a proper
subject line still stands.
Wei
>
> > ---
> > drivers/hv/Makefile | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
> > index 58b8d07639f3..6d929fb0e13d 100644
> > --- a/drivers/hv/Makefile
> > +++ b/drivers/hv/Makefile
> > @@ -20,6 +20,6 @@ mshv_vtl-y := mshv_vtl_main.o
> > # Code that must be built-in
> > obj-$(CONFIG_HYPERV) += hv_common.o
> > obj-$(subst m,y,$(CONFIG_MSHV_ROOT)) += hv_proc.o
> > -ifneq ($(CONFIG_MSHV_ROOT) $(CONFIG_MSHV_VTL),)
> > +ifneq ($(CONFIG_MSHV_ROOT)$(CONFIG_MSHV_VTL),)
> > obj-y += mshv_common.o
> > endif
> > --
> > 2.51.0
> >
^ permalink raw reply
* Re: [PATCH v7 1/4] fixup! Drivers: hv: Introduce mshv_vtl driver
From: Wei Liu @ 2025-12-05 23:11 UTC (permalink / raw)
To: Praveen K Paladugu
Cc: kys, haiyangz, wei.liu, decui, tglx, mingo, linux-hyperv,
linux-kernel, bp, dave.hansen, x86, hpa, arnd, anbelski,
easwar.hariharan, nunodasneves, skinsburskii
In-Reply-To: <20251205230806.GB1942423@liuwe-devbox-debian-v2.local>
On Fri, Dec 05, 2025 at 11:08:06PM +0000, Wei Liu wrote:
> On Fri, Dec 05, 2025 at 11:06:24PM +0000, Wei Liu wrote:
> > Please provide a proper commit subject.
> >
> > On Fri, Dec 05, 2025 at 02:17:05PM -0600, Praveen K Paladugu wrote:
> > > Drop the spurios "space" character in Makefile condition check
> > > that causes mshv_common.o to be built regardless of the CONFIG settings.
> > >
> > > Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
> > > Suggested-by: Michael Kelley <mhklinux@outlook.com>
> >
> > This should come with a Fixes: tag.
>
> Since the bug is not in any released kernel, so it is not needed.
>
> I don't want to squash this though. So the request to have a proper
> subject line still stands.
Never mind. I can fix this myself. There is no need to have another
round.
Wei
^ permalink raw reply
* Re: [PATCH v9 0/6] Introduce movable pages for Hyper-V guests
From: Wei Liu @ 2025-12-05 23:22 UTC (permalink / raw)
To: Stanislav Kinsburskii
Cc: kys, haiyangz, wei.liu, decui, linux-hyperv, linux-kernel
In-Reply-To: <176479772384.304819.9168337792948347657.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
On Wed, Dec 03, 2025 at 09:40:33PM +0000, Stanislav Kinsburskii wrote:
[...]
>
> Stanislav Kinsburskii (6):
> Drivers: hv: Refactor and rename memory region handling functions
> Drivers: hv: Centralize guest memory region destruction
> Drivers: hv: Move region management to mshv_regions.c
> Drivers: hv: Fix huge page handling in memory region traversal
> Drivers: hv: Add refcount and locking to mem regions
> Drivers: hv: Add support for movable memory regions
Queued. I changed the prefix to mshv and drop a not needed paragraph
from the commit message for the add refcount patch.
Wei
^ permalink raw reply
* Re: [PATCH v7 0/4] Add support for clean shutdown with MSHV
From: Wei Liu @ 2025-12-05 23:26 UTC (permalink / raw)
To: Praveen K Paladugu
Cc: kys, haiyangz, wei.liu, decui, tglx, mingo, linux-hyperv,
linux-kernel, bp, dave.hansen, x86, hpa, arnd, anbelski,
easwar.hariharan, nunodasneves, skinsburskii
In-Reply-To: <20251205201721.7253-1-prapal@linux.microsoft.com>
On Fri, Dec 05, 2025 at 02:17:04PM -0600, Praveen K Paladugu wrote:
> Add support for clean shutdown of the root partition when running on
> MSHV Hypervisor.
>
> Praveen K Paladugu (4):
[...]
> hyperv: Add definitions for MSHV sleep state configuration
> hyperv: Use reboot notifier to configure sleep state
> hyperv: Cleanly shutdown root partition with MSHV
I queued these patches to hyperv-next.
Wei
^ permalink raw reply
* Re: [PATCH v2 3/3] mshv: Add debugfs to view hypervisor statistics
From: kernel test robot @ 2025-12-08 3:04 UTC (permalink / raw)
To: Nuno Das Neves, linux-hyperv, linux-kernel, skinsburskii
Cc: oe-kbuild-all, kys, haiyangz, wei.liu, decui, longli, mhklinux,
prapal, mrathor, paekkaladevi, Nuno Das Neves, Jinank Jain
In-Reply-To: <1764961122-31679-4-git-send-email-nunodasneves@linux.microsoft.com>
Hi Nuno,
kernel test robot noticed the following build warnings:
[auto build test WARNING on next-20251205]
[cannot apply to linus/master v6.18 v6.18-rc7 v6.18-rc6 v6.18]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Nuno-Das-Neves/mshv-Ignore-second-stats-page-map-result-failure/20251206-033756
base: next-20251205
patch link: https://lore.kernel.org/r/1764961122-31679-4-git-send-email-nunodasneves%40linux.microsoft.com
patch subject: [PATCH v2 3/3] mshv: Add debugfs to view hypervisor statistics
config: x86_64-randconfig-002-20251208 (https://download.01.org/0day-ci/archive/20251208/202512081050.0AW9Ecvg-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.4.0-5) 12.4.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251208/202512081050.0AW9Ecvg-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202512081050.0AW9Ecvg-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/hv/mshv_root_main.c: In function 'mshv_parent_partition_init':
>> drivers/hv/mshv_root_main.c:2369:1: warning: label 'destroy_irqds_wq' defined but not used [-Wunused-label]
2369 | destroy_irqds_wq:
| ^~~~~~~~~~~~~~~~
vim +/destroy_irqds_wq +2369 drivers/hv/mshv_root_main.c
2299
2300 static int __init mshv_parent_partition_init(void)
2301 {
2302 int ret;
2303 struct device *dev;
2304 union hv_hypervisor_version_info version_info;
2305
2306 if (!hv_parent_partition() || is_kdump_kernel())
2307 return -ENODEV;
2308
2309 if (hv_get_hypervisor_version(&version_info))
2310 return -ENODEV;
2311
2312 ret = misc_register(&mshv_dev);
2313 if (ret)
2314 return ret;
2315
2316 dev = mshv_dev.this_device;
2317
2318 if (version_info.build_number < MSHV_HV_MIN_VERSION ||
2319 version_info.build_number > MSHV_HV_MAX_VERSION) {
2320 dev_err(dev, "Running on unvalidated Hyper-V version\n");
2321 dev_err(dev, "Versions: current: %u min: %u max: %u\n",
2322 version_info.build_number, MSHV_HV_MIN_VERSION,
2323 MSHV_HV_MAX_VERSION);
2324 }
2325
2326 mshv_root.synic_pages = alloc_percpu(struct hv_synic_pages);
2327 if (!mshv_root.synic_pages) {
2328 dev_err(dev, "Failed to allocate percpu synic page\n");
2329 ret = -ENOMEM;
2330 goto device_deregister;
2331 }
2332
2333 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mshv_synic",
2334 mshv_synic_init,
2335 mshv_synic_cleanup);
2336 if (ret < 0) {
2337 dev_err(dev, "Failed to setup cpu hotplug state: %i\n", ret);
2338 goto free_synic_pages;
2339 }
2340
2341 mshv_cpuhp_online = ret;
2342
2343 ret = mshv_retrieve_scheduler_type(dev);
2344 if (ret)
2345 goto remove_cpu_state;
2346
2347 if (hv_root_partition())
2348 ret = mshv_root_partition_init(dev);
2349 if (ret)
2350 goto remove_cpu_state;
2351
2352 mshv_init_vmm_caps(dev);
2353
2354 ret = mshv_debugfs_init();
2355 if (ret)
2356 goto exit_partition;
2357
2358 ret = mshv_irqfd_wq_init();
2359 if (ret)
2360 goto exit_debugfs;
2361
2362 spin_lock_init(&mshv_root.pt_ht_lock);
2363 hash_init(mshv_root.pt_htable);
2364
2365 hv_setup_mshv_handler(mshv_isr);
2366
2367 return 0;
2368
> 2369 destroy_irqds_wq:
2370 mshv_irqfd_wq_cleanup();
2371 exit_debugfs:
2372 mshv_debugfs_exit();
2373 exit_partition:
2374 if (hv_root_partition())
2375 mshv_root_partition_exit();
2376 remove_cpu_state:
2377 cpuhp_remove_state(mshv_cpuhp_online);
2378 free_synic_pages:
2379 free_percpu(mshv_root.synic_pages);
2380 device_deregister:
2381 misc_deregister(&mshv_dev);
2382 return ret;
2383 }
2384
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v2 3/3] mshv: Add debugfs to view hypervisor statistics
From: kernel test robot @ 2025-12-08 6:02 UTC (permalink / raw)
To: Nuno Das Neves, linux-hyperv, linux-kernel, skinsburskii
Cc: llvm, oe-kbuild-all, kys, haiyangz, wei.liu, decui, longli,
mhklinux, prapal, mrathor, paekkaladevi, Nuno Das Neves,
Jinank Jain
In-Reply-To: <1764961122-31679-4-git-send-email-nunodasneves@linux.microsoft.com>
Hi Nuno,
kernel test robot noticed the following build warnings:
[auto build test WARNING on next-20251205]
[cannot apply to linus/master v6.18 v6.18-rc7 v6.18-rc6 v6.18]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Nuno-Das-Neves/mshv-Ignore-second-stats-page-map-result-failure/20251206-033756
base: next-20251205
patch link: https://lore.kernel.org/r/1764961122-31679-4-git-send-email-nunodasneves%40linux.microsoft.com
patch subject: [PATCH v2 3/3] mshv: Add debugfs to view hypervisor statistics
config: x86_64-randconfig-076-20251208 (https://download.01.org/0day-ci/archive/20251208/202512081314.ULBIWb1d-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251208/202512081314.ULBIWb1d-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202512081314.ULBIWb1d-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/hv/mshv_root_main.c:2369:1: warning: unused label 'destroy_irqds_wq' [-Wunused-label]
2369 | destroy_irqds_wq:
| ^~~~~~~~~~~~~~~~~
1 warning generated.
vim +/destroy_irqds_wq +2369 drivers/hv/mshv_root_main.c
2299
2300 static int __init mshv_parent_partition_init(void)
2301 {
2302 int ret;
2303 struct device *dev;
2304 union hv_hypervisor_version_info version_info;
2305
2306 if (!hv_parent_partition() || is_kdump_kernel())
2307 return -ENODEV;
2308
2309 if (hv_get_hypervisor_version(&version_info))
2310 return -ENODEV;
2311
2312 ret = misc_register(&mshv_dev);
2313 if (ret)
2314 return ret;
2315
2316 dev = mshv_dev.this_device;
2317
2318 if (version_info.build_number < MSHV_HV_MIN_VERSION ||
2319 version_info.build_number > MSHV_HV_MAX_VERSION) {
2320 dev_err(dev, "Running on unvalidated Hyper-V version\n");
2321 dev_err(dev, "Versions: current: %u min: %u max: %u\n",
2322 version_info.build_number, MSHV_HV_MIN_VERSION,
2323 MSHV_HV_MAX_VERSION);
2324 }
2325
2326 mshv_root.synic_pages = alloc_percpu(struct hv_synic_pages);
2327 if (!mshv_root.synic_pages) {
2328 dev_err(dev, "Failed to allocate percpu synic page\n");
2329 ret = -ENOMEM;
2330 goto device_deregister;
2331 }
2332
2333 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mshv_synic",
2334 mshv_synic_init,
2335 mshv_synic_cleanup);
2336 if (ret < 0) {
2337 dev_err(dev, "Failed to setup cpu hotplug state: %i\n", ret);
2338 goto free_synic_pages;
2339 }
2340
2341 mshv_cpuhp_online = ret;
2342
2343 ret = mshv_retrieve_scheduler_type(dev);
2344 if (ret)
2345 goto remove_cpu_state;
2346
2347 if (hv_root_partition())
2348 ret = mshv_root_partition_init(dev);
2349 if (ret)
2350 goto remove_cpu_state;
2351
2352 mshv_init_vmm_caps(dev);
2353
2354 ret = mshv_debugfs_init();
2355 if (ret)
2356 goto exit_partition;
2357
2358 ret = mshv_irqfd_wq_init();
2359 if (ret)
2360 goto exit_debugfs;
2361
2362 spin_lock_init(&mshv_root.pt_ht_lock);
2363 hash_init(mshv_root.pt_htable);
2364
2365 hv_setup_mshv_handler(mshv_isr);
2366
2367 return 0;
2368
> 2369 destroy_irqds_wq:
2370 mshv_irqfd_wq_cleanup();
2371 exit_debugfs:
2372 mshv_debugfs_exit();
2373 exit_partition:
2374 if (hv_root_partition())
2375 mshv_root_partition_exit();
2376 remove_cpu_state:
2377 cpuhp_remove_state(mshv_cpuhp_online);
2378 free_synic_pages:
2379 free_percpu(mshv_root.synic_pages);
2380 device_deregister:
2381 misc_deregister(&mshv_dev);
2382 return ret;
2383 }
2384
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [GIT PULL] Hyper-V patches for 6.19
From: Wei Liu @ 2025-12-08 6:46 UTC (permalink / raw)
To: Linus Torvalds
Cc: Wei Liu, Linux on Hyper-V List, Linux Kernel List, kys, haiyangz,
decui, longli
Hi Linus,
The following changes since commit 3a8660878839faadb4f1a6dd72c3179c1df56787:
Linux 6.18-rc1 (2025-10-12 13:42:36 -0700)
are available in the Git repository at:
ssh://git@gitolite.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git tags/hyperv-next-signed-20251207
for you to fetch changes up to 615a6e7d83f958e7ef3bc818e818f7c6433b4c2a:
mshv: Cleanly shutdown root partition with MSHV (2025-12-05 23:25:05 +0000)
You will see a merge conflict with the master branch. The resolution is
here.
https://lore.kernel.org/all/20251125171130.67ba74e1@canb.auug.org.au/
----------------------------------------------------------------
hyperv-next for v6.19
- Enhancements to Linux as the root partition for Microsoft
Hypervisor.
* Support a new mode called L1VH, which allows Linux to drive
the hypervisor running the Azure Host directly.
* Support for MSHV crash dump collection.
* Allow Linux's memory management subsystem to better manage guest
memory regions.
* Fix issues that prevented a clean shutdown of the whole system on
bare metal and nested configurations.
* ARM64 support for the MSHV driver.
* Various other bug fixes and cleanups.
- Add support for Confidential VMBus for Linux guest on Hyper-V.
- Secure AVIC support for Linux guests on Hyper-V.
- Add the mshv_vtl driver to allow Linux to run as the secure kernel
in a higher virtual trust level for Hyper-V.
----------------------------------------------------------------
Anirudh Rayabharam (Microsoft) (1):
mshv: Add ioctl for self targeted passthrough hvcalls
Gongwei Li (1):
Drivers: hv: use kmalloc_array() instead of kmalloc()
Jiapeng Chong (1):
x86: mshyperv: Remove duplicate asm/msr.h header
Jinank Jain (3):
mshv: Allocate vp state page for HVCALL_MAP_VP_STATE_PAGE on L1VH
mshv: Introduce new hypercall to map stats page for L1VH partitions
mshv: adjust interrupt control structure for ARM64
Kriish Sharma (1):
Drivers: hv: fix missing kernel-doc description for 'size' in request_arr_init()
Long Li (1):
MAINTAINERS: Add Long Li as a Hyper-V maintainer
Magnus Kulke (1):
mshv: Allow mappings that overlap in uaddr
Marco Crivellari (1):
mshv: add WQ_PERCPU to alloc_workqueue users
Mukesh Rathor (6):
x86/hyperv: Rename guest crash shutdown function
hyperv: Add two new hypercall numbers to guest ABI public header
hyperv: Add definitions for hypervisor crash dump support
x86/hyperv: Add trampoline asm code to transition from hypervisor
x86/hyperv: Implement hypervisor RAM collection into vmcore
x86/hyperv: Enable build of hypervisor crashdump collection files
Muminul Islam (1):
mshv: Extend create partition ioctl to support cpu features
Naman Jain (3):
static_call: allow using STATIC_CALL_TRAMP_STR() from assembly
Drivers: hv: Export some symbols for mshv_vtl
Drivers: hv: Introduce mshv_vtl driver
Nuno Das Neves (4):
mshv: Fix VpRootDispatchThreadBlocked value
mshv: Fix deposit memory in MSHV_ROOT_HVCALL
mshv: Only map vp->vp_stats_pages if on root scheduler
mshv: Fix create memory region overlap check
Praveen K Paladugu (3):
mshv: Add definitions for MSHV sleep state configuration
mshv: Use reboot notifier to configure sleep state
mshv: Cleanly shutdown root partition with MSHV
Purna Pavan Chandra Aekkaladevi (2):
mshv: Add the HVCALL_GET_PARTITION_PROPERTY_EX hypercall
mshv: Get the vmm capabilities offered by the hypervisor
Rahul Kumar (1):
Drivers: hv: Use kmalloc_array() instead of kmalloc()
Roman Kisel (17):
Documentation: hyperv: Confidential VMBus
Drivers: hv: VMBus protocol version 6.0
arch/x86: mshyperv: Discover Confidential VMBus availability
arch: hyperv: Get/set SynIC synth.registers via paravisor
arch/x86: mshyperv: Trap on access for some synthetic MSRs
Drivers: hv: Rename fields for SynIC message and event pages
Drivers: hv: Allocate the paravisor SynIC pages when required
Drivers: hv: Post messages through the confidential VMBus if available
Drivers: hv: remove stale comment
Drivers: hv: Check message and event pages for non-NULL before iounmap()
Drivers: hv: Rename the SynIC enable and disable routines
Drivers: hv: Functions for setting up and tearing down the paravisor SynIC
Drivers: hv: Allocate encrypted buffers when requested
Drivers: hv: Free msginfo when the buffer fails to decrypt
Drivers: hv: Support confidential VMBus channels
Drivers: hv: Set the default VMBus version to 6.0
Drivers: hv: Support establishing the confidential VMBus connection
Stanislav Kinsburskii (7):
Drivers: hv: Resolve ambiguity in hypervisor version log
mshv: Refactor and rename memory region handling functions
mshv: Centralize guest memory region destruction
mshv: Move region management to mshv_regions.c
mshv: Fix huge page handling in memory region traversal
mshv: Add refcount and locking to mem regions
mshv: Add support for movable memory regions
Tianyu Lan (4):
x86/hyperv: Don't use hv apic driver when Secure AVIC is available
drivers: hv: Allow vmbus message synic interrupt injected from Hyper-V
x86/hyperv: Don't use auto-eoi when Secure AVIC is available
x86/hyperv: Allow Hyper-V to inject STIMER0 interrupts
Documentation/virt/hyperv/coco.rst | 139 ++-
MAINTAINERS | 3 +
arch/x86/hyperv/Makefile | 16 +-
arch/x86/hyperv/hv_apic.c | 8 +
arch/x86/hyperv/hv_crash.c | 642 ++++++++++++++
arch/x86/hyperv/hv_init.c | 9 +
arch/x86/hyperv/hv_trampoline.S | 101 +++
arch/x86/hyperv/hv_vtl.c | 30 +
arch/x86/hyperv/mshv-asm-offsets.c | 37 +
arch/x86/hyperv/mshv_vtl_asm.S | 116 +++
arch/x86/include/asm/mshyperv.h | 45 +
arch/x86/kernel/cpu/mshyperv.c | 88 +-
drivers/hv/Kconfig | 29 +-
drivers/hv/Makefile | 9 +-
drivers/hv/channel.c | 75 +-
drivers/hv/channel_mgmt.c | 27 +-
drivers/hv/connection.c | 6 +-
drivers/hv/hv.c | 377 ++++++---
drivers/hv/hv_common.c | 27 +-
drivers/hv/hv_util.c | 2 +-
drivers/hv/hyperv_vmbus.h | 76 +-
drivers/hv/mshv_common.c | 99 +++
drivers/hv/mshv_eventfd.c | 8 +-
drivers/hv/mshv_irq.c | 4 +
drivers/hv/mshv_regions.c | 555 ++++++++++++
drivers/hv/mshv_root.h | 57 +-
drivers/hv/mshv_root_hv_call.c | 196 ++++-
drivers/hv/mshv_root_main.c | 749 +++++++++--------
drivers/hv/mshv_synic.c | 6 +-
drivers/hv/mshv_vtl.h | 25 +
drivers/hv/mshv_vtl_main.c | 1392 +++++++++++++++++++++++++++++++
drivers/hv/ring_buffer.c | 5 +-
drivers/hv/vmbus_drv.c | 188 +++--
include/asm-generic/mshyperv.h | 63 +-
include/hyperv/hvgdk_mini.h | 115 ++-
include/hyperv/hvhdk.h | 46 +
include/hyperv/hvhdk_mini.h | 128 +++
include/linux/compiler_types.h | 8 +-
include/linux/hyperv.h | 69 +-
include/linux/static_call_types.h | 4 +
include/uapi/linux/mshv.h | 116 ++-
tools/include/linux/static_call_types.h | 4 +
42 files changed, 5005 insertions(+), 694 deletions(-)
create mode 100644 arch/x86/hyperv/hv_crash.c
create mode 100644 arch/x86/hyperv/hv_trampoline.S
create mode 100644 arch/x86/hyperv/mshv-asm-offsets.c
create mode 100644 arch/x86/hyperv/mshv_vtl_asm.S
create mode 100644 drivers/hv/mshv_regions.c
create mode 100644 drivers/hv/mshv_vtl.h
create mode 100644 drivers/hv/mshv_vtl_main.c
^ permalink raw reply
* RE: [PATCH v2 1/3] mshv: Ignore second stats page map result failure
From: Michael Kelley @ 2025-12-08 15:12 UTC (permalink / raw)
To: Nuno Das Neves, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, skinsburskii@linux.microsoft.com
Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
decui@microsoft.com, longli@microsoft.com,
prapal@linux.microsoft.com, mrathor@linux.microsoft.com,
paekkaladevi@linux.microsoft.com
In-Reply-To: <1764961122-31679-2-git-send-email-nunodasneves@linux.microsoft.com>
From: Nuno Das Neves <nunodasneves@linux.microsoft.com> Sent: Friday, December 5, 2025 10:59 AM
>
> From: Purna Pavan Chandra Aekkaladevi <paekkaladevi@linux.microsoft.com>
>
> Older versions of the hypervisor do not support HV_STATS_AREA_PARENT
> and return HV_STATUS_INVALID_PARAMETER for the second stats page
> mapping request.
>
> This results a failure in module init. Instead of failing, gracefully
> fall back to populating stats_pages[HV_STATS_AREA_PARENT] with the
> already-mapped stats_pages[HV_STATS_AREA_SELF].
This explains "what" this patch does. But could you add an explanation of "why"
substituting SELF for the unavailable PARENT is the right thing to do? As a somewhat
outside reviewer, I don't know enough about SELF vs. PARENT to immediately know
why this substitution makes sense.
Also, does this patch affect the logic in mshv_vp_dispatch_thread_blocked() where
a zero value for the SELF version of VpRootDispatchThreadBlocked is replaced by
the PARENT value? But that logic seems to be in the reverse direction -- replacing
a missing SELF value with the PARENT value -- whereas this patch is about replacing
missing PARENT values with SELF values. So are there two separate PARENT vs. SELF
issues overall? And after this patch is in place and PARENT values are replaced with
SELF on older hypervisor versions, the logic in mshv_vp_dispatch_thread_blocked()
then effectively becomes a no-op if the SELF value is zero, and the return value will
be zero. Is that problem?
>
> Signed-off-by: Purna Pavan Chandra Aekkaladevi <paekkaladevi@linux.microsoft.com>
> Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
> Reviewed-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> ---
> drivers/hv/mshv_root_hv_call.c | 41 ++++++++++++++++++++++++++++++----
> drivers/hv/mshv_root_main.c | 3 +++
> 2 files changed, 40 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
> index 598eaff4ff29..b1770c7b500c 100644
> --- a/drivers/hv/mshv_root_hv_call.c
> +++ b/drivers/hv/mshv_root_hv_call.c
> @@ -855,6 +855,24 @@ static int hv_call_map_stats_page2(enum
> hv_stats_object_type type,
> return ret;
> }
>
> +static int
> +hv_stats_get_area_type(enum hv_stats_object_type type,
> + const union hv_stats_object_identity *identity)
> +{
> + switch (type) {
> + case HV_STATS_OBJECT_HYPERVISOR:
> + return identity->hv.stats_area_type;
> + case HV_STATS_OBJECT_LOGICAL_PROCESSOR:
> + return identity->lp.stats_area_type;
> + case HV_STATS_OBJECT_PARTITION:
> + return identity->partition.stats_area_type;
> + case HV_STATS_OBJECT_VP:
> + return identity->vp.stats_area_type;
> + }
> +
> + return -EINVAL;
> +}
> +
> static int hv_call_map_stats_page(enum hv_stats_object_type type,
> const union hv_stats_object_identity *identity,
> void **addr)
> @@ -863,7 +881,7 @@ static int 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 ret = 0;
> + int hv_status, ret = 0;
>
> do {
> local_irq_save(flags);
> @@ -878,11 +896,26 @@ static int hv_call_map_stats_page(enum hv_stats_object_type type,
> pfn = output->map_location;
>
> local_irq_restore(flags);
> - if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
> - ret = hv_result_to_errno(status);
> +
> + hv_status = hv_result(status);
> + if (hv_status != HV_STATUS_INSUFFICIENT_MEMORY) {
> if (hv_result_success(status))
> break;
> - return ret;
> +
> + /*
> + * Older versions of the hypervisor do not support the
> + * PARENT stats area. In this case return "success" but
> + * set the page to NULL. The caller should check for
> + * this case and instead just use the SELF area.
> + */
> + if (hv_stats_get_area_type(type, identity) == HV_STATS_AREA_PARENT &&
> + hv_status == HV_STATUS_INVALID_PARAMETER) {
> + *addr = NULL;
> + return 0;
> + }
> +
> + hv_status_debug(status, "\n");
> + return hv_result_to_errno(status);
Does the hv_call_map_stats_page2() function need a similar fix? Or is there a linkage
in hypervisor functionality where any hypervisor version that supports an overlay GPFN
also supports the PARENT stats? If such a linkage is why hv_call_map_stats_page2()
doesn't need a similar fix, please add a code comment to that effect in
hv_call_map_stats_page2().
> }
>
> ret = hv_call_deposit_pages(NUMA_NO_NODE,
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index bc15d6f6922f..f59a4ab47685 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -905,6 +905,9 @@ static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
> if (err)
> goto unmap_self;
>
> + if (!stats_pages[HV_STATS_AREA_PARENT])
> + stats_pages[HV_STATS_AREA_PARENT] =
> stats_pages[HV_STATS_AREA_SELF];
> +
> return 0;
>
> unmap_self:
> --
> 2.34.1
^ permalink raw reply
* RE: [PATCH v2 2/3] mshv: Add definitions for stats pages
From: Michael Kelley @ 2025-12-08 15:13 UTC (permalink / raw)
To: Nuno Das Neves, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, skinsburskii@linux.microsoft.com
Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
decui@microsoft.com, longli@microsoft.com,
prapal@linux.microsoft.com, mrathor@linux.microsoft.com,
paekkaladevi@linux.microsoft.com
In-Reply-To: <1764961122-31679-3-git-send-email-nunodasneves@linux.microsoft.com>
From: Nuno Das Neves <nunodasneves@linux.microsoft.com> Sent: Friday, December 5, 2025 10:59 AM
>
> Add the definitions for hypervisor, logical processor, and partition
> stats pages.
>
> Move the definition for the VP stats page to its rightful place in
> hvhdk.h, and add the missing members.
>
> These enum members retain their CamelCase style, since they are imported
> directly from the hypervisor code They will be stringified when printing
Missing a '.' (period) after "hypervisor code".
> the stats out, and retain more readability in this form.
>
> Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
> ---
> drivers/hv/mshv_root_main.c | 17 --
> include/hyperv/hvhdk.h | 437 ++++++++++++++++++++++++++++++++++++
> 2 files changed, 437 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index f59a4ab47685..19006b788e85 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -38,23 +38,6 @@ MODULE_AUTHOR("Microsoft");
> MODULE_LICENSE("GPL");
> MODULE_DESCRIPTION("Microsoft Hyper-V root partition VMM interface
> /dev/mshv");
>
> -/* TODO move this to another file when debugfs code is added */
> -enum hv_stats_vp_counters { /* HV_THREAD_COUNTER */
> -#if defined(CONFIG_X86)
> - VpRootDispatchThreadBlocked = 202,
> -#elif defined(CONFIG_ARM64)
> - VpRootDispatchThreadBlocked = 94,
> -#endif
> - VpStatsMaxCounter
> -};
> -
> -struct hv_stats_page {
> - union {
> - u64 vp_cntrs[VpStatsMaxCounter]; /* VP counters */
> - u8 data[HV_HYP_PAGE_SIZE];
> - };
> -} __packed;
> -
> struct mshv_root mshv_root;
>
> enum hv_scheduler_type hv_scheduler_type;
> diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h
> index 469186df7826..51abbcd0ec37 100644
> --- a/include/hyperv/hvhdk.h
> +++ b/include/hyperv/hvhdk.h
> @@ -10,6 +10,443 @@
> #include "hvhdk_mini.h"
> #include "hvgdk.h"
>
> +enum hv_stats_hypervisor_counters { /* HV_HYPERVISOR_COUNTER */
> + HvLogicalProcessors = 1,
> + HvPartitions = 2,
> + HvTotalPages = 3,
> + HvVirtualProcessors = 4,
> + HvMonitoredNotifications = 5,
> + HvModernStandbyEntries = 6,
> + HvPlatformIdleTransitions = 7,
> + HvHypervisorStartupCost = 8,
> + HvIOSpacePages = 10,
> + HvNonEssentialPagesForDump = 11,
> + HvSubsumedPages = 12,
> + HvStatsMaxCounter
> +};
> +
> +enum hv_stats_partition_counters { /* HV_PROCESS_COUNTER */
> + PartitionVirtualProcessors = 1,
> + PartitionTlbSize = 3,
> + PartitionAddressSpaces = 4,
> + PartitionDepositedPages = 5,
> + PartitionGpaPages = 6,
> + PartitionGpaSpaceModifications = 7,
> + PartitionVirtualTlbFlushEntires = 8,
> + PartitionRecommendedTlbSize = 9,
> + PartitionGpaPages4K = 10,
> + PartitionGpaPages2M = 11,
> + PartitionGpaPages1G = 12,
> + PartitionGpaPages512G = 13,
> + PartitionDevicePages4K = 14,
> + PartitionDevicePages2M = 15,
> + PartitionDevicePages1G = 16,
> + PartitionDevicePages512G = 17,
> + PartitionAttachedDevices = 18,
> + PartitionDeviceInterruptMappings = 19,
> + PartitionIoTlbFlushes = 20,
> + PartitionIoTlbFlushCost = 21,
> + PartitionDeviceInterruptErrors = 22,
> + PartitionDeviceDmaErrors = 23,
> + PartitionDeviceInterruptThrottleEvents = 24,
> + PartitionSkippedTimerTicks = 25,
> + PartitionPartitionId = 26,
> +#if IS_ENABLED(CONFIG_X86_64)
> + PartitionNestedTlbSize = 27,
> + PartitionRecommendedNestedTlbSize = 28,
> + PartitionNestedTlbFreeListSize = 29,
> + PartitionNestedTlbTrimmedPages = 30,
> + PartitionPagesShattered = 31,
> + PartitionPagesRecombined = 32,
> + PartitionHwpRequestValue = 33,
> +#elif IS_ENABLED(CONFIG_ARM64)
> + PartitionHwpRequestValue = 27,
> +#endif
> + PartitionStatsMaxCounter
> +};
> +
> +enum hv_stats_vp_counters { /* HV_THREAD_COUNTER */
> + VpTotalRunTime = 1,
> + VpHypervisorRunTime = 2,
> + VpRemoteNodeRunTime = 3,
> + VpNormalizedRunTime = 4,
> + VpIdealCpu = 5,
> + VpHypercallsCount = 7,
> + VpHypercallsTime = 8,
> +#if IS_ENABLED(CONFIG_X86_64)
> + VpPageInvalidationsCount = 9,
> + VpPageInvalidationsTime = 10,
> + VpControlRegisterAccessesCount = 11,
> + VpControlRegisterAccessesTime = 12,
> + VpIoInstructionsCount = 13,
> + VpIoInstructionsTime = 14,
> + VpHltInstructionsCount = 15,
> + VpHltInstructionsTime = 16,
> + VpMwaitInstructionsCount = 17,
> + VpMwaitInstructionsTime = 18,
> + VpCpuidInstructionsCount = 19,
> + VpCpuidInstructionsTime = 20,
> + VpMsrAccessesCount = 21,
> + VpMsrAccessesTime = 22,
> + VpOtherInterceptsCount = 23,
> + VpOtherInterceptsTime = 24,
> + VpExternalInterruptsCount = 25,
> + VpExternalInterruptsTime = 26,
> + VpPendingInterruptsCount = 27,
> + VpPendingInterruptsTime = 28,
> + VpEmulatedInstructionsCount = 29,
> + VpEmulatedInstructionsTime = 30,
> + VpDebugRegisterAccessesCount = 31,
> + VpDebugRegisterAccessesTime = 32,
> + VpPageFaultInterceptsCount = 33,
> + VpPageFaultInterceptsTime = 34,
> + VpGuestPageTableMaps = 35,
> + VpLargePageTlbFills = 36,
> + VpSmallPageTlbFills = 37,
> + VpReflectedGuestPageFaults = 38,
> + VpApicMmioAccesses = 39,
> + VpIoInterceptMessages = 40,
> + VpMemoryInterceptMessages = 41,
> + VpApicEoiAccesses = 42,
> + VpOtherMessages = 43,
> + VpPageTableAllocations = 44,
> + VpLogicalProcessorMigrations = 45,
> + VpAddressSpaceEvictions = 46,
> + VpAddressSpaceSwitches = 47,
> + VpAddressDomainFlushes = 48,
> + VpAddressSpaceFlushes = 49,
> + VpGlobalGvaRangeFlushes = 50,
> + VpLocalGvaRangeFlushes = 51,
> + VpPageTableEvictions = 52,
> + VpPageTableReclamations = 53,
> + VpPageTableResets = 54,
> + VpPageTableValidations = 55,
> + VpApicTprAccesses = 56,
> + VpPageTableWriteIntercepts = 57,
> + VpSyntheticInterrupts = 58,
> + VpVirtualInterrupts = 59,
> + VpApicIpisSent = 60,
> + VpApicSelfIpisSent = 61,
> + VpGpaSpaceHypercalls = 62,
> + VpLogicalProcessorHypercalls = 63,
> + VpLongSpinWaitHypercalls = 64,
> + VpOtherHypercalls = 65,
> + VpSyntheticInterruptHypercalls = 66,
> + VpVirtualInterruptHypercalls = 67,
> + VpVirtualMmuHypercalls = 68,
> + VpVirtualProcessorHypercalls = 69,
> + VpHardwareInterrupts = 70,
> + VpNestedPageFaultInterceptsCount = 71,
> + VpNestedPageFaultInterceptsTime = 72,
> + VpPageScans = 73,
> + VpLogicalProcessorDispatches = 74,
> + VpWaitingForCpuTime = 75,
> + VpExtendedHypercalls = 76,
> + VpExtendedHypercallInterceptMessages = 77,
> + VpMbecNestedPageTableSwitches = 78,
> + VpOtherReflectedGuestExceptions = 79,
> + VpGlobalIoTlbFlushes = 80,
> + VpGlobalIoTlbFlushCost = 81,
> + VpLocalIoTlbFlushes = 82,
> + VpLocalIoTlbFlushCost = 83,
> + VpHypercallsForwardedCount = 84,
> + VpHypercallsForwardingTime = 85,
> + VpPageInvalidationsForwardedCount = 86,
> + VpPageInvalidationsForwardingTime = 87,
> + VpControlRegisterAccessesForwardedCount = 88,
> + VpControlRegisterAccessesForwardingTime = 89,
> + VpIoInstructionsForwardedCount = 90,
> + VpIoInstructionsForwardingTime = 91,
> + VpHltInstructionsForwardedCount = 92,
> + VpHltInstructionsForwardingTime = 93,
> + VpMwaitInstructionsForwardedCount = 94,
> + VpMwaitInstructionsForwardingTime = 95,
> + VpCpuidInstructionsForwardedCount = 96,
> + VpCpuidInstructionsForwardingTime = 97,
> + VpMsrAccessesForwardedCount = 98,
> + VpMsrAccessesForwardingTime = 99,
> + VpOtherInterceptsForwardedCount = 100,
> + VpOtherInterceptsForwardingTime = 101,
> + VpExternalInterruptsForwardedCount = 102,
> + VpExternalInterruptsForwardingTime = 103,
> + VpPendingInterruptsForwardedCount = 104,
> + VpPendingInterruptsForwardingTime = 105,
> + VpEmulatedInstructionsForwardedCount = 106,
> + VpEmulatedInstructionsForwardingTime = 107,
> + VpDebugRegisterAccessesForwardedCount = 108,
> + VpDebugRegisterAccessesForwardingTime = 109,
> + VpPageFaultInterceptsForwardedCount = 110,
> + VpPageFaultInterceptsForwardingTime = 111,
> + VpVmclearEmulationCount = 112,
> + VpVmclearEmulationTime = 113,
> + VpVmptrldEmulationCount = 114,
> + VpVmptrldEmulationTime = 115,
> + VpVmptrstEmulationCount = 116,
> + VpVmptrstEmulationTime = 117,
> + VpVmreadEmulationCount = 118,
> + VpVmreadEmulationTime = 119,
> + VpVmwriteEmulationCount = 120,
> + VpVmwriteEmulationTime = 121,
> + VpVmxoffEmulationCount = 122,
> + VpVmxoffEmulationTime = 123,
> + VpVmxonEmulationCount = 124,
> + VpVmxonEmulationTime = 125,
> + VpNestedVMEntriesCount = 126,
> + VpNestedVMEntriesTime = 127,
> + VpNestedSLATSoftPageFaultsCount = 128,
> + VpNestedSLATSoftPageFaultsTime = 129,
> + VpNestedSLATHardPageFaultsCount = 130,
> + VpNestedSLATHardPageFaultsTime = 131,
> + VpInvEptAllContextEmulationCount = 132,
> + VpInvEptAllContextEmulationTime = 133,
> + VpInvEptSingleContextEmulationCount = 134,
> + VpInvEptSingleContextEmulationTime = 135,
> + VpInvVpidAllContextEmulationCount = 136,
> + VpInvVpidAllContextEmulationTime = 137,
> + VpInvVpidSingleContextEmulationCount = 138,
> + VpInvVpidSingleContextEmulationTime = 139,
> + VpInvVpidSingleAddressEmulationCount = 140,
> + VpInvVpidSingleAddressEmulationTime = 141,
> + VpNestedTlbPageTableReclamations = 142,
> + VpNestedTlbPageTableEvictions = 143,
> + VpFlushGuestPhysicalAddressSpaceHypercalls = 144,
> + VpFlushGuestPhysicalAddressListHypercalls = 145,
> + VpPostedInterruptNotifications = 146,
> + VpPostedInterruptScans = 147,
> + VpTotalCoreRunTime = 148,
> + VpMaximumRunTime = 149,
> + VpHwpRequestContextSwitches = 150,
> + VpWaitingForCpuTimeBucket0 = 151,
> + VpWaitingForCpuTimeBucket1 = 152,
> + VpWaitingForCpuTimeBucket2 = 153,
> + VpWaitingForCpuTimeBucket3 = 154,
> + VpWaitingForCpuTimeBucket4 = 155,
> + VpWaitingForCpuTimeBucket5 = 156,
> + VpWaitingForCpuTimeBucket6 = 157,
> + VpVmloadEmulationCount = 158,
> + VpVmloadEmulationTime = 159,
> + VpVmsaveEmulationCount = 160,
> + VpVmsaveEmulationTime = 161,
> + VpGifInstructionEmulationCount = 162,
> + VpGifInstructionEmulationTime = 163,
> + VpEmulatedErrataSvmInstructions = 164,
> + VpPlaceholder1 = 165,
> + VpPlaceholder2 = 166,
> + VpPlaceholder3 = 167,
> + VpPlaceholder4 = 168,
> + VpPlaceholder5 = 169,
> + VpPlaceholder6 = 170,
> + VpPlaceholder7 = 171,
> + VpPlaceholder8 = 172,
> + VpPlaceholder9 = 173,
> + VpPlaceholder10 = 174,
> + VpSchedulingPriority = 175,
> + VpRdpmcInstructionsCount = 176,
> + VpRdpmcInstructionsTime = 177,
> + VpPerfmonPmuMsrAccessesCount = 178,
> + VpPerfmonLbrMsrAccessesCount = 179,
> + VpPerfmonIptMsrAccessesCount = 180,
> + VpPerfmonInterruptCount = 181,
> + VpVtl1DispatchCount = 182,
> + VpVtl2DispatchCount = 183,
> + VpVtl2DispatchBucket0 = 184,
> + VpVtl2DispatchBucket1 = 185,
> + VpVtl2DispatchBucket2 = 186,
> + VpVtl2DispatchBucket3 = 187,
> + VpVtl2DispatchBucket4 = 188,
> + VpVtl2DispatchBucket5 = 189,
> + VpVtl2DispatchBucket6 = 190,
> + VpVtl1RunTime = 191,
> + VpVtl2RunTime = 192,
> + VpIommuHypercalls = 193,
> + VpCpuGroupHypercalls = 194,
> + VpVsmHypercalls = 195,
> + VpEventLogHypercalls = 196,
> + VpDeviceDomainHypercalls = 197,
> + VpDepositHypercalls = 198,
> + VpSvmHypercalls = 199,
> + VpBusLockAcquisitionCount = 200,
> + VpUnused = 201,
> + VpRootDispatchThreadBlocked = 202,
> +#elif IS_ENABLED(CONFIG_ARM64)
> + VpSysRegAccessesCount = 9,
> + VpSysRegAccessesTime = 10,
> + VpSmcInstructionsCount = 11,
> + VpSmcInstructionsTime = 12,
> + VpOtherInterceptsCount = 13,
> + VpOtherInterceptsTime = 14,
> + VpExternalInterruptsCount = 15,
> + VpExternalInterruptsTime = 16,
> + VpPendingInterruptsCount = 17,
> + VpPendingInterruptsTime = 18,
> + VpGuestPageTableMaps = 19,
> + VpLargePageTlbFills = 20,
> + VpSmallPageTlbFills = 21,
> + VpReflectedGuestPageFaults = 22,
> + VpMemoryInterceptMessages = 23,
> + VpOtherMessages = 24,
> + VpLogicalProcessorMigrations = 25,
> + VpAddressDomainFlushes = 26,
> + VpAddressSpaceFlushes = 27,
> + VpSyntheticInterrupts = 28,
> + VpVirtualInterrupts = 29,
> + VpApicSelfIpisSent = 30,
> + VpGpaSpaceHypercalls = 31,
> + VpLogicalProcessorHypercalls = 32,
> + VpLongSpinWaitHypercalls = 33,
> + VpOtherHypercalls = 34,
> + VpSyntheticInterruptHypercalls = 35,
> + VpVirtualInterruptHypercalls = 36,
> + VpVirtualMmuHypercalls = 37,
> + VpVirtualProcessorHypercalls = 38,
> + VpHardwareInterrupts = 39,
> + VpNestedPageFaultInterceptsCount = 40,
> + VpNestedPageFaultInterceptsTime = 41,
> + VpLogicalProcessorDispatches = 42,
> + VpWaitingForCpuTime = 43,
> + VpExtendedHypercalls = 44,
> + VpExtendedHypercallInterceptMessages = 45,
> + VpMbecNestedPageTableSwitches = 46,
> + VpOtherReflectedGuestExceptions = 47,
> + VpGlobalIoTlbFlushes = 48,
> + VpGlobalIoTlbFlushCost = 49,
> + VpLocalIoTlbFlushes = 50,
> + VpLocalIoTlbFlushCost = 51,
> + VpFlushGuestPhysicalAddressSpaceHypercalls = 52,
> + VpFlushGuestPhysicalAddressListHypercalls = 53,
> + VpPostedInterruptNotifications = 54,
> + VpPostedInterruptScans = 55,
> + VpTotalCoreRunTime = 56,
> + VpMaximumRunTime = 57,
> + VpWaitingForCpuTimeBucket0 = 58,
> + VpWaitingForCpuTimeBucket1 = 59,
> + VpWaitingForCpuTimeBucket2 = 60,
> + VpWaitingForCpuTimeBucket3 = 61,
> + VpWaitingForCpuTimeBucket4 = 62,
> + VpWaitingForCpuTimeBucket5 = 63,
> + VpWaitingForCpuTimeBucket6 = 64,
> + VpHwpRequestContextSwitches = 65,
> + VpPlaceholder2 = 66,
> + VpPlaceholder3 = 67,
> + VpPlaceholder4 = 68,
> + VpPlaceholder5 = 69,
> + VpPlaceholder6 = 70,
> + VpPlaceholder7 = 71,
> + VpPlaceholder8 = 72,
> + VpContentionTime = 73,
> + VpWakeUpTime = 74,
> + VpSchedulingPriority = 75,
> + VpVtl1DispatchCount = 76,
> + VpVtl2DispatchCount = 77,
> + VpVtl2DispatchBucket0 = 78,
> + VpVtl2DispatchBucket1 = 79,
> + VpVtl2DispatchBucket2 = 80,
> + VpVtl2DispatchBucket3 = 81,
> + VpVtl2DispatchBucket4 = 82,
> + VpVtl2DispatchBucket5 = 83,
> + VpVtl2DispatchBucket6 = 84,
> + VpVtl1RunTime = 85,
> + VpVtl2RunTime = 86,
> + VpIommuHypercalls = 87,
> + VpCpuGroupHypercalls = 88,
> + VpVsmHypercalls = 89,
> + VpEventLogHypercalls = 90,
> + VpDeviceDomainHypercalls = 91,
> + VpDepositHypercalls = 92,
> + VpSvmHypercalls = 93,
> + VpLoadAvg = 94,
> + VpRootDispatchThreadBlocked = 95,
In current code, VpRootDispatchThreadBlocked on ARM64 is 94. Is that an
error that is being corrected by this patch?
> +#endif
> + VpStatsMaxCounter
> +};
> +
> +enum hv_stats_lp_counters { /* HV_CPU_COUNTER */
> + LpGlobalTime = 1,
> + LpTotalRunTime = 2,
> + LpHypervisorRunTime = 3,
> + LpHardwareInterrupts = 4,
> + LpContextSwitches = 5,
> + LpInterProcessorInterrupts = 6,
> + LpSchedulerInterrupts = 7,
> + LpTimerInterrupts = 8,
> + LpInterProcessorInterruptsSent = 9,
> + LpProcessorHalts = 10,
> + LpMonitorTransitionCost = 11,
> + LpContextSwitchTime = 12,
> + LpC1TransitionsCount = 13,
> + LpC1RunTime = 14,
> + LpC2TransitionsCount = 15,
> + LpC2RunTime = 16,
> + LpC3TransitionsCount = 17,
> + LpC3RunTime = 18,
> + LpRootVpIndex = 19,
> + LpIdleSequenceNumber = 20,
> + LpGlobalTscCount = 21,
> + LpActiveTscCount = 22,
> + LpIdleAccumulation = 23,
> + LpReferenceCycleCount0 = 24,
> + LpActualCycleCount0 = 25,
> + LpReferenceCycleCount1 = 26,
> + LpActualCycleCount1 = 27,
> + LpProximityDomainId = 28,
> + LpPostedInterruptNotifications = 29,
> + LpBranchPredictorFlushes = 30,
> +#if IS_ENABLED(CONFIG_X86_64)
> + LpL1DataCacheFlushes = 31,
> + LpImmediateL1DataCacheFlushes = 32,
> + LpMbFlushes = 33,
> + LpCounterRefreshSequenceNumber = 34,
> + LpCounterRefreshReferenceTime = 35,
> + LpIdleAccumulationSnapshot = 36,
> + LpActiveTscCountSnapshot = 37,
> + LpHwpRequestContextSwitches = 38,
> + LpPlaceholder1 = 39,
> + LpPlaceholder2 = 40,
> + LpPlaceholder3 = 41,
> + LpPlaceholder4 = 42,
> + LpPlaceholder5 = 43,
> + LpPlaceholder6 = 44,
> + LpPlaceholder7 = 45,
> + LpPlaceholder8 = 46,
> + LpPlaceholder9 = 47,
> + LpPlaceholder10 = 48,
> + LpReserveGroupId = 49,
> + LpRunningPriority = 50,
> + LpPerfmonInterruptCount = 51,
> +#elif IS_ENABLED(CONFIG_ARM64)
> + LpCounterRefreshSequenceNumber = 31,
> + LpCounterRefreshReferenceTime = 32,
> + LpIdleAccumulationSnapshot = 33,
> + LpActiveTscCountSnapshot = 34,
> + LpHwpRequestContextSwitches = 35,
> + LpPlaceholder2 = 36,
> + LpPlaceholder3 = 37,
> + LpPlaceholder4 = 38,
> + LpPlaceholder5 = 39,
> + LpPlaceholder6 = 40,
> + LpPlaceholder7 = 41,
> + LpPlaceholder8 = 42,
> + LpPlaceholder9 = 43,
> + LpSchLocalRunListSize = 44,
> + LpReserveGroupId = 45,
> + LpRunningPriority = 46,
> +#endif
> + LpStatsMaxCounter
> +};
> +
> +/*
> + * Hypervisor statsitics page format
s/statsitics/statistics/
> + */
> +struct hv_stats_page {
> + union {
> + u64 hv_cntrs[HvStatsMaxCounter]; /* Hypervisor counters
> */
> + u64 pt_cntrs[PartitionStatsMaxCounter]; /* Partition
> counters */
> + u64 vp_cntrs[VpStatsMaxCounter]; /* VP counters */
> + u64 lp_cntrs[LpStatsMaxCounter]; /* LP counters */
> + u8 data[HV_HYP_PAGE_SIZE];
> + };
> +} __packed;
> +
> /* Bits for dirty mask of hv_vp_register_page */
> #define HV_X64_REGISTER_CLASS_GENERAL 0
> #define HV_X64_REGISTER_CLASS_IP 1
> --
> 2.34.1
^ permalink raw reply
* RE: [PATCH v2 3/3] mshv: Add debugfs to view hypervisor statistics
From: Michael Kelley @ 2025-12-08 15:21 UTC (permalink / raw)
To: Nuno Das Neves, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, skinsburskii@linux.microsoft.com
Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
decui@microsoft.com, longli@microsoft.com,
prapal@linux.microsoft.com, mrathor@linux.microsoft.com,
paekkaladevi@linux.microsoft.com, Jinank Jain
In-Reply-To: <1764961122-31679-4-git-send-email-nunodasneves@linux.microsoft.com>
From: Nuno Das Neves <nunodasneves@linux.microsoft.com> Sent: Friday, December 5, 2025 10:59 AM
>
> Introduce a debugfs interface to expose root and child partition stats
> when running with mshv_root.
>
> Create a debugfs directory "mshv" containing 'stats' files organized by
> type and id. A stats file contains a number of counters depending on
> its type. e.g. an excerpt from a VP stats file:
>
> TotalRunTime : 1997602722
> HypervisorRunTime : 649671371
> RemoteNodeRunTime : 0
> NormalizedRunTime : 1997602721
> IdealCpu : 0
> HypercallsCount : 1708169
> HypercallsTime : 111914774
> PageInvalidationsCount : 0
> PageInvalidationsTime : 0
>
> On a root partition with some active child partitions, the entire
> directory structure may look like:
>
> mshv/
> stats # hypervisor stats
> lp/ # logical processors
> 0/ # LP id
> stats # LP 0 stats
> 1/
> 2/
> 3/
> partition/ # partition stats
> 1/ # root partition id
> stats # root partition stats
> vp/ # root virtual processors
> 0/ # root VP id
> stats # root VP 0 stats
> 1/
> 2/
> 3/
> 42/ # child partition id
> stats # child partition stats
> vp/ # child VPs
> 0/ # child VP id
> stats # child VP 0 stats
> 1/
> 43/
> 55/
>
In the above directory tree, each of the "stats" files is in a directory
by itself, where the directory name is the number of whatever
entity the stats are for (lp, partition, or vp). Do you expect there to
be other files parallel to "stats" that will be added later? Otherwise
you could collapse one directory level. The "best" directory structure
is somewhat a matter of taste and judgment, so there's not a "right"
answer. I don't object if your preference is to keep the numbered
directories, even if they are likely to never contain more than the
"stats" file.
> On L1VH, some stats are not present as it does not own the hardware
> like the root partition does:
> - The hypervisor and lp stats are not present
> - L1VH's partition directory is named "self" because it can't get its
> own id
> - Some of L1VH's partition and VP stats fields are not populated, because
> it can't map its own HV_STATS_AREA_PARENT page.
>
> Co-developed-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> Co-developed-by: Praveen K Paladugu <prapal@linux.microsoft.com>
> Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
> Co-developed-by: Mukesh Rathor <mrathor@linux.microsoft.com>
> Signed-off-by: Mukesh Rathor <mrathor@linux.microsoft.com>
> Co-developed-by: Purna Pavan Chandra Aekkaladevi
> <paekkaladevi@linux.microsoft.com>
> Signed-off-by: Purna Pavan Chandra Aekkaladevi <paekkaladevi@linux.microsoft.com>
> Co-developed-by: Jinank Jain <jinankjain@microsoft.com>
> Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
> Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
> Reviewed-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> ---
> drivers/hv/Makefile | 1 +
> drivers/hv/mshv_debugfs.c | 1122 +++++++++++++++++++++++++++++++++++
> drivers/hv/mshv_root.h | 34 ++
> drivers/hv/mshv_root_main.c | 32 +-
> 4 files changed, 1185 insertions(+), 4 deletions(-)
> create mode 100644 drivers/hv/mshv_debugfs.c
>
> diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
> index 58b8d07639f3..36278c936914 100644
> --- a/drivers/hv/Makefile
> +++ b/drivers/hv/Makefile
> @@ -15,6 +15,7 @@ hv_vmbus-$(CONFIG_HYPERV_TESTING) += hv_debugfs.o
> hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_utils_transport.o
> mshv_root-y := mshv_root_main.o mshv_synic.o mshv_eventfd.o mshv_irq.o \
> mshv_root_hv_call.o mshv_portid_table.o
> +mshv_root-$(CONFIG_DEBUG_FS) += mshv_debugfs.o
> mshv_vtl-y := mshv_vtl_main.o
>
> # Code that must be built-in
> diff --git a/drivers/hv/mshv_debugfs.c b/drivers/hv/mshv_debugfs.c
> new file mode 100644
> index 000000000000..581018690a27
> --- /dev/null
> +++ b/drivers/hv/mshv_debugfs.c
> @@ -0,0 +1,1122 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2025, Microsoft Corporation.
> + *
> + * The /sys/kernel/debug/mshv directory contents.
> + * Contains various statistics data, provided by the hypervisor.
> + *
> + * Authors: Microsoft Linux virtualization team
> + */
> +
> +#include <linux/debugfs.h>
> +#include <linux/stringify.h>
> +#include <asm/mshyperv.h>
> +#include <linux/slab.h>
> +
> +#include "mshv.h"
> +#include "mshv_root.h"
> +
> +#define U32_BUF_SZ 11
> +#define U64_BUF_SZ 21
> +
> +static struct dentry *mshv_debugfs;
> +static struct dentry *mshv_debugfs_partition;
> +static struct dentry *mshv_debugfs_lp;
> +
> +static u64 mshv_lps_count;
> +
> +static bool is_l1vh_parent(u64 partition_id)
> +{
> + return hv_l1vh_partition() && (partition_id == HV_PARTITION_ID_SELF);
> +}
> +
> +static int lp_stats_show(struct seq_file *m, void *v)
> +{
> + const struct hv_stats_page *stats = m->private;
> +
> +#define LP_SEQ_PRINTF(cnt) \
> + seq_printf(m, "%-29s: %llu\n", __stringify(cnt), stats->lp_cntrs[Lp##cnt])
> +
> + LP_SEQ_PRINTF(GlobalTime);
> + LP_SEQ_PRINTF(TotalRunTime);
> + LP_SEQ_PRINTF(HypervisorRunTime);
> + LP_SEQ_PRINTF(HardwareInterrupts);
> + LP_SEQ_PRINTF(ContextSwitches);
> + LP_SEQ_PRINTF(InterProcessorInterrupts);
> + LP_SEQ_PRINTF(SchedulerInterrupts);
> + LP_SEQ_PRINTF(TimerInterrupts);
> + LP_SEQ_PRINTF(InterProcessorInterruptsSent);
> + LP_SEQ_PRINTF(ProcessorHalts);
> + LP_SEQ_PRINTF(MonitorTransitionCost);
> + LP_SEQ_PRINTF(ContextSwitchTime);
> + LP_SEQ_PRINTF(C1TransitionsCount);
> + LP_SEQ_PRINTF(C1RunTime);
> + LP_SEQ_PRINTF(C2TransitionsCount);
> + LP_SEQ_PRINTF(C2RunTime);
> + LP_SEQ_PRINTF(C3TransitionsCount);
> + LP_SEQ_PRINTF(C3RunTime);
> + LP_SEQ_PRINTF(RootVpIndex);
> + LP_SEQ_PRINTF(IdleSequenceNumber);
> + LP_SEQ_PRINTF(GlobalTscCount);
> + LP_SEQ_PRINTF(ActiveTscCount);
> + LP_SEQ_PRINTF(IdleAccumulation);
> + LP_SEQ_PRINTF(ReferenceCycleCount0);
> + LP_SEQ_PRINTF(ActualCycleCount0);
> + LP_SEQ_PRINTF(ReferenceCycleCount1);
> + LP_SEQ_PRINTF(ActualCycleCount1);
> + LP_SEQ_PRINTF(ProximityDomainId);
> + LP_SEQ_PRINTF(PostedInterruptNotifications);
> + LP_SEQ_PRINTF(BranchPredictorFlushes);
> +#if IS_ENABLED(CONFIG_X86_64)
> + LP_SEQ_PRINTF(L1DataCacheFlushes);
> + LP_SEQ_PRINTF(ImmediateL1DataCacheFlushes);
> + LP_SEQ_PRINTF(MbFlushes);
> + LP_SEQ_PRINTF(CounterRefreshSequenceNumber);
> + LP_SEQ_PRINTF(CounterRefreshReferenceTime);
> + LP_SEQ_PRINTF(IdleAccumulationSnapshot);
> + LP_SEQ_PRINTF(ActiveTscCountSnapshot);
> + LP_SEQ_PRINTF(HwpRequestContextSwitches);
> + LP_SEQ_PRINTF(Placeholder1);
> + LP_SEQ_PRINTF(Placeholder2);
> + LP_SEQ_PRINTF(Placeholder3);
> + LP_SEQ_PRINTF(Placeholder4);
> + LP_SEQ_PRINTF(Placeholder5);
> + LP_SEQ_PRINTF(Placeholder6);
> + LP_SEQ_PRINTF(Placeholder7);
> + LP_SEQ_PRINTF(Placeholder8);
> + LP_SEQ_PRINTF(Placeholder9);
> + LP_SEQ_PRINTF(Placeholder10);
> + LP_SEQ_PRINTF(ReserveGroupId);
> + LP_SEQ_PRINTF(RunningPriority);
> + LP_SEQ_PRINTF(PerfmonInterruptCount);
> +#elif IS_ENABLED(CONFIG_ARM64)
> + LP_SEQ_PRINTF(CounterRefreshSequenceNumber);
> + LP_SEQ_PRINTF(CounterRefreshReferenceTime);
> + LP_SEQ_PRINTF(IdleAccumulationSnapshot);
> + LP_SEQ_PRINTF(ActiveTscCountSnapshot);
> + LP_SEQ_PRINTF(HwpRequestContextSwitches);
> + LP_SEQ_PRINTF(Placeholder2);
> + LP_SEQ_PRINTF(Placeholder3);
> + LP_SEQ_PRINTF(Placeholder4);
> + LP_SEQ_PRINTF(Placeholder5);
> + LP_SEQ_PRINTF(Placeholder6);
> + LP_SEQ_PRINTF(Placeholder7);
> + LP_SEQ_PRINTF(Placeholder8);
> + LP_SEQ_PRINTF(Placeholder9);
> + LP_SEQ_PRINTF(SchLocalRunListSize);
> + LP_SEQ_PRINTF(ReserveGroupId);
> + LP_SEQ_PRINTF(RunningPriority);
> +#endif
> +
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(lp_stats);
> +
> +static void mshv_lp_stats_unmap(u32 lp_index, void *stats_page_addr)
> +{
> + union hv_stats_object_identity identity = {
> + .lp.lp_index = lp_index,
> + .lp.stats_area_type = HV_STATS_AREA_SELF,
> + };
> + int err;
> +
> + err = hv_unmap_stats_page(HV_STATS_OBJECT_LOGICAL_PROCESSOR,
> + stats_page_addr, &identity);
> + if (err)
> + pr_err("%s: failed to unmap logical processor %u stats, err: %d\n",
> + __func__, lp_index, err);
> +}
> +
> +static void __init *mshv_lp_stats_map(u32 lp_index)
> +{
> + union hv_stats_object_identity identity = {
> + .lp.lp_index = lp_index,
> + .lp.stats_area_type = HV_STATS_AREA_SELF,
> + };
> + void *stats;
> + int err;
> +
> + err = hv_map_stats_page(HV_STATS_OBJECT_LOGICAL_PROCESSOR, &identity,
> + &stats);
> + if (err) {
> + pr_err("%s: failed to map logical processor %u stats, err: %d\n",
> + __func__, lp_index, err);
> + return ERR_PTR(err);
> + }
> +
> + return stats;
> +}
> +
> +static void __init *lp_debugfs_stats_create(u32 lp_index, struct dentry *parent)
> +{
> + struct dentry *dentry;
> + void *stats;
> +
> + stats = mshv_lp_stats_map(lp_index);
> + if (IS_ERR(stats))
> + return stats;
> +
> + dentry = debugfs_create_file("stats", 0400, parent,
> + stats, &lp_stats_fops);
> + if (IS_ERR(dentry)) {
> + mshv_lp_stats_unmap(lp_index, stats);
> + return dentry;
> + }
> + return stats;
> +}
> +
> +static int __init lp_debugfs_create(u32 lp_index, struct dentry *parent)
> +{
> + struct dentry *idx;
> + char lp_idx_str[U32_BUF_SZ];
> + void *stats;
> + int err;
> +
> + sprintf(lp_idx_str, "%u", lp_index);
> +
> + idx = debugfs_create_dir(lp_idx_str, parent);
> + if (IS_ERR(idx))
> + return PTR_ERR(idx);
> +
> + stats = lp_debugfs_stats_create(lp_index, idx);
> + if (IS_ERR(stats)) {
> + err = PTR_ERR(stats);
> + goto remove_debugfs_lp_idx;
> + }
> +
> + return 0;
> +
> +remove_debugfs_lp_idx:
> + debugfs_remove_recursive(idx);
> + return err;
> +}
> +
> +static void mshv_debugfs_lp_remove(void)
> +{
> + int lp_index;
> +
> + debugfs_remove_recursive(mshv_debugfs_lp);
> +
> + for (lp_index = 0; lp_index < mshv_lps_count; lp_index++)
> + mshv_lp_stats_unmap(lp_index, NULL);
Passing NULL as the second argument here leaks the stats page
memory if Linux allocated the page as an overlay GPFN. But is that
considered OK because the debugfs entries for LPs are removed
only when the root partition is shutting down? That works as
long as hot-add/remove of CPUs isn't supported in the root
partition.
> +}
> +
> +static int __init mshv_debugfs_lp_create(struct dentry *parent)
> +{
> + struct dentry *lp_dir;
> + int err, lp_index;
> +
> + lp_dir = debugfs_create_dir("lp", parent);
> + if (IS_ERR(lp_dir))
> + return PTR_ERR(lp_dir);
> +
> + for (lp_index = 0; lp_index < mshv_lps_count; lp_index++) {
> + err = lp_debugfs_create(lp_index, lp_dir);
> + if (err)
> + goto remove_debugfs_lps;
> + }
> +
> + mshv_debugfs_lp = lp_dir;
> +
> + return 0;
> +
> +remove_debugfs_lps:
> + for (lp_index -= 1; lp_index >= 0; lp_index--)
> + mshv_lp_stats_unmap(lp_index, NULL);
> + debugfs_remove_recursive(lp_dir);
> + return err;
> +}
> +
> +static int vp_stats_show(struct seq_file *m, void *v)
> +{
> + const struct hv_stats_page **pstats = m->private;
> +
> +#define VP_SEQ_PRINTF(cnt) \
> +do { \
> + if (pstats[HV_STATS_AREA_SELF]->vp_cntrs[Vp##cnt]) \
> + seq_printf(m, "%-30s: %llu\n", __stringify(cnt), \
> + pstats[HV_STATS_AREA_SELF]->vp_cntrs[Vp##cnt]); \
> + else \
> + seq_printf(m, "%-30s: %llu\n", __stringify(cnt), \
> + pstats[HV_STATS_AREA_PARENT]->vp_cntrs[Vp##cnt]); \
> +} while (0)
I don't understand this logic. Like in mshv_vp_dispatch_thread_blocked(), if
the SELF value is zero, then the PARENT value is used. The implication is that
you never want to display a SELF value of zero, which is a bit unexpected
since I could imagine zero being valid for some counters. But the overall result
is that the displayed values may be a mix of SELF and PARENT values.
And of course after Patch 1 of this series, if running on an older hypervisor
that doesn't provide PARENT, then SELF will be used anyway, which further
muddies what's going on here, at least for me. :-)
If this is the correct behavior, please add some code comments as to
why it makes sense, including in the case where PARENT isn't available.
> +
> + VP_SEQ_PRINTF(TotalRunTime);
> + VP_SEQ_PRINTF(HypervisorRunTime);
> + VP_SEQ_PRINTF(RemoteNodeRunTime);
> + VP_SEQ_PRINTF(NormalizedRunTime);
> + VP_SEQ_PRINTF(IdealCpu);
> + VP_SEQ_PRINTF(HypercallsCount);
> + VP_SEQ_PRINTF(HypercallsTime);
> +#if IS_ENABLED(CONFIG_X86_64)
> + VP_SEQ_PRINTF(PageInvalidationsCount);
> + VP_SEQ_PRINTF(PageInvalidationsTime);
> + VP_SEQ_PRINTF(ControlRegisterAccessesCount);
> + VP_SEQ_PRINTF(ControlRegisterAccessesTime);
> + VP_SEQ_PRINTF(IoInstructionsCount);
> + VP_SEQ_PRINTF(IoInstructionsTime);
> + VP_SEQ_PRINTF(HltInstructionsCount);
> + VP_SEQ_PRINTF(HltInstructionsTime);
> + VP_SEQ_PRINTF(MwaitInstructionsCount);
> + VP_SEQ_PRINTF(MwaitInstructionsTime);
> + VP_SEQ_PRINTF(CpuidInstructionsCount);
> + VP_SEQ_PRINTF(CpuidInstructionsTime);
> + VP_SEQ_PRINTF(MsrAccessesCount);
> + VP_SEQ_PRINTF(MsrAccessesTime);
> + VP_SEQ_PRINTF(OtherInterceptsCount);
> + VP_SEQ_PRINTF(OtherInterceptsTime);
> + VP_SEQ_PRINTF(ExternalInterruptsCount);
> + VP_SEQ_PRINTF(ExternalInterruptsTime);
> + VP_SEQ_PRINTF(PendingInterruptsCount);
> + VP_SEQ_PRINTF(PendingInterruptsTime);
> + VP_SEQ_PRINTF(EmulatedInstructionsCount);
> + VP_SEQ_PRINTF(EmulatedInstructionsTime);
> + VP_SEQ_PRINTF(DebugRegisterAccessesCount);
> + VP_SEQ_PRINTF(DebugRegisterAccessesTime);
> + VP_SEQ_PRINTF(PageFaultInterceptsCount);
> + VP_SEQ_PRINTF(PageFaultInterceptsTime);
> + VP_SEQ_PRINTF(GuestPageTableMaps);
> + VP_SEQ_PRINTF(LargePageTlbFills);
> + VP_SEQ_PRINTF(SmallPageTlbFills);
> + VP_SEQ_PRINTF(ReflectedGuestPageFaults);
> + VP_SEQ_PRINTF(ApicMmioAccesses);
> + VP_SEQ_PRINTF(IoInterceptMessages);
> + VP_SEQ_PRINTF(MemoryInterceptMessages);
> + VP_SEQ_PRINTF(ApicEoiAccesses);
> + VP_SEQ_PRINTF(OtherMessages);
> + VP_SEQ_PRINTF(PageTableAllocations);
> + VP_SEQ_PRINTF(LogicalProcessorMigrations);
> + VP_SEQ_PRINTF(AddressSpaceEvictions);
> + VP_SEQ_PRINTF(AddressSpaceSwitches);
> + VP_SEQ_PRINTF(AddressDomainFlushes);
> + VP_SEQ_PRINTF(AddressSpaceFlushes);
> + VP_SEQ_PRINTF(GlobalGvaRangeFlushes);
> + VP_SEQ_PRINTF(LocalGvaRangeFlushes);
> + VP_SEQ_PRINTF(PageTableEvictions);
> + VP_SEQ_PRINTF(PageTableReclamations);
> + VP_SEQ_PRINTF(PageTableResets);
> + VP_SEQ_PRINTF(PageTableValidations);
> + VP_SEQ_PRINTF(ApicTprAccesses);
> + VP_SEQ_PRINTF(PageTableWriteIntercepts);
> + VP_SEQ_PRINTF(SyntheticInterrupts);
> + VP_SEQ_PRINTF(VirtualInterrupts);
> + VP_SEQ_PRINTF(ApicIpisSent);
> + VP_SEQ_PRINTF(ApicSelfIpisSent);
> + VP_SEQ_PRINTF(GpaSpaceHypercalls);
> + VP_SEQ_PRINTF(LogicalProcessorHypercalls);
> + VP_SEQ_PRINTF(LongSpinWaitHypercalls);
> + VP_SEQ_PRINTF(OtherHypercalls);
> + VP_SEQ_PRINTF(SyntheticInterruptHypercalls);
> + VP_SEQ_PRINTF(VirtualInterruptHypercalls);
> + VP_SEQ_PRINTF(VirtualMmuHypercalls);
> + VP_SEQ_PRINTF(VirtualProcessorHypercalls);
> + VP_SEQ_PRINTF(HardwareInterrupts);
> + VP_SEQ_PRINTF(NestedPageFaultInterceptsCount);
> + VP_SEQ_PRINTF(NestedPageFaultInterceptsTime);
> + VP_SEQ_PRINTF(PageScans);
> + VP_SEQ_PRINTF(LogicalProcessorDispatches);
> + VP_SEQ_PRINTF(WaitingForCpuTime);
> + VP_SEQ_PRINTF(ExtendedHypercalls);
> + VP_SEQ_PRINTF(ExtendedHypercallInterceptMessages);
> + VP_SEQ_PRINTF(MbecNestedPageTableSwitches);
> + VP_SEQ_PRINTF(OtherReflectedGuestExceptions);
> + VP_SEQ_PRINTF(GlobalIoTlbFlushes);
> + VP_SEQ_PRINTF(GlobalIoTlbFlushCost);
> + VP_SEQ_PRINTF(LocalIoTlbFlushes);
> + VP_SEQ_PRINTF(LocalIoTlbFlushCost);
> + VP_SEQ_PRINTF(HypercallsForwardedCount);
> + VP_SEQ_PRINTF(HypercallsForwardingTime);
> + VP_SEQ_PRINTF(PageInvalidationsForwardedCount);
> + VP_SEQ_PRINTF(PageInvalidationsForwardingTime);
> + VP_SEQ_PRINTF(ControlRegisterAccessesForwardedCount);
> + VP_SEQ_PRINTF(ControlRegisterAccessesForwardingTime);
> + VP_SEQ_PRINTF(IoInstructionsForwardedCount);
> + VP_SEQ_PRINTF(IoInstructionsForwardingTime);
> + VP_SEQ_PRINTF(HltInstructionsForwardedCount);
> + VP_SEQ_PRINTF(HltInstructionsForwardingTime);
> + VP_SEQ_PRINTF(MwaitInstructionsForwardedCount);
> + VP_SEQ_PRINTF(MwaitInstructionsForwardingTime);
> + VP_SEQ_PRINTF(CpuidInstructionsForwardedCount);
> + VP_SEQ_PRINTF(CpuidInstructionsForwardingTime);
> + VP_SEQ_PRINTF(MsrAccessesForwardedCount);
> + VP_SEQ_PRINTF(MsrAccessesForwardingTime);
> + VP_SEQ_PRINTF(OtherInterceptsForwardedCount);
> + VP_SEQ_PRINTF(OtherInterceptsForwardingTime);
> + VP_SEQ_PRINTF(ExternalInterruptsForwardedCount);
> + VP_SEQ_PRINTF(ExternalInterruptsForwardingTime);
> + VP_SEQ_PRINTF(PendingInterruptsForwardedCount);
> + VP_SEQ_PRINTF(PendingInterruptsForwardingTime);
> + VP_SEQ_PRINTF(EmulatedInstructionsForwardedCount);
> + VP_SEQ_PRINTF(EmulatedInstructionsForwardingTime);
> + VP_SEQ_PRINTF(DebugRegisterAccessesForwardedCount);
> + VP_SEQ_PRINTF(DebugRegisterAccessesForwardingTime);
> + VP_SEQ_PRINTF(PageFaultInterceptsForwardedCount);
> + VP_SEQ_PRINTF(PageFaultInterceptsForwardingTime);
> + VP_SEQ_PRINTF(VmclearEmulationCount);
> + VP_SEQ_PRINTF(VmclearEmulationTime);
> + VP_SEQ_PRINTF(VmptrldEmulationCount);
> + VP_SEQ_PRINTF(VmptrldEmulationTime);
> + VP_SEQ_PRINTF(VmptrstEmulationCount);
> + VP_SEQ_PRINTF(VmptrstEmulationTime);
> + VP_SEQ_PRINTF(VmreadEmulationCount);
> + VP_SEQ_PRINTF(VmreadEmulationTime);
> + VP_SEQ_PRINTF(VmwriteEmulationCount);
> + VP_SEQ_PRINTF(VmwriteEmulationTime);
> + VP_SEQ_PRINTF(VmxoffEmulationCount);
> + VP_SEQ_PRINTF(VmxoffEmulationTime);
> + VP_SEQ_PRINTF(VmxonEmulationCount);
> + VP_SEQ_PRINTF(VmxonEmulationTime);
> + VP_SEQ_PRINTF(NestedVMEntriesCount);
> + VP_SEQ_PRINTF(NestedVMEntriesTime);
> + VP_SEQ_PRINTF(NestedSLATSoftPageFaultsCount);
> + VP_SEQ_PRINTF(NestedSLATSoftPageFaultsTime);
> + VP_SEQ_PRINTF(NestedSLATHardPageFaultsCount);
> + VP_SEQ_PRINTF(NestedSLATHardPageFaultsTime);
> + VP_SEQ_PRINTF(InvEptAllContextEmulationCount);
> + VP_SEQ_PRINTF(InvEptAllContextEmulationTime);
> + VP_SEQ_PRINTF(InvEptSingleContextEmulationCount);
> + VP_SEQ_PRINTF(InvEptSingleContextEmulationTime);
> + VP_SEQ_PRINTF(InvVpidAllContextEmulationCount);
> + VP_SEQ_PRINTF(InvVpidAllContextEmulationTime);
> + VP_SEQ_PRINTF(InvVpidSingleContextEmulationCount);
> + VP_SEQ_PRINTF(InvVpidSingleContextEmulationTime);
> + VP_SEQ_PRINTF(InvVpidSingleAddressEmulationCount);
> + VP_SEQ_PRINTF(InvVpidSingleAddressEmulationTime);
> + VP_SEQ_PRINTF(NestedTlbPageTableReclamations);
> + VP_SEQ_PRINTF(NestedTlbPageTableEvictions);
> + VP_SEQ_PRINTF(FlushGuestPhysicalAddressSpaceHypercalls);
> + VP_SEQ_PRINTF(FlushGuestPhysicalAddressListHypercalls);
> + VP_SEQ_PRINTF(PostedInterruptNotifications);
> + VP_SEQ_PRINTF(PostedInterruptScans);
> + VP_SEQ_PRINTF(TotalCoreRunTime);
> + VP_SEQ_PRINTF(MaximumRunTime);
> + VP_SEQ_PRINTF(HwpRequestContextSwitches);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket0);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket1);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket2);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket3);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket4);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket5);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket6);
> + VP_SEQ_PRINTF(VmloadEmulationCount);
> + VP_SEQ_PRINTF(VmloadEmulationTime);
> + VP_SEQ_PRINTF(VmsaveEmulationCount);
> + VP_SEQ_PRINTF(VmsaveEmulationTime);
> + VP_SEQ_PRINTF(GifInstructionEmulationCount);
> + VP_SEQ_PRINTF(GifInstructionEmulationTime);
> + VP_SEQ_PRINTF(EmulatedErrataSvmInstructions);
> + VP_SEQ_PRINTF(Placeholder1);
> + VP_SEQ_PRINTF(Placeholder2);
> + VP_SEQ_PRINTF(Placeholder3);
> + VP_SEQ_PRINTF(Placeholder4);
> + VP_SEQ_PRINTF(Placeholder5);
> + VP_SEQ_PRINTF(Placeholder6);
> + VP_SEQ_PRINTF(Placeholder7);
> + VP_SEQ_PRINTF(Placeholder8);
> + VP_SEQ_PRINTF(Placeholder9);
> + VP_SEQ_PRINTF(Placeholder10);
> + VP_SEQ_PRINTF(SchedulingPriority);
> + VP_SEQ_PRINTF(RdpmcInstructionsCount);
> + VP_SEQ_PRINTF(RdpmcInstructionsTime);
> + VP_SEQ_PRINTF(PerfmonPmuMsrAccessesCount);
> + VP_SEQ_PRINTF(PerfmonLbrMsrAccessesCount);
> + VP_SEQ_PRINTF(PerfmonIptMsrAccessesCount);
> + VP_SEQ_PRINTF(PerfmonInterruptCount);
> + VP_SEQ_PRINTF(Vtl1DispatchCount);
> + VP_SEQ_PRINTF(Vtl2DispatchCount);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket0);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket1);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket2);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket3);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket4);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket5);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket6);
> + VP_SEQ_PRINTF(Vtl1RunTime);
> + VP_SEQ_PRINTF(Vtl2RunTime);
> + VP_SEQ_PRINTF(IommuHypercalls);
> + VP_SEQ_PRINTF(CpuGroupHypercalls);
> + VP_SEQ_PRINTF(VsmHypercalls);
> + VP_SEQ_PRINTF(EventLogHypercalls);
> + VP_SEQ_PRINTF(DeviceDomainHypercalls);
> + VP_SEQ_PRINTF(DepositHypercalls);
> + VP_SEQ_PRINTF(SvmHypercalls);
> + VP_SEQ_PRINTF(BusLockAcquisitionCount);
The x86 VpUnused counter is not shown. Any reason for that? All the
Placeholder counters *are* shown, so I'm just wondering what's
different.
> +#elif IS_ENABLED(CONFIG_ARM64)
> + VP_SEQ_PRINTF(SysRegAccessesCount);
> + VP_SEQ_PRINTF(SysRegAccessesTime);
> + VP_SEQ_PRINTF(SmcInstructionsCount);
> + VP_SEQ_PRINTF(SmcInstructionsTime);
> + VP_SEQ_PRINTF(OtherInterceptsCount);
> + VP_SEQ_PRINTF(OtherInterceptsTime);
> + VP_SEQ_PRINTF(ExternalInterruptsCount);
> + VP_SEQ_PRINTF(ExternalInterruptsTime);
> + VP_SEQ_PRINTF(PendingInterruptsCount);
> + VP_SEQ_PRINTF(PendingInterruptsTime);
> + VP_SEQ_PRINTF(GuestPageTableMaps);
> + VP_SEQ_PRINTF(LargePageTlbFills);
> + VP_SEQ_PRINTF(SmallPageTlbFills);
> + VP_SEQ_PRINTF(ReflectedGuestPageFaults);
> + VP_SEQ_PRINTF(MemoryInterceptMessages);
> + VP_SEQ_PRINTF(OtherMessages);
> + VP_SEQ_PRINTF(LogicalProcessorMigrations);
> + VP_SEQ_PRINTF(AddressDomainFlushes);
> + VP_SEQ_PRINTF(AddressSpaceFlushes);
> + VP_SEQ_PRINTF(SyntheticInterrupts);
> + VP_SEQ_PRINTF(VirtualInterrupts);
> + VP_SEQ_PRINTF(ApicSelfIpisSent);
> + VP_SEQ_PRINTF(GpaSpaceHypercalls);
> + VP_SEQ_PRINTF(LogicalProcessorHypercalls);
> + VP_SEQ_PRINTF(LongSpinWaitHypercalls);
> + VP_SEQ_PRINTF(OtherHypercalls);
> + VP_SEQ_PRINTF(SyntheticInterruptHypercalls);
> + VP_SEQ_PRINTF(VirtualInterruptHypercalls);
> + VP_SEQ_PRINTF(VirtualMmuHypercalls);
> + VP_SEQ_PRINTF(VirtualProcessorHypercalls);
> + VP_SEQ_PRINTF(HardwareInterrupts);
> + VP_SEQ_PRINTF(NestedPageFaultInterceptsCount);
> + VP_SEQ_PRINTF(NestedPageFaultInterceptsTime);
> + VP_SEQ_PRINTF(LogicalProcessorDispatches);
> + VP_SEQ_PRINTF(WaitingForCpuTime);
> + VP_SEQ_PRINTF(ExtendedHypercalls);
> + VP_SEQ_PRINTF(ExtendedHypercallInterceptMessages);
> + VP_SEQ_PRINTF(MbecNestedPageTableSwitches);
> + VP_SEQ_PRINTF(OtherReflectedGuestExceptions);
> + VP_SEQ_PRINTF(GlobalIoTlbFlushes);
> + VP_SEQ_PRINTF(GlobalIoTlbFlushCost);
> + VP_SEQ_PRINTF(LocalIoTlbFlushes);
> + VP_SEQ_PRINTF(LocalIoTlbFlushCost);
> + VP_SEQ_PRINTF(FlushGuestPhysicalAddressSpaceHypercalls);
> + VP_SEQ_PRINTF(FlushGuestPhysicalAddressListHypercalls);
> + VP_SEQ_PRINTF(PostedInterruptNotifications);
> + VP_SEQ_PRINTF(PostedInterruptScans);
> + VP_SEQ_PRINTF(TotalCoreRunTime);
> + VP_SEQ_PRINTF(MaximumRunTime);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket0);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket1);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket2);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket3);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket4);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket5);
> + VP_SEQ_PRINTF(WaitingForCpuTimeBucket6);
> + VP_SEQ_PRINTF(HwpRequestContextSwitches);
> + VP_SEQ_PRINTF(Placeholder2);
> + VP_SEQ_PRINTF(Placeholder3);
> + VP_SEQ_PRINTF(Placeholder4);
> + VP_SEQ_PRINTF(Placeholder5);
> + VP_SEQ_PRINTF(Placeholder6);
> + VP_SEQ_PRINTF(Placeholder7);
> + VP_SEQ_PRINTF(Placeholder8);
> + VP_SEQ_PRINTF(ContentionTime);
> + VP_SEQ_PRINTF(WakeUpTime);
> + VP_SEQ_PRINTF(SchedulingPriority);
> + VP_SEQ_PRINTF(Vtl1DispatchCount);
> + VP_SEQ_PRINTF(Vtl2DispatchCount);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket0);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket1);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket2);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket3);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket4);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket5);
> + VP_SEQ_PRINTF(Vtl2DispatchBucket6);
> + VP_SEQ_PRINTF(Vtl1RunTime);
> + VP_SEQ_PRINTF(Vtl2RunTime);
> + VP_SEQ_PRINTF(IommuHypercalls);
> + VP_SEQ_PRINTF(CpuGroupHypercalls);
> + VP_SEQ_PRINTF(VsmHypercalls);
> + VP_SEQ_PRINTF(EventLogHypercalls);
> + VP_SEQ_PRINTF(DeviceDomainHypercalls);
> + VP_SEQ_PRINTF(DepositHypercalls);
> + VP_SEQ_PRINTF(SvmHypercalls);
The ARM64 VpLoadAvg counter is not shown? Any reason why?
> +#endif
The VpRootDispatchThreadBlocked counter is not shown for either
x86 or ARM64. Is that intentional, and if so, why? I know the counter
is used in mshv_vp_dispatch_thread_blocked(), but it's not clear why
that means it shouldn't be shown here.
> +
> + return 0;
> +}
This function, vp_stats_show(), seems like a candidate for redoing based on a
static table that lists the counter names and index. Then the code just loops
through the table. On x86 each VP_SEQ_PRINTF() generates 42 bytes of code,
and there are 199 entries, so 8358 bytes. The table entries would probably
be 16 bytes each (a 64-bit pointer to the string constant, a 32-bit index value,
and 4 bytes of padding so each entry is 8-byte aligned). The actual space
saving isn't that large, but the code would be a lot more compact. The
other *_stats_shows() functions could do the same.
It's distasteful to me to see 420 lines of enum entries in Patch 2 of this series,
then followed by another 420 lines of matching *_SEQ_PRINTF entries. But I
realize that the goal of the enum entries is to match the Windows code, so I
guess it is what it is. But there's an argument for ditching the enum entries
entirely, and using the putative static table to capture the information. It
doesn't seem like matching the Windows code is saving much sync effort
since any additions/ subtractions to the enum entries need to be matched
with changes in the *_stats_show() functions, or in my putative static table.
But I guess if Windows changed only the value for an enum entry without
additions/subtractions, that would sync more easily.
I'm just throwing this out as a thought. You may prefer to keep everything
"as is", in which case ignore my comment and I won't raise it again.
> +DEFINE_SHOW_ATTRIBUTE(vp_stats);
> +
> +static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index, void *stats_page_addr,
> + enum hv_stats_area_type stats_area_type)
> +{
> + union hv_stats_object_identity identity = {
> + .vp.partition_id = partition_id,
> + .vp.vp_index = vp_index,
> + .vp.stats_area_type = stats_area_type,
> + };
> + int err;
> +
> + err = hv_unmap_stats_page(HV_STATS_OBJECT_VP, stats_page_addr, &identity);
> + if (err)
> + pr_err("%s: failed to unmap partition %llu vp %u %s stats, err: %d\n",
> + __func__, partition_id, vp_index,
> + (stats_area_type == HV_STATS_AREA_SELF) ? "self" : "parent",
> + err);
> +}
> +
> +static void *mshv_vp_stats_map(u64 partition_id, u32 vp_index,
> + enum hv_stats_area_type stats_area_type)
> +{
> + union hv_stats_object_identity identity = {
> + .vp.partition_id = partition_id,
> + .vp.vp_index = vp_index,
> + .vp.stats_area_type = stats_area_type,
> + };
> + void *stats;
> + int err;
> +
> + err = hv_map_stats_page(HV_STATS_OBJECT_VP, &identity, &stats);
> + if (err) {
> + pr_err("%s: failed to map partition %llu vp %u %s stats, err: %d\n",
> + __func__, partition_id, vp_index,
> + (stats_area_type == HV_STATS_AREA_SELF) ? "self" : "parent",
> + err);
> + return ERR_PTR(err);
> + }
> + return stats;
> +}
Presumably you've noticed that the functions mshv_vp_stats_map() and
mshv_vp_stats_unmap() also exist in mshv_root_main.c. They are static
functions in both places, so the compiler & linker do the right thing, but
it sure does make things a bit more complex for human readers. The versions
here follow a consistent pattern for (lp, vp, hv, partition), so maybe the ones
in mshv_root_main.c could be renamed to avoid confusion?
> +
> +static int vp_debugfs_stats_create(u64 partition_id, u32 vp_index,
> + struct dentry **vp_stats_ptr,
> + struct dentry *parent)
> +{
> + struct dentry *dentry;
> + struct hv_stats_page **pstats;
> + int err;
> +
> + pstats = kcalloc(2, sizeof(struct hv_stats_page *), GFP_KERNEL_ACCOUNT);
Open coding "2" as the first parameter makes assumptions about the values of
HV_STATS_AREA_SELF and HV_STATS_AREA_PARENT. Should use
HV_STATS_AREA_COUNT instead of "2" so that indexing into the array is certain
to work.
> + if (!pstats)
> + return -ENOMEM;
> +
> + pstats[HV_STATS_AREA_SELF] = mshv_vp_stats_map(partition_id, vp_index,
> + HV_STATS_AREA_SELF);
> + if (IS_ERR(pstats[HV_STATS_AREA_SELF])) {
> + err = PTR_ERR(pstats[HV_STATS_AREA_SELF]);
> + goto cleanup;
> + }
> +
> + /*
> + * L1VH partition cannot access its vp stats in parent area.
> + */
> + if (is_l1vh_parent(partition_id)) {
> + pstats[HV_STATS_AREA_PARENT] = pstats[HV_STATS_AREA_SELF];
> + } else {
> + pstats[HV_STATS_AREA_PARENT] = mshv_vp_stats_map(
> + partition_id, vp_index, HV_STATS_AREA_PARENT);
> + if (IS_ERR(pstats[HV_STATS_AREA_PARENT])) {
> + err = PTR_ERR(pstats[HV_STATS_AREA_PARENT]);
> + goto unmap_self;
> + }
> + if (!pstats[HV_STATS_AREA_PARENT])
> + pstats[HV_STATS_AREA_PARENT] = pstats[HV_STATS_AREA_SELF];
> + }
> +
> + dentry = debugfs_create_file("stats", 0400, parent,
> + pstats, &vp_stats_fops);
> + if (IS_ERR(dentry)) {
> + err = PTR_ERR(dentry);
> + goto unmap_vp_stats;
> + }
> +
> + *vp_stats_ptr = dentry;
> + return 0;
> +
> +unmap_vp_stats:
> + if (pstats[HV_STATS_AREA_PARENT] != pstats[HV_STATS_AREA_SELF])
> + mshv_vp_stats_unmap(partition_id, vp_index, pstats[HV_STATS_AREA_PARENT],
> + HV_STATS_AREA_PARENT);
> +unmap_self:
> + mshv_vp_stats_unmap(partition_id, vp_index, pstats[HV_STATS_AREA_SELF],
> + HV_STATS_AREA_SELF);
> +cleanup:
> + kfree(pstats);
> + return err;
> +}
> +
> +static void vp_debugfs_remove(u64 partition_id, u32 vp_index,
> + struct dentry *vp_stats)
> +{
> + struct hv_stats_page **pstats = NULL;
> + void *stats;
> +
> + pstats = vp_stats->d_inode->i_private;
> + debugfs_remove_recursive(vp_stats->d_parent);
> + if (pstats[HV_STATS_AREA_PARENT] != pstats[HV_STATS_AREA_SELF]) {
> + stats = pstats[HV_STATS_AREA_PARENT];
> + mshv_vp_stats_unmap(partition_id, vp_index, stats,
> + HV_STATS_AREA_PARENT);
> + }
> +
> + stats = pstats[HV_STATS_AREA_SELF];
> + mshv_vp_stats_unmap(partition_id, vp_index, stats, HV_STATS_AREA_SELF);
> +
> + kfree(pstats);
> +}
> +
> +static int vp_debugfs_create(u64 partition_id, u32 vp_index,
> + struct dentry **vp_stats_ptr,
> + struct dentry *parent)
> +{
> + struct dentry *vp_idx_dir;
> + char vp_idx_str[U32_BUF_SZ];
> + int err;
> +
> + sprintf(vp_idx_str, "%u", vp_index);
> +
> + vp_idx_dir = debugfs_create_dir(vp_idx_str, parent);
> + if (IS_ERR(vp_idx_dir))
> + return PTR_ERR(vp_idx_dir);
> +
> + err = vp_debugfs_stats_create(partition_id, vp_index, vp_stats_ptr,
> + vp_idx_dir);
> + if (err)
> + goto remove_debugfs_vp_idx;
> +
> + return 0;
> +
> +remove_debugfs_vp_idx:
> + debugfs_remove_recursive(vp_idx_dir);
> + return err;
> +}
> +
> +static int partition_stats_show(struct seq_file *m, void *v)
> +{
> + const struct hv_stats_page **pstats = m->private;
> +
> +#define PARTITION_SEQ_PRINTF(cnt) \
> +do { \
> + if (pstats[HV_STATS_AREA_SELF]->pt_cntrs[Partition##cnt]) \
> + seq_printf(m, "%-30s: %llu\n", __stringify(cnt), \
> + pstats[HV_STATS_AREA_SELF]->pt_cntrs[Partition##cnt]); \
> + else \
> + seq_printf(m, "%-30s: %llu\n", __stringify(cnt), \
> + pstats[HV_STATS_AREA_PARENT]->pt_cntrs[Partition##cnt]); \
> +} while (0)
Same comment as for VP_SEQ_PRINTF.
> +
> + PARTITION_SEQ_PRINTF(VirtualProcessors);
> + PARTITION_SEQ_PRINTF(TlbSize);
> + PARTITION_SEQ_PRINTF(AddressSpaces);
> + PARTITION_SEQ_PRINTF(DepositedPages);
> + PARTITION_SEQ_PRINTF(GpaPages);
> + PARTITION_SEQ_PRINTF(GpaSpaceModifications);
> + PARTITION_SEQ_PRINTF(VirtualTlbFlushEntires);
> + PARTITION_SEQ_PRINTF(RecommendedTlbSize);
> + PARTITION_SEQ_PRINTF(GpaPages4K);
> + PARTITION_SEQ_PRINTF(GpaPages2M);
> + PARTITION_SEQ_PRINTF(GpaPages1G);
> + PARTITION_SEQ_PRINTF(GpaPages512G);
> + PARTITION_SEQ_PRINTF(DevicePages4K);
> + PARTITION_SEQ_PRINTF(DevicePages2M);
> + PARTITION_SEQ_PRINTF(DevicePages1G);
> + PARTITION_SEQ_PRINTF(DevicePages512G);
> + PARTITION_SEQ_PRINTF(AttachedDevices);
> + PARTITION_SEQ_PRINTF(DeviceInterruptMappings);
> + PARTITION_SEQ_PRINTF(IoTlbFlushes);
> + PARTITION_SEQ_PRINTF(IoTlbFlushCost);
> + PARTITION_SEQ_PRINTF(DeviceInterruptErrors);
> + PARTITION_SEQ_PRINTF(DeviceDmaErrors);
> + PARTITION_SEQ_PRINTF(DeviceInterruptThrottleEvents);
> + PARTITION_SEQ_PRINTF(SkippedTimerTicks);
> + PARTITION_SEQ_PRINTF(PartitionId);
> +#if IS_ENABLED(CONFIG_X86_64)
> + PARTITION_SEQ_PRINTF(NestedTlbSize);
> + PARTITION_SEQ_PRINTF(RecommendedNestedTlbSize);
> + PARTITION_SEQ_PRINTF(NestedTlbFreeListSize);
> + PARTITION_SEQ_PRINTF(NestedTlbTrimmedPages);
> + PARTITION_SEQ_PRINTF(PagesShattered);
> + PARTITION_SEQ_PRINTF(PagesRecombined);
> + PARTITION_SEQ_PRINTF(HwpRequestValue);
> +#elif IS_ENABLED(CONFIG_ARM64)
> + PARTITION_SEQ_PRINTF(HwpRequestValue);
> +#endif
> +
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(partition_stats);
> +
> +static void mshv_partition_stats_unmap(u64 partition_id, void *stats_page_addr,
> + enum hv_stats_area_type stats_area_type)
> +{
> + union hv_stats_object_identity identity = {
> + .partition.partition_id = partition_id,
> + .partition.stats_area_type = stats_area_type,
> + };
> + int err;
> +
> + err = hv_unmap_stats_page(HV_STATS_OBJECT_PARTITION, stats_page_addr,
> + &identity);
> + if (err) {
> + pr_err("%s: failed to unmap partition %lld %s stats, err: %d\n",
> + __func__, partition_id,
> + (stats_area_type == HV_STATS_AREA_SELF) ? "self" : "parent",
> + err);
> + }
> +}
> +
> +static void *mshv_partition_stats_map(u64 partition_id,
> + enum hv_stats_area_type stats_area_type)
> +{
> + union hv_stats_object_identity identity = {
> + .partition.partition_id = partition_id,
> + .partition.stats_area_type = stats_area_type,
> + };
> + void *stats;
> + int err;
> +
> + err = hv_map_stats_page(HV_STATS_OBJECT_PARTITION, &identity, &stats);
> + if (err) {
> + pr_err("%s: failed to map partition %lld %s stats, err: %d\n",
> + __func__, partition_id,
> + (stats_area_type == HV_STATS_AREA_SELF) ? "self" : "parent",
> + err);
> + return ERR_PTR(err);
> + }
> + return stats;
> +}
> +
> +static int mshv_debugfs_partition_stats_create(u64 partition_id,
> + struct dentry **partition_stats_ptr,
> + struct dentry *parent)
> +{
> + struct dentry *dentry;
> + struct hv_stats_page **pstats;
> + int err;
> +
> + pstats = kcalloc(2, sizeof(struct hv_stats_page *), GFP_KERNEL_ACCOUNT);
Same comment here about the use of "2" as the first parameter.
> + if (!pstats)
> + return -ENOMEM;
> +
> + pstats[HV_STATS_AREA_SELF] = mshv_partition_stats_map(partition_id,
> + HV_STATS_AREA_SELF);
> + if (IS_ERR(pstats[HV_STATS_AREA_SELF])) {
> + err = PTR_ERR(pstats[HV_STATS_AREA_SELF]);
> + goto cleanup;
> + }
> +
> + /*
> + * L1VH partition cannot access its partition stats in parent area.
> + */
> + if (is_l1vh_parent(partition_id)) {
> + pstats[HV_STATS_AREA_PARENT] = pstats[HV_STATS_AREA_SELF];
> + } else {
> + pstats[HV_STATS_AREA_PARENT] = mshv_partition_stats_map(partition_id,
> + HV_STATS_AREA_PARENT);
> + if (IS_ERR(pstats[HV_STATS_AREA_PARENT])) {
> + err = PTR_ERR(pstats[HV_STATS_AREA_PARENT]);
> + goto unmap_self;
> + }
> + if (!pstats[HV_STATS_AREA_PARENT])
> + pstats[HV_STATS_AREA_PARENT] = pstats[HV_STATS_AREA_SELF];
> + }
> +
> + dentry = debugfs_create_file("stats", 0400, parent,
> + pstats, &partition_stats_fops);
> + if (IS_ERR(dentry)) {
> + err = PTR_ERR(dentry);
> + goto unmap_partition_stats;
> + }
> +
> + *partition_stats_ptr = dentry;
> + return 0;
> +
> +unmap_partition_stats:
> + if (pstats[HV_STATS_AREA_PARENT] != pstats[HV_STATS_AREA_SELF])
> + mshv_partition_stats_unmap(partition_id, pstats[HV_STATS_AREA_PARENT],
> + HV_STATS_AREA_PARENT);
> +unmap_self:
> + mshv_partition_stats_unmap(partition_id, pstats[HV_STATS_AREA_SELF],
> + HV_STATS_AREA_SELF);
> +cleanup:
> + kfree(pstats);
> + return err;
> +}
> +
> +static void partition_debugfs_remove(u64 partition_id, struct dentry *dentry)
> +{
> + struct hv_stats_page **pstats = NULL;
> + void *stats;
> +
> + pstats = dentry->d_inode->i_private;
> +
> + debugfs_remove_recursive(dentry->d_parent);
> +
> + if (pstats[HV_STATS_AREA_PARENT] != pstats[HV_STATS_AREA_SELF]) {
> + stats = pstats[HV_STATS_AREA_PARENT];
> + mshv_partition_stats_unmap(partition_id, stats, HV_STATS_AREA_PARENT);
> + }
> +
> + stats = pstats[HV_STATS_AREA_SELF];
> + mshv_partition_stats_unmap(partition_id, stats, HV_STATS_AREA_SELF);
> +
> + kfree(pstats);
> +}
> +
> +static int partition_debugfs_create(u64 partition_id,
> + struct dentry **vp_dir_ptr,
> + struct dentry **partition_stats_ptr,
> + struct dentry *parent)
> +{
> + char part_id_str[U64_BUF_SZ];
> + struct dentry *part_id_dir, *vp_dir;
> + int err;
> +
> + if (is_l1vh_parent(partition_id))
> + sprintf(part_id_str, "self");
> + else
> + sprintf(part_id_str, "%llu", partition_id);
> +
> + part_id_dir = debugfs_create_dir(part_id_str, parent);
> + if (IS_ERR(part_id_dir))
> + return PTR_ERR(part_id_dir);
> +
> + vp_dir = debugfs_create_dir("vp", part_id_dir);
> + if (IS_ERR(vp_dir)) {
> + err = PTR_ERR(vp_dir);
> + goto remove_debugfs_partition_id;
> + }
> +
> + err = mshv_debugfs_partition_stats_create(partition_id,
> + partition_stats_ptr,
> + part_id_dir);
> + if (err)
> + goto remove_debugfs_partition_id;
> +
> + *vp_dir_ptr = vp_dir;
> +
> + return 0;
> +
> +remove_debugfs_partition_id:
> + debugfs_remove_recursive(part_id_dir);
> + return err;
> +}
> +
> +static void mshv_debugfs_parent_partition_remove(void)
> +{
> + int idx;
> +
> + for_each_online_cpu(idx)
> + vp_debugfs_remove(hv_current_partition_id, idx, NULL);
> +
> + partition_debugfs_remove(hv_current_partition_id, NULL);
> +}
> +
> +static int __init mshv_debugfs_parent_partition_create(void)
> +{
> + struct dentry *partition_stats, *vp_dir;
> + int err, idx, i;
> +
> + mshv_debugfs_partition = debugfs_create_dir("partition",
> + mshv_debugfs);
> + if (IS_ERR(mshv_debugfs_partition))
> + return PTR_ERR(mshv_debugfs_partition);
> +
> + err = partition_debugfs_create(hv_current_partition_id,
> + &vp_dir,
> + &partition_stats,
> + mshv_debugfs_partition);
> + if (err)
> + goto remove_debugfs_partition;
> +
> + for_each_online_cpu(idx) {
> + struct dentry *vp_stats;
> +
> + err = vp_debugfs_create(hv_current_partition_id,
> + hv_vp_index[idx],
> + &vp_stats,
> + vp_dir);
> + if (err)
> + goto remove_debugfs_partition_vp;
> + }
> +
> + return 0;
> +
> +remove_debugfs_partition_vp:
> + for_each_online_cpu(i) {
> + if (i >= idx)
> + break;
> + vp_debugfs_remove(hv_current_partition_id, i, NULL);
> + }
> + partition_debugfs_remove(hv_current_partition_id, NULL);
> +remove_debugfs_partition:
> + debugfs_remove_recursive(mshv_debugfs_partition);
> + return err;
> +}
> +
> +static int hv_stats_show(struct seq_file *m, void *v)
> +{
> + const struct hv_stats_page *stats = m->private;
> +
> +#define HV_SEQ_PRINTF(cnt) \
> + seq_printf(m, "%-25s: %llu\n", __stringify(cnt), stats->hv_cntrs[Hv##cnt])
> +
> + HV_SEQ_PRINTF(LogicalProcessors);
> + HV_SEQ_PRINTF(Partitions);
> + HV_SEQ_PRINTF(TotalPages);
> + HV_SEQ_PRINTF(VirtualProcessors);
> + HV_SEQ_PRINTF(MonitoredNotifications);
> + HV_SEQ_PRINTF(ModernStandbyEntries);
> + HV_SEQ_PRINTF(PlatformIdleTransitions);
> + HV_SEQ_PRINTF(HypervisorStartupCost);
> + HV_SEQ_PRINTF(IOSpacePages);
> + HV_SEQ_PRINTF(NonEssentialPagesForDump);
> + HV_SEQ_PRINTF(SubsumedPages);
> +
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(hv_stats);
> +
> +static void mshv_hv_stats_unmap(void)
> +{
> + union hv_stats_object_identity identity = {
> + .hv.stats_area_type = HV_STATS_AREA_SELF,
> + };
> + int err;
> +
> + err = hv_unmap_stats_page(HV_STATS_OBJECT_HYPERVISOR, NULL, &identity);
> + if (err)
> + pr_err("%s: failed to unmap hypervisor stats: %d\n",
> + __func__, err);
> +}
> +
> +static void * __init mshv_hv_stats_map(void)
> +{
> + union hv_stats_object_identity identity = {
> + .hv.stats_area_type = HV_STATS_AREA_SELF,
> + };
> + void *stats;
> + int err;
> +
> + err = hv_map_stats_page(HV_STATS_OBJECT_HYPERVISOR, &identity, &stats);
> + if (err) {
> + pr_err("%s: failed to map hypervisor stats: %d\n",
> + __func__, err);
> + return ERR_PTR(err);
> + }
> + return stats;
> +}
> +
> +static int __init mshv_debugfs_hv_stats_create(struct dentry *parent)
> +{
> + struct dentry *dentry;
> + u64 *stats;
> + int err;
> +
> + stats = mshv_hv_stats_map();
> + if (IS_ERR(stats))
> + return PTR_ERR(stats);
> +
> + dentry = debugfs_create_file("stats", 0400, parent,
> + stats, &hv_stats_fops);
> + if (IS_ERR(dentry)) {
> + err = PTR_ERR(dentry);
> + pr_err("%s: failed to create hypervisor stats dentry: %d\n",
> + __func__, err);
> + goto unmap_hv_stats;
> + }
> +
> + mshv_lps_count = stats[HvLogicalProcessors];
> +
> + return 0;
> +
> +unmap_hv_stats:
> + mshv_hv_stats_unmap();
> + return err;
> +}
> +
> +int mshv_debugfs_vp_create(struct mshv_vp *vp)
> +{
> + struct mshv_partition *p = vp->vp_partition;
> + int err;
> +
> + if (!mshv_debugfs)
> + return 0;
> +
> + err = vp_debugfs_create(p->pt_id, vp->vp_index,
> + &vp->vp_debugfs_stats_dentry,
> + p->pt_debugfs_vp_dentry);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> +void mshv_debugfs_vp_remove(struct mshv_vp *vp)
> +{
> + if (!mshv_debugfs)
> + return;
> +
> + vp_debugfs_remove(vp->vp_partition->pt_id, vp->vp_index,
> + vp->vp_debugfs_stats_dentry);
> +}
> +
> +int mshv_debugfs_partition_create(struct mshv_partition *partition)
> +{
> + int err;
> +
> + if (!mshv_debugfs)
> + return 0;
> +
> + err = partition_debugfs_create(partition->pt_id,
> + &partition->pt_debugfs_vp_dentry,
> + &partition->pt_debugfs_stats_dentry,
> + mshv_debugfs_partition);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> +void mshv_debugfs_partition_remove(struct mshv_partition *partition)
> +{
> + if (!mshv_debugfs)
> + return;
> +
> + partition_debugfs_remove(partition->pt_id,
> + partition->pt_debugfs_stats_dentry);
> +}
> +
> +int __init mshv_debugfs_init(void)
> +{
> + int err;
> +
> + mshv_debugfs = debugfs_create_dir("mshv", NULL);
> + if (IS_ERR(mshv_debugfs)) {
> + pr_err("%s: failed to create debugfs directory\n", __func__);
> + return PTR_ERR(mshv_debugfs);
> + }
> +
> + if (hv_root_partition()) {
> + err = mshv_debugfs_hv_stats_create(mshv_debugfs);
> + if (err)
> + goto remove_mshv_dir;
> +
> + err = mshv_debugfs_lp_create(mshv_debugfs);
> + if (err)
> + goto unmap_hv_stats;
> + }
> +
> + err = mshv_debugfs_parent_partition_create();
> + if (err)
> + goto unmap_lp_stats;
> +
> + return 0;
> +
> +unmap_lp_stats:
> + if (hv_root_partition())
> + mshv_debugfs_lp_remove();
> +unmap_hv_stats:
> + if (hv_root_partition())
> + mshv_hv_stats_unmap();
> +remove_mshv_dir:
> + debugfs_remove_recursive(mshv_debugfs);
> + return err;
> +}
> +
> +void mshv_debugfs_exit(void)
> +{
> + mshv_debugfs_parent_partition_remove();
> +
> + if (hv_root_partition()) {
> + mshv_debugfs_lp_remove();
> + mshv_hv_stats_unmap();
> + }
> +
> + debugfs_remove_recursive(mshv_debugfs);
> +}
> diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
> index 3eb815011b46..1f1b1984449b 100644
> --- a/drivers/hv/mshv_root.h
> +++ b/drivers/hv/mshv_root.h
> @@ -51,6 +51,9 @@ struct mshv_vp {
> unsigned int kicked_by_hv;
> wait_queue_head_t vp_suspend_queue;
> } run;
> +#if IS_ENABLED(CONFIG_DEBUG_FS)
> + struct dentry *vp_debugfs_stats_dentry;
> +#endif
> };
>
> #define vp_fmt(fmt) "p%lluvp%u: " fmt
> @@ -128,6 +131,10 @@ struct mshv_partition {
> u64 isolation_type;
> bool import_completed;
> bool pt_initialized;
> +#if IS_ENABLED(CONFIG_DEBUG_FS)
> + struct dentry *pt_debugfs_stats_dentry;
> + struct dentry *pt_debugfs_vp_dentry;
> +#endif
> };
>
> #define pt_fmt(fmt) "p%llu: " fmt
> @@ -308,6 +315,33 @@ int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
> int hv_call_get_partition_property_ex(u64 partition_id, u64 property_code, u64 arg,
> void *property_value, size_t property_value_sz);
>
> +#if IS_ENABLED(CONFIG_DEBUG_FS)
> +int __init mshv_debugfs_init(void);
> +void mshv_debugfs_exit(void);
> +
> +int mshv_debugfs_partition_create(struct mshv_partition *partition);
> +void mshv_debugfs_partition_remove(struct mshv_partition *partition);
> +int mshv_debugfs_vp_create(struct mshv_vp *vp);
> +void mshv_debugfs_vp_remove(struct mshv_vp *vp);
> +#else
> +static inline int __init mshv_debugfs_init(void)
> +{
> + return 0;
> +}
> +static inline void mshv_debugfs_exit(void) { }
> +
> +static inline int mshv_debugfs_partition_create(struct mshv_partition *partition)
> +{
> + return 0;
> +}
> +static inline void mshv_debugfs_partition_remove(struct mshv_partition *partition) { }
> +static inline int mshv_debugfs_vp_create(struct mshv_vp *vp)
> +{
> + return 0;
> +}
> +static inline void mshv_debugfs_vp_remove(struct mshv_vp *vp) { }
> +#endif
> +
> extern struct mshv_root mshv_root;
> extern enum hv_scheduler_type hv_scheduler_type;
> extern u8 * __percpu *hv_synic_eventring_tail;
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index 19006b788e85..152fcd9b45e6 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -982,6 +982,10 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
> if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
> memcpy(vp->vp_stats_pages, stats_pages, sizeof(stats_pages));
>
> + ret = mshv_debugfs_vp_create(vp);
> + if (ret)
> + goto put_partition;
> +
> /*
> * Keep anon_inode_getfd last: it installs fd in the file struct and
> * thus makes the state accessible in user space.
> @@ -989,7 +993,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
> ret = anon_inode_getfd("mshv_vp", &mshv_vp_fops, vp,
> O_RDWR | O_CLOEXEC);
> if (ret < 0)
> - goto put_partition;
> + goto remove_debugfs_vp;
>
> /* already exclusive with the partition mutex for all ioctls */
> partition->pt_vp_count++;
> @@ -997,6 +1001,8 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
>
> return ret;
>
> +remove_debugfs_vp:
> + mshv_debugfs_vp_remove(vp);
> put_partition:
> mshv_partition_put(partition);
> free_vp:
> @@ -1556,13 +1562,18 @@ mshv_partition_ioctl_initialize(struct mshv_partition *partition)
>
> ret = hv_call_initialize_partition(partition->pt_id);
> if (ret)
> - goto withdraw_mem;
> + return ret;
> +
> + ret = mshv_debugfs_partition_create(partition);
> + if (ret)
> + goto finalize_partition;
>
> partition->pt_initialized = true;
>
> return 0;
>
> -withdraw_mem:
> +finalize_partition:
> + hv_call_finalize_partition(partition->pt_id);
> hv_call_withdraw_memory(U64_MAX, NUMA_NO_NODE, partition->pt_id);
>
> return ret;
> @@ -1741,6 +1752,8 @@ static void destroy_partition(struct mshv_partition *partition)
> if (!vp)
> continue;
>
> + mshv_debugfs_vp_remove(vp);
> +
> if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
> mshv_vp_stats_unmap(partition->pt_id, vp->vp_index,
> (void **)vp->vp_stats_pages);
> @@ -1775,6 +1788,8 @@ static void destroy_partition(struct mshv_partition *partition)
> partition->pt_vp_array[i] = NULL;
> }
>
> + mshv_debugfs_partition_remove(partition);
> +
> /* Deallocates and unmaps everything including vcpus, GPA mappings etc */
> hv_call_finalize_partition(partition->pt_id);
>
> @@ -2351,10 +2366,14 @@ static int __init mshv_parent_partition_init(void)
>
> mshv_init_vmm_caps(dev);
>
> - ret = mshv_irqfd_wq_init();
> + ret = mshv_debugfs_init();
> if (ret)
> goto exit_partition;
>
> + ret = mshv_irqfd_wq_init();
> + if (ret)
> + goto exit_debugfs;
> +
> spin_lock_init(&mshv_root.pt_ht_lock);
> hash_init(mshv_root.pt_htable);
>
> @@ -2362,6 +2381,10 @@ static int __init mshv_parent_partition_init(void)
>
> return 0;
>
> +destroy_irqds_wq:
> + mshv_irqfd_wq_cleanup();
> +exit_debugfs:
> + mshv_debugfs_exit();
> exit_partition:
> if (hv_root_partition())
> mshv_root_partition_exit();
> @@ -2378,6 +2401,7 @@ static void __exit mshv_parent_partition_exit(void)
> {
> hv_setup_mshv_handler(NULL);
> mshv_port_table_fini();
> + mshv_debugfs_exit();
> misc_deregister(&mshv_dev);
> mshv_irqfd_wq_cleanup();
> if (hv_root_partition())
> --
> 2.34.1
^ permalink raw reply
* Re: [GIT PULL] Hyper-V patches for 6.19
From: pr-tracker-bot @ 2025-12-08 21:56 UTC (permalink / raw)
To: Wei Liu
Cc: Linus Torvalds, Wei Liu, Linux on Hyper-V List, Linux Kernel List,
kys, haiyangz, decui, longli
In-Reply-To: <20251208064614.GA2485915@liuwe-devbox-debian-v2.local>
The pull request you sent on Mon, 8 Dec 2025 06:46:14 +0000:
> ssh://git@gitolite.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git tags/hyperv-next-signed-20251207
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/feb06d2690bb826fd33798a99ce5cff8d07b38f9
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* [PATCH AUTOSEL 6.18-6.12] net: mana: Drop TX skb on post_work_request failure and unmap resources
From: Sasha Levin @ 2025-12-09 0:15 UTC (permalink / raw)
To: patches, stable
Cc: Aditya Garg, Haiyang Zhang, Jakub Kicinski, Sasha Levin, kys,
wei.liu, decui, longli, kotaranov, leon, shradhagupta, mlevitsk,
ernis, yury.norov, ssengar, dipayanroy, shirazsaleem,
linux-hyperv, linux-rdma
In-Reply-To: <20251209001610.611575-1-sashal@kernel.org>
From: Aditya Garg <gargaditya@linux.microsoft.com>
[ Upstream commit 45120304e84171fd215c1b57b15b285446d15106 ]
Drop TX packets when posting the work request fails and ensure DMA
mappings are always cleaned up.
Signed-off-by: Aditya Garg <gargaditya@linux.microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Link: https://patch.msgid.link/1763464269-10431-3-git-send-email-gargaditya@linux.microsoft.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
Looking at this commit, I need to analyze it for stable backporting
suitability.
## Commit Analysis
### What the commit does:
1. **In `mana_en.c`**: When `mana_gd_post_work_request()` fails during
TX:
- Previously: returned `NETDEV_TX_BUSY` without cleaning up DMA
mappings
- Now: calls `mana_unmap_skb()` to clean up DMA mappings, then drops
the packet properly
2. **In `gdma_main.c`**: Removes a noisy `dev_err` message for flow
control failure
3. **In `mana.h`**: Exports `mana_unmap_skb()` function for use across
files
### Bug Being Fixed
Looking at the original error path:
```c
if (err) {
(void)skb_dequeue_tail(&txq->pending_skbs);
netdev_warn(ndev, "Failed to post TX OOB: %d\n", err);
err = NETDEV_TX_BUSY;
goto tx_busy; // DMA mappings NOT unmapped!
}
```
The original code has **two bugs**:
1. **DMA resource leak**: When `mana_gd_post_work_request()` fails, the
DMA mappings set up earlier (via `mana_map_skb()`) are never freed
2. **Improper error handling**: Returns `NETDEV_TX_BUSY` which tells the
network stack to retry, but the skb was already dequeued from
`pending_skbs`, leading to inconsistent state
### Code Change Assessment
The fix is straightforward and surgical:
```c
if (err) {
(void)skb_dequeue_tail(&txq->pending_skbs);
mana_unmap_skb(skb, apc); // NEW: Clean up DMA mappings
netdev_warn(ndev, "Failed to post TX OOB: %d\n", err);
goto free_sgl_ptr; // Drop packet properly
}
```
### Stable Criteria Evaluation
| Criterion | Assessment |
|-----------|------------|
| Fixes real bug | ✅ Yes - DMA resource leak and improper error handling
|
| Obviously correct | ✅ Yes - unmapping DMA on error is clearly correct
|
| Small and contained | ✅ Yes - only affects error path in MANA driver |
| No new features | ✅ Yes - pure bug fix |
| User impact | ✅ Medium-high - affects Azure VM users; leaks can cause
long-term instability |
### Risk Assessment
- **Low risk**: Changes only affect the TX error path
- **Self-contained**: The `mana_unmap_skb()` function already exists,
just made non-static
- **No dependencies**: This commit is standalone
- **Well-tested**: Has "Reviewed-by" tag from Microsoft maintainer
### Missing Signals
- No `Cc: stable@vger.kernel.org` tag
- No `Fixes:` tag
However, this is clearly fixing a resource leak bug that has existed
since the error path was written. Resource leaks in network drivers are
exactly the kind of bugs stable trees should fix.
### User Impact
The MANA driver is used in Microsoft Azure VMs. DMA mapping leaks:
- Cause memory pressure over time
- Can lead to allocation failures under sustained TX errors
- May cause VM instability in production cloud environments
This is a production-relevant bug affecting real cloud users.
### Conclusion
This commit fixes a genuine resource leak bug in the MANA network
driver. The fix is:
- Small (adds one function call + declaration)
- Surgical (only affects error path)
- Obviously correct (unmapping DMA on error is mandatory)
- Low risk (doesn't touch normal TX path)
- Important for production Azure VM users
The lack of stable/Fixes tags is unfortunate but doesn't diminish the
clear bug-fix nature of this change.
**YES**
drivers/net/ethernet/microsoft/mana/gdma_main.c | 6 +-----
drivers/net/ethernet/microsoft/mana/mana_en.c | 7 +++----
include/net/mana/mana.h | 1 +
3 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index 43f034e180c41..7d232ac250958 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -1299,7 +1299,6 @@ int mana_gd_post_work_request(struct gdma_queue *wq,
struct gdma_posted_wqe_info *wqe_info)
{
u32 client_oob_size = wqe_req->inline_oob_size;
- struct gdma_context *gc;
u32 sgl_data_size;
u32 max_wqe_size;
u32 wqe_size;
@@ -1329,11 +1328,8 @@ int mana_gd_post_work_request(struct gdma_queue *wq,
if (wqe_size > max_wqe_size)
return -EINVAL;
- if (wq->monitor_avl_buf && wqe_size > mana_gd_wq_avail_space(wq)) {
- gc = wq->gdma_dev->gdma_context;
- dev_err(gc->dev, "unsuccessful flow control!\n");
+ if (wq->monitor_avl_buf && wqe_size > mana_gd_wq_avail_space(wq))
return -ENOSPC;
- }
if (wqe_info)
wqe_info->wqe_size_in_bu = wqe_size / GDMA_WQE_BU_SIZE;
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 0142fd98392c2..6d37f39930453 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -438,9 +438,9 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
if (err) {
(void)skb_dequeue_tail(&txq->pending_skbs);
+ mana_unmap_skb(skb, apc);
netdev_warn(ndev, "Failed to post TX OOB: %d\n", err);
- err = NETDEV_TX_BUSY;
- goto tx_busy;
+ goto free_sgl_ptr;
}
err = NETDEV_TX_OK;
@@ -460,7 +460,6 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
tx_stats->bytes += len + ((num_gso_seg - 1) * gso_hs);
u64_stats_update_end(&tx_stats->syncp);
-tx_busy:
if (netif_tx_queue_stopped(net_txq) && mana_can_tx(gdma_sq)) {
netif_tx_wake_queue(net_txq);
apc->eth_stats.wake_queue++;
@@ -1606,7 +1605,7 @@ static int mana_move_wq_tail(struct gdma_queue *wq, u32 num_units)
return 0;
}
-static void mana_unmap_skb(struct sk_buff *skb, struct mana_port_context *apc)
+void mana_unmap_skb(struct sk_buff *skb, struct mana_port_context *apc)
{
struct mana_skb_head *ash = (struct mana_skb_head *)skb->head;
struct gdma_context *gc = apc->ac->gdma_dev->gdma_context;
diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
index 0921485565c05..330e1bb088bb9 100644
--- a/include/net/mana/mana.h
+++ b/include/net/mana/mana.h
@@ -580,6 +580,7 @@ int mana_set_bw_clamp(struct mana_port_context *apc, u32 speed,
void mana_query_phy_stats(struct mana_port_context *apc);
int mana_pre_alloc_rxbufs(struct mana_port_context *apc, int mtu, int num_queues);
void mana_pre_dealloc_rxbufs(struct mana_port_context *apc);
+void mana_unmap_skb(struct sk_buff *skb, struct mana_port_context *apc);
extern const struct ethtool_ops mana_ethtool_ops;
extern struct dentry *mana_debugfs_root;
--
2.51.0
^ permalink raw reply related
* [RFC v1 0/5] Hyper-V: Add para-virtualized IOMMU support for Linux guests
From: Yu Zhang @ 2025-12-09 5:11 UTC (permalink / raw)
To: linux-kernel, linux-hyperv, iommu, linux-pci
Cc: kys, haiyangz, wei.liu, decui, lpieralisi, kwilczynski, mani,
robh, bhelgaas, arnd, joro, will, robin.murphy, easwar.hariharan,
jacob.pan, nunodasneves, mrathor, mhklinux, peterz, linux-arch
This patch series introduces a para-virtualized IOMMU driver for
Linux guests running on Microsoft Hyper-V. The primary objective
is to enable hardware-assisted DMA isolation and scalable device
assignment for Hyper-V child partitions, bypassing the performance
overhead and complexity associated with emulated IOMMU hardware.
The driver implements the following core functionality:
* Hypercall-based Enumeration
Unlike traditional ACPI-based discovery (e.g., DMAR/IVRS),
this driver enumerates the Hyper-V IOMMU capabilities directly
via hypercalls. This approach allows the guest to discover
IOMMU presence and features without requiring specific virtual
firmware extensions or modifications.
* Domain Management
The driver manages IOMMU domains through a new set of Hyper-V
hypercall interfaces, handling domain allocation, attachment,
and detachment for endpoint devices.
* IOTLB Invalidation
IOTLB invalidation requests are marshaled and issued to the
hypervisor through the same hypercall mechanism.
* Nested Translation Support
This implementation leverages guest-managed stage-1 I/O page
tables nested with host stage-2 translations. It is built
upon the consolidated IOMMU page table framework designed by
Jason Gunthorpe [1]. This design eliminates the need for complex
emulation during map operations and ensures scalability across
different architectures.
Implementation Notes:
* Architecture Independence
While the current implementation only supports x86 platforms (Intel
VT-d and AMD IOMMU), the driver design aims to be as architecture-
agnostic as possible. To achieve this, initialization occurs via
`device_initcall` rather than `x86_init.iommu.iommu_init`, and shutdown
is handled via `syscore_ops` instead of `x86_platform.iommu_shutdown`.
* MSI Region Handling
In this RFC, the hardware MSI region is hard-coded to the standard
x86 interrupt range (0xfee00000 - 0xfeefffff). Future updates may
allow this configuration to be queried via hypercalls if new hardware
platforms are to be supported.
* Reserved Regions (RMRR)
There is currently no requirement to support assigned devices with
ACPI RMRR limitations. Consequently, this patch series does not specify
or query reserved memory regions.
Testing:
This series has been validated using dmatest with Intel DSA devices
assigned to the child partition. The tests confirmed successful DMA
transactions under the para-virtualized IOMMU.
Future Work:
* Page-selective IOTLB Invalidation
The current implementation relies on full-domain flushes. Support
for page-selective invalidation is planned for a future series.
* Advanced Features
Support for vSVA and virtual PRI will be addressed in subsequent
updates.
* Root Partition Co-existence
Ensure compatibility with the distinct para-virtualized IOMMU driver
used by Hyper-V's Linux root partition, in which the DMA remapping
is not achieved by stage-1 IO page tables and another set of iommu
ops is provided.
[1] https://github.com/jgunthorpe/linux/tree/iommu_pt_all
Easwar Hariharan (2):
PCI: hv: Create and export hv_build_logical_dev_id()
iommu: Move Hyper-V IOMMU driver to its own subdirectory
Wei Liu (1):
hyperv: Introduce new hypercall interfaces used by Hyper-V guest IOMMU
Yu Zhang (2):
hyperv: allow hypercall output pages to be allocated for child
partitions
iommu/hyperv: Add para-virtualized IOMMU support for Hyper-V guest
drivers/hv/hv_common.c | 21 +-
drivers/iommu/Kconfig | 10 +-
drivers/iommu/Makefile | 2 +-
drivers/iommu/hyperv/Kconfig | 24 +
drivers/iommu/hyperv/Makefile | 3 +
drivers/iommu/hyperv/iommu.c | 608 ++++++++++++++++++
drivers/iommu/hyperv/iommu.h | 53 ++
.../irq_remapping.c} | 2 +-
drivers/pci/controller/pci-hyperv.c | 28 +-
include/asm-generic/mshyperv.h | 2 +
include/hyperv/hvgdk_mini.h | 8 +
include/hyperv/hvhdk_mini.h | 123 ++++
12 files changed, 850 insertions(+), 34 deletions(-)
create mode 100644 drivers/iommu/hyperv/Kconfig
create mode 100644 drivers/iommu/hyperv/Makefile
create mode 100644 drivers/iommu/hyperv/iommu.c
create mode 100644 drivers/iommu/hyperv/iommu.h
rename drivers/iommu/{hyperv-iommu.c => hyperv/irq_remapping.c} (99%)
--
2.49.0
^ permalink raw reply
* [RFC v1 1/5] PCI: hv: Create and export hv_build_logical_dev_id()
From: Yu Zhang @ 2025-12-09 5:11 UTC (permalink / raw)
To: linux-kernel, linux-hyperv, iommu, linux-pci
Cc: kys, haiyangz, wei.liu, decui, lpieralisi, kwilczynski, mani,
robh, bhelgaas, arnd, joro, will, robin.murphy, easwar.hariharan,
jacob.pan, nunodasneves, mrathor, mhklinux, peterz, linux-arch
In-Reply-To: <20251209051128.76913-1-zhangyu1@linux.microsoft.com>
From: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
Hyper-V uses a logical device ID to identify a PCI endpoint device for
child partitions. This ID will also be required for future hypercalls
used by the Hyper-V IOMMU driver.
Refactor the logic for building this logical device ID into a standalone
helper function and export the interface for wider use.
Signed-off-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
Signed-off-by: Yu Zhang <zhangyu1@linux.microsoft.com>
---
drivers/pci/controller/pci-hyperv.c | 28 ++++++++++++++++++++--------
include/asm-generic/mshyperv.h | 2 ++
2 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c
index 146b43981b27..4b82e06b5d93 100644
--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -598,15 +598,31 @@ static unsigned int hv_msi_get_int_vector(struct irq_data *data)
#define hv_msi_prepare pci_msi_prepare
+/**
+ * Build a "Device Logical ID" out of this PCI bus's instance GUID and the
+ * function number of the device.
+ */
+u64 hv_build_logical_dev_id(struct pci_dev *pdev)
+{
+ struct pci_bus *pbus = pdev->bus;
+ struct hv_pcibus_device *hbus = container_of(pbus->sysdata,
+ struct hv_pcibus_device, sysdata);
+
+ return (u64)((hbus->hdev->dev_instance.b[5] << 24) |
+ (hbus->hdev->dev_instance.b[4] << 16) |
+ (hbus->hdev->dev_instance.b[7] << 8) |
+ (hbus->hdev->dev_instance.b[6] & 0xf8) |
+ PCI_FUNC(pdev->devfn));
+}
+EXPORT_SYMBOL_GPL(hv_build_logical_dev_id);
+
/**
* hv_irq_retarget_interrupt() - "Unmask" the IRQ by setting its current
* affinity.
* @data: Describes the IRQ
*
* Build new a destination for the MSI and make a hypercall to
- * update the Interrupt Redirection Table. "Device Logical ID"
- * is built out of this PCI bus's instance GUID and the function
- * number of the device.
+ * update the Interrupt Redirection Table.
*/
static void hv_irq_retarget_interrupt(struct irq_data *data)
{
@@ -642,11 +658,7 @@ static void hv_irq_retarget_interrupt(struct irq_data *data)
params->int_entry.source = HV_INTERRUPT_SOURCE_MSI;
params->int_entry.msi_entry.address.as_uint32 = int_desc->address & 0xffffffff;
params->int_entry.msi_entry.data.as_uint32 = int_desc->data;
- params->device_id = (hbus->hdev->dev_instance.b[5] << 24) |
- (hbus->hdev->dev_instance.b[4] << 16) |
- (hbus->hdev->dev_instance.b[7] << 8) |
- (hbus->hdev->dev_instance.b[6] & 0xf8) |
- PCI_FUNC(pdev->devfn);
+ params->device_id = hv_build_logical_dev_id(pdev);
params->int_target.vector = hv_msi_get_int_vector(data);
if (hbus->protocol_version >= PCI_PROTOCOL_VERSION_1_2) {
diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
index 64ba6bc807d9..1a205ed69435 100644
--- a/include/asm-generic/mshyperv.h
+++ b/include/asm-generic/mshyperv.h
@@ -71,6 +71,8 @@ extern enum hv_partition_type hv_curr_partition_type;
extern void * __percpu *hyperv_pcpu_input_arg;
extern void * __percpu *hyperv_pcpu_output_arg;
+extern u64 hv_build_logical_dev_id(struct pci_dev *pdev);
+
u64 hv_do_hypercall(u64 control, void *inputaddr, void *outputaddr);
u64 hv_do_fast_hypercall8(u16 control, u64 input8);
u64 hv_do_fast_hypercall16(u16 control, u64 input1, u64 input2);
--
2.49.0
^ permalink raw reply related
* [RFC v1 2/5] iommu: Move Hyper-V IOMMU driver to its own subdirectory
From: Yu Zhang @ 2025-12-09 5:11 UTC (permalink / raw)
To: linux-kernel, linux-hyperv, iommu, linux-pci
Cc: kys, haiyangz, wei.liu, decui, lpieralisi, kwilczynski, mani,
robh, bhelgaas, arnd, joro, will, robin.murphy, easwar.hariharan,
jacob.pan, nunodasneves, mrathor, mhklinux, peterz, linux-arch
In-Reply-To: <20251209051128.76913-1-zhangyu1@linux.microsoft.com>
From: Easwar Hariharan <eahariha@linux.microsoft.com>
The Hyper-V IOMMU driver currently only supports IRQ remapping.
As it will be adding DMA remapping support, prepare a directory
to contain all the different feature files.
This is a simple rename commit and has no functional changes.
Signed-off-by: Easwar Hariharan <eahariha@linux.microsoft.com>
Signed-off-by: Yu Zhang <zhangyu1@linux.microsoft.com>
---
drivers/iommu/Kconfig | 10 +---------
drivers/iommu/Makefile | 2 +-
drivers/iommu/hyperv/Kconfig | 10 ++++++++++
drivers/iommu/hyperv/Makefile | 2 ++
.../iommu/{hyperv-iommu.c => hyperv/irq_remapping.c} | 2 +-
5 files changed, 15 insertions(+), 11 deletions(-)
create mode 100644 drivers/iommu/hyperv/Kconfig
create mode 100644 drivers/iommu/hyperv/Makefile
rename drivers/iommu/{hyperv-iommu.c => hyperv/irq_remapping.c} (99%)
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index c9ae3221cd6f..661ff4e764cc 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -194,6 +194,7 @@ config MSM_IOMMU
source "drivers/iommu/amd/Kconfig"
source "drivers/iommu/arm/Kconfig"
source "drivers/iommu/intel/Kconfig"
+source "drivers/iommu/hyperv/Kconfig"
source "drivers/iommu/iommufd/Kconfig"
source "drivers/iommu/riscv/Kconfig"
@@ -350,15 +351,6 @@ config MTK_IOMMU_V1
if unsure, say N here.
-config HYPERV_IOMMU
- bool "Hyper-V IRQ Handling"
- depends on HYPERV && X86
- select IOMMU_API
- default HYPERV
- help
- Stub IOMMU driver to handle IRQs to support Hyper-V Linux
- guest and root partitions.
-
config VIRTIO_IOMMU
tristate "Virtio IOMMU driver"
depends on VIRTIO
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index b17ef9818759..757dc377cb66 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_AMD_IOMMU) += amd/
obj-$(CONFIG_INTEL_IOMMU) += intel/
obj-$(CONFIG_RISCV_IOMMU) += riscv/
obj-$(CONFIG_GENERIC_PT) += generic_pt/fmt/
+obj-$(CONFIG_HYPERV_IOMMU) += hyperv/
obj-$(CONFIG_IOMMU_API) += iommu.o
obj-$(CONFIG_IOMMU_SUPPORT) += iommu-pages.o
obj-$(CONFIG_IOMMU_API) += iommu-traces.o
@@ -29,7 +30,6 @@ obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
-obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o
obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o
obj-$(CONFIG_IOMMU_IOPF) += io-pgfault.o
diff --git a/drivers/iommu/hyperv/Kconfig b/drivers/iommu/hyperv/Kconfig
new file mode 100644
index 000000000000..30f40d867036
--- /dev/null
+++ b/drivers/iommu/hyperv/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# HyperV paravirtualized IOMMU support
+config HYPERV_IOMMU
+ bool "Hyper-V IRQ Handling"
+ depends on HYPERV && X86
+ select IOMMU_API
+ default HYPERV
+ help
+ Stub IOMMU driver to handle IRQs to support Hyper-V Linux
+ guest and root partitions.
diff --git a/drivers/iommu/hyperv/Makefile b/drivers/iommu/hyperv/Makefile
new file mode 100644
index 000000000000..9f557bad94ff
--- /dev/null
+++ b/drivers/iommu/hyperv/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_HYPERV_IOMMU) += irq_remapping.o
diff --git a/drivers/iommu/hyperv-iommu.c b/drivers/iommu/hyperv/irq_remapping.c
similarity index 99%
rename from drivers/iommu/hyperv-iommu.c
rename to drivers/iommu/hyperv/irq_remapping.c
index 0961ac805944..f2c4c7d67302 100644
--- a/drivers/iommu/hyperv-iommu.c
+++ b/drivers/iommu/hyperv/irq_remapping.c
@@ -22,7 +22,7 @@
#include <asm/hypervisor.h>
#include <asm/mshyperv.h>
-#include "irq_remapping.h"
+#include "../irq_remapping.h"
#ifdef CONFIG_IRQ_REMAP
--
2.49.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox