From nobody Sun Feb 8 19:54:57 2026 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 6B86336E465; Wed, 14 Jan 2026 21:38:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768426742; cv=none; b=fzoChOAYCjehHfP9OnMwccLS/gGpXT64SkmjLvVKrKmnG1g1rlaXyzRh/PfsWCKYPEEecbuCB5qBYcLt0eyNk2oYykS2Lz+rKsQrzuBdc4ofQ0xVkhLsikAidma60lsi+7hIeRJecGG2ZB4gE3izvIt5DHoLZbx7jzXWJXFLGE0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768426742; c=relaxed/simple; bh=W79zQwwH5G9YKSH0TMTBGmYR3xOZh3ha4O1Ipr51510=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=B0GxY1thkD8uG0Vky4RB446NUUlbEYOmGvXNwYL19wBUqVd0VY49YkMbvjZeGvTayZYyS1+16/X9ilyRq9sw6wLu+7E4DZ4mEruxLzJfIS6dDNnPyveKI+e3mlcjSH2q+YyGXdFyw0pQc5jvQFdnVlQ4mHuffBRLJIfhe3j9laI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=SfzEin7a; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="SfzEin7a" Received: by linux.microsoft.com (Postfix, from userid 1032) id 3C2C420B7172; Wed, 14 Jan 2026 13:38:09 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 3C2C420B7172 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1768426689; bh=GeGrfjTFmnfdOW9uvkJOb0GQcFVMDz6m+r8GU7Kt3mM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SfzEin7a5Zp/Kn0GrcjXKWWW+8fp5md9OIm8xlBv47zzuYzGTvQPn+ZOqqfYOAXKZ BfKpPLZ+b/ddUkR/eNz5l/QjJU0hcPWgZE7GEhOeP5pwB+QLs8APzzG0VuihsP4cNX vUve0XrXhMxkr8R7V+po0nz3rOo50m9aoE2Ef5Ac= From: Nuno Das Neves To: linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org, mhklinux@outlook.com, skinsburskii@linux.microsoft.com Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org, decui@microsoft.com, longli@microsoft.com, prapal@linux.microsoft.com, mrathor@linux.microsoft.com, paekkaladevi@linux.microsoft.com, Nuno Das Neves , Jinank Jain Subject: [PATCH v3 6/6] mshv: Add debugfs to view hypervisor statistics Date: Wed, 14 Jan 2026 13:38:03 -0800 Message-ID: <20260114213803.143486-7-nunodasneves@linux.microsoft.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260114213803.143486-1-nunodasneves@linux.microsoft.com> References: <20260114213803.143486-1-nunodasneves@linux.microsoft.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 Signed-off-by: Stanislav Kinsburskii Co-developed-by: Praveen K Paladugu Signed-off-by: Praveen K Paladugu Co-developed-by: Mukesh Rathor Signed-off-by: Mukesh Rathor Co-developed-by: Purna Pavan Chandra Aekkaladevi Signed-off-by: Purna Pavan Chandra Aekkaladevi Co-developed-by: Jinank Jain Signed-off-by: Jinank Jain Signed-off-by: Nuno Das Neves Reviewed-by: Stanislav Kinsburskii --- drivers/hv/Makefile | 1 + drivers/hv/mshv_debugfs.c | 1103 +++++++++++++++++++++++++++++++++++ drivers/hv/mshv_root.h | 34 ++ drivers/hv/mshv_root_main.c | 26 +- 4 files changed, 1162 insertions(+), 2 deletions(-) create mode 100644 drivers/hv/mshv_debugfs.c diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile index a49f93c2d245..2593711c3628 100644 --- a/drivers/hv/Makefile +++ b/drivers/hv/Makefile @@ -15,6 +15,7 @@ hv_vmbus-$(CONFIG_HYPERV_TESTING) +=3D hv_debugfs.o hv_utils-y :=3D hv_util.o hv_kvp.o hv_snapshot.o hv_utils_transport.o mshv_root-y :=3D mshv_root_main.o mshv_synic.o mshv_eventfd.o mshv_irq.o \ mshv_root_hv_call.o mshv_portid_table.o mshv_regions.o +mshv_root-$(CONFIG_DEBUG_FS) +=3D mshv_debugfs.o mshv_vtl-y :=3D mshv_vtl_main.o =20 # 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..2922db16e83f --- /dev/null +++ b/drivers/hv/mshv_debugfs.c @@ -0,0 +1,1103 @@ +// 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 +#include +#include +#include + +#include "mshv.h" +#include "mshv_root.h" + +#define U32_BUF_SZ 11 +#define U64_BUF_SZ 21 +#define NUM_STATS_AREAS (HV_STATS_AREA_PARENT + 1) + +static struct dentry *mshv_debugfs; +static struct dentry *mshv_debugfs_partition; +static struct dentry *mshv_debugfs_lp; +static struct dentry **parent_vp_stats; +static struct dentry *parent_partition_stats; + +static u64 mshv_lps_count; +static struct hv_stats_page **mshv_lps_stats; + +static int lp_stats_show(struct seq_file *m, void *v) +{ + const struct hv_stats_page *stats =3D 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) +{ + union hv_stats_object_identity identity =3D { + .lp.lp_index =3D lp_index, + .lp.stats_area_type =3D HV_STATS_AREA_SELF, + }; + int err; + + err =3D hv_unmap_stats_page(HV_STATS_OBJECT_LOGICAL_PROCESSOR, + mshv_lps_stats[lp_index], &identity); + if (err) + pr_err("%s: failed to unmap logical processor %u stats, err: %d\n", + __func__, lp_index, err); +} + +static struct hv_stats_page * __init mshv_lp_stats_map(u32 lp_index) +{ + union hv_stats_object_identity identity =3D { + .lp.lp_index =3D lp_index, + .lp.stats_area_type =3D HV_STATS_AREA_SELF, + }; + struct hv_stats_page *stats; + int err; + + err =3D 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); + } + mshv_lps_stats[lp_index] =3D stats; + + return stats; +} + +static struct hv_stats_page * __init lp_debugfs_stats_create(u32 lp_index, + struct dentry *parent) +{ + struct dentry *dentry; + struct hv_stats_page *stats; + + stats =3D mshv_lp_stats_map(lp_index); + if (IS_ERR(stats)) + return stats; + + dentry =3D debugfs_create_file("stats", 0400, parent, + stats, &lp_stats_fops); + if (IS_ERR(dentry)) { + mshv_lp_stats_unmap(lp_index); + return ERR_CAST(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]; + struct hv_stats_page *stats; + int err; + + sprintf(lp_idx_str, "%u", lp_index); + + idx =3D debugfs_create_dir(lp_idx_str, parent); + if (IS_ERR(idx)) + return PTR_ERR(idx); + + stats =3D lp_debugfs_stats_create(lp_index, idx); + if (IS_ERR(stats)) { + err =3D 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 =3D 0; lp_index < mshv_lps_count; lp_index++) + mshv_lp_stats_unmap(lp_index); + + kfree(mshv_lps_stats); + mshv_lps_stats =3D NULL; +} + +static int __init mshv_debugfs_lp_create(struct dentry *parent) +{ + struct dentry *lp_dir; + int err, lp_index; + + mshv_lps_stats =3D kcalloc(mshv_lps_count, + sizeof(*mshv_lps_stats), + GFP_KERNEL_ACCOUNT); + + if (!mshv_lps_stats) + return -ENOMEM; + + lp_dir =3D debugfs_create_dir("lp", parent); + if (IS_ERR(lp_dir)) { + err =3D PTR_ERR(lp_dir); + goto free_lp_stats; + } + + for (lp_index =3D 0; lp_index < mshv_lps_count; lp_index++) { + err =3D lp_debugfs_create(lp_index, lp_dir); + if (err) + goto remove_debugfs_lps; + } + + mshv_debugfs_lp =3D lp_dir; + + return 0; + +remove_debugfs_lps: + for (lp_index -=3D 1; lp_index >=3D 0; lp_index--) + mshv_lp_stats_unmap(lp_index); + debugfs_remove_recursive(lp_dir); +free_lp_stats: + kfree(mshv_lps_stats); + + return err; +} + +static int vp_stats_show(struct seq_file *m, void *v) +{ + const struct hv_stats_page **pstats =3D m->private; + +/* + * For VP and partition stats, there may be two stats areas mapped, SELF a= nd + * PARENT. These refer to the privilege level of the data in each page. So= me + * fields may be 0 in SELF and nonzero in PARENT, or vice versa. + * + * Hence, prioritize printing from the PARENT page (more privileged data),= but + * use the value from the SELF page if the PARENT value is 0. + */ + +#define VP_SEQ_PRINTF(cnt) \ +do { \ + if (pstats[HV_STATS_AREA_PARENT]->vp_cntrs[Vp##cnt]) \ + seq_printf(m, "%-30s: %llu\n", __stringify(cnt), \ + pstats[HV_STATS_AREA_PARENT]->vp_cntrs[Vp##cnt]); \ + else \ + seq_printf(m, "%-30s: %llu\n", __stringify(cnt), \ + pstats[HV_STATS_AREA_SELF]->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); + VP_SEQ_PRINTF(LoadAvg); +#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); + VP_SEQ_PRINTF(LoadAvg); +#endif + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(vp_stats); + +static void vp_debugfs_remove(struct dentry *vp_stats) +{ + debugfs_remove_recursive(vp_stats->d_parent); +} + +static int vp_debugfs_create(u64 partition_id, u32 vp_index, + struct hv_stats_page **pstats, + struct dentry **vp_stats_ptr, + struct dentry *parent) +{ + struct dentry *vp_idx_dir, *d; + char vp_idx_str[U32_BUF_SZ]; + int err; + + sprintf(vp_idx_str, "%u", vp_index); + + vp_idx_dir =3D debugfs_create_dir(vp_idx_str, parent); + if (IS_ERR(vp_idx_dir)) + return PTR_ERR(vp_idx_dir); + + d =3D debugfs_create_file("stats", 0400, vp_idx_dir, + pstats, &vp_stats_fops); + if (IS_ERR(d)) { + err =3D PTR_ERR(d); + goto remove_debugfs_vp_idx; + } + + *vp_stats_ptr =3D d; + + 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 =3D 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, + struct hv_stats_page *stats_page, + enum hv_stats_area_type stats_area_type) +{ + union hv_stats_object_identity identity =3D { + .partition.partition_id =3D partition_id, + .partition.stats_area_type =3D stats_area_type, + }; + int err; + + err =3D hv_unmap_stats_page(HV_STATS_OBJECT_PARTITION, stats_page, + &identity); + if (err) + pr_err("%s: failed to unmap partition %lld %s stats, err: %d\n", + __func__, partition_id, + (stats_area_type =3D=3D HV_STATS_AREA_SELF) ? "self" : "parent", + err); +} + +static struct hv_stats_page *mshv_partition_stats_map(u64 partition_id, + enum hv_stats_area_type stats_area_type) +{ + union hv_stats_object_identity identity =3D { + .partition.partition_id =3D partition_id, + .partition.stats_area_type =3D stats_area_type, + }; + struct hv_stats_page *stats; + int err; + + err =3D 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 =3D=3D 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 =3D kcalloc(NUM_STATS_AREAS, sizeof(struct hv_stats_page *), + GFP_KERNEL_ACCOUNT); + if (!pstats) + return -ENOMEM; + + pstats[HV_STATS_AREA_SELF] =3D mshv_partition_stats_map(partition_id, + HV_STATS_AREA_SELF); + if (IS_ERR(pstats[HV_STATS_AREA_SELF])) { + err =3D 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] =3D pstats[HV_STATS_AREA_SELF]; + } else { + pstats[HV_STATS_AREA_PARENT] =3D mshv_partition_stats_map(partition_id, + HV_STATS_AREA_PARENT); + if (IS_ERR(pstats[HV_STATS_AREA_PARENT])) { + err =3D PTR_ERR(pstats[HV_STATS_AREA_PARENT]); + goto unmap_self; + } + if (!pstats[HV_STATS_AREA_PARENT]) + pstats[HV_STATS_AREA_PARENT] =3D pstats[HV_STATS_AREA_SELF]; + } + + dentry =3D debugfs_create_file("stats", 0400, parent, + pstats, &partition_stats_fops); + if (IS_ERR(dentry)) { + err =3D PTR_ERR(dentry); + goto unmap_partition_stats; + } + + *partition_stats_ptr =3D dentry; + return 0; + +unmap_partition_stats: + if (pstats[HV_STATS_AREA_PARENT] !=3D 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 *dent= ry) +{ + struct hv_stats_page **pstats =3D NULL; + + pstats =3D dentry->d_inode->i_private; + + debugfs_remove_recursive(dentry->d_parent); + + if (pstats[HV_STATS_AREA_PARENT] !=3D pstats[HV_STATS_AREA_SELF]) { + mshv_partition_stats_unmap(partition_id, + pstats[HV_STATS_AREA_PARENT], + HV_STATS_AREA_PARENT); + } + + mshv_partition_stats_unmap(partition_id, + pstats[HV_STATS_AREA_SELF], + 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 =3D debugfs_create_dir(part_id_str, parent); + if (IS_ERR(part_id_dir)) + return PTR_ERR(part_id_dir); + + vp_dir =3D debugfs_create_dir("vp", part_id_dir); + if (IS_ERR(vp_dir)) { + err =3D PTR_ERR(vp_dir); + goto remove_debugfs_partition_id; + } + + err =3D mshv_debugfs_partition_stats_create(partition_id, + partition_stats_ptr, + part_id_dir); + if (err) + goto remove_debugfs_partition_id; + + *vp_dir_ptr =3D vp_dir; + + return 0; + +remove_debugfs_partition_id: + debugfs_remove_recursive(part_id_dir); + return err; +} + +static void parent_vp_debugfs_remove(u32 vp_index, + struct dentry *vp_stats_ptr) +{ + struct hv_stats_page **pstats; + + pstats =3D vp_stats_ptr->d_inode->i_private; + vp_debugfs_remove(vp_stats_ptr); + mshv_vp_stats_unmap(hv_current_partition_id, vp_index, pstats); + kfree(pstats); +} + +static void mshv_debugfs_parent_partition_remove(void) +{ + int idx; + + for_each_online_cpu(idx) + parent_vp_debugfs_remove(idx, + parent_vp_stats[idx]); + + partition_debugfs_remove(hv_current_partition_id, + parent_partition_stats); + kfree(parent_vp_stats); + parent_vp_stats =3D NULL; + parent_partition_stats =3D NULL; + +} + +static int __init parent_vp_debugfs_create(u32 vp_index, + struct dentry **vp_stats_ptr, + struct dentry *parent) +{ + struct hv_stats_page **pstats; + int err; + + pstats =3D kcalloc(2, sizeof(struct hv_stats_page *), GFP_KERNEL_ACCOUNT); + if (!pstats) + return -ENOMEM; + + err =3D mshv_vp_stats_map(hv_current_partition_id, vp_index, pstats); + if (err) + goto cleanup; + + err =3D vp_debugfs_create(hv_current_partition_id, vp_index, pstats, + vp_stats_ptr, parent); + if (err) + goto unmap_vp_stats; + + return 0; + +unmap_vp_stats: + mshv_vp_stats_unmap(hv_current_partition_id, vp_index, pstats); +cleanup: + kfree(pstats); + return err; +} + +static int __init mshv_debugfs_parent_partition_create(void) +{ + struct dentry *vp_dir; + int err, idx, i; + + mshv_debugfs_partition =3D debugfs_create_dir("partition", + mshv_debugfs); + if (IS_ERR(mshv_debugfs_partition)) + return PTR_ERR(mshv_debugfs_partition); + + err =3D partition_debugfs_create(hv_current_partition_id, + &vp_dir, + &parent_partition_stats, + mshv_debugfs_partition); + if (err) + goto remove_debugfs_partition; + + parent_vp_stats =3D kcalloc(num_possible_cpus(), + sizeof(*parent_vp_stats), + GFP_KERNEL); + if (!parent_vp_stats) { + err =3D -ENOMEM; + goto remove_debugfs_partition; + } + + for_each_online_cpu(idx) { + err =3D parent_vp_debugfs_create(hv_vp_index[idx], + &parent_vp_stats[idx], + vp_dir); + if (err) + goto remove_debugfs_partition_vp; + } + + return 0; + +remove_debugfs_partition_vp: + for_each_online_cpu(i) { + if (i >=3D idx) + break; + parent_vp_debugfs_remove(i, parent_vp_stats[i]); + } + partition_debugfs_remove(hv_current_partition_id, + parent_partition_stats); + + kfree(parent_vp_stats); + parent_vp_stats =3D NULL; + parent_partition_stats =3D NULL; + +remove_debugfs_partition: + debugfs_remove_recursive(mshv_debugfs_partition); + mshv_debugfs_partition =3D NULL; + return err; +} + +static int hv_stats_show(struct seq_file *m, void *v) +{ + const struct hv_stats_page *stats =3D 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 =3D { + .hv.stats_area_type =3D HV_STATS_AREA_SELF, + }; + int err; + + err =3D 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 =3D { + .hv.stats_area_type =3D HV_STATS_AREA_SELF, + }; + struct hv_stats_page *stats; + int err; + + err =3D 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 =3D mshv_hv_stats_map(); + if (IS_ERR(stats)) + return PTR_ERR(stats); + + dentry =3D debugfs_create_file("stats", 0400, parent, + stats, &hv_stats_fops); + if (IS_ERR(dentry)) { + err =3D PTR_ERR(dentry); + pr_err("%s: failed to create hypervisor stats dentry: %d\n", + __func__, err); + goto unmap_hv_stats; + } + + mshv_lps_count =3D 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 =3D vp->vp_partition; + + if (!mshv_debugfs) + return 0; + + return vp_debugfs_create(p->pt_id, vp->vp_index, + vp->vp_stats_pages, + &vp->vp_stats_dentry, + p->pt_vp_dentry); +} + +void mshv_debugfs_vp_remove(struct mshv_vp *vp) +{ + if (!mshv_debugfs) + return; + + vp_debugfs_remove(vp->vp_stats_dentry); +} + +int mshv_debugfs_partition_create(struct mshv_partition *partition) +{ + int err; + + if (!mshv_debugfs) + return 0; + + err =3D partition_debugfs_create(partition->pt_id, + &partition->pt_vp_dentry, + &partition->pt_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_stats_dentry); +} + +int __init mshv_debugfs_init(void) +{ + int err; + + mshv_debugfs =3D 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 =3D mshv_debugfs_hv_stats_create(mshv_debugfs); + if (err) + goto remove_mshv_dir; + + err =3D mshv_debugfs_lp_create(mshv_debugfs); + if (err) + goto unmap_hv_stats; + } + + err =3D mshv_debugfs_parent_partition_create(); + if (err) + goto unmap_lp_stats; + + return 0; + +unmap_lp_stats: + if (hv_root_partition()) { + mshv_debugfs_lp_remove(); + mshv_debugfs_lp =3D NULL; + } +unmap_hv_stats: + if (hv_root_partition()) + mshv_hv_stats_unmap(); +remove_mshv_dir: + debugfs_remove_recursive(mshv_debugfs); + mshv_debugfs =3D NULL; + return err; +} + +void mshv_debugfs_exit(void) +{ + mshv_debugfs_parent_partition_remove(); + + if (hv_root_partition()) { + mshv_debugfs_lp_remove(); + mshv_debugfs_lp =3D NULL; + mshv_hv_stats_unmap(); + } + + debugfs_remove_recursive(mshv_debugfs); + mshv_debugfs =3D NULL; + mshv_debugfs_partition =3D NULL; +} diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h index e4912b0618fa..7332d9af8373 100644 --- a/drivers/hv/mshv_root.h +++ b/drivers/hv/mshv_root.h @@ -52,6 +52,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_stats_dentry; +#endif }; =20 #define vp_fmt(fmt) "p%lluvp%u: " fmt @@ -136,6 +139,10 @@ struct mshv_partition { u64 isolation_type; bool import_completed; bool pt_initialized; +#if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *pt_stats_dentry; + struct dentry *pt_vp_dentry; +#endif }; =20 #define pt_fmt(fmt) "p%llu: " fmt @@ -327,6 +334,33 @@ int hv_call_modify_spa_host_access(u64 partition_id, s= truct 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); =20 +#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 *par= tition) +{ + return 0; +} +static inline void mshv_debugfs_partition_remove(struct mshv_partition *pa= rtition) { } +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 724bbaa0b08c..9d46ddb43d70 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1089,6 +1089,10 @@ mshv_partition_ioctl_create_vp(struct mshv_partition= *partition, =20 memcpy(vp->vp_stats_pages, stats_pages, sizeof(stats_pages)); =20 + ret =3D 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. @@ -1096,7 +1100,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition = *partition, ret =3D anon_inode_getfd("mshv_vp", &mshv_vp_fops, vp, O_RDWR | O_CLOEXEC); if (ret < 0) - goto put_partition; + goto remove_debugfs_vp; =20 /* already exclusive with the partition mutex for all ioctls */ partition->pt_vp_count++; @@ -1104,6 +1108,8 @@ mshv_partition_ioctl_create_vp(struct mshv_partition = *partition, =20 return ret; =20 +remove_debugfs_vp: + mshv_debugfs_vp_remove(vp); put_partition: mshv_partition_put(partition); free_vp: @@ -1546,10 +1552,16 @@ mshv_partition_ioctl_initialize(struct mshv_partiti= on *partition) if (ret) goto withdraw_mem; =20 + ret =3D mshv_debugfs_partition_create(partition); + if (ret) + goto finalize_partition; + partition->pt_initialized =3D true; =20 return 0; =20 +finalize_partition: + hv_call_finalize_partition(partition->pt_id); withdraw_mem: hv_call_withdraw_memory(U64_MAX, NUMA_NO_NODE, partition->pt_id); =20 @@ -1729,6 +1741,7 @@ static void destroy_partition(struct mshv_partition *= partition) if (!vp) continue; =20 + mshv_debugfs_vp_remove(vp); mshv_vp_stats_unmap(partition->pt_id, vp->vp_index, vp->vp_stats_pages); =20 @@ -1762,6 +1775,8 @@ static void destroy_partition(struct mshv_partition *= partition) partition->pt_vp_array[i] =3D NULL; } =20 + mshv_debugfs_partition_remove(partition); + /* Deallocates and unmaps everything including vcpus, GPA mappings etc */ hv_call_finalize_partition(partition->pt_id); =20 @@ -2307,10 +2322,14 @@ static int __init mshv_parent_partition_init(void) =20 mshv_init_vmm_caps(dev); =20 - ret =3D mshv_irqfd_wq_init(); + ret =3D mshv_debugfs_init(); if (ret) goto exit_partition; =20 + ret =3D mshv_irqfd_wq_init(); + if (ret) + goto exit_debugfs; + spin_lock_init(&mshv_root.pt_ht_lock); hash_init(mshv_root.pt_htable); =20 @@ -2318,6 +2337,8 @@ static int __init mshv_parent_partition_init(void) =20 return 0; =20 +exit_debugfs: + mshv_debugfs_exit(); exit_partition: if (hv_root_partition()) mshv_root_partition_exit(); @@ -2334,6 +2355,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()) --=20 2.34.1