* [PATCH v3 0/2] perf/x86/amd/uncore: Add Hygon uncore PMU and JSON events
@ 2026-07-08 13:13 Qi Liu
2026-07-08 13:13 ` [PATCH v3 1/2] perf/x86/amd/uncore: Add Hygon uncore PMU support Qi Liu
2026-07-08 13:13 ` [PATCH v3 2/2] perf vendor events hygon: Add Hygon Data Fabric PMU events and metrics Qi Liu
0 siblings, 2 replies; 5+ messages in thread
From: Qi Liu @ 2026-07-08 13:13 UTC (permalink / raw)
To: peterz, mingo, namhyung, alexander.shishkin, jolsa, sandipan.das,
ravi.bangoria, x86
Cc: linux-perf-users, linux-kernel, lijing, liuqi
This series adds initial uncore PMU support for Hygon Family 18h
processors.
Hygon uncore PMUs use a programming model similar to AMD's, but
differ in MSR base addresses, event encodings, and counter allocation
semantics. Rather than embedding Hygon support in the AMD uncore
driver with vendor checks, a dedicated driver is introduced to keep
both drivers clean and independently maintainable.
The new driver provides hygon_df and hygon_df_iod PMUs covering the
Data Fabric on both the CPU die and the IO Die, together with perf
JSON events and metrics for three processor models. A per-context
hrtimer handles counter reads, and CPU hotplug callbacks manage
context lifecycle.
Changes since RFC:
- Address sashiko bot review.
Changes since v1:
- Rename the helper namespace to amd_uncore_common_* to avoid
an generic uncore_common_* prefix, and to keep it distinct
from the existing amd_uncore_* names.
Changes since v2:
- Drop the common helpers refactoring. Keep the Hygon driver
self-contained instead, avoiding unnecessary changes in the AMD
uncore driver.
Qi Liu (2):
perf/x86/amd/uncore: Add Hygon uncore PMU support
perf vendor events hygon: Add Hygon Data Fabric PMU events and metrics
arch/x86/events/Kconfig | 11 +
arch/x86/events/amd/Makefile | 2 +
arch/x86/events/amd/hygon_uncore.c | 863 ++++++++++++++++++
arch/x86/events/amd/uncore.c | 13 +-
arch/x86/include/asm/msr-index.h | 2 +
arch/x86/include/asm/perf_event.h | 20 +
include/linux/cpuhotplug.h | 3 +
tools/perf/pmu-events/Build | 2 +-
.../arch/x86/hygonm1/data-fabric.json | 87 ++
.../arch/x86/hygonm4/data-fabric.json | 122 +++
.../arch/x86/hygonm6/data-fabric.json | 154 ++++
tools/perf/pmu-events/arch/x86/mapfile.csv | 3 +
tools/perf/pmu-events/jevents.py | 2 +
13 files changed, 1274 insertions(+), 10 deletions(-)
create mode 100644 arch/x86/events/amd/hygon_uncore.c
create mode 100644 tools/perf/pmu-events/arch/x86/hygonm1/data-fabric.json
create mode 100644 tools/perf/pmu-events/arch/x86/hygonm4/data-fabric.json
create mode 100644 tools/perf/pmu-events/arch/x86/hygonm6/data-fabric.json
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v3 1/2] perf/x86/amd/uncore: Add Hygon uncore PMU support
2026-07-08 13:13 [PATCH v3 0/2] perf/x86/amd/uncore: Add Hygon uncore PMU and JSON events Qi Liu
@ 2026-07-08 13:13 ` Qi Liu
2026-07-08 13:27 ` sashiko-bot
2026-07-08 13:13 ` [PATCH v3 2/2] perf vendor events hygon: Add Hygon Data Fabric PMU events and metrics Qi Liu
1 sibling, 1 reply; 5+ messages in thread
From: Qi Liu @ 2026-07-08 13:13 UTC (permalink / raw)
To: peterz, mingo, namhyung, alexander.shishkin, jolsa, sandipan.das,
ravi.bangoria, x86
Cc: linux-perf-users, linux-kernel, lijing, liuqi, Zhenglang Hu
Add uncore PMU driver for Hygon Family 18h processors, supporting two
PMU types:
- hygon_df: Data Fabric counters shared across CPUs within the
same DF instance, identified via Node ID.
- hygon_df_iod: Data Fabric counters on the IO Die (IOD) for
monitoring memory access metrics.
A per-context hrtimer periodically reads the counters since uncore
PMUs do not generate interrupts. CPU hotplug callbacks handle context
creation, migration, and teardown.
Keeping Hygon support in a separate driver avoids accumulating vendor
checks in uncore.c as more Hygon-specific features are introduced.
Remove the now-unnecessary Hygon checks from the AMD uncore driver.
Signed-off-by: Qi Liu <liuqi@hygon.cn>
Tested-by: Zhenglang Hu <huzhenglang@hygon.cn>
---
arch/x86/events/Kconfig | 11 +
arch/x86/events/amd/Makefile | 2 +
arch/x86/events/amd/hygon_uncore.c | 863 +++++++++++++++++++++++++++++
arch/x86/events/amd/uncore.c | 13 +-
arch/x86/include/asm/msr-index.h | 2 +
arch/x86/include/asm/perf_event.h | 20 +
include/linux/cpuhotplug.h | 3 +
7 files changed, 905 insertions(+), 9 deletions(-)
create mode 100644 arch/x86/events/amd/hygon_uncore.c
diff --git a/arch/x86/events/Kconfig b/arch/x86/events/Kconfig
index dabdf3d7bf84..cc8387236b95 100644
--- a/arch/x86/events/Kconfig
+++ b/arch/x86/events/Kconfig
@@ -45,6 +45,17 @@ config PERF_EVENTS_AMD_UNCORE
To compile this driver as a module, choose M here: the
module will be called 'amd-uncore'.
+config PERF_EVENTS_HYGON_UNCORE
+ tristate "Hygon Uncore performance events"
+ depends on PERF_EVENTS && CPU_SUP_HYGON
+ default y
+ help
+ Include support for Hygon uncore performance events for use with
+ e.g., perf stat -e hygon_df/.../.
+
+ To compile this driver as a module, choose M here: the
+ module will be called 'hygon-uncore'.
+
config PERF_EVENTS_AMD_BRS
depends on PERF_EVENTS && CPU_SUP_AMD
bool "AMD Zen3 Branch Sampling support"
diff --git a/arch/x86/events/amd/Makefile b/arch/x86/events/amd/Makefile
index 527d947eb76b..c373fd1d1525 100644
--- a/arch/x86/events/amd/Makefile
+++ b/arch/x86/events/amd/Makefile
@@ -5,6 +5,8 @@ obj-$(CONFIG_PERF_EVENTS_AMD_POWER) += power.o
obj-$(CONFIG_X86_LOCAL_APIC) += ibs.o
obj-$(CONFIG_PERF_EVENTS_AMD_UNCORE) += amd-uncore.o
amd-uncore-objs := uncore.o
+obj-$(CONFIG_PERF_EVENTS_HYGON_UNCORE) += hygon-uncore.o
+hygon-uncore-objs := hygon_uncore.o
ifdef CONFIG_AMD_IOMMU
obj-$(CONFIG_CPU_SUP_AMD) += iommu.o
endif
diff --git a/arch/x86/events/amd/hygon_uncore.c b/arch/x86/events/amd/hygon_uncore.c
new file mode 100644
index 000000000000..6c60c2392674
--- /dev/null
+++ b/arch/x86/events/amd/hygon_uncore.c
@@ -0,0 +1,863 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Chengdu Haiguang IC Design Co., Ltd.
+ *
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/cpufeature.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/perf_event.h>
+#include <linux/percpu.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/types.h>
+
+#include <asm/amd/nb.h>
+#include <asm/cpuid/api.h>
+#include <asm/perf_event.h>
+#include <asm/msr.h>
+
+#define NUM_COUNTERS_DF 4
+#define NUM_COUNTERS_MAX 8
+
+#define UNCORE_NAME_LEN 16
+#define COUNTER_SHIFT 16
+
+#undef pr_fmt
+#define pr_fmt(fmt) "hygon_uncore: " fmt
+
+struct hygon_uncore_ctx {
+ int refcnt;
+ int cpu;
+ struct perf_event **events;
+ unsigned long active_mask[BITS_TO_LONGS(NUM_COUNTERS_MAX)];
+ int nr_active;
+ struct hrtimer hrtimer;
+ u64 hrtimer_duration;
+};
+
+struct hygon_uncore_pmu {
+ char name[UNCORE_NAME_LEN];
+ int type;
+ int num_counters;
+ u32 msr_base;
+ cpumask_t active_mask;
+ struct pmu pmu;
+ struct hygon_uncore_ctx * __percpu *ctx;
+};
+
+enum {
+ HYGON_UNCORE_TYPE_DF,
+ HYGON_UNCORE_TYPE_DF_IOD,
+ HYGON_UNCORE_TYPE_MAX
+};
+
+union hygon_uncore_info {
+ struct {
+ u64 num_iods:8; /* number of iods of each package */
+ u64 num_pmcs:8; /* number of counters */
+ u64 cid:8; /* context id */
+ } split;
+ u64 full;
+};
+
+struct hygon_uncore {
+ union hygon_uncore_info __percpu *info;
+ struct hygon_uncore_pmu pmu;
+ bool pmu_registered;
+ bool init_done;
+ void (*scan)(struct hygon_uncore *uncore, unsigned int cpu);
+ int (*init)(struct hygon_uncore *uncore, unsigned int cpu);
+ void (*move)(struct hygon_uncore *uncore, unsigned int cpu);
+ void (*free)(struct hygon_uncore *uncore, unsigned int cpu);
+};
+static struct hygon_uncore uncores[];
+
+/* Interval for hrtimer, defaults to 60000 milliseconds */
+static unsigned int update_interval = 60 * MSEC_PER_SEC;
+module_param(update_interval, uint, 0444);
+
+static struct hygon_uncore_pmu *event_to_hygon_uncore_pmu(struct perf_event *event)
+{
+ return container_of(event->pmu, struct hygon_uncore_pmu, pmu);
+}
+
+static __always_inline bool is_uncore_df_iod_event(struct hygon_uncore_pmu *pmu)
+{
+ return pmu->type == HYGON_UNCORE_TYPE_DF_IOD;
+}
+
+static __always_inline int hygon_uncore_ctx_num_pmcs(struct hygon_uncore *uncore, unsigned int cpu)
+{
+ union hygon_uncore_info *info = per_cpu_ptr(uncore->info, cpu);
+
+ return info->split.num_pmcs;
+}
+
+static __always_inline int hygon_uncore_ctx_cid(struct hygon_uncore *uncore, unsigned int cpu)
+{
+ union hygon_uncore_info *info = per_cpu_ptr(uncore->info, cpu);
+
+ return info->split.cid;
+}
+
+static __always_inline int hygon_uncore_ctx_num_iods(struct hygon_uncore *uncore, unsigned int cpu)
+{
+ union hygon_uncore_info *info = per_cpu_ptr(uncore->info, cpu);
+
+ return info->split.num_iods;
+}
+
+static enum hrtimer_restart hygon_uncore_hrtimer(struct hrtimer *hrtimer)
+{
+ struct hygon_uncore_ctx *ctx;
+ struct perf_event *event;
+ int bit;
+
+ ctx = container_of(hrtimer, struct hygon_uncore_ctx, hrtimer);
+
+ if (!ctx->nr_active || ctx->cpu != smp_processor_id())
+ return HRTIMER_NORESTART;
+
+ for_each_set_bit(bit, ctx->active_mask, NUM_COUNTERS_MAX) {
+ event = ctx->events[bit];
+ event->pmu->read(event);
+ }
+
+ hrtimer_forward_now(hrtimer, ns_to_ktime(ctx->hrtimer_duration));
+ return HRTIMER_RESTART;
+}
+
+static void hygon_uncore_start_hrtimer(struct hygon_uncore_ctx *ctx)
+{
+ hrtimer_start(&ctx->hrtimer, ns_to_ktime(ctx->hrtimer_duration),
+ HRTIMER_MODE_REL_PINNED_HARD);
+}
+
+static void hygon_uncore_cancel_hrtimer(struct hygon_uncore_ctx *ctx)
+{
+ hrtimer_cancel(&ctx->hrtimer);
+}
+
+static void hygon_uncore_init_hrtimer(struct hygon_uncore_ctx *ctx)
+{
+ hrtimer_setup(&ctx->hrtimer, hygon_uncore_hrtimer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL_HARD);
+}
+
+static void hygon_uncore_read(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ u64 prev, cur;
+ s64 delta;
+
+ prev = local64_read(&hwc->prev_count);
+ rdmsrq(hwc->event_base, cur);
+ local64_set(&hwc->prev_count, cur);
+ delta = (cur << COUNTER_SHIFT) - (prev << COUNTER_SHIFT);
+ delta >>= COUNTER_SHIFT;
+ local64_add(delta, &event->count);
+}
+
+static void hygon_uncore_start(struct perf_event *event, int flags)
+{
+ struct hygon_uncore_pmu *pmu = event_to_hygon_uncore_pmu(event);
+ struct hygon_uncore_ctx *ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
+ struct hw_perf_event *hwc = &event->hw;
+
+ if (!ctx->nr_active++)
+ hygon_uncore_start_hrtimer(ctx);
+
+ if (flags & PERF_EF_RELOAD)
+ wrmsrq(hwc->event_base, (u64)local64_read(&hwc->prev_count));
+
+ hwc->state = 0;
+ __set_bit(hwc->idx, ctx->active_mask);
+ wrmsrq(hwc->config_base, (hwc->config | ARCH_PERFMON_EVENTSEL_ENABLE));
+ perf_event_update_userpage(event);
+}
+
+static void hygon_uncore_stop(struct perf_event *event, int flags)
+{
+ struct hygon_uncore_pmu *pmu = event_to_hygon_uncore_pmu(event);
+ struct hygon_uncore_ctx *ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
+ struct hw_perf_event *hwc = &event->hw;
+
+ wrmsrq(hwc->config_base, hwc->config);
+ hwc->state |= PERF_HES_STOPPED;
+
+ if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
+ event->pmu->read(event);
+ hwc->state |= PERF_HES_UPTODATE;
+ }
+
+ if (!--ctx->nr_active)
+ hygon_uncore_cancel_hrtimer(ctx);
+
+ __clear_bit(hwc->idx, ctx->active_mask);
+}
+
+static int hygon_uncore_add(struct perf_event *event, int flags)
+{
+ struct hygon_uncore_pmu *pmu = event_to_hygon_uncore_pmu(event);
+ struct hygon_uncore_ctx *ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
+ struct hw_perf_event *hwc = &event->hw;
+ int i, iod_idx;
+
+ if (!ctx)
+ return -ENODEV;
+
+ if (hwc->idx != -1 && ctx->events[hwc->idx] == event)
+ goto out;
+
+ for (i = 0; i < pmu->num_counters; i++) {
+ if (ctx->events[i] == event) {
+ hwc->idx = i;
+ goto out;
+ }
+ }
+
+ hwc->idx = -1;
+ if (is_uncore_df_iod_event(pmu)) {
+ iod_idx = event->attr.config1;
+ for (i = iod_idx * NUM_COUNTERS_DF; i < (iod_idx + 1) * NUM_COUNTERS_DF; i++) {
+ struct perf_event *tmp = NULL;
+
+ if (try_cmpxchg(&ctx->events[i], &tmp, event)) {
+ hwc->idx = i;
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < pmu->num_counters; i++) {
+ struct perf_event *tmp = NULL;
+
+ if (try_cmpxchg(&ctx->events[i], &tmp, event)) {
+ hwc->idx = i;
+ break;
+ }
+ }
+ }
+out:
+ if (hwc->idx == -1)
+ return -EBUSY;
+
+ hwc->config_base = pmu->msr_base + (2 * hwc->idx);
+ hwc->event_base = pmu->msr_base + 1 + (2 * hwc->idx);
+ hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+ if (flags & PERF_EF_START)
+ event->pmu->start(event, PERF_EF_RELOAD);
+
+ return 0;
+}
+
+static void hygon_uncore_del(struct perf_event *event, int flags)
+{
+ struct hygon_uncore_pmu *pmu = event_to_hygon_uncore_pmu(event);
+ struct hygon_uncore_ctx *ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
+ struct hw_perf_event *hwc = &event->hw;
+ int i;
+
+ if (!ctx)
+ return;
+
+ event->pmu->stop(event, PERF_EF_UPDATE);
+ for (i = 0; i < pmu->num_counters; i++) {
+ struct perf_event *tmp = event;
+
+ if (try_cmpxchg(&ctx->events[i], &tmp, NULL))
+ break;
+ }
+
+ hwc->idx = -1;
+}
+
+static int hygon_uncore_event_init(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hygon_uncore_pmu *pmu;
+ struct hygon_uncore_ctx *ctx;
+
+ if (event->attr.type != event->pmu->type)
+ return -ENOENT;
+
+ if (event->cpu < 0)
+ return -EINVAL;
+
+ pmu = event_to_hygon_uncore_pmu(event);
+ ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
+ if (!ctx)
+ return -ENODEV;
+
+ event->cpu = ctx->cpu;
+ hwc->config = event->attr.config;
+ hwc->idx = -1;
+
+ return 0;
+}
+
+static int hygon_uncore_df_event_init(struct perf_event *event)
+{
+ int ret = hygon_uncore_event_init(event);
+ struct hw_perf_event *hwc = &event->hw;
+ struct hygon_uncore_pmu *pmu;
+ struct hygon_uncore *uncore;
+ u64 event_mask = HYGON_F18H_RAW_EVENT_MASK_DF;
+
+ if (ret)
+ return ret;
+
+ pmu = event_to_hygon_uncore_pmu(event);
+ uncore = &uncores[pmu->type];
+ if (is_uncore_df_iod_event(pmu) &&
+ (event->attr.config1 >= hygon_uncore_ctx_num_iods(uncore, event->cpu)))
+ return -EINVAL;
+
+ if (boot_cpu_data.x86_model == 0x4 ||
+ boot_cpu_data.x86_model == 0x5)
+ event_mask = HYGON_F18H_M4H_RAW_EVENT_MASK_DF;
+ else if (boot_cpu_data.x86_model >= 0x6 &&
+ boot_cpu_data.x86_model <= 0x18)
+ event_mask = HYGON_F18H_M6H_RAW_EVENT_MASK_DF;
+
+ hwc->config = event->attr.config & event_mask;
+ return 0;
+}
+
+static ssize_t cpumask_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pmu *ptr = dev_get_drvdata(dev);
+ struct hygon_uncore_pmu *pmu = container_of(ptr, struct hygon_uncore_pmu, pmu);
+
+ return cpumap_print_to_pagebuf(true, buf, &pmu->active_mask);
+}
+static DEVICE_ATTR_RO(cpumask);
+
+static struct attribute *hygon_uncore_attrs[] = {
+ &dev_attr_cpumask.attr,
+ NULL,
+};
+
+static struct attribute_group hygon_uncore_attr_group = {
+ .attrs = hygon_uncore_attrs,
+};
+
+#define DEFINE_UNCORE_FORMAT_ATTR(_var, _name, _format) \
+static ssize_t __uncore_##_var##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *page) \
+{ \
+ BUILD_BUG_ON(sizeof(_format) >= PAGE_SIZE); \
+ return sprintf(page, _format "\n"); \
+} \
+static struct device_attribute format_attr_##_var = \
+ __ATTR(_name, 0444, __uncore_##_var##_show, NULL)
+
+DEFINE_UNCORE_FORMAT_ATTR(event, event, "config:0-5");
+DEFINE_UNCORE_FORMAT_ATTR(umask8, umask, "config:8-15");
+DEFINE_UNCORE_FORMAT_ATTR(umask10, umask, "config:8-17"); /* F18h M4h DF */
+DEFINE_UNCORE_FORMAT_ATTR(umask12, umask, "config:8-19"); /* F18h M6h DF */
+DEFINE_UNCORE_FORMAT_ATTR(constid, constid, "config:6-7,32-35,61-62");
+DEFINE_UNCORE_FORMAT_ATTR(iod, iod, "config1:0-1");
+
+static struct attribute *hygon_uncore_df_umask_attr(void)
+{
+ if (boot_cpu_data.x86_model == 0x4 ||
+ boot_cpu_data.x86_model == 0x5)
+ return &format_attr_umask10.attr;
+
+ if (boot_cpu_data.x86_model >= 0x6 &&
+ boot_cpu_data.x86_model <= 0x18)
+ return &format_attr_umask12.attr;
+
+ return &format_attr_umask8.attr;
+}
+
+static struct attribute *hygon_uncore_df_format_attr[] = {
+ &format_attr_event.attr, /* event */
+ &format_attr_umask8.attr, /* umask */
+ &format_attr_constid.attr, /* constid */
+ NULL,
+};
+
+static struct attribute_group hygon_uncore_df_format_group = {
+ .name = "format",
+ .attrs = hygon_uncore_df_format_attr,
+};
+
+static const struct attribute_group *hygon_uncore_df_attr_groups[] = {
+ &hygon_uncore_attr_group,
+ &hygon_uncore_df_format_group,
+ NULL,
+};
+
+static struct attribute *hygon_uncore_df_iod_format_attr[] = {
+ &format_attr_event.attr, /* event */
+ &format_attr_umask10.attr, /* umask */
+ &format_attr_constid.attr, /* constid */
+ &format_attr_iod.attr, /* iod */
+ NULL,
+};
+
+static struct attribute_group hygon_uncore_df_iod_format_group = {
+ .name = "format",
+ .attrs = hygon_uncore_df_iod_format_attr,
+};
+
+static const struct attribute_group *hygon_uncore_df_iod_attr_groups[] = {
+ &hygon_uncore_attr_group,
+ &hygon_uncore_df_iod_format_group,
+ NULL,
+};
+
+static int hygon_uncore_cpu_starting(unsigned int cpu)
+{
+ struct hygon_uncore *uncore;
+ int i;
+
+ for (i = 0; i < HYGON_UNCORE_TYPE_MAX; i++) {
+ uncore = &uncores[i];
+ uncore->scan(uncore, cpu);
+ }
+
+ return 0;
+}
+
+static int hygon_uncore_cpu_dead(unsigned int cpu)
+{
+ struct hygon_uncore *uncore;
+ int i;
+
+ for (i = 0; i < HYGON_UNCORE_TYPE_MAX; i++) {
+ uncore = &uncores[i];
+ uncore->free(uncore, cpu);
+ }
+
+ return 0;
+}
+
+static int hygon_uncore_cpu_online(unsigned int cpu)
+{
+ struct hygon_uncore *uncore;
+ int i;
+
+ for (i = 0; i < HYGON_UNCORE_TYPE_MAX; i++) {
+ uncore = &uncores[i];
+ if (uncore->init(uncore, cpu))
+ break;
+ }
+
+ return 0;
+}
+
+static int hygon_uncore_cpu_down_prepare(unsigned int cpu)
+{
+ struct hygon_uncore *uncore;
+ int i;
+
+ for (i = 0; i < HYGON_UNCORE_TYPE_MAX; i++) {
+ uncore = &uncores[i];
+ uncore->move(uncore, cpu);
+ }
+
+ return 0;
+}
+
+static void hygon_uncore_ctx_free(struct hygon_uncore *uncore, unsigned int cpu)
+{
+ struct hygon_uncore_pmu *pmu = &uncore->pmu;
+ struct hygon_uncore_ctx *ctx;
+
+ if (!uncore->init_done || !uncore->pmu_registered)
+ return;
+
+ if (!pmu->ctx)
+ return;
+
+ ctx = *per_cpu_ptr(pmu->ctx, cpu);
+ if (!ctx)
+ return;
+
+ if (cpu == ctx->cpu)
+ cpumask_clear_cpu(cpu, &pmu->active_mask);
+
+ if (!--ctx->refcnt) {
+ hygon_uncore_cancel_hrtimer(ctx);
+ kfree(ctx->events);
+ kfree(ctx);
+ }
+
+ *per_cpu_ptr(pmu->ctx, cpu) = NULL;
+}
+
+static void hygon_uncore_ctx_move(struct hygon_uncore *uncore, unsigned int cpu)
+{
+ struct hygon_uncore_pmu *pmu = &uncore->pmu;
+ struct hygon_uncore_ctx *curr, *next;
+ int i;
+
+ if (!uncore->init_done || !uncore->pmu_registered)
+ return;
+
+ if (!pmu->ctx)
+ return;
+
+ curr = *per_cpu_ptr(pmu->ctx, cpu);
+ if (!curr)
+ return;
+
+ for_each_online_cpu(i) {
+ next = *per_cpu_ptr(pmu->ctx, i);
+ if (!next || cpu == i)
+ continue;
+
+ if (curr == next) {
+ perf_pmu_migrate_context(&pmu->pmu, cpu, i);
+ cpumask_clear_cpu(cpu, &pmu->active_mask);
+ cpumask_set_cpu(i, &pmu->active_mask);
+ next->cpu = i;
+ break;
+ }
+ }
+}
+
+static int hygon_uncore_ctx_init(struct hygon_uncore *uncore, unsigned int cpu)
+{
+ struct hygon_uncore_ctx *curr, *prev;
+ struct hygon_uncore_pmu *pmu = &uncore->pmu;
+ int node, cid, i;
+
+ if (!uncore->init_done || !uncore->pmu_registered)
+ return 0;
+
+ if (!pmu->ctx)
+ return 0;
+
+ cid = hygon_uncore_ctx_cid(uncore, cpu);
+ *per_cpu_ptr(pmu->ctx, cpu) = NULL;
+ curr = NULL;
+
+ for_each_online_cpu(i) {
+ if (cpu == i)
+ continue;
+
+ prev = *per_cpu_ptr(pmu->ctx, i);
+ if (!prev)
+ continue;
+ if (cid == hygon_uncore_ctx_cid(uncore, i)) {
+ curr = prev;
+ break;
+ }
+ }
+
+ if (!curr) {
+ node = cpu_to_node(cpu);
+ curr = kzalloc_node(sizeof(*curr), GFP_KERNEL, node);
+ if (!curr)
+ goto fail;
+
+ curr->cpu = cpu;
+ curr->events = kzalloc_node(sizeof(*curr->events) *
+ pmu->num_counters,
+ GFP_KERNEL, node);
+ if (!curr->events) {
+ kfree(curr);
+ goto fail;
+ }
+
+ cpumask_set_cpu(cpu, &pmu->active_mask);
+
+ hygon_uncore_init_hrtimer(curr);
+ curr->hrtimer_duration = (u64)update_interval * NSEC_PER_MSEC;
+ }
+
+ curr->refcnt++;
+ *per_cpu_ptr(pmu->ctx, cpu) = curr;
+
+ return 0;
+
+fail:
+ hygon_uncore_ctx_free(uncore, cpu);
+
+ return -ENOMEM;
+}
+
+static void hygon_uncore_df_ctx_scan(struct hygon_uncore *uncore, unsigned int cpu)
+{
+ union hygon_uncore_info info = {};
+
+ if (!boot_cpu_has(X86_FEATURE_PERFCTR_NB))
+ return;
+
+ info.split.num_iods = 0;
+ info.split.num_pmcs = NUM_COUNTERS_DF;
+ info.split.cid = topology_amd_node_id(cpu);
+
+ *per_cpu_ptr(uncore->info, cpu) = info;
+}
+
+static
+int hygon_uncore_df_ctx_init(struct hygon_uncore *uncore, unsigned int cpu)
+{
+ struct hygon_uncore_pmu *pmu = &uncore->pmu;
+ int num_counters;
+
+ if (uncore->init_done)
+ return hygon_uncore_ctx_init(uncore, cpu);
+
+ num_counters = hygon_uncore_ctx_num_pmcs(uncore, cpu);
+ if (!num_counters)
+ goto done;
+
+ strscpy(pmu->name, "hygon_df", sizeof(pmu->name));
+ pmu->num_counters = num_counters;
+ pmu->msr_base = MSR_HYGON_F18H_DF_CTL;
+ pmu->type = HYGON_UNCORE_TYPE_DF;
+
+ hygon_uncore_df_format_attr[1] = hygon_uncore_df_umask_attr();
+
+ pmu->ctx = alloc_percpu(struct hygon_uncore_ctx *);
+ if (!pmu->ctx)
+ goto done;
+
+ pmu->pmu = (struct pmu) {
+ .task_ctx_nr = perf_invalid_context,
+ .attr_groups = hygon_uncore_df_attr_groups,
+ .name = pmu->name,
+ .event_init = hygon_uncore_df_event_init,
+ .add = hygon_uncore_add,
+ .del = hygon_uncore_del,
+ .start = hygon_uncore_start,
+ .stop = hygon_uncore_stop,
+ .read = hygon_uncore_read,
+ .capabilities = PERF_PMU_CAP_NO_EXCLUDE | PERF_PMU_CAP_NO_INTERRUPT,
+ .module = THIS_MODULE,
+ };
+
+ if (perf_pmu_register(&pmu->pmu, pmu->pmu.name, -1)) {
+ free_percpu(pmu->ctx);
+ pmu->ctx = NULL;
+ goto done;
+ }
+ uncore->pmu_registered = true;
+
+done:
+ uncore->init_done = true;
+ return hygon_uncore_ctx_init(uncore, cpu);
+}
+
+static
+void hygon_uncore_df_iod_ctx_scan(struct hygon_uncore *uncore, unsigned int cpu)
+{
+ union hygon_uncore_info info = {};
+ int num_packages, num_cdds, iods_per_package;
+
+ if (!boot_cpu_has(X86_FEATURE_PERFCTR_NB))
+ return;
+
+ if (boot_cpu_data.x86_model < 0x4 || boot_cpu_data.x86_model == 0x6)
+ return;
+
+ num_packages = topology_max_packages();
+ num_cdds = topology_max_dies_per_package() * num_packages;
+ iods_per_package = (amd_nb_num() - num_cdds) / num_packages;
+
+ /* Hardware does not support more than 2 IODs per package. */
+ if (iods_per_package <= 0 || iods_per_package > 2)
+ return;
+
+ info.split.cid = topology_physical_package_id(cpu);
+ info.split.num_iods = iods_per_package;
+ info.split.num_pmcs = NUM_COUNTERS_DF * iods_per_package;
+
+ *per_cpu_ptr(uncore->info, cpu) = info;
+}
+
+static
+int hygon_uncore_df_iod_ctx_init(struct hygon_uncore *uncore, unsigned int cpu)
+{
+ struct hygon_uncore_pmu *pmu = &uncore->pmu;
+ int num_counters;
+
+ if (uncore->init_done)
+ return hygon_uncore_ctx_init(uncore, cpu);
+
+ num_counters = hygon_uncore_ctx_num_pmcs(uncore, cpu);
+ if (!num_counters)
+ goto done;
+
+ strscpy(pmu->name, "hygon_df_iod", sizeof(pmu->name));
+ pmu->num_counters = num_counters;
+ pmu->msr_base = MSR_HYGON_F18H_DF_IOD_CTL;
+ pmu->type = HYGON_UNCORE_TYPE_DF_IOD;
+
+ hygon_uncore_df_iod_format_attr[1] = hygon_uncore_df_umask_attr();
+
+ pmu->ctx = alloc_percpu(struct hygon_uncore_ctx *);
+ if (!pmu->ctx)
+ goto done;
+
+ pmu->pmu = (struct pmu) {
+ .task_ctx_nr = perf_invalid_context,
+ .attr_groups = hygon_uncore_df_iod_attr_groups,
+ .name = pmu->name,
+ .event_init = hygon_uncore_df_event_init,
+ .add = hygon_uncore_add,
+ .del = hygon_uncore_del,
+ .start = hygon_uncore_start,
+ .stop = hygon_uncore_stop,
+ .read = hygon_uncore_read,
+ .capabilities = PERF_PMU_CAP_NO_EXCLUDE | PERF_PMU_CAP_NO_INTERRUPT,
+ .module = THIS_MODULE,
+ };
+
+ if (perf_pmu_register(&pmu->pmu, pmu->pmu.name, -1)) {
+ free_percpu(pmu->ctx);
+ pmu->ctx = NULL;
+ goto done;
+ }
+ uncore->pmu_registered = true;
+done:
+ uncore->init_done = true;
+ return hygon_uncore_ctx_init(uncore, cpu);
+}
+
+static struct hygon_uncore uncores[HYGON_UNCORE_TYPE_MAX] = {
+ /* HYGON DF */
+ {
+ .scan = hygon_uncore_df_ctx_scan,
+ .init = hygon_uncore_df_ctx_init,
+ .move = hygon_uncore_ctx_move,
+ .free = hygon_uncore_ctx_free,
+ },
+ /* HYGON DF IOD */
+ {
+ .scan = hygon_uncore_df_iod_ctx_scan,
+ .init = hygon_uncore_df_iod_ctx_init,
+ .move = hygon_uncore_ctx_move,
+ .free = hygon_uncore_ctx_free,
+ }
+};
+
+static void hygon_uncore_pmu_cleanup(void)
+{
+ struct hygon_uncore_ctx *ctx;
+ struct hygon_uncore *uncore;
+ int i, cpu;
+
+ for (i = 0; i < HYGON_UNCORE_TYPE_MAX; i++) {
+ uncore = &uncores[i];
+
+ /* Unregister PMU before freeing its contexts. */
+ if (uncore->pmu_registered) {
+ perf_pmu_unregister(&uncore->pmu.pmu);
+ uncore->pmu_registered = false;
+ }
+
+ if (!uncore->pmu.ctx)
+ continue;
+
+ /* Shared ctx objects are released through their refcnt. */
+ for_each_possible_cpu(cpu) {
+ ctx = *per_cpu_ptr(uncore->pmu.ctx, cpu);
+ if (!ctx)
+ continue;
+
+ if (!--ctx->refcnt) {
+ hygon_uncore_cancel_hrtimer(ctx);
+ kfree(ctx->events);
+ kfree(ctx);
+ }
+ *per_cpu_ptr(uncore->pmu.ctx, cpu) = NULL;
+ }
+
+ free_percpu(uncore->pmu.ctx);
+ uncore->pmu.ctx = NULL;
+ }
+}
+
+static void hygon_uncore_info_cleanup(void)
+{
+ struct hygon_uncore *uncore;
+ int i;
+
+ for (i = 0; i < HYGON_UNCORE_TYPE_MAX; i++) {
+ uncore = &uncores[i];
+ if (uncore->info) {
+ free_percpu(uncore->info);
+ uncore->info = NULL;
+ }
+ }
+}
+
+static int __init hygon_uncore_init(void)
+{
+ struct hygon_uncore *uncore;
+ int ret = -ENODEV;
+ int i;
+
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
+ return -ENODEV;
+
+ if (!boot_cpu_has(X86_FEATURE_TOPOEXT))
+ return -ENODEV;
+
+ for (i = 0; i < HYGON_UNCORE_TYPE_MAX; i++) {
+ uncore = &uncores[i];
+
+ uncore->info = alloc_percpu(union hygon_uncore_info);
+ if (!uncore->info) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ ret = cpuhp_setup_state(CPUHP_PERF_X86_HYGON_UNCORE_PREP,
+ "perf/x86/hygon/uncore:prepare",
+ NULL, hygon_uncore_cpu_dead);
+ if (ret)
+ goto fail;
+
+ ret = cpuhp_setup_state(CPUHP_AP_PERF_X86_HYGON_UNCORE_STARTING,
+ "perf/x86/hygon/uncore:starting",
+ hygon_uncore_cpu_starting, NULL);
+ if (ret)
+ goto fail_prep;
+
+ ret = cpuhp_setup_state(CPUHP_AP_PERF_X86_HYGON_UNCORE_ONLINE,
+ "perf/x86/hygon/uncore:online",
+ hygon_uncore_cpu_online,
+ hygon_uncore_cpu_down_prepare);
+ if (ret)
+ goto fail_start;
+
+ return 0;
+
+fail_start:
+ cpuhp_remove_state(CPUHP_AP_PERF_X86_HYGON_UNCORE_STARTING);
+fail_prep:
+ cpuhp_remove_state(CPUHP_PERF_X86_HYGON_UNCORE_PREP);
+fail:
+ hygon_uncore_info_cleanup();
+ return ret;
+}
+
+static void __exit hygon_uncore_exit(void)
+{
+ cpuhp_remove_state(CPUHP_AP_PERF_X86_HYGON_UNCORE_ONLINE);
+ cpuhp_remove_state(CPUHP_AP_PERF_X86_HYGON_UNCORE_STARTING);
+ cpuhp_remove_state(CPUHP_PERF_X86_HYGON_UNCORE_PREP);
+
+ hygon_uncore_pmu_cleanup();
+ hygon_uncore_info_cleanup();
+}
+module_init(hygon_uncore_init);
+module_exit(hygon_uncore_exit);
+
+MODULE_DESCRIPTION("Hygon Uncore Driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/x86/events/amd/uncore.c b/arch/x86/events/amd/uncore.c
index dbc00b6dd69e..3f8e40abba73 100644
--- a/arch/x86/events/amd/uncore.c
+++ b/arch/x86/events/amd/uncore.c
@@ -620,7 +620,7 @@ static int amd_uncore_cpu_online(unsigned int cpu)
for (i = 0; i < UNCORE_TYPE_MAX; i++) {
uncore = &uncores[i];
if (uncore->init(uncore, cpu))
- break;
+ continue;
}
return 0;
@@ -775,9 +775,7 @@ int amd_uncore_df_ctx_init(struct amd_uncore *uncore, unsigned int cpu)
goto done;
}
- pr_info("%d %s%s counters detected\n", pmu->num_counters,
- boot_cpu_data.x86_vendor == X86_VENDOR_HYGON ? "HYGON " : "",
- pmu->pmu.name);
+ pr_info("%d %s counters detected\n", pmu->num_counters, pmu->pmu.name);
uncore->num_pmus = 1;
@@ -911,9 +909,7 @@ int amd_uncore_l3_ctx_init(struct amd_uncore *uncore, unsigned int cpu)
goto done;
}
- pr_info("%d %s%s counters detected\n", pmu->num_counters,
- boot_cpu_data.x86_vendor == X86_VENDOR_HYGON ? "HYGON " : "",
- pmu->pmu.name);
+ pr_info("%d %s counters detected\n", pmu->num_counters, pmu->pmu.name);
uncore->num_pmus = 1;
@@ -1120,8 +1116,7 @@ static int __init amd_uncore_init(void)
int ret = -ENODEV;
int i;
- if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
- boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
return -ENODEV;
if (!boot_cpu_has(X86_FEATURE_TOPOEXT))
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 18c4be75e927..d20338f0b2b2 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -851,6 +851,8 @@
#define MSR_F15H_PTSC 0xc0010280
#define MSR_F15H_IC_CFG 0xc0011021
#define MSR_F15H_EX_CFG 0xc001102c
+#define MSR_HYGON_F18H_DF_CTL 0xc0010240
+#define MSR_HYGON_F18H_DF_IOD_CTL 0xc0010250
/* Fam 10h MSRs */
#define MSR_FAM10H_MMIO_CONF_BASE 0xc0010058
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index 1eb13673e889..7c8c00ad56e9 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -124,6 +124,26 @@
(AMD64_PERFMON_V2_EVENTSEL_EVENT_NB | \
AMD64_PERFMON_V2_EVENTSEL_UMASK_NB)
+#define HYGON_F18H_EVENTSEL_EVENT_DF 0x0000003FULL
+#define HYGON_F18H_EVENTSEL_UMASK_DF 0x0000FF00ULL
+#define HYGON_F18H_M4H_EVENTSEL_UMASK_DF 0x0003FF00ULL
+#define HYGON_F18H_M6H_EVENTSEL_UMASK_DF 0x000FFF00ULL
+#define HYGON_F18H_EVENTSEL_CONSTID_DF \
+ (0x000000C0ULL | GENMASK_ULL(35, 32) | GENMASK_ULL(62, 61))
+
+#define HYGON_F18H_RAW_EVENT_MASK_DF \
+ (HYGON_F18H_EVENTSEL_EVENT_DF | \
+ HYGON_F18H_EVENTSEL_UMASK_DF | \
+ HYGON_F18H_EVENTSEL_CONSTID_DF)
+#define HYGON_F18H_M4H_RAW_EVENT_MASK_DF \
+ (HYGON_F18H_EVENTSEL_EVENT_DF | \
+ HYGON_F18H_M4H_EVENTSEL_UMASK_DF | \
+ HYGON_F18H_EVENTSEL_CONSTID_DF)
+#define HYGON_F18H_M6H_RAW_EVENT_MASK_DF \
+ (HYGON_F18H_EVENTSEL_EVENT_DF | \
+ HYGON_F18H_M6H_EVENTSEL_UMASK_DF | \
+ HYGON_F18H_EVENTSEL_CONSTID_DF)
+
#define AMD64_PERFMON_V2_ENABLE_UMC BIT_ULL(31)
#define AMD64_PERFMON_V2_EVENTSEL_EVENT_UMC GENMASK_ULL(7, 0)
#define AMD64_PERFMON_V2_EVENTSEL_RDWRMASK_UMC GENMASK_ULL(9, 8)
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 0fb3a2a62eb0..bccdb87a0c05 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -62,6 +62,7 @@ enum cpuhp_state {
CPUHP_CREATE_THREADS,
CPUHP_PERF_X86_PREPARE,
CPUHP_PERF_X86_AMD_UNCORE_PREP,
+ CPUHP_PERF_X86_HYGON_UNCORE_PREP,
CPUHP_PERF_POWER,
CPUHP_PERF_SUPERH,
CPUHP_X86_HPET_DEAD,
@@ -148,6 +149,7 @@ enum cpuhp_state {
CPUHP_AP_IRQ_RISCV_SBI_IPI_STARTING,
CPUHP_AP_ARM_MVEBU_COHERENCY,
CPUHP_AP_PERF_X86_AMD_UNCORE_STARTING,
+ CPUHP_AP_PERF_X86_HYGON_UNCORE_STARTING,
CPUHP_AP_PERF_X86_STARTING,
CPUHP_AP_PERF_X86_AMD_IBS_STARTING,
CPUHP_AP_PERF_XTENSA_STARTING,
@@ -205,6 +207,7 @@ enum cpuhp_state {
CPUHP_AP_PERF_X86_ONLINE,
CPUHP_AP_PERF_X86_UNCORE_ONLINE,
CPUHP_AP_PERF_X86_AMD_UNCORE_ONLINE,
+ CPUHP_AP_PERF_X86_HYGON_UNCORE_ONLINE,
CPUHP_AP_PERF_X86_AMD_POWER_ONLINE,
CPUHP_AP_PERF_S390_CF_ONLINE,
CPUHP_AP_PERF_S390_SF_ONLINE,
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 2/2] perf vendor events hygon: Add Hygon Data Fabric PMU events and metrics
2026-07-08 13:13 [PATCH v3 0/2] perf/x86/amd/uncore: Add Hygon uncore PMU and JSON events Qi Liu
2026-07-08 13:13 ` [PATCH v3 1/2] perf/x86/amd/uncore: Add Hygon uncore PMU support Qi Liu
@ 2026-07-08 13:13 ` Qi Liu
1 sibling, 0 replies; 5+ messages in thread
From: Qi Liu @ 2026-07-08 13:13 UTC (permalink / raw)
To: peterz, mingo, namhyung, alexander.shishkin, jolsa, sandipan.das,
ravi.bangoria, x86
Cc: linux-perf-users, linux-kernel, lijing, liuqi
Add perf JSON event definitions and metrics for the Hygon Data Fabric (DF)
uncore PMU.
Signed-off-by: Qi Liu <liuqi@hygon.cn>
---
tools/perf/pmu-events/Build | 2 +-
.../arch/x86/hygonm1/data-fabric.json | 87 ++++++++++
.../arch/x86/hygonm4/data-fabric.json | 122 ++++++++++++++
.../arch/x86/hygonm6/data-fabric.json | 154 ++++++++++++++++++
tools/perf/pmu-events/arch/x86/mapfile.csv | 3 +
tools/perf/pmu-events/jevents.py | 2 +
6 files changed, 369 insertions(+), 1 deletion(-)
create mode 100644 tools/perf/pmu-events/arch/x86/hygonm1/data-fabric.json
create mode 100644 tools/perf/pmu-events/arch/x86/hygonm4/data-fabric.json
create mode 100644 tools/perf/pmu-events/arch/x86/hygonm6/data-fabric.json
diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build
index 372773b998e4..79a9b4cad202 100644
--- a/tools/perf/pmu-events/Build
+++ b/tools/perf/pmu-events/Build
@@ -95,7 +95,7 @@ endif
ifeq ($(JEVENTS_ARCH),$(filter $(JEVENTS_ARCH),x86 all))
# Generate Intel Json
-INTELS := $(shell ls -d pmu-events/arch/x86/*|grep -v amdzen|grep -v mapfile.csv)
+INTELS := $(shell ls -d pmu-events/arch/x86/*|grep -v amdzen|grep -v hygon|grep -v mapfile.csv)
INTEL_METRICS = $(foreach x,$(INTELS),$(OUTPUT)$(x)/extra-metrics.json)
INTEL_METRICGROUPS = $(foreach x,$(INTELS),$(OUTPUT)$(x)/extra-metricgroups.json)
GEN_JSON += $(INTEL_METRICS) $(INTEL_METRICGROUPS)
diff --git a/tools/perf/pmu-events/arch/x86/hygonm1/data-fabric.json b/tools/perf/pmu-events/arch/x86/hygonm1/data-fabric.json
new file mode 100644
index 000000000000..99c243bd9ba0
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/hygonm1/data-fabric.json
@@ -0,0 +1,87 @@
+[
+ {
+ "EventName": "cpu_read_requests_cs_0",
+ "BriefDescription": "The number of CPU read requests to memory: Data (64B): CS0",
+ "EventCode": "0x2",
+ "UMask": "0x07",
+ "ConstId": "0x0",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_read_requests_cs_1",
+ "BriefDescription": "The number of CPU read requests to memory: Data (64B): CS1",
+ "EventCode": "0x2",
+ "UMask": "0x07",
+ "ConstId": "0x1",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_write_requests_cs_0",
+ "BriefDescription": "The number of CPU write requests to memory: Data (64B): CS0",
+ "EventCode": "0x2",
+ "UMask": "0x08",
+ "ConstId": "0x0",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_write_requests_cs_1",
+ "BriefDescription": "The number of CPU write requests to memory: Data (64B): CS1",
+ "EventCode": "0x2",
+ "UMask": "0x08",
+ "ConstId": "0x1",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_read_requests_cs_0",
+ "BriefDescription": "The number of inbound I/O read requests to memory: Data (32B or 64B): CS0",
+ "EventCode": "0x3",
+ "UMask": "0x0F",
+ "ConstId": "0x0",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_read_requests_cs_1",
+ "BriefDescription": "The number of inbound I/O read requests to memory: Data (32B or 64B): CS1",
+ "EventCode": "0x3",
+ "UMask": "0x0F",
+ "ConstId": "0x1",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_write_requests_cs_0",
+ "BriefDescription": "The number of inbound I/O write requests to memory: Data (32B or 64B): CS0",
+ "EventCode": "0x3",
+ "UMask": "0x70",
+ "ConstId": "0x0",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_write_requests_cs_1",
+ "BriefDescription": "The number of inbound I/O write requests to memory: Data (32B or 64B): CS1",
+ "EventCode": "0x3",
+ "UMask": "0x70",
+ "ConstId": "0x1",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "MetricName": "cpu_memory_bandwidth",
+ "MetricExpr": "(cpu_read_requests_cs_0 + cpu_read_requests_cs_1 + cpu_write_requests_cs_0 + cpu_write_requests_cs_1) * 64 / (duration_time * 1e6)",
+ "BriefDescription": "Total CPU memory access bandwidth",
+ "MetricGroup": "DF_Memory_BW",
+ "ScaleUnit": "1MB/s"
+ },
+ {
+ "MetricName": "io_memory_max_bandwidth",
+ "MetricExpr": "(io_read_requests_cs_0 + io_read_requests_cs_1 + io_write_requests_cs_0 + io_write_requests_cs_1) * 64 / (duration_time * 1e6)",
+ "BriefDescription": "Maximum inbound I/O memory access bandwidth (upper bound, assuming 64B per request. Actual inbound I/O requests may be 32B or 64B)",
+ "MetricGroup": "DF_Memory_BW",
+ "ScaleUnit": "1MB/s"
+ },
+ {
+ "MetricName": "io_memory_min_bandwidth",
+ "MetricExpr": "(io_read_requests_cs_0 + io_read_requests_cs_1 + io_write_requests_cs_0 + io_write_requests_cs_1) * 32 / (duration_time * 1e6)",
+ "BriefDescription": "Minimum inbound I/O memory access bandwidth (lower bound, assuming 32B per request. Actual inbound I/O requests may be 32B or 64B)",
+ "MetricGroup": "DF_Memory_BW",
+ "ScaleUnit": "1MB/s"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/hygonm4/data-fabric.json b/tools/perf/pmu-events/arch/x86/hygonm4/data-fabric.json
new file mode 100644
index 000000000000..f32dcea7061c
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/hygonm4/data-fabric.json
@@ -0,0 +1,122 @@
+[
+ {
+ "EventName": "cpu_read_requests_cs_0",
+ "BriefDescription": "The number of CPU read requests to memory: Data (64B): CS0",
+ "EventCode": "0x2",
+ "UMask": "0x07",
+ "ConstId": "0x0",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_read_requests_cs_1",
+ "BriefDescription": "The number of CPU read requests to memory: Data (64B): CS1",
+ "EventCode": "0x2",
+ "UMask": "0x07",
+ "ConstId": "0x1",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_read_requests_cs_2",
+ "BriefDescription": "The number of CPU read requests to memory: Data (64B): CS2",
+ "EventCode": "0x2",
+ "UMask": "0x07",
+ "ConstId": "0x2",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_write_requests_cs_0",
+ "BriefDescription": "The number of CPU write requests to memory: Data (64B): CS0",
+ "EventCode": "0x2",
+ "UMask": "0x08",
+ "ConstId": "0x0",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_write_requests_cs_1",
+ "BriefDescription": "The number of CPU write requests to memory: Data (64B): CS1",
+ "EventCode": "0x2",
+ "UMask": "0x08",
+ "ConstId": "0x1",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_write_requests_cs_2",
+ "BriefDescription": "The number of CPU write requests to memory: Data (64B): CS2",
+ "EventCode": "0x2",
+ "UMask": "0x08",
+ "ConstId": "0x2",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_read_requests_cs_0",
+ "BriefDescription": "The number of inbound I/O read requests to memory: Data (32B or 64B): CS0",
+ "EventCode": "0x3",
+ "UMask": "0x0F",
+ "ConstId": "0x0",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_read_requests_cs_1",
+ "BriefDescription": "The number of inbound I/O read requests to memory: Data (32B or 64B): CS1",
+ "EventCode": "0x3",
+ "UMask": "0x0F",
+ "ConstId": "0x1",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_read_requests_cs_2",
+ "BriefDescription": "The number of inbound I/O read requests to memory: Data (32B or 64B): CS2",
+ "EventCode": "0x3",
+ "UMask": "0x0F",
+ "ConstId": "0x2",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_write_requests_cs_0",
+ "BriefDescription": "The number of inbound I/O write requests to memory: Data (32B or 64B): CS0",
+ "EventCode": "0x3",
+ "UMask": "0x70",
+ "ConstId": "0x0",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_write_requests_cs_1",
+ "BriefDescription": "The number of inbound I/O write requests to memory: Data (32B or 64B): CS1",
+ "EventCode": "0x3",
+ "UMask": "0x70",
+ "ConstId": "0x1",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_write_requests_cs_2",
+ "BriefDescription": "The number of inbound I/O write requests to memory: Data (32B or 64B): CS2",
+ "EventCode": "0x3",
+ "UMask": "0x70",
+ "ConstId": "0x2",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "MetricName": "cpu_memory_bandwidth",
+ "MetricExpr": "(cpu_read_requests_cs_0 + cpu_read_requests_cs_1 + cpu_read_requests_cs_2 + cpu_write_requests_cs_0 + cpu_write_requests_cs_1 + cpu_write_requests_cs_2) * 64 / (duration_time * 1e6)",
+ "MetricConstraint": "NO_GROUP_EVENTS",
+ "BriefDescription": "Total CPU memory access bandwidth",
+ "MetricGroup": "DF_Memory_BW",
+ "ScaleUnit": "1MB/s"
+ },
+ {
+ "MetricName": "io_memory_max_bandwidth",
+ "MetricExpr": "(io_read_requests_cs_0 + io_read_requests_cs_1 + io_read_requests_cs_2 + io_write_requests_cs_0 + io_write_requests_cs_1 + io_write_requests_cs_2) * 64 / (duration_time * 1e6)",
+ "MetricConstraint": "NO_GROUP_EVENTS",
+ "BriefDescription": "Maximum inbound I/O memory access bandwidth (upper bound, assuming 64B per request. Actual inbound I/O requests may be 32B or 64B)",
+ "MetricGroup": "DF_Memory_BW",
+ "ScaleUnit": "1MB/s"
+ },
+ {
+ "MetricName": "io_memory_min_bandwidth",
+ "MetricExpr": "(io_read_requests_cs_0 + io_read_requests_cs_1 + io_read_requests_cs_2 + io_write_requests_cs_0 + io_write_requests_cs_1 + io_write_requests_cs_2) * 32 / (duration_time * 1e6)",
+ "MetricConstraint": "NO_GROUP_EVENTS",
+ "BriefDescription": "Minimum inbound I/O memory access bandwidth (lower bound, assuming 32B per request. Actual inbound I/O requests may be 32B or 64B)",
+ "MetricGroup": "DF_Memory_BW",
+ "ScaleUnit": "1MB/s"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/hygonm6/data-fabric.json b/tools/perf/pmu-events/arch/x86/hygonm6/data-fabric.json
new file mode 100644
index 000000000000..bff5c0959d82
--- /dev/null
+++ b/tools/perf/pmu-events/arch/x86/hygonm6/data-fabric.json
@@ -0,0 +1,154 @@
+[
+ {
+ "EventName": "cpu_read_requests_cs_0",
+ "BriefDescription": "The number of CPU read requests to memory: Data (64B): CS0",
+ "EventCode": "0x2",
+ "UMask": "0x07",
+ "ConstId": "0x0",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_read_requests_cs_1",
+ "BriefDescription": "The number of CPU read requests to memory: Data (64B): CS1",
+ "EventCode": "0x2",
+ "UMask": "0x07",
+ "ConstId": "0x1",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_read_requests_cs_2",
+ "BriefDescription": "The number of CPU read requests to memory: Data (64B): CS2",
+ "EventCode": "0x2",
+ "UMask": "0x07",
+ "ConstId": "0x2",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_read_requests_cs_3",
+ "BriefDescription": "The number of CPU read requests to memory: Data (64B): CS3",
+ "EventCode": "0x2",
+ "UMask": "0x07",
+ "ConstId": "0x3",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_write_requests_cs_0",
+ "BriefDescription": "The number of CPU write requests to memory: Data (64B): CS0",
+ "EventCode": "0x2",
+ "UMask": "0x08",
+ "ConstId": "0x0",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_write_requests_cs_1",
+ "BriefDescription": "The number of CPU write requests to memory: Data (64B): CS1",
+ "EventCode": "0x2",
+ "UMask": "0x08",
+ "ConstId": "0x1",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_write_requests_cs_2",
+ "BriefDescription": "The number of CPU write requests to memory: Data (64B): CS2",
+ "EventCode": "0x2",
+ "UMask": "0x08",
+ "ConstId": "0x2",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "cpu_write_requests_cs_3",
+ "BriefDescription": "The number of CPU write requests to memory: Data (64B): CS3",
+ "EventCode": "0x2",
+ "UMask": "0x08",
+ "ConstId": "0x3",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_read_requests_cs_0",
+ "BriefDescription": "The number of inbound I/O read requests to memory: Data (32B or 64B): CS0",
+ "EventCode": "0x3",
+ "UMask": "0x0F",
+ "ConstId": "0x0",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_read_requests_cs_1",
+ "BriefDescription": "The number of inbound I/O read requests to memory: Data (32B or 64B): CS1",
+ "EventCode": "0x3",
+ "UMask": "0x0F",
+ "ConstId": "0x1",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_read_requests_cs_2",
+ "BriefDescription": "The number of inbound I/O read requests to memory: Data (32B or 64B): CS2",
+ "EventCode": "0x3",
+ "UMask": "0x0F",
+ "ConstId": "0x2",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_read_requests_cs_3",
+ "BriefDescription": "The number of inbound I/O read requests to memory: Data (32B or 64B): CS3",
+ "EventCode": "0x3",
+ "UMask": "0x0F",
+ "ConstId": "0x3",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_write_requests_cs_0",
+ "BriefDescription": "The number of inbound I/O write requests to memory: Data (32B or 64B): CS0",
+ "EventCode": "0x3",
+ "UMask": "0x70",
+ "ConstId": "0x0",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_write_requests_cs_1",
+ "BriefDescription": "The number of inbound I/O write requests to memory: Data (32B or 64B): CS1",
+ "EventCode": "0x3",
+ "UMask": "0x70",
+ "ConstId": "0x1",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_write_requests_cs_2",
+ "BriefDescription": "The number of inbound I/O write requests to memory: Data (32B or 64B): CS2",
+ "EventCode": "0x3",
+ "UMask": "0x70",
+ "ConstId": "0x2",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "EventName": "io_write_requests_cs_3",
+ "BriefDescription": "The number of inbound I/O write requests to memory: Data (32B or 64B): CS3",
+ "EventCode": "0x3",
+ "UMask": "0x70",
+ "ConstId": "0x3",
+ "Unit": "HYGON_DF"
+ },
+ {
+ "MetricName": "cpu_memory_bandwidth",
+ "MetricExpr": "(cpu_read_requests_cs_0 + cpu_read_requests_cs_1 + cpu_read_requests_cs_2 + cpu_read_requests_cs_3 + cpu_write_requests_cs_0 + cpu_write_requests_cs_1 + cpu_write_requests_cs_2 + cpu_write_requests_cs_3) * 64 / (duration_time * 1e6)",
+ "MetricConstraint": "NO_GROUP_EVENTS",
+ "BriefDescription": "Total CPU memory access bandwidth",
+ "MetricGroup": "DF_Memory_BW",
+ "ScaleUnit": "1MB/s"
+ },
+ {
+ "MetricName": "io_memory_max_bandwidth",
+ "MetricExpr": "(io_read_requests_cs_0 + io_read_requests_cs_1 + io_read_requests_cs_2 + io_read_requests_cs_3 + io_write_requests_cs_0 + io_write_requests_cs_1 + io_write_requests_cs_2 + io_write_requests_cs_3) * 64 / (duration_time * 1e6)",
+ "MetricConstraint": "NO_GROUP_EVENTS",
+ "BriefDescription": "Maximum inbound I/O memory access bandwidth (upper bound, assuming 64B per request. Actual inbound I/O requests may be 32B or 64B)",
+ "MetricGroup": "DF_Memory_BW",
+ "ScaleUnit": "1MB/s"
+ },
+ {
+ "MetricName": "io_memory_min_bandwidth",
+ "MetricExpr": "(io_read_requests_cs_0 + io_read_requests_cs_1 + io_read_requests_cs_2 + io_read_requests_cs_3 + io_write_requests_cs_0 + io_write_requests_cs_1 + io_write_requests_cs_2 + io_write_requests_cs_3) * 32 / (duration_time * 1e6)",
+ "MetricConstraint": "NO_GROUP_EVENTS",
+ "BriefDescription": "Minimum inbound I/O memory access bandwidth (lower bound, assuming 32B per request. Actual inbound I/O requests may be 32B or 64B)",
+ "MetricGroup": "DF_Memory_BW",
+ "ScaleUnit": "1MB/s"
+ }
+]
diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv
index 50a403b429b1..ba17117245d1 100644
--- a/tools/perf/pmu-events/arch/x86/mapfile.csv
+++ b/tools/perf/pmu-events/arch/x86/mapfile.csv
@@ -45,3 +45,6 @@ AuthenticAMD-25-([245][[:xdigit:]]|[[:xdigit:]]),v1,amdzen3,core
AuthenticAMD-25-[[:xdigit:]]+,v1,amdzen4,core
AuthenticAMD-26-([12467][[:xdigit:]]|[[:xdigit:]]),v1,amdzen5,core
AuthenticAMD-26-[[:xdigit:]]+,v1,amdzen6,core
+HygonGenuine-24-[0123],v1,hygonm1,core
+HygonGenuine-24-[45],v1,hygonm4,core
+HygonGenuine-24-(1[012345678]|[6789ABCDEF]),v1,hygonm6,core
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 376dc2d24162..63ae3a763a4f 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -294,6 +294,7 @@ class JsonEvent:
'L3PMC': 'amd_l3',
'DFPMC': 'amd_df',
'UMCPMC': 'amd_umc',
+ 'HYGON_DF': 'hygon_df',
'cpu_core': 'cpu_core',
'cpu_atom': 'cpu_atom',
'ali_drw': 'ali_drw',
@@ -390,6 +391,7 @@ class JsonEvent:
('Invert', 'inv='),
('SampleAfterValue', 'period='),
('UMask', 'umask='),
+ ('ConstId', 'constid='),
('NodeType', 'type='),
('RdWrMask', 'rdwrmask='),
('EnAllCores', 'enallcores='),
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v3 1/2] perf/x86/amd/uncore: Add Hygon uncore PMU support
2026-07-08 13:13 ` [PATCH v3 1/2] perf/x86/amd/uncore: Add Hygon uncore PMU support Qi Liu
@ 2026-07-08 13:27 ` sashiko-bot
2026-07-09 2:09 ` Qi Liu
0 siblings, 1 reply; 5+ messages in thread
From: sashiko-bot @ 2026-07-08 13:27 UTC (permalink / raw)
To: Qi Liu; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Module initialization error path fails to unregister PMUs and leaks per-CPU arrays, leading to a Use-After-Free.
- [Medium] The CPU online callback aborts PMU initialization on the first failure, leaving remaining independent PMUs uninitialized.
- [Medium] The patch removes L3 cache uncore PMU support for Hygon processors without providing a replacement.
- [Medium] The `update_interval` module parameter lacks bounds checking, allowing a value of 0 to cause a CPU-locking interrupt storm.
--
--- Patch [1]: [PATCH v3 1/2] perf/x86/amd/uncore: Add Hygon uncore PMU support ---
commit 451a8d6c91c9ade8b2dbc942f864d5c36dc872c9
Author: Qi Liu <liuqi@hygon.cn>
Subject: perf/x86/amd/uncore: Add Hygon uncore PMU support
This commit adds a new uncore PMU driver specifically for Hygon Family 18h
processors, supporting Data Fabric and IO Die counters. It also removes the
Hygon-specific checks from the AMD uncore driver to keep the vendor
implementations separate.
> diff --git a/arch/x86/events/amd/hygon_uncore.c b/arch/x86/events/amd/hygon_uncore.c
> new file mode 100644
> --- /dev/null
> +++ b/arch/x86/events/amd/hygon_uncore.c
> @@ -0,0 +1,863 @@
[ ... ]
> +/* Interval for hrtimer, defaults to 60000 milliseconds */
> +static unsigned int update_interval = 60 * MSEC_PER_SEC;
> +module_param(update_interval, uint, 0444);
[Severity: Medium]
Could setting this to 0 cause an interrupt storm? If a privileged user loads
the module with update_interval=0, hrtimer_duration becomes 0. When this is
passed to hrtimer_forward_now(), it is clamped to the minimum resolution,
causing the high-resolution timer to fire continuously in a loop. Should
there be a minimum bounds check here to prevent a CPU lockup?
[ ... ]
> +static int hygon_uncore_cpu_online(unsigned int cpu)
> +{
> + struct hygon_uncore *uncore;
> + int i;
> +
> + for (i = 0; i < HYGON_UNCORE_TYPE_MAX; i++) {
> + uncore = &uncores[i];
> + if (uncore->init(uncore, cpu))
> + break;
[Severity: Medium]
Should this be a continue instead of a break? If the first PMU type (e.g.,
hygon_df) fails to initialize, this break aborts the loop, which prevents
any subsequent independent PMUs (like hygon_df_iod) from initializing on
this CPU. The same patch corrects this exact logic flaw in
amd_uncore_cpu_online(), but it seems to have been reintroduced here.
> + }
> +
> + return 0;
> +}
[ ... ]
> +fail_start:
> + cpuhp_remove_state(CPUHP_AP_PERF_X86_HYGON_UNCORE_STARTING);
> +fail_prep:
> + cpuhp_remove_state(CPUHP_PERF_X86_HYGON_UNCORE_PREP);
> +fail:
> + hygon_uncore_info_cleanup();
> + return ret;
> +}
[Severity: High]
Does this error path leak the registered PMUs? If cpuhp_setup_state()
fails for the ONLINE state, we remove the previous hotplug states but it
looks like hygon_uncore_pmu_cleanup() is never called.
Any PMUs successfully registered during the initial CPU online callbacks
would remain registered, and their per-CPU arrays would not be freed. When
the module unloads, could this lead to a use-after-free if the perf
subsystem tries to access the freed module memory?
[ ... ]
> diff --git a/arch/x86/events/amd/uncore.c b/arch/x86/events/amd/uncore.c
> index dbc00b6dd69ee..3f8e40abba736 100644
> --- a/arch/x86/events/amd/uncore.c
> +++ b/arch/x86/events/amd/uncore.c
[ ... ]
> @@ -1120,8 +1116,7 @@ static int __init amd_uncore_init(void)
> int ret = -ENODEV;
> int i;
>
> - if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
> - boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
> + if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
> return -ENODEV;
[Severity: Medium]
Does this intentionally drop L3 cache uncore support for Hygon? By
removing Hygon support here in amd_uncore.c, the previous L3 cache
counters are no longer available. Looking at the new hygon_uncore.c, it
only implements the Data Fabric and IO Die PMUs, meaning L3 PMU support
appears to be lost completely.
> if (!boot_cpu_has(X86_FEATURE_TOPOEXT))
> return -ENODEV;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260708131315.13766-1-liuqi@hygon.cn?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v3 1/2] perf/x86/amd/uncore: Add Hygon uncore PMU support
2026-07-08 13:27 ` sashiko-bot
@ 2026-07-09 2:09 ` Qi Liu
0 siblings, 0 replies; 5+ messages in thread
From: Qi Liu @ 2026-07-09 2:09 UTC (permalink / raw)
To: sashiko-reviews; +Cc: linux-perf-users
Hi,
On 7/8/2026 9:27 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
> - [High] Module initialization error path fails to unregister PMUs and leaks per-CPU arrays, leading to a Use-After-Free.
> - [Medium] The CPU online callback aborts PMU initialization on the first failure, leaving remaining independent PMUs uninitialized.
> - [Medium] The patch removes L3 cache uncore PMU support for Hygon processors without providing a replacement.
> - [Medium] The `update_interval` module parameter lacks bounds checking, allowing a value of 0 to cause a CPU-locking interrupt storm.
> --
>
> --- Patch [1]: [PATCH v3 1/2] perf/x86/amd/uncore: Add Hygon uncore PMU support ---
> commit 451a8d6c91c9ade8b2dbc942f864d5c36dc872c9
> Author: Qi Liu <liuqi@hygon.cn>
> Subject: perf/x86/amd/uncore: Add Hygon uncore PMU support
>
> This commit adds a new uncore PMU driver specifically for Hygon Family 18h
> processors, supporting Data Fabric and IO Die counters. It also removes the
> Hygon-specific checks from the AMD uncore driver to keep the vendor
> implementations separate.
>
>> diff --git a/arch/x86/events/amd/hygon_uncore.c b/arch/x86/events/amd/hygon_uncore.c
>> new file mode 100644
>> --- /dev/null
>> +++ b/arch/x86/events/amd/hygon_uncore.c
>> @@ -0,0 +1,863 @@
>
> [ ... ]
>
>> +/* Interval for hrtimer, defaults to 60000 milliseconds */
>> +static unsigned int update_interval = 60 * MSEC_PER_SEC;
>> +module_param(update_interval, uint, 0444);
>
> [Severity: Medium]
> Could setting this to 0 cause an interrupt storm? If a privileged user loads
> the module with update_interval=0, hrtimer_duration becomes 0. When this is
> passed to hrtimer_forward_now(), it is clamped to the minimum resolution,
> causing the high-resolution timer to fire continuously in a loop. Should
> there be a minimum bounds check here to prevent a CPU lockup?
will fix this.>
> [ ... ]
>
>> +static int hygon_uncore_cpu_online(unsigned int cpu)
>> +{
>> + struct hygon_uncore *uncore;
>> + int i;
>> +
>> + for (i = 0; i < HYGON_UNCORE_TYPE_MAX; i++) {
>> + uncore = &uncores[i];
>> + if (uncore->init(uncore, cpu))
>> + break;
>
> [Severity: Medium]
> Should this be a continue instead of a break? If the first PMU type (e.g.,
> hygon_df) fails to initialize, this break aborts the loop, which prevents
> any subsequent independent PMUs (like hygon_df_iod) from initializing on
> this CPU. The same patch corrects this exact logic flaw in
> amd_uncore_cpu_online(), but it seems to have been reintroduced here.
>
will fix this.>> + }
>> +
>> + return 0;
>> +}
>
> [ ... ]
>
>> +fail_start:
>> + cpuhp_remove_state(CPUHP_AP_PERF_X86_HYGON_UNCORE_STARTING);
>> +fail_prep:
>> + cpuhp_remove_state(CPUHP_PERF_X86_HYGON_UNCORE_PREP);
>> +fail:
>> + hygon_uncore_info_cleanup();
>> + return ret;
>> +}
>
> [Severity: High]
> Does this error path leak the registered PMUs? If cpuhp_setup_state()
> fails for the ONLINE state, we remove the previous hotplug states but it
> looks like hygon_uncore_pmu_cleanup() is never called.
>
> Any PMUs successfully registered during the initial CPU online callbacks
> would remain registered, and their per-CPU arrays would not be freed. When
> the module unloads, could this lead to a use-after-free if the perf
> subsystem tries to access the freed module memory?
>
hygon_uncore_cpu_online() always returns 0, so cpuhp_setup_state() for the
ONLINE state will never fail due to a callback error. The only failure mode is
that the state slot itself cannot be registered — in which case the startup
callbacks were never invoked and no PMUs were ever registered.
> [ ... ]
>
>> diff --git a/arch/x86/events/amd/uncore.c b/arch/x86/events/amd/uncore.c
>> index dbc00b6dd69ee..3f8e40abba736 100644
>> --- a/arch/x86/events/amd/uncore.c
>> +++ b/arch/x86/events/amd/uncore.c
>
> [ ... ]
>
>> @@ -1120,8 +1116,7 @@ static int __init amd_uncore_init(void)
>> int ret = -ENODEV;
>> int i;
>>
>> - if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
>> - boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
>> + if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
>> return -ENODEV;
>
> [Severity: Medium]
> Does this intentionally drop L3 cache uncore support for Hygon? By
> removing Hygon support here in amd_uncore.c, the previous L3 cache
> counters are no longer available. Looking at the new hygon_uncore.c, it
> only implements the Data Fabric and IO Die PMUs, meaning L3 PMU support
> appears to be lost completely.
>
Yes, this is intentional. L3 and UMC PMU support for Hygon will be added in
follow-up series. This series focuses specifically on Data Fabric uncore PMU
support as the initial step.
Thanks,
Qi>> if (!boot_cpu_has(X86_FEATURE_TOPOEXT))
>> return -ENODEV;
>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-09 2:09 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-08 13:13 [PATCH v3 0/2] perf/x86/amd/uncore: Add Hygon uncore PMU and JSON events Qi Liu
2026-07-08 13:13 ` [PATCH v3 1/2] perf/x86/amd/uncore: Add Hygon uncore PMU support Qi Liu
2026-07-08 13:27 ` sashiko-bot
2026-07-09 2:09 ` Qi Liu
2026-07-08 13:13 ` [PATCH v3 2/2] perf vendor events hygon: Add Hygon Data Fabric PMU events and metrics Qi Liu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox