Linux-HyperV List
 help / color / mirror / Atom feed
* [PATCH 3/3] mshv: Add debugfs to view hypervisor statistics
From: Nuno Das Neves @ 2025-12-03 17:53 UTC (permalink / raw)
  To: linux-hyperv, linux-kernel
  Cc: kys, haiyangz, wei.liu, decui, longli, mhklinux, skinsburskii,
	prapal, mrathor, paekkaladevi, Nuno Das Neves, Jinank Jain
In-Reply-To: <1764784405-4484-1-git-send-email-nunodasneves@linux.microsoft.com>

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.

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>
---
 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);
+}
+
+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) {
+		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;
+
+	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 related

* [PATCH 2/3] mshv: Add definitions for stats pages
From: Nuno Das Neves @ 2025-12-03 17:53 UTC (permalink / raw)
  To: linux-hyperv, linux-kernel
  Cc: kys, haiyangz, wei.liu, decui, longli, mhklinux, skinsburskii,
	prapal, mrathor, paekkaladevi, Nuno Das Neves
In-Reply-To: <1764784405-4484-1-git-send-email-nunodasneves@linux.microsoft.com>

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.

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..2e3a7f80a560 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)
+	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)
+	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)
+	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 related

* [PATCH 1/3] mshv: Ignore second stats page map result failure
From: Nuno Das Neves @ 2025-12-03 17:53 UTC (permalink / raw)
  To: linux-hyperv, linux-kernel
  Cc: kys, haiyangz, wei.liu, decui, longli, mhklinux, skinsburskii,
	prapal, mrathor, paekkaladevi, Nuno Das Neves
In-Reply-To: <1764784405-4484-1-git-send-email-nunodasneves@linux.microsoft.com>

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].

Signed-off-by: Purna Pavan Chandra Aekkaladevi <paekkaladevi@linux.microsoft.com>
Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
---
 drivers/hv/mshv_root_hv_call.c | 43 ++++++++++++++++++++++++++++++----
 drivers/hv/mshv_root_main.c    |  3 +++
 2 files changed, 42 insertions(+), 4 deletions(-)

diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index 598eaff4ff29..0427785bb7fe 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,28 @@ 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) {
+				pr_debug_once("%s: PARENT area type is unsupported\n",
+					      __func__);
+				*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 related

* [PATCH 0/3] mshv: Debugfs interface for mshv_root
From: Nuno Das Neves @ 2025-12-03 17:53 UTC (permalink / raw)
  To: linux-hyperv, linux-kernel
  Cc: kys, haiyangz, wei.liu, decui, longli, mhklinux, skinsburskii,
	prapal, mrathor, paekkaladevi, Nuno Das Neves

Expose hypervisor, logical processor, partition, and virtual processor
statistics via debugfs. These are provided by mapping 'stats' pages via
hypercall.

Patch #1: Update hv_call_map_stats_page() to return success when
          HV_STATS_AREA_PARENT is unavailable, which is the case on some
          hypervisor versions, where it can fall back to HV_STATS_AREA_SELF
Patch #2: Introduce the definitions needed for the various stats pages
Patch #3: Add mshv_debugfs.c, and integrate it with the mshv_root driver to
          expose the partition and VP stats.

Nuno Das Neves (2):
  mshv: Add definitions for stats pages
  mshv: Add debugfs to view hypervisor statistics

Purna Pavan Chandra Aekkaladevi (1):
  mshv: Ignore second stats page map result failure

 drivers/hv/Makefile            |    1 +
 drivers/hv/mshv_debugfs.c      | 1122 ++++++++++++++++++++++++++++++++
 drivers/hv/mshv_root.h         |   34 +
 drivers/hv/mshv_root_hv_call.c |   43 +-
 drivers/hv/mshv_root_main.c    |   52 +-
 include/hyperv/hvhdk.h         |  437 +++++++++++++
 6 files changed, 1664 insertions(+), 25 deletions(-)
 create mode 100644 drivers/hv/mshv_debugfs.c

-- 
2.34.1


^ permalink raw reply

* Re: [PATCH v7 5/7] Drivers: hv: Improve region overlap detection in partition create
From: Stanislav Kinsburskii @ 2025-12-03 17:46 UTC (permalink / raw)
  To: Michael Kelley
  Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
	decui@microsoft.com, Nuno Das Neves, linux-hyperv@vger.kernel.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <SN6PR02MB41573FF177210DBC95509B09D4D8A@SN6PR02MB4157.namprd02.prod.outlook.com>

On Tue, Dec 02, 2025 at 06:39:51PM +0000, Michael Kelley wrote:
> From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com> Sent: Tuesday, November 25, 2025 6:09 PM
> > 
> > Refactor region overlap check in mshv_partition_create_region to use
> > mshv_partition_region_by_gfn for both start and end guest PFNs, replacing
> > manual iteration.
> > 
> > This is a cleaner approach that leverages existing functionality to
> > accurately detect overlapping memory regions.
> 
> Unfortunately, the cleaner approach doesn't work. :-( It doesn't detect a
> new region request that completely overlaps an existing region.
> 
> See https://lore.kernel.org/linux-hyperv/6a5f4ed5-63ae-4760-84c9-7290aaff8bd1@linux.microsoft.com/T/#ma91254da1900de61da520acb96c0de38c43562f6.
> I couldn't see anything that prevents the scenario. Nuno created this 
> patch less than a month ago: https://lore.kernel.org/linux-hyperv/1762467211-8213-2-git-send-email-nunodasneves@linux.microsoft.com/.
> 
> Michael
> 

I see. 
I'll drop it then.

Thanks,
Stanislav

> > 
> > Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> > ---
> >  drivers/hv/mshv_root_main.c |    8 ++------
> >  1 file changed, 2 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> > index 5dfb933da981..ae600b927f49 100644
> > --- a/drivers/hv/mshv_root_main.c
> > +++ b/drivers/hv/mshv_root_main.c
> > @@ -1086,13 +1086,9 @@ static int mshv_partition_create_region(struct
> > mshv_partition *partition,
> >  	u64 nr_pages = HVPFN_DOWN(mem->size);
> > 
> >  	/* Reject overlapping regions */
> > -	hlist_for_each_entry(rg, &partition->pt_mem_regions, hnode) {
> > -		if (mem->guest_pfn + nr_pages <= rg->start_gfn ||
> > -		    rg->start_gfn + rg->nr_pages <= mem->guest_pfn)
> > -			continue;
> > -
> > +	if (mshv_partition_region_by_gfn(partition, mem->guest_pfn) ||
> > +	    mshv_partition_region_by_gfn(partition, mem->guest_pfn + nr_pages - 1))
> >  		return -EEXIST;
> > -	}
> > 
> >  	rg = mshv_region_create(mem->guest_pfn, nr_pages,
> >  				mem->userspace_addr, mem->flags,
> > 
> > 
> 

^ permalink raw reply

* Re: [PATCH v6 2/3] hyperv: Use reboot notifier to configure sleep state
From: Ingo Molnar @ 2025-12-03 17:13 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: <20251126215013.11549-3-prapal@linux.microsoft.com>

* Praveen K Paladugu <prapal@linux.microsoft.com> wrote:

> Configure sleep state information in mshv hypervisor. This sleep state
> information from ACPI will be used by hypervisor to poweroff the host.
> 
> 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/hyperv/hv_init.c       |  1 +
>  arch/x86/include/asm/mshyperv.h |  2 +
>  drivers/hv/mshv_common.c        | 80 +++++++++++++++++++++++++++++++++
>  3 files changed, 83 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..166053df0484 100644
> --- a/arch/x86/include/asm/mshyperv.h
> +++ b/arch/x86/include/asm/mshyperv.h
> @@ -182,8 +182,10 @@ int hyperv_fill_flush_guest_mapping_list(
>  void hv_apic_init(void);
>  void __init hv_init_spinlocks(void);
>  bool hv_vcpu_is_preempted(int vcpu);
> +void hv_sleep_notifiers_register(void);
>  #else
>  static inline void hv_apic_init(void) {}
> +static inline void hv_sleep_notifiers_register(void) {};
>  #endif
>  
>  struct irq_domain *hv_create_pci_msi_domain(void);

Acked-by: Ingo Molnar <mingo@kernel.org>

Thanks,

	Ingo

^ permalink raw reply

* Re: [RFC PATCH] Drivers: hv: Confidential VMBus exernal memory support
From: Tianyu Lan @ 2025-12-03 14:21 UTC (permalink / raw)
  To: Michael Kelley, Christoph Hellwig, Robin Murphy
  Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
	decui@microsoft.com, longli@microsoft.com, vdso@hexbites.dev,
	Tianyu Lan, linux-hyperv@vger.kernel.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <SN6PR02MB4157DAE6D8CC6BA11CA87298D4DCA@SN6PR02MB4157.namprd02.prod.outlook.com>

On Sat, Nov 29, 2025 at 1:47 AM Michael Kelley <mhklinux@outlook.com> wrote:
>
> From: Tianyu Lan <ltykernel@gmail.com> Sent: Monday, November 24, 2025 10:29 AM
> >
> > In CVM(Confidential VM), system memory is encrypted
> > by default. Device drivers typically use the swiotlb
> > bounce buffer for DMA memory, which is decrypted
> > and shared between the guest and host. Confidential
> > Vmbus, however, supports a confidential channel
>

Hi Michael:
      Thanks for your review. I spent some time to do test
and still need paravisor(HCL) team to double check.

> s/Vmbus/VMBus/   [and elsewhere in this commit msg]
>

Will update.

> > that employs encrypted memory for the Vmbus ring
> > buffer and external DMA memory. The support for
> > the confidential ring buffer has already been
> > integrated.
> >
> > In CVM, device drivers usually employ the standard
> > DMA API to map DMA memory with the bounce buffer,
> > which remains transparent to the device driver.
> > For external DMA memory support,
>
> The "external memory" terminology is not at all clear in the context
> of Linux. Presumably the terminology came from Hyper-V or the
> paravisor, but I never understood what the "external" refers to. Maybe
> it is memory that is "external" with respect to the hypervisor, and therefore
> not shared between the hypervisor and guest? But that meaning won't be
> evident to other reviewers in the Linux kernel community. In any case, some
> explanation of "external memory" is needed. And even consider changing the
> field names in the code to be something that makes more sense to Linux.
>

Agree. For VMbus devices, there are two major kinds of communication
methods with
the host.

* VMbus ring buffer.
* Dynamic DMA transition

The confidential ring buffer has been upstreamed and merged into the
Linux kernel 6.18
by Roman. Additionally, the confidential DMA transition, also known as
external memory
allows the use of private/encrypted memory as DMA memory.

 > > Hyper-V specific
> > DMA operations are introduced, bypassing the bounce
> > buffer when the confidential external memory flag
> > is set.
>
> This commit message never explains why it is important to not do bounce
> buffering. There's the obvious but unstated reason of improving performance,
> but that's not the main reason. The main reason is a confidentiality leak.
> When available, Confidential VMBus would be used because it keeps the
> DMA data private (i.e., encrypted) and confidential to the guest. Bounce
> buffering copies the data to shared (i.e., decrypted) swiotlb memory, where
> it is exposed to the hypervisor. That's a confidentiality leak, and is the
> primary reason the bounce buffering must be eliminated. This requirement
> was pointed out by Robin Murphy in the discussion of Roman Kisel's
> original code to eliminate the bounce buffering.
>

For CVM on Hyper-V,, there are typically two layers: VTL0 and VTL2. VTL2
plays a similar role as L1 hypervisor of normal guest in VTL0. Communication
between these two layers can utilize private or encrypted memory directly, as
they both reside within the same CVM. This differs from the
communication between
the host and VTL0/VTL2, where shared or decrypted memory (bounce
buffer) is required.

For Confidential VMbus devices in CVM, the device module (emulated
device code) in
VTL2 and interacts with the normal guest in VTL0. As a result, the
Confidential VMbus
device's ring buffer and DMA transactions may use private or encrypted memory.

> Separately, I have major qualms about using an approach with Hyper-V specific
> DMA operations. As implemented here, these DMA operations bypass all
> the kernel DMA layer logic when the VMBus synthetic device indicates
> "external memory". In that case, the supplied physical address is just used
> as the DMA address and everything else is bypassed. While this actually works
> for VMBus synthetic devices because of their simple requirements, it also
> is implicitly making some assumptions. These assumptions are true today
> (as far as I know) but won't necessarily be true in the future.  The assumptions
> include:
>
> * There's no vIOMMU in the guest VM. IOMMUs in CoCo VMs have their
> own issues to work out, but an implementation that bypasses any IOMMU
> logic in the DMA layer is very short-term thinking.
>
> * There are no PCI pass-thru devices in the guest.  Since the implementation
> changes the global dma_ops, the drivers for such PCI pass-thru devices would
> use the same global dma_ops, and would fail.

For PCI pass-thru devices, it may be reused with additional changes(Co-work with
Dexuan for this support)  and we may rework the current callbacks to
meet the future
requirement.

>
> The code also has some other problems that I point out further down.
>
> In any case, an approach with Hyper-V specific DMA operations seems like
> a very temporary fix (hack?) at best. Further down, I'll proposed at alternate
> approach that preserves all the existing DMA layer functionality.
>
> > These DMA operations might also be reused
> > for TDISP devices in the future, which also support
> > DMA operations with encrypted memory.
> >
> > The DMA operations used are global architecture
> > DMA operations (for details, see get_arch_dma_ops()
> > and get_dma_ops()), and there is no need to set up
> > for each device individually.
> >
> > Signed-off-by: Tianyu Lan <tiala@microsoft.com>
> > ---
> >  drivers/hv/vmbus_drv.c | 90 +++++++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 89 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
> > index 0dc4692b411a..ca31231b2c32 100644
> > --- a/drivers/hv/vmbus_drv.c
> > +++ b/drivers/hv/vmbus_drv.c
> > @@ -39,6 +39,9 @@
> >  #include <clocksource/hyperv_timer.h>
> >  #include <asm/mshyperv.h>
> >  #include "hyperv_vmbus.h"
> > +#include "../../kernel/dma/direct.h"
> > +
> > +extern const struct dma_map_ops *dma_ops;
> >
> >  struct vmbus_dynid {
> >       struct list_head node;
> > @@ -1429,6 +1432,88 @@ static int vmbus_alloc_synic_and_connect(void)
> >       return -ENOMEM;
> >  }
> >
> > +
> > +static bool hyperv_private_memory_dma(struct device *dev)
> > +{
> > +     struct hv_device *hv_dev = device_to_hv_device(dev);
>
> device_to_hv_device() works only when "dev" is for a VMBus device. As noted above,
> if a CoCo VM were ever to have a PCI pass-thru device doing DMA, "dev" would
> be some PCI device, and device_to_hv_device() would return garbage.

Yes, Nice catch. PCI device support is not included in this patch and
still on the way
and post this with ConfidentialVMbus device for review first.

>
> > +
> > +     if (hv_dev && hv_dev->channel && hv_dev->channel->co_external_memory)
> > +             return true;
> > +     else
> > +             return false;
> > +}
> > +
> > +static dma_addr_t hyperv_dma_map_page(struct device *dev, struct page *page,
> > +             unsigned long offset, size_t size,
> > +             enum dma_data_direction dir,
> > +             unsigned long attrs)
> > +{
> > +     phys_addr_t phys = page_to_phys(page) + offset;
> > +
> > +     if (hyperv_private_memory_dma(dev))
> > +             return __phys_to_dma(dev, phys);
> > +     else
> > +             return dma_direct_map_phys(dev, phys, size, dir, attrs);
>
> This code won't build when the VMBus driver is built as a module.
> dma_direct_map_phys() is in inline function that references several other
> DMA functions that aren't exported because they aren't intended to be
> used by drivers. Same problems occur with dma_direct_unmap_phys()
> and similar functions used below.

Yes, Nice catch! We may add a function to register dma ops in the built-in file
and call it in the VMbus driver.

>
> > +}
> > +
> > +static void hyperv_dma_unmap_page(struct device *dev, dma_addr_t dma_handle,
> > +             size_t size, enum dma_data_direction dir, unsigned long attrs)
> > +{
> > +     if (!hyperv_private_memory_dma(dev))
> > +             dma_direct_unmap_phys(dev, dma_handle, size, dir, attrs);
> > +}
> > +
> > +static int hyperv_dma_map_sg(struct device *dev, struct scatterlist *sgl,
> > +             int nelems, enum dma_data_direction dir,
> > +             unsigned long attrs)
> > +{
> > +     struct scatterlist *sg;
> > +     dma_addr_t dma_addr;
> > +     int i;
> > +
> > +     if (hyperv_private_memory_dma(dev)) {
> > +             for_each_sg(sgl, sg, nelems, i) {
> > +                     dma_addr = __phys_to_dma(dev, sg_phys(sg));
> > +                     sg_dma_address(sg) = dma_addr;
> > +                     sg_dma_len(sg) = sg->length;
> > +             }
> > +
> > +             return nelems;
> > +     } else {
> > +             return dma_direct_map_sg(dev, sgl, nelems, dir, attrs);
> > +     }
> > +}
> > +
> > +static void hyperv_dma_unmap_sg(struct device *dev, struct scatterlist *sgl,
> > +             int nelems, enum dma_data_direction dir, unsigned long attrs)
> > +{
> > +     if (!hyperv_private_memory_dma(dev))
> > +             dma_direct_unmap_sg(dev, sgl, nelems, dir, attrs);
> > +}
> > +
> > +static int hyperv_dma_supported(struct device *dev, u64 mask)
> > +{
> > +     dev->coherent_dma_mask = mask;
> > +     return 1;
> > +}
> > +
> > +static size_t hyperv_dma_max_mapping_size(struct device *dev)
> > +{
> > +     if (hyperv_private_memory_dma(dev))
> > +             return SIZE_MAX;
> > +     else
> > +             return swiotlb_max_mapping_size(dev);
> > +}
> > +
> > +const struct dma_map_ops hyperv_dma_ops = {
> > +     .map_page               = hyperv_dma_map_page,
> > +     .unmap_page             = hyperv_dma_unmap_page,
> > +     .map_sg                 = hyperv_dma_map_sg,
> > +     .unmap_sg               = hyperv_dma_unmap_sg,
> > +     .dma_supported          = hyperv_dma_supported,
> > +     .max_mapping_size       = hyperv_dma_max_mapping_size,
> > +};
> > +
> >  /*
> >   * vmbus_bus_init -Main vmbus driver initialization routine.
> >   *
> > @@ -1479,8 +1564,11 @@ static int vmbus_bus_init(void)
> >        * doing that on each VP while initializing SynIC's wastes time.
> >        */
> >       is_confidential = ms_hyperv.confidential_vmbus_available;
> > -     if (is_confidential)
> > +     if (is_confidential) {
> > +             dma_ops = &hyperv_dma_ops;
>
> arm64 doesn't have the global variable dma_ops, so this patch
> won't build on arm64, even if Confidential VMBus isn't yet available
> for arm64.

Yes, there should be a stub function for both ARM and x86 to implement it..

>
> >               pr_info("Establishing connection to the confidential VMBus\n");
> > +     }
> > +
> >       hv_para_set_sint_proxy(!is_confidential);
> >       ret = vmbus_alloc_synic_and_connect();
> >       if (ret)
> > --
> > 2.50.1
> >
>
> Here's my idea for an alternate approach.  The goal is to allow use of the
> swiotlb to be disabled on a per-device basis. A device is initialized for swiotlb
> usage by swiotlb_dev_init(), which sets dev->dma_io_tlb_mem to point to the
> default swiotlb memory.  For VMBus devices, the calling sequence is
> vmbus_device_register() -> device_register() -> device_initialize() ->
> swiotlb_dev_init(). But if vmbus_device_register() could override the
> dev->dma_io_tlb_mem value and put it back to NULL, swiotlb operations
> would be disabled on the device. Furthermore, is_swiotlb_force_bounce()
> would return "false", and the normal DMA functions would not force the
> use of bounce buffers. The entire code change looks like this:
>
> --- a/drivers/hv/vmbus_drv.c
> +++ b/drivers/hv/vmbus_drv.c
> @@ -2133,11 +2133,15 @@ int vmbus_device_register(struct hv_device *child_device_obj)
>         child_device_obj->device.dma_mask = &child_device_obj->dma_mask;
>         dma_set_mask(&child_device_obj->device, DMA_BIT_MASK(64));
>
> +       device_initialize(&child_device_obj->device);
> +       if (child_device_obj->channel->co_external_memory)
> +               child_device_obj->device.dma_io_tlb_mem = NULL;
> +
>         /*
>          * Register with the LDM. This will kick off the driver/device
>          * binding...which will eventually call vmbus_match() and vmbus_probe()
>          */
> -       ret = device_register(&child_device_obj->device);
> +       ret = device_add(&child_device_obj->device);
>         if (ret) {
>                 pr_err("Unable to register child device\n");
>                 put_device(&child_device_obj->device);
>
> I've only compile tested the above since I don't have an environment where
> I can test Confidential VMBus. You would need to verify whether my thinking
> is correct and this produces the intended result.

Thanks Michael. I tested it and it seems to hit an issue. Will double check.with
HCL/paravisor team.

 We considered such a change before. From Roman's previous patch, it seems to
need to change phys_to_dma() and force_dma_unencrypted()..

>
> Directly setting dma_io_tlb_mem to NULL isn't great. It would be better
> to add an exported function swiotlb_dev_disable() to swiotlb code that sets
> dma_io_tlb_mem to NULL, but you get the idea.
>
> Other reviewers may still see this approach as a bit of a hack, but it's a lot
> less of a hack than introducing Hyper-V specific DMA functions.
> swiotlb_dev_disable() is conceptually needed for TDISP devices, as TDISP
> devices must similarly protect confidentiality by not allowing use of the swiotlb.
> So adding swiotlb_dev_disable() is a step in the right direction, even if the
> eventual TDISP code does it slightly differently. Doing the disable on a
> per-device basis is also the right thing in the long run.
>

Agree. Include Christoph and Robin here.

Now, we have three options to disable swiotlb bounce buffer to per-device.
1) Add platform DMA ops and check platform condition(e.g, Confidential
VMbus or TDISP
   device) in platform callback before calling stand DMA API which may
use a bounce buffer.

2)  Directly setting dma_io_tlb_mem to NULL in the device/bus level
driver. It seems not
    enough for Confidential VM which may use high bit of address to
identify encrypted
    or decrypted.(Detail please see Roman's previous patch)
https://lore.kernel.org/linux-hyperv/20250409000835.285105-6-romank@linux.microsoft.com/
https://lore.kernel.org/linux-hyperv/20250409000835.285105-7-romank@linux.microsoft.com/

3) Add a new stand swiotlb function to disable the use of bounce
buffer for the device.

--
Thanks
Tianyu Lan

^ permalink raw reply

* Re: [PATCH net-next v12 04/12] vsock: add netns support to virtio transports
From: Stefano Garzarella @ 2025-12-03 14:16 UTC (permalink / raw)
  To: Bobby Eshleman
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin, Jason Wang,
	Eugenio Pérez, Xuan Zhuo, K. Y. Srinivasan, Haiyang Zhang,
	Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
	Broadcom internal kernel review list, Shuah Khan, linux-kernel,
	virtualization, netdev, kvm, linux-hyperv, linux-kselftest,
	berrange, Sargun Dhillon, Bobby Eshleman
In-Reply-To: <aS9hoOKb7yA5Qgod@devvm11784.nha0.facebook.com>

On Tue, 2 Dec 2025 at 23:01, Bobby Eshleman <bobbyeshleman@gmail.com> wrote:
>
> On Tue, Dec 02, 2025 at 09:47:19PM +0100, Paolo Abeni wrote:
> > On 12/2/25 6:56 PM, Bobby Eshleman wrote:
> > > On Tue, Dec 02, 2025 at 11:18:14AM +0100, Paolo Abeni wrote:
> > >> On 11/27/25 8:47 AM, Bobby Eshleman wrote:
> > >>> @@ -674,6 +689,17 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
> > >>>           goto out;
> > >>>   }
> > >>>
> > >>> + net = current->nsproxy->net_ns;
> > >>> + vsock->net = get_net_track(net, &vsock->ns_tracker, GFP_KERNEL);
> > >>> +
> > >>> + /* Store the mode of the namespace at the time of creation. If this
> > >>> +  * namespace later changes from "global" to "local", we want this vsock
> > >>> +  * to continue operating normally and not suddenly break. For that
> > >>> +  * reason, we save the mode here and later use it when performing
> > >>> +  * socket lookups with vsock_net_check_mode() (see vhost_vsock_get()).
> > >>> +  */
> > >>> + vsock->net_mode = vsock_net_mode(net);
> > >>
> > >> I'm sorry for the very late feedback. I think that at very least the
> > >> user-space needs a way to query if the given transport is in local or
> > >> global mode, as AFAICS there is no way to tell that when socket creation
> > >> races with mode change.
> > >
> > > Are you thinking something along the lines of sockopt?
> >
> > I'd like to see a way for the user-space to query the socket 'namespace
> > mode'.
> >
> > sockopt could be an option; a possibly better one could be sock_diag. Or
> > you could do both using dumping the info with a shared helper invoked by
> > both code paths, alike what TCP is doing.
> > >> Also I'm a bit uneasy with the model implemented here, as 'local' socket
> > >> may cross netns boundaris and connect to 'local' socket in other netns
> > >> (if I read correctly patch 2/12). That in turns AFAICS break the netns
> > >> isolation.
> > >
> > > Local mode sockets are unable to communicate with local mode (and global
> > > mode too) sockets that are in other namespaces. The key piece of code
> > > for that is vsock_net_check_mode(), where if either modes is local the
> > > namespaces must be the same.
> >
> > Sorry, I likely misread the large comment in patch 2:
> >
> > https://lore.kernel.org/netdev/20251126-vsock-vmtest-v12-2-257ee21cd5de@meta.com/
> >
> > >> Have you considered instead a slightly different model, where the
> > >> local/global model is set in stone at netns creation time - alike what
> > >> /proc/sys/net/ipv4/tcp_child_ehash_entries is doing[1] - and
> > >> inter-netns connectivity is explicitly granted by the admin (I guess
> > >> you will need new transport operations for that)?
> > >>
> > >> /P
> > >>
> > >> [1] tcp allows using per-netns established socket lookup tables - as
> > >> opposed to the default global lookup table (even if match always takes
> > >> in account the netns obviously). The mentioned sysctl specify such
> > >> configuration for the children namespaces, if any.
> > >
> > > I'll save this discussion if the above doesn't resolve your concerns.
> > I still have some concern WRT the dynamic mode change after netns
> > creation. I fear some 'unsolvable' (or very hard to solve) race I can't
> > see now. A tcp_child_ehash_entries-like model will avoid completely the
> > issue, but I understand it would be a significant change over the
> > current status.
> >
> > "Luckily" the merge window is on us and we have some time to discuss. Do
> > you have a specific use-case for the ability to change the netns mode
> > after creation?
> >
> > /P
>
> I don't think there is a hard requirement that the mode be change-able
> after creation. Though I'd love to avoid such a big change... or at
> least leave unchanged as much of what we've already reviewed as
> possible.

I think the big part is done, IIUC this should just be a change to the
uAPI and maybe simplify what we have a little (e.g., avoid saving the
mode each socket had when it was created).

>
> In the scheme of defining the mode at creation and following the
> tcp_child_ehash_entries-ish model, what I'm imagining is:
> - /proc/sys/net/vsock/child_ns_mode can be set to "local" or "global"
> - /proc/sys/net/vsock/child_ns_mode is not immutable, can change any
>   number of times
>
> - when a netns is created, the new netns mode is inherited from
>   child_ns_mode, being assigned using something like:
>
>           net->vsock.ns_mode =
>                 get_net_ns_by_pid(current->pid)->child_ns_mode
>
> - /proc/sys/net/vsock/ns_mode queries the current mode, returning
>   "local" or "global", returning value of net->vsock.ns_mode
> - /proc/sys/net/vsock/ns_mode and net->vsock.ns_mode are immutable and
>   reject writes
>
> Does that align with what you have in mind?
>
> Stefano, what are your thoughts?

If we can avoid having sockets in a namespace that can be both global
and local, perhaps it makes a lot of sense to make this change.

My only concern is that there is still a small window where the mode
can change, but we are sure that only one is picked during creation
and then within the namespace this can be easily checked and give us
the assurance that all sockets comply with it, right?

Thanks,
Stefano


^ permalink raw reply

* Re: [PATCH net-next v12 04/12] vsock: add netns support to virtio transports
From: Bobby Eshleman @ 2025-12-02 22:01 UTC (permalink / raw)
  To: Paolo Abeni
  Cc: Stefano Garzarella, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin, Jason Wang,
	Eugenio Pérez, Xuan Zhuo, K. Y. Srinivasan, Haiyang Zhang,
	Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
	Broadcom internal kernel review list, Shuah Khan, linux-kernel,
	virtualization, netdev, kvm, linux-hyperv, linux-kselftest,
	berrange, Sargun Dhillon, Bobby Eshleman
In-Reply-To: <06b7cfea-d366-44f7-943e-087ead2f25c2@redhat.com>

On Tue, Dec 02, 2025 at 09:47:19PM +0100, Paolo Abeni wrote:
> On 12/2/25 6:56 PM, Bobby Eshleman wrote:
> > On Tue, Dec 02, 2025 at 11:18:14AM +0100, Paolo Abeni wrote:
> >> On 11/27/25 8:47 AM, Bobby Eshleman wrote:
> >>> @@ -674,6 +689,17 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
> >>>  		goto out;
> >>>  	}
> >>>  
> >>> +	net = current->nsproxy->net_ns;
> >>> +	vsock->net = get_net_track(net, &vsock->ns_tracker, GFP_KERNEL);
> >>> +
> >>> +	/* Store the mode of the namespace at the time of creation. If this
> >>> +	 * namespace later changes from "global" to "local", we want this vsock
> >>> +	 * to continue operating normally and not suddenly break. For that
> >>> +	 * reason, we save the mode here and later use it when performing
> >>> +	 * socket lookups with vsock_net_check_mode() (see vhost_vsock_get()).
> >>> +	 */
> >>> +	vsock->net_mode = vsock_net_mode(net);
> >>
> >> I'm sorry for the very late feedback. I think that at very least the
> >> user-space needs a way to query if the given transport is in local or
> >> global mode, as AFAICS there is no way to tell that when socket creation
> >> races with mode change.
> > 
> > Are you thinking something along the lines of sockopt?
> 
> I'd like to see a way for the user-space to query the socket 'namespace
> mode'.
> 
> sockopt could be an option; a possibly better one could be sock_diag. Or
> you could do both using dumping the info with a shared helper invoked by
> both code paths, alike what TCP is doing.
> >> Also I'm a bit uneasy with the model implemented here, as 'local' socket
> >> may cross netns boundaris and connect to 'local' socket in other netns
> >> (if I read correctly patch 2/12). That in turns AFAICS break the netns
> >> isolation.
> > 
> > Local mode sockets are unable to communicate with local mode (and global
> > mode too) sockets that are in other namespaces. The key piece of code
> > for that is vsock_net_check_mode(), where if either modes is local the
> > namespaces must be the same.
> 
> Sorry, I likely misread the large comment in patch 2:
> 
> https://lore.kernel.org/netdev/20251126-vsock-vmtest-v12-2-257ee21cd5de@meta.com/
> 
> >> Have you considered instead a slightly different model, where the
> >> local/global model is set in stone at netns creation time - alike what
> >> /proc/sys/net/ipv4/tcp_child_ehash_entries is doing[1] - and
> >> inter-netns connectivity is explicitly granted by the admin (I guess
> >> you will need new transport operations for that)?
> >>
> >> /P
> >>
> >> [1] tcp allows using per-netns established socket lookup tables - as
> >> opposed to the default global lookup table (even if match always takes
> >> in account the netns obviously). The mentioned sysctl specify such
> >> configuration for the children namespaces, if any.
> > 
> > I'll save this discussion if the above doesn't resolve your concerns.
> I still have some concern WRT the dynamic mode change after netns
> creation. I fear some 'unsolvable' (or very hard to solve) race I can't
> see now. A tcp_child_ehash_entries-like model will avoid completely the
> issue, but I understand it would be a significant change over the
> current status.
> 
> "Luckily" the merge window is on us and we have some time to discuss. Do
> you have a specific use-case for the ability to change the netns mode
> after creation?
> 
> /P

I don't think there is a hard requirement that the mode be change-able
after creation. Though I'd love to avoid such a big change... or at
least leave unchanged as much of what we've already reviewed as
possible.

In the scheme of defining the mode at creation and following the
tcp_child_ehash_entries-ish model, what I'm imagining is:
- /proc/sys/net/vsock/child_ns_mode can be set to "local" or "global"
- /proc/sys/net/vsock/child_ns_mode is not immutable, can change any
  number of times

- when a netns is created, the new netns mode is inherited from
  child_ns_mode, being assigned using something like:

	  net->vsock.ns_mode =
		get_net_ns_by_pid(current->pid)->child_ns_mode

- /proc/sys/net/vsock/ns_mode queries the current mode, returning
  "local" or "global", returning value of net->vsock.ns_mode
- /proc/sys/net/vsock/ns_mode and net->vsock.ns_mode are immutable and
  reject writes

Does that align with what you have in mind?

Stefano, what are your thoughts?

Best,
Bobby

^ permalink raw reply

* Re: [PATCH net-next v12 04/12] vsock: add netns support to virtio transports
From: Paolo Abeni @ 2025-12-02 20:47 UTC (permalink / raw)
  To: Bobby Eshleman
  Cc: Stefano Garzarella, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin, Jason Wang,
	Eugenio Pérez, Xuan Zhuo, K. Y. Srinivasan, Haiyang Zhang,
	Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
	Broadcom internal kernel review list, Shuah Khan, linux-kernel,
	virtualization, netdev, kvm, linux-hyperv, linux-kselftest,
	berrange, Sargun Dhillon, Bobby Eshleman
In-Reply-To: <aS8oMqafpJxkRKW5@devvm11784.nha0.facebook.com>

On 12/2/25 6:56 PM, Bobby Eshleman wrote:
> On Tue, Dec 02, 2025 at 11:18:14AM +0100, Paolo Abeni wrote:
>> On 11/27/25 8:47 AM, Bobby Eshleman wrote:
>>> @@ -674,6 +689,17 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
>>>  		goto out;
>>>  	}
>>>  
>>> +	net = current->nsproxy->net_ns;
>>> +	vsock->net = get_net_track(net, &vsock->ns_tracker, GFP_KERNEL);
>>> +
>>> +	/* Store the mode of the namespace at the time of creation. If this
>>> +	 * namespace later changes from "global" to "local", we want this vsock
>>> +	 * to continue operating normally and not suddenly break. For that
>>> +	 * reason, we save the mode here and later use it when performing
>>> +	 * socket lookups with vsock_net_check_mode() (see vhost_vsock_get()).
>>> +	 */
>>> +	vsock->net_mode = vsock_net_mode(net);
>>
>> I'm sorry for the very late feedback. I think that at very least the
>> user-space needs a way to query if the given transport is in local or
>> global mode, as AFAICS there is no way to tell that when socket creation
>> races with mode change.
> 
> Are you thinking something along the lines of sockopt?

I'd like to see a way for the user-space to query the socket 'namespace
mode'.

sockopt could be an option; a possibly better one could be sock_diag. Or
you could do both using dumping the info with a shared helper invoked by
both code paths, alike what TCP is doing.
>> Also I'm a bit uneasy with the model implemented here, as 'local' socket
>> may cross netns boundaris and connect to 'local' socket in other netns
>> (if I read correctly patch 2/12). That in turns AFAICS break the netns
>> isolation.
> 
> Local mode sockets are unable to communicate with local mode (and global
> mode too) sockets that are in other namespaces. The key piece of code
> for that is vsock_net_check_mode(), where if either modes is local the
> namespaces must be the same.

Sorry, I likely misread the large comment in patch 2:

https://lore.kernel.org/netdev/20251126-vsock-vmtest-v12-2-257ee21cd5de@meta.com/

>> Have you considered instead a slightly different model, where the
>> local/global model is set in stone at netns creation time - alike what
>> /proc/sys/net/ipv4/tcp_child_ehash_entries is doing[1] - and
>> inter-netns connectivity is explicitly granted by the admin (I guess
>> you will need new transport operations for that)?
>>
>> /P
>>
>> [1] tcp allows using per-netns established socket lookup tables - as
>> opposed to the default global lookup table (even if match always takes
>> in account the netns obviously). The mentioned sysctl specify such
>> configuration for the children namespaces, if any.
> 
> I'll save this discussion if the above doesn't resolve your concerns.
I still have some concern WRT the dynamic mode change after netns
creation. I fear some 'unsolvable' (or very hard to solve) race I can't
see now. A tcp_child_ehash_entries-like model will avoid completely the
issue, but I understand it would be a significant change over the
current status.

"Luckily" the merge window is on us and we have some time to discuss. Do
you have a specific use-case for the ability to change the netns mode
after creation?

/P


^ permalink raw reply

* Re: [PATCH net-next v12 04/12] vsock: add netns support to virtio transports
From: Bobby Eshleman @ 2025-12-02 20:00 UTC (permalink / raw)
  To: Paolo Abeni, g
  Cc: Stefano Garzarella, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin, Jason Wang,
	Eugenio Pérez, Xuan Zhuo, K. Y. Srinivasan, Haiyang Zhang,
	Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
	Broadcom internal kernel review list, Shuah Khan, linux-kernel,
	virtualization, netdev, kvm, linux-hyperv, linux-kselftest,
	berrange, Sargun Dhillon, Bobby Eshleman
In-Reply-To: <aS8oMqafpJxkRKW5@devvm11784.nha0.facebook.com>

On Tue, Dec 02, 2025 at 09:56:02AM -0800, Bobby Eshleman wrote:
> On Tue, Dec 02, 2025 at 11:18:14AM +0100, Paolo Abeni wrote:
> > On 11/27/25 8:47 AM, Bobby Eshleman wrote:
> > > @@ -674,6 +689,17 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
> > >  		goto out;
> > >  	}
> > >  
> > > +	net = current->nsproxy->net_ns;
> > > +	vsock->net = get_net_track(net, &vsock->ns_tracker, GFP_KERNEL);
> > > +
> > > +	/* Store the mode of the namespace at the time of creation. If this
> > > +	 * namespace later changes from "global" to "local", we want this vsock
> > > +	 * to continue operating normally and not suddenly break. For that
> > > +	 * reason, we save the mode here and later use it when performing
> > > +	 * socket lookups with vsock_net_check_mode() (see vhost_vsock_get()).
> > > +	 */
> > > +	vsock->net_mode = vsock_net_mode(net);
> > 
> > I'm sorry for the very late feedback. I think that at very least the
> > user-space needs a way to query if the given transport is in local or
> > global mode, as AFAICS there is no way to tell that when socket creation
> > races with mode change.
> 
> Are you thinking something along the lines of sockopt?
> 

To clarify... do we want the user to be able to query the socket for
which namespace mode it is in (so the results of the race can be
queried), or are you looking for a way for the user to query if the
transport supports local mode (maybe via /dev/vsock ioctl).

I'm not sure we can attach a namespace to a transport per-se, as
different namespaces in different modes can use the same transport.

Best,
Bobby

> > 
> > Also I'm a bit uneasy with the model implemented here, as 'local' socket
> > may cross netns boundaris and connect to 'local' socket in other netns
> > (if I read correctly patch 2/12). That in turns AFAICS break the netns
> > isolation.
> 
> Local mode sockets are unable to communicate with local mode (and global
> mode too) sockets that are in other namespaces. The key piece of code
> for that is vsock_net_check_mode(), where if either modes is local the
> namespaces must be the same.
> 
> > 
> > Have you considered instead a slightly different model, where the
> > local/global model is set in stone at netns creation time - alike what
> > /proc/sys/net/ipv4/tcp_child_ehash_entries is doing[1] - and
> > inter-netns connectivity is explicitly granted by the admin (I guess
> > you will need new transport operations for that)?
> > 
> > /P
> > 
> > [1] tcp allows using per-netns established socket lookup tables - as
> > opposed to the default global lookup table (even if match always takes
> > in account the netns obviously). The mentioned sysctl specify such
> > configuration for the children namespaces, if any.
> > 
> 
> I'll save this discussion if the above doesn't resolve your concerns.
> 
> Best,
> Bobby

^ permalink raw reply

* RE: [PATCH v7 5/7] Drivers: hv: Improve region overlap detection in partition create
From: Michael Kelley @ 2025-12-02 18:39 UTC (permalink / raw)
  To: Stanislav Kinsburskii, kys@microsoft.com, haiyangz@microsoft.com,
	wei.liu@kernel.org, decui@microsoft.com, Nuno Das Neves
  Cc: linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <176412295734.447063.5692907557041244468.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>

From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com> Sent: Tuesday, November 25, 2025 6:09 PM
> 
> Refactor region overlap check in mshv_partition_create_region to use
> mshv_partition_region_by_gfn for both start and end guest PFNs, replacing
> manual iteration.
> 
> This is a cleaner approach that leverages existing functionality to
> accurately detect overlapping memory regions.

Unfortunately, the cleaner approach doesn't work. :-( It doesn't detect a
new region request that completely overlaps an existing region.

See https://lore.kernel.org/linux-hyperv/6a5f4ed5-63ae-4760-84c9-7290aaff8bd1@linux.microsoft.com/T/#ma91254da1900de61da520acb96c0de38c43562f6.
I couldn't see anything that prevents the scenario. Nuno created this 
patch less than a month ago: https://lore.kernel.org/linux-hyperv/1762467211-8213-2-git-send-email-nunodasneves@linux.microsoft.com/.

Michael

> 
> Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> ---
>  drivers/hv/mshv_root_main.c |    8 ++------
>  1 file changed, 2 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index 5dfb933da981..ae600b927f49 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -1086,13 +1086,9 @@ static int mshv_partition_create_region(struct
> mshv_partition *partition,
>  	u64 nr_pages = HVPFN_DOWN(mem->size);
> 
>  	/* Reject overlapping regions */
> -	hlist_for_each_entry(rg, &partition->pt_mem_regions, hnode) {
> -		if (mem->guest_pfn + nr_pages <= rg->start_gfn ||
> -		    rg->start_gfn + rg->nr_pages <= mem->guest_pfn)
> -			continue;
> -
> +	if (mshv_partition_region_by_gfn(partition, mem->guest_pfn) ||
> +	    mshv_partition_region_by_gfn(partition, mem->guest_pfn + nr_pages - 1))
>  		return -EEXIST;
> -	}
> 
>  	rg = mshv_region_create(mem->guest_pfn, nr_pages,
>  				mem->userspace_addr, mem->flags,
> 
> 


^ permalink raw reply

* Re: [PATCH net-next v12 04/12] vsock: add netns support to virtio transports
From: Bobby Eshleman @ 2025-12-02 17:56 UTC (permalink / raw)
  To: Paolo Abeni
  Cc: Stefano Garzarella, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin, Jason Wang,
	Eugenio Pérez, Xuan Zhuo, K. Y. Srinivasan, Haiyang Zhang,
	Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
	Broadcom internal kernel review list, Shuah Khan, linux-kernel,
	virtualization, netdev, kvm, linux-hyperv, linux-kselftest,
	berrange, Sargun Dhillon, Bobby Eshleman
In-Reply-To: <6cef5a68-375a-4bb6-84f8-fccc00cf7162@redhat.com>

On Tue, Dec 02, 2025 at 11:18:14AM +0100, Paolo Abeni wrote:
> On 11/27/25 8:47 AM, Bobby Eshleman wrote:
> > @@ -674,6 +689,17 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
> >  		goto out;
> >  	}
> >  
> > +	net = current->nsproxy->net_ns;
> > +	vsock->net = get_net_track(net, &vsock->ns_tracker, GFP_KERNEL);
> > +
> > +	/* Store the mode of the namespace at the time of creation. If this
> > +	 * namespace later changes from "global" to "local", we want this vsock
> > +	 * to continue operating normally and not suddenly break. For that
> > +	 * reason, we save the mode here and later use it when performing
> > +	 * socket lookups with vsock_net_check_mode() (see vhost_vsock_get()).
> > +	 */
> > +	vsock->net_mode = vsock_net_mode(net);
> 
> I'm sorry for the very late feedback. I think that at very least the
> user-space needs a way to query if the given transport is in local or
> global mode, as AFAICS there is no way to tell that when socket creation
> races with mode change.

Are you thinking something along the lines of sockopt?

> 
> Also I'm a bit uneasy with the model implemented here, as 'local' socket
> may cross netns boundaris and connect to 'local' socket in other netns
> (if I read correctly patch 2/12). That in turns AFAICS break the netns
> isolation.

Local mode sockets are unable to communicate with local mode (and global
mode too) sockets that are in other namespaces. The key piece of code
for that is vsock_net_check_mode(), where if either modes is local the
namespaces must be the same.

> 
> Have you considered instead a slightly different model, where the
> local/global model is set in stone at netns creation time - alike what
> /proc/sys/net/ipv4/tcp_child_ehash_entries is doing[1] - and
> inter-netns connectivity is explicitly granted by the admin (I guess
> you will need new transport operations for that)?
> 
> /P
> 
> [1] tcp allows using per-netns established socket lookup tables - as
> opposed to the default global lookup table (even if match always takes
> in account the netns obviously). The mentioned sysctl specify such
> configuration for the children namespaces, if any.
> 

I'll save this discussion if the above doesn't resolve your concerns.

Best,
Bobby

^ permalink raw reply

* Re: [PATCH net-next v12 04/12] vsock: add netns support to virtio transports
From: Paolo Abeni @ 2025-12-02 10:18 UTC (permalink / raw)
  To: Bobby Eshleman, Stefano Garzarella, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin,
	Jason Wang, Eugenio Pérez, Xuan Zhuo, K. Y. Srinivasan,
	Haiyang Zhang, Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
	Broadcom internal kernel review list, Shuah Khan
  Cc: linux-kernel, virtualization, netdev, kvm, linux-hyperv,
	linux-kselftest, berrange, Sargun Dhillon, Bobby Eshleman
In-Reply-To: <20251126-vsock-vmtest-v12-4-257ee21cd5de@meta.com>

On 11/27/25 8:47 AM, Bobby Eshleman wrote:
> @@ -674,6 +689,17 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
>  		goto out;
>  	}
>  
> +	net = current->nsproxy->net_ns;
> +	vsock->net = get_net_track(net, &vsock->ns_tracker, GFP_KERNEL);
> +
> +	/* Store the mode of the namespace at the time of creation. If this
> +	 * namespace later changes from "global" to "local", we want this vsock
> +	 * to continue operating normally and not suddenly break. For that
> +	 * reason, we save the mode here and later use it when performing
> +	 * socket lookups with vsock_net_check_mode() (see vhost_vsock_get()).
> +	 */
> +	vsock->net_mode = vsock_net_mode(net);

I'm sorry for the very late feedback. I think that at very least the
user-space needs a way to query if the given transport is in local or
global mode, as AFAICS there is no way to tell that when socket creation
races with mode change.

Also I'm a bit uneasy with the model implemented here, as 'local' socket
may cross netns boundaris and connect to 'local' socket in other netns
(if I read correctly patch 2/12). That in turns AFAICS break the netns
isolation.

Have you considered instead a slightly different model, where the
local/global model is set in stone at netns creation time - alike what
/proc/sys/net/ipv4/tcp_child_ehash_entries is doing[1] - and inter-netns
connectivity is explicitly granted by the admin (I guess you will need
new transport operations for that)?

/P

[1] tcp allows using per-netns established socket lookup tables - as
opposed to the default global lookup table (even if match always takes
in account the netns obviously). The mentioned sysctl specify such
configuration for the children namespaces, if any.


^ permalink raw reply

* Re: [PATCH net-next, v5] net: mana: Implement ndo_tx_timeout and serialize queue resets per port.
From: Jakub Kicinski @ 2025-12-01 22:04 UTC (permalink / raw)
  To: Dipayaan Roy
  Cc: kys, haiyangz, wei.liu, decui, andrew+netdev, davem, edumazet,
	pabeni, longli, kotaranov, horms, shradhagupta, ssengar, ernis,
	shirazsaleem, linux-hyperv, netdev, linux-kernel, linux-rdma,
	dipayanroy
In-Reply-To: <20251127135849.GA8411@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net>

On Thu, 27 Nov 2025 05:58:49 -0800 Dipayaan Roy wrote:
> Implement .ndo_tx_timeout for MANA so any stalled TX queue can be detected
> and a device-controlled port reset for all queues can be scheduled to a
> ordered workqueue. The reset for all queues on stall detection is
> recommended by hardware team.

no longer applies, please repost after net-next reopens
-- 
pw-bot: cr

^ permalink raw reply

* Re: [Patch net-next v4] net: mana: Handle hardware recovery events when probing the device
From: patchwork-bot+netdevbpf @ 2025-12-01 22:00 UTC (permalink / raw)
  To: None
  Cc: kys, haiyangz, wei.liu, decui, davem, edumazet, kuba, pabeni,
	shradhagupta, horms, kotaranov, schakrabarti, erick.archer,
	linux-hyperv, netdev, linux-kernel, linux-rdma, longli
In-Reply-To: <1764193552-9712-1-git-send-email-longli@linux.microsoft.com>

Hello:

This patch was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Wed, 26 Nov 2025 13:45:52 -0800 you wrote:
> From: Long Li <longli@microsoft.com>
> 
> When MANA is being probed, it's possible that hardware is in recovery
> mode and the device may get GDMA_EQE_HWC_RESET_REQUEST over HWC in the
> middle of the probe. Detect such condition and go through the recovery
> service procedure.
> 
> [...]

Here is the summary with links:
  - [net-next,v4] net: mana: Handle hardware recovery events when probing the device
    https://git.kernel.org/netdev/net-next/c/9bf66036d686

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH v7 4/7] Drivers: hv: Fix huge page handling in memory region traversal
From: Stanislav Kinsburskii @ 2025-12-01 18:26 UTC (permalink / raw)
  To: Anirudh Rayabharam
  Cc: kys, haiyangz, wei.liu, decui, linux-hyperv, linux-kernel
In-Reply-To: <aS2vtXBx9uJ2U7F1@anirudh-surface.localdomain>

On Mon, Dec 01, 2025 at 03:09:41PM +0000, Anirudh Rayabharam wrote:
> On Wed, Nov 26, 2025 at 02:09:11AM +0000, Stanislav Kinsburskii wrote:
> > The previous code assumed that if a region's first page was huge, the
> > entire region consisted of huge pages and stored this in a large_pages
> > flag. This premise is incorrect not only for movable regions (where
> > pages can be split and merged on invalidate callbacks or page faults),
> > but even for pinned regions: THPs can be split and merged during
> > allocation, so a large, pinned region may contain a mix of huge and
> > regular pages.
> > 
> > This change removes the large_pages flag and replaces region-wide
> > assumptions with per-chunk inspection of the actual page size when
> > mapping, unmapping, sharing, and unsharing. This makes huge page
> > handling correct for mixed-page regions and avoids relying on stale
> > metadata that can easily become invalid as memory is remapped.
> > 
> > Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> > ---
> >  drivers/hv/mshv_regions.c |  213 +++++++++++++++++++++++++++++++++++++++------
> >  drivers/hv/mshv_root.h    |    3 -
> >  2 files changed, 184 insertions(+), 32 deletions(-)
> 
> Except the warning reported by kernel test robot:
> 

This one is a good catch.
I'll fix it in the next revision.

Thanks,
Stanislav

> Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>

^ permalink raw reply

* Re: [PATCH v6 3/3] hyperv: Cleanly shutdown root partition with MSHV
From: Stanislav Kinsburskii @ 2025-12-01 17:05 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
In-Reply-To: <20251126215013.11549-4-prapal@linux.microsoft.com>

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>

> +	local_irq_restore(flags);
> +
> +}
>  #endif
> -- 
> 2.51.0

^ permalink raw reply

* Re: [PATCH v6 2/3] hyperv: Use reboot notifier to configure sleep state
From: Stanislav Kinsburskii @ 2025-12-01 17:02 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
In-Reply-To: <20251126215013.11549-3-prapal@linux.microsoft.com>

On Wed, Nov 26, 2025 at 03:49:52PM -0600, Praveen K Paladugu wrote:
> Configure sleep state information in mshv hypervisor. This sleep state
> information from ACPI will be used by hypervisor to poweroff the host.
> 

Acked-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>

> 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/hyperv/hv_init.c       |  1 +
>  arch/x86/include/asm/mshyperv.h |  2 +
>  drivers/hv/mshv_common.c        | 80 +++++++++++++++++++++++++++++++++
>  3 files changed, 83 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..166053df0484 100644
> --- a/arch/x86/include/asm/mshyperv.h
> +++ b/arch/x86/include/asm/mshyperv.h
> @@ -182,8 +182,10 @@ int hyperv_fill_flush_guest_mapping_list(
>  void hv_apic_init(void);
>  void __init hv_init_spinlocks(void);
>  bool hv_vcpu_is_preempted(int vcpu);
> +void hv_sleep_notifiers_register(void);
>  #else
>  static inline void hv_apic_init(void) {}
> +static inline void hv_sleep_notifiers_register(void) {};
>  #endif
>  
>  struct irq_domain *hv_create_pci_msi_domain(void);
> diff --git a/drivers/hv/mshv_common.c b/drivers/hv/mshv_common.c
> index aa2be51979fd..f1d4e81107ee 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,80 @@ int hv_call_get_partition_property(u64 partition_id,
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(hv_call_get_partition_property);
> +
> +#ifdef CONFIG_X86_64
> +/*
> + * 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);
> +}
> +#endif
> -- 
> 2.51.0

^ permalink raw reply

* Re: [PATCH v6 1/3] hyperv: Add definitions for MSHV sleep state configuration
From: Stanislav Kinsburskii @ 2025-12-01 17:00 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
In-Reply-To: <20251126215013.11549-2-prapal@linux.microsoft.com>

On Wed, Nov 26, 2025 at 03:49:51PM -0600, Praveen K Paladugu wrote:
> Add the definitions required to configure sleep states in mshv hypervsior.
> 

Acked-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>

> 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>
> ---
>  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

* Re: [PATCH v7 3/7] Drivers: hv: Move region management to mshv_regions.c
From: Stanislav Kinsburskii @ 2025-12-01 16:46 UTC (permalink / raw)
  To: Anirudh Rayabharam
  Cc: kys, haiyangz, wei.liu, decui, linux-hyperv, linux-kernel
In-Reply-To: <aS12nwVq_jWGwpNI@anirudh-surface.localdomain>

On Mon, Dec 01, 2025 at 11:06:07AM +0000, Anirudh Rayabharam wrote:
> On Wed, Nov 26, 2025 at 02:09:05AM +0000, Stanislav Kinsburskii wrote:
> > Refactor memory region management functions from mshv_root_main.c into
> > mshv_regions.c for better modularity and code organization.
> > 
> > Adjust function calls and headers to use the new implementation. Improve
> > maintainability and separation of concerns in the mshv_root module.
> > 
> > Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> > ---
> >  drivers/hv/Makefile         |    2 
> >  drivers/hv/mshv_regions.c   |  175 +++++++++++++++++++++++++++++++++++++++++++
> >  drivers/hv/mshv_root.h      |   10 ++
> >  drivers/hv/mshv_root_main.c |  176 +++----------------------------------------
> >  4 files changed, 198 insertions(+), 165 deletions(-)
> >  create mode 100644 drivers/hv/mshv_regions.c
> > 
> > diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
> > index 58b8d07639f3..46d4f4f1b252 100644
> > --- a/drivers/hv/Makefile
> > +++ b/drivers/hv/Makefile
> > @@ -14,7 +14,7 @@ hv_vmbus-y := vmbus_drv.o \
> >  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_hv_call.o mshv_portid_table.o mshv_regions.o
> >  mshv_vtl-y := mshv_vtl_main.o
> >  
> >  # Code that must be built-in
> > diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
> > new file mode 100644
> > index 000000000000..35b866670840
> > --- /dev/null
> > +++ b/drivers/hv/mshv_regions.c
> 
> How about mshv_mem_regions.c?
> 

I'd rather rename mshv_mem_region into mshv_region instead, as MMIO
regions aren't memory regions.

Thanks,
Stanislav

> Nevertheless:
> 
> Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
> 

^ permalink raw reply

* Re: [PATCH v7 4/7] Drivers: hv: Fix huge page handling in memory region traversal
From: Anirudh Rayabharam @ 2025-12-01 15:09 UTC (permalink / raw)
  To: Stanislav Kinsburskii
  Cc: kys, haiyangz, wei.liu, decui, linux-hyperv, linux-kernel
In-Reply-To: <176412295155.447063.16512843211428609586.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>

On Wed, Nov 26, 2025 at 02:09:11AM +0000, Stanislav Kinsburskii wrote:
> The previous code assumed that if a region's first page was huge, the
> entire region consisted of huge pages and stored this in a large_pages
> flag. This premise is incorrect not only for movable regions (where
> pages can be split and merged on invalidate callbacks or page faults),
> but even for pinned regions: THPs can be split and merged during
> allocation, so a large, pinned region may contain a mix of huge and
> regular pages.
> 
> This change removes the large_pages flag and replaces region-wide
> assumptions with per-chunk inspection of the actual page size when
> mapping, unmapping, sharing, and unsharing. This makes huge page
> handling correct for mixed-page regions and avoids relying on stale
> metadata that can easily become invalid as memory is remapped.
> 
> Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> ---
>  drivers/hv/mshv_regions.c |  213 +++++++++++++++++++++++++++++++++++++++------
>  drivers/hv/mshv_root.h    |    3 -
>  2 files changed, 184 insertions(+), 32 deletions(-)

Except the warning reported by kernel test robot:

Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>


^ permalink raw reply

* Re: [PATCH v7 5/7] Drivers: hv: Improve region overlap detection in partition create
From: Anirudh Rayabharam @ 2025-12-01 15:06 UTC (permalink / raw)
  To: Stanislav Kinsburskii
  Cc: kys, haiyangz, wei.liu, decui, linux-hyperv, linux-kernel
In-Reply-To: <176412295734.447063.5692907557041244468.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>

On Wed, Nov 26, 2025 at 02:09:17AM +0000, Stanislav Kinsburskii wrote:
> Refactor region overlap check in mshv_partition_create_region to use
> mshv_partition_region_by_gfn for both start and end guest PFNs, replacing
> manual iteration.
> 
> This is a cleaner approach that leverages existing functionality to
> accurately detect overlapping memory regions.
> 
> Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> ---
>  drivers/hv/mshv_root_main.c |    8 ++------
>  1 file changed, 2 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index 5dfb933da981..ae600b927f49 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -1086,13 +1086,9 @@ static int mshv_partition_create_region(struct mshv_partition *partition,
>  	u64 nr_pages = HVPFN_DOWN(mem->size);
>  
>  	/* Reject overlapping regions */
> -	hlist_for_each_entry(rg, &partition->pt_mem_regions, hnode) {
> -		if (mem->guest_pfn + nr_pages <= rg->start_gfn ||
> -		    rg->start_gfn + rg->nr_pages <= mem->guest_pfn)
> -			continue;
> -
> +	if (mshv_partition_region_by_gfn(partition, mem->guest_pfn) ||
> +	    mshv_partition_region_by_gfn(partition, mem->guest_pfn + nr_pages - 1))
>  		return -EEXIST;
> -	}
>  
>  	rg = mshv_region_create(mem->guest_pfn, nr_pages,
>  				mem->userspace_addr, mem->flags,
> 
> 

Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>


^ permalink raw reply

* Re: [PATCH v7 1/7] Drivers: hv: Refactor and rename memory region handling functions
From: Anirudh Rayabharam @ 2025-12-01 11:20 UTC (permalink / raw)
  To: Stanislav Kinsburskii
  Cc: kys, haiyangz, wei.liu, decui, linux-hyperv, linux-kernel
In-Reply-To: <176412293200.447063.17654735378558127143.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>

On Wed, Nov 26, 2025 at 02:08:52AM +0000, Stanislav Kinsburskii wrote:
> Simplify and unify memory region management to improve code clarity and
> reliability. Consolidate pinning and invalidation logic, adopt consistent
> naming, and remove redundant checks to reduce complexity.
> 
> Enhance documentation and update call sites for maintainability.
> 
> Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> Reviewed-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
> ---
>  drivers/hv/mshv_root_main.c |   80 +++++++++++++++++++------------------------
>  1 file changed, 36 insertions(+), 44 deletions(-)

Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>


^ permalink raw reply

* Re: [PATCH v7 2/7] Drivers: hv: Centralize guest memory region destruction
From: Anirudh Rayabharam @ 2025-12-01 11:12 UTC (permalink / raw)
  To: Stanislav Kinsburskii
  Cc: kys, haiyangz, wei.liu, decui, linux-hyperv, linux-kernel
In-Reply-To: <176412293764.447063.2221992979416155779.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>

On Wed, Nov 26, 2025 at 02:08:57AM +0000, Stanislav Kinsburskii wrote:
> Centralize guest memory region destruction to prevent resource leaks and
> inconsistent cleanup across unmap and partition destruction paths.
> 
> Unify region removal, encrypted partition access recovery, and region
> invalidation to improve maintainability and reliability. Reduce code
> duplication and make future updates less error-prone by encapsulating
> cleanup logic in a single helper.
> 
> Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> Reviewed-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
> ---
>  drivers/hv/mshv_root_main.c |   65 ++++++++++++++++++++++---------------------
>  1 file changed, 34 insertions(+), 31 deletions(-)
> 
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index fec82619684a..ec18984c3f2d 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -1356,13 +1356,42 @@ mshv_map_user_memory(struct mshv_partition *partition,
>  	return ret;
>  }
>  
> +static void mshv_partition_destroy_region(struct mshv_mem_region *region)
> +{
> +	struct mshv_partition *partition = region->partition;
> +	u32 unmap_flags = 0;
> +	int ret;
> +
> +	hlist_del(&region->hnode);
> +
> +	if (mshv_partition_encrypted(partition)) {
> +		ret = mshv_partition_region_share(region);
> +		if (ret) {
> +			pt_err(partition,
> +			       "Failed to regain access to memory, unpinning user pages will fail and crash the host error: %d\n",
> +			       ret);
> +			return;
> +		}
> +	}
> +
> +	if (region->flags.large_pages)
> +		unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
> +
> +	/* ignore unmap failures and continue as process may be exiting */
> +	hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
> +				region->nr_pages, unmap_flags);
> +
> +	mshv_region_invalidate(region);
> +
> +	vfree(region);
> +}
> +
>  /* Called for unmapping both the guest ram and the mmio space */
>  static long
>  mshv_unmap_user_memory(struct mshv_partition *partition,
>  		       struct mshv_user_mem_region mem)
>  {
>  	struct mshv_mem_region *region;
> -	u32 unmap_flags = 0;
>  
>  	if (!(mem.flags & BIT(MSHV_SET_MEM_BIT_UNMAP)))
>  		return -EINVAL;
> @@ -1377,18 +1406,8 @@ mshv_unmap_user_memory(struct mshv_partition *partition,
>  	    region->nr_pages != HVPFN_DOWN(mem.size))
>  		return -EINVAL;
>  
> -	hlist_del(&region->hnode);
> +	mshv_partition_destroy_region(region);
>  
> -	if (region->flags.large_pages)
> -		unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
> -
> -	/* ignore unmap failures and continue as process may be exiting */
> -	hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
> -				region->nr_pages, unmap_flags);
> -
> -	mshv_region_invalidate(region);
> -
> -	vfree(region);
>  	return 0;
>  }
>  
> @@ -1724,8 +1743,8 @@ static void destroy_partition(struct mshv_partition *partition)
>  {
>  	struct mshv_vp *vp;
>  	struct mshv_mem_region *region;
> -	int i, ret;
>  	struct hlist_node *n;
> +	int i;
>  
>  	if (refcount_read(&partition->pt_ref_count)) {
>  		pt_err(partition,
> @@ -1789,25 +1808,9 @@ static void destroy_partition(struct mshv_partition *partition)
>  
>  	remove_partition(partition);
>  
> -	/* Remove regions, regain access to the memory and unpin the pages */
>  	hlist_for_each_entry_safe(region, n, &partition->pt_mem_regions,
> -				  hnode) {
> -		hlist_del(&region->hnode);
> -
> -		if (mshv_partition_encrypted(partition)) {
> -			ret = mshv_partition_region_share(region);
> -			if (ret) {
> -				pt_err(partition,
> -				       "Failed to regain access to memory, unpinning user pages will fail and crash the host error: %d\n",
> -				      ret);
> -				return;
> -			}
> -		}
> -
> -		mshv_region_invalidate(region);
> -
> -		vfree(region);
> -	}
> +				  hnode)
> +		mshv_partition_destroy_region(region);
>  
>  	/* Withdraw and free all pages we deposited */
>  	hv_call_withdraw_memory(U64_MAX, NUMA_NO_NODE, partition->pt_id);
> 
> 

Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox