From 9bd69c167011915abf5c8aab7afe25a061984980 Mon Sep 17 00:00:00 2001 From: Qi Liu Date: Mon, 9 Mar 2026 20:45:31 +0800 Subject: [PATCH] anolis: perf/x86/uncore: Add Uncore PMU support for Hygon ANBZ: #31927 Implement a Hygon uncore PMU driver to expose hardware performance counters for L3 and Data Fabric (DF) units. Revert previous Hygon-specific adaptations in uncore.c to ensure that the Hygon platform only loads the dedicated hygon-uncore driver. Signed-off-by: Qi Liu Signed-off-by: Liao Xuan --- .../x86/CONFIG_PERF_EVENTS_HYGON_UNCORE | 1 + arch/x86/events/Kconfig | 11 + arch/x86/events/amd/Makefile | 2 + arch/x86/events/amd/hygon_uncore.c | 893 ++++++++++++++++++ arch/x86/events/amd/uncore.c | 123 +-- arch/x86/include/asm/msr-index.h | 2 + arch/x86/include/asm/perf_event.h | 33 +- include/linux/cpuhotplug.h | 3 + 8 files changed, 956 insertions(+), 112 deletions(-) create mode 100644 anolis/configs/L1-RECOMMEND/x86/CONFIG_PERF_EVENTS_HYGON_UNCORE create mode 100644 arch/x86/events/amd/hygon_uncore.c diff --git a/anolis/configs/L1-RECOMMEND/x86/CONFIG_PERF_EVENTS_HYGON_UNCORE b/anolis/configs/L1-RECOMMEND/x86/CONFIG_PERF_EVENTS_HYGON_UNCORE new file mode 100644 index 000000000000..8bdc52d80765 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/x86/CONFIG_PERF_EVENTS_HYGON_UNCORE @@ -0,0 +1 @@ +CONFIG_PERF_EVENTS_HYGON_UNCORE=y diff --git a/arch/x86/events/Kconfig b/arch/x86/events/Kconfig index 09c56965750a..0caddc0d8bcc 100644 --- a/arch/x86/events/Kconfig +++ b/arch/x86/events/Kconfig @@ -52,4 +52,15 @@ config PERF_EVENTS_AMD_BRS Enable AMD Zen3 branch sampling support (BRS) which samples up to 16 consecutive taken branches in registers. +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_l3/.../,hygon_df/.../. + + To compile this driver as a module, choose M here: the + module will be called 'hygon-uncore'. + endmenu diff --git a/arch/x86/events/amd/Makefile b/arch/x86/events/amd/Makefile index 527d947eb76b..97720071235d 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..354c1d37fd95 --- /dev/null +++ b/arch/x86/events/amd/hygon_uncore.c @@ -0,0 +1,893 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Chengdu Haiguang IC Design Co., Ltd. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define NUM_COUNTERS_L3 6 +#define NUM_COUNTERS_DF 4 + +#define UNCORE_NAME_LEN 16 +#define COUNTER_SHIFT 16 + +struct hygon_uncore_ctx { + int refcnt; + int cpu; + struct perf_event **events; +}; + +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 { + UNCORE_TYPE_L3, + UNCORE_TYPE_DF, + UNCORE_TYPE_DF_IOD, + 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[]; + +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 == 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 void hygon_uncore_read(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + u64 prev, new; + s64 delta; + + prev = local64_read(&hwc->prev_count); + rdmsrl(hwc->event_base, new); + local64_set(&hwc->prev_count, new); + delta = (new << 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 hw_perf_event *hwc = &event->hw; + + if (flags & PERF_EF_RELOAD) + wrmsrl(hwc->event_base, (u64)local64_read(&hwc->prev_count)); + + hwc->state = 0; + wrmsrl(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 hw_perf_event *hwc = &event->hw; + + wrmsrl(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; + } +} + +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 iod_idx; + int i; + + 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) +{ + int i; + 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; + + 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 u64 hygon_uncore_l3_thread_slice_mask(u64 config) +{ + if (boot_cpu_data.x86_model >= 0x6 && boot_cpu_data.x86_model <= 0xf) + return ((config & HYGON_L3_SLICE_MASK) ? : HYGON_L3_SLICE_MASK) | + ((config & HYGON_L3_THREAD_MASK) ? : HYGON_L3_THREAD_MASK); + + return ((config & AMD64_L3_SLICE_MASK) ? : AMD64_L3_SLICE_MASK) | + ((config & AMD64_L3_THREAD_MASK) ? : AMD64_L3_THREAD_MASK); +} + +static int hygon_uncore_l3_event_init(struct perf_event *event) +{ + int ret = hygon_uncore_event_init(event); + struct hw_perf_event *hwc = &event->hw; + u64 config = event->attr.config; + + if (ret) + return ret; + + hwc->config = config & AMD64_RAW_EVENT_MASK_NB; + hwc->config |= hygon_uncore_l3_thread_slice_mask(config); + 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(event8, event, "config:0-7"); +DEFINE_UNCORE_FORMAT_ATTR(umask8, umask, "config:8-15"); +DEFINE_UNCORE_FORMAT_ATTR(slicemask, slicemask, "config:48-51"); /* F18h L3 */ +DEFINE_UNCORE_FORMAT_ATTR(slicemask4, slicemask, "config:28-31"); /* F18h M6H L3 */ +DEFINE_UNCORE_FORMAT_ATTR(threadmask8, threadmask, "config:56-63"); /* F18h L3 */ +DEFINE_UNCORE_FORMAT_ATTR(threadmask32, threadmask, "config:32-63"); /* F18h M6H L3 */ +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_l3_format_attr[] = { + &format_attr_event8.attr, /* event */ + &format_attr_umask8.attr, /* umask */ + &format_attr_slicemask.attr, /* slicemask */ + &format_attr_threadmask8.attr, /* threadmask */ + NULL, +}; + +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_l3_format_group = { + .name = "format", + .attrs = hygon_uncore_l3_format_attr, +}; + +static struct attribute_group hygon_uncore_df_format_group = { + .name = "format", + .attrs = hygon_uncore_df_format_attr, +}; + +static const struct attribute_group *hygon_uncore_l3_attr_groups[] = { + &hygon_uncore_attr_group, + &hygon_uncore_l3_format_group, + NULL, +}; + +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 < 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 < 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 ret; + int i; + + for (i = 0; i < UNCORE_TYPE_MAX; i++) { + uncore = &uncores[i]; + ret = uncore->init(uncore, cpu); + if (ret) + return ret; + } + + return 0; +} + +static int hygon_uncore_cpu_down_prepare(unsigned int cpu) +{ + struct hygon_uncore *uncore; + int i; + + for (i = 0; i < 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) { + 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); + } + + curr->refcnt++; + *per_cpu_ptr(pmu->ctx, cpu) = curr; + + return 0; + +fail: + hygon_uncore_ctx_free(uncore, cpu); + + return -ENOMEM; +} + +static void hygon_uncore_l3_ctx_scan(struct hygon_uncore *uncore, unsigned int cpu) +{ + union hygon_uncore_info info = {}; + + if (!boot_cpu_has(X86_FEATURE_PERFCTR_LLC)) + return; + + info.split.num_pmcs = NUM_COUNTERS_L3; + info.split.cid = get_llc_id(cpu); + info.split.num_iods = 0; + + *per_cpu_ptr(uncore->info, cpu) = info; +} + +static int hygon_uncore_l3_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_l3", sizeof(pmu->name)); + pmu->num_counters = num_counters; + pmu->msr_base = MSR_F16H_L2I_PERF_CTL; + pmu->type = UNCORE_TYPE_L3; + + if (boot_cpu_data.x86_model >= 0x6 && boot_cpu_data.x86_model <= 0xf) { + hygon_uncore_l3_format_attr[2] = &format_attr_slicemask4.attr; + hygon_uncore_l3_format_attr[3] = &format_attr_threadmask32.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_l3_attr_groups, + .name = pmu->name, + .event_init = hygon_uncore_l3_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_ctx_scan(struct hygon_uncore *uncore, unsigned int cpu) +{ + union hygon_uncore_info info = {}; + unsigned int eax, ebx, ecx, edx; + + if (!boot_cpu_has(X86_FEATURE_PERFCTR_NB)) + return; + + info.split.num_iods = 0; + info.split.num_pmcs = NUM_COUNTERS_DF; + cpuid(0x8000001e, &eax, &ebx, &ecx, &edx); + info.split.cid = ecx & 0xff; + + *per_cpu_ptr(uncore->info, cpu) = info; +} + +static +int hygon_uncore_df_ctx_init(struct hygon_uncore *uncore, unsigned int cpu) +{ + struct attribute *df_attr; + 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 = UNCORE_TYPE_DF; + + df_attr = &format_attr_umask8.attr; + if (boot_cpu_data.x86_model == 0x4 || + boot_cpu_data.x86_model == 0x5) + df_attr = &format_attr_umask10.attr; + else if (boot_cpu_data.x86_model >= 0x6 && + boot_cpu_data.x86_model <= 0x18) + df_attr = &format_attr_umask12.attr; + hygon_uncore_df_format_attr[1] = df_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_package, 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_package = topology_max_packages(); + num_cdds = topology_max_die_per_package() * num_package; + iods_per_package = (amd_nb_num() - num_cdds) / num_package; + + 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 = UNCORE_TYPE_DF_IOD; + + if (boot_cpu_data.x86_model >= 0x6 && + boot_cpu_data.x86_model <= 0x18) + hygon_uncore_df_iod_format_attr[1] = &format_attr_umask12.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[UNCORE_TYPE_MAX] = { + /* HYGON L3 */ + { + .scan = hygon_uncore_l3_ctx_scan, + .init = hygon_uncore_l3_ctx_init, + .move = hygon_uncore_ctx_move, + .free = hygon_uncore_ctx_free, + }, + /* 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 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 < UNCORE_TYPE_MAX; i++) { + uncore = &uncores[i]; + + BUG_ON(!uncore->scan); + BUG_ON(!uncore->init); + BUG_ON(!uncore->move); + BUG_ON(!uncore->free); + + 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: + for (i = 0; i < UNCORE_TYPE_MAX; i++) { + uncore = &uncores[i]; + if (uncore->info) { + free_percpu(uncore->info); + uncore->info = NULL; + } + } + return ret; +} + +static void __exit hygon_uncore_exit(void) +{ + struct hygon_uncore *uncore; + struct hygon_uncore_pmu *pmu; + int i; + + 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); + + for (i = 0; i < UNCORE_TYPE_MAX; i++) { + uncore = &uncores[i]; + if (!uncore->info) + continue; + + free_percpu(uncore->info); + uncore->info = NULL; + + if (!uncore->pmu_registered) + continue; + + pmu = &uncore->pmu; + if (!pmu->ctx) + continue; + + uncore->pmu_registered = false; + perf_pmu_unregister(&pmu->pmu); + free_percpu(pmu->ctx); + pmu->ctx = NULL; + } +} +module_init(hygon_uncore_init); +module_exit(hygon_uncore_exit); + +MODULE_DESCRIPTION("Hygon Uncore Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/x86/events/amd/uncore.c b/arch/x86/events/amd/uncore.c index eb454e23f303..ebbecf6b8dbc 100644 --- a/arch/x86/events/amd/uncore.c +++ b/arch/x86/events/amd/uncore.c @@ -203,42 +203,6 @@ static void amd_uncore_del(struct perf_event *event, int flags) hwc->idx = -1; } -/* - * Return a full thread and slice mask unless user - * has provided them - */ -static u64 l3_thread_slice_mask(u64 config) -{ - if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && - boot_cpu_data.x86 <= 0x18) - return ((config & AMD64_L3_SLICE_MASK) ? : AMD64_L3_SLICE_MASK) | - ((config & AMD64_L3_THREAD_MASK) ? : AMD64_L3_THREAD_MASK); - - if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON && - boot_cpu_data.x86 == 0x18) { - if (boot_cpu_data.x86_model >= 0x6 && boot_cpu_data.x86_model <= 0xf) - return ((config & HYGON_L3_SLICE_MASK) ? : HYGON_L3_SLICE_MASK) | - ((config & HYGON_L3_THREAD_MASK) ? : HYGON_L3_THREAD_MASK); - else - return ((config & AMD64_L3_SLICE_MASK) ? : AMD64_L3_SLICE_MASK) | - ((config & AMD64_L3_THREAD_MASK) ? : AMD64_L3_THREAD_MASK); - } - - /* - * If the user doesn't specify a threadmask, they're not trying to - * count core 0, so we enable all cores & threads. - * We'll also assume that they want to count slice 0 if they specify - * a threadmask and leave sliceid and enallslices unpopulated. - */ - if (!(config & AMD64_L3_F19H_THREAD_MASK)) - return AMD64_L3_F19H_THREAD_MASK | AMD64_L3_EN_ALL_SLICES | - AMD64_L3_EN_ALL_CORES; - - return config & (AMD64_L3_F19H_THREAD_MASK | AMD64_L3_SLICEID_MASK | - AMD64_L3_EN_ALL_CORES | AMD64_L3_EN_ALL_SLICES | - AMD64_L3_COREID_MASK); -} - static int amd_uncore_event_init(struct perf_event *event) { struct amd_uncore_pmu *pmu; @@ -283,14 +247,6 @@ amd_f17h_uncore_is_visible(struct kobject *kobj, struct attribute *attr, int i) attr->mode : 0; } -static umode_t -hygon_f18h_m6h_uncore_is_visible(struct kobject *kobj, struct attribute *attr, int i) -{ - return boot_cpu_data.x86 == 0x18 && - boot_cpu_data.x86_model >= 0x6 && boot_cpu_data.x86_model <= 0xf ? - attr->mode : 0; -} - static umode_t amd_f19h_uncore_is_visible(struct kobject *kobj, struct attribute *attr, int i) { @@ -331,11 +287,8 @@ static struct device_attribute format_attr_##_var = \ DEFINE_UNCORE_FORMAT_ATTR(event12, event, "config:0-7,32-35"); DEFINE_UNCORE_FORMAT_ATTR(event14, event, "config:0-7,32-35,59-60"); /* F17h+ DF */ DEFINE_UNCORE_FORMAT_ATTR(event14v2, event, "config:0-7,32-37"); /* PerfMonV2 DF */ -DEFINE_UNCORE_FORMAT_ATTR(event14f18h, event, "config:0-7,32-35,61-62"); /* F18h DF */ DEFINE_UNCORE_FORMAT_ATTR(event8, event, "config:0-7"); /* F17h+ L3, PerfMonV2 UMC */ DEFINE_UNCORE_FORMAT_ATTR(umask8, umask, "config:8-15"); -DEFINE_UNCORE_FORMAT_ATTR(umask10f18h, umask, "config:8-17"); /* F18h M4h DF */ -DEFINE_UNCORE_FORMAT_ATTR(umask12f18h, umask, "config:8-19"); /* F18h M6h DF */ DEFINE_UNCORE_FORMAT_ATTR(umask12, umask, "config:8-15,24-27"); /* PerfMonV2 DF */ DEFINE_UNCORE_FORMAT_ATTR(coreid, coreid, "config:42-44"); /* F19h L3 */ DEFINE_UNCORE_FORMAT_ATTR(slicemask, slicemask, "config:48-51"); /* F17h L3 */ @@ -344,8 +297,6 @@ DEFINE_UNCORE_FORMAT_ATTR(threadmask2, threadmask, "config:56-57"); /* F19h L DEFINE_UNCORE_FORMAT_ATTR(enallslices, enallslices, "config:46"); /* F19h L3 */ DEFINE_UNCORE_FORMAT_ATTR(enallcores, enallcores, "config:47"); /* F19h L3 */ DEFINE_UNCORE_FORMAT_ATTR(sliceid, sliceid, "config:48-50"); /* F19h L3 */ -DEFINE_UNCORE_FORMAT_ATTR(slicemask4, slicemask, "config:28-31"); /* F18h L3 */ -DEFINE_UNCORE_FORMAT_ATTR(threadmask32, threadmask, "config:32-63"); /* F18h L3 */ DEFINE_UNCORE_FORMAT_ATTR(rdwrmask, rdwrmask, "config:8-9"); /* PerfMonV2 UMC */ /* Common DF and NB attributes */ @@ -376,12 +327,6 @@ static struct attribute *amd_f17h_uncore_l3_format_attr[] = { NULL, }; -/* F18h M06h unique L3 attributes */ -static struct attribute *hygon_f18h_m6h_uncore_l3_format_attr[] = { - &format_attr_slicemask4.attr, /* slicemask */ - NULL, -}; - /* F19h unique L3 attributes */ static struct attribute *amd_f19h_uncore_l3_format_attr[] = { &format_attr_coreid.attr, /* coreid */ @@ -407,12 +352,6 @@ static struct attribute_group amd_f17h_uncore_l3_format_group = { .is_visible = amd_f17h_uncore_is_visible, }; -static struct attribute_group hygon_f18h_m6h_uncore_l3_format_group = { - .name = "format", - .attrs = hygon_f18h_m6h_uncore_l3_format_attr, - .is_visible = hygon_f18h_m6h_uncore_is_visible, -}; - static struct attribute_group amd_f19h_uncore_l3_format_group = { .name = "format", .attrs = amd_f19h_uncore_l3_format_attr, @@ -442,11 +381,6 @@ static const struct attribute_group *amd_uncore_l3_attr_update[] = { NULL, }; -static const struct attribute_group *hygon_uncore_l3_attr_update[] = { - &hygon_f18h_m6h_uncore_l3_format_group, - NULL, -}; - static const struct attribute_group *amd_uncore_umc_attr_groups[] = { &amd_uncore_attr_group, &amd_uncore_umc_format_group, @@ -748,18 +682,8 @@ int amd_uncore_df_ctx_init(struct amd_uncore *uncore, unsigned int cpu) if (pmu_version >= 2) { *df_attr++ = &format_attr_event14v2.attr; *df_attr++ = &format_attr_umask12.attr; - } else if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && - boot_cpu_data.x86 >= 0x17) { + } else if (boot_cpu_data.x86 >= 0x17) { *df_attr = &format_attr_event14.attr; - } else if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON && - boot_cpu_data.x86 == 0x18) { - *df_attr++ = &format_attr_event14f18h.attr; - if (boot_cpu_data.x86_model == 0x4 || - boot_cpu_data.x86_model == 0x5) - *df_attr++ = &format_attr_umask10f18h.attr; - else if (boot_cpu_data.x86_model >= 0x6 && - boot_cpu_data.x86_model <= 0x18) - *df_attr++ = &format_attr_umask12f18h.attr; } pmu->ctx = alloc_percpu(struct amd_uncore_ctx *); @@ -786,9 +710,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; @@ -803,6 +725,7 @@ static int amd_uncore_l3_event_init(struct perf_event *event) int ret = amd_uncore_event_init(event); struct hw_perf_event *hwc = &event->hw; u64 config = event->attr.config; + u64 mask; hwc->config = config & AMD64_RAW_EVENT_MASK_NB; @@ -813,7 +736,25 @@ static int amd_uncore_l3_event_init(struct perf_event *event) if (ret || boot_cpu_data.x86 < 0x17) return ret; - hwc->config |= l3_thread_slice_mask(config); + mask = config & (AMD64_L3_F19H_THREAD_MASK | AMD64_L3_SLICEID_MASK | + AMD64_L3_EN_ALL_CORES | AMD64_L3_EN_ALL_SLICES | + AMD64_L3_COREID_MASK); + + if (boot_cpu_data.x86 <= 0x18) + mask = ((config & AMD64_L3_SLICE_MASK) ? : AMD64_L3_SLICE_MASK) | + ((config & AMD64_L3_THREAD_MASK) ? : AMD64_L3_THREAD_MASK); + + /* + * If the user doesn't specify a threadmask, they're not trying to + * count core 0, so we enable all cores & threads. + * We'll also assume that they want to count slice 0 if they specify + * a threadmask and leave sliceid and enallslices unpopulated. + */ + else if (!(config & AMD64_L3_F19H_THREAD_MASK)) + mask = AMD64_L3_F19H_THREAD_MASK | AMD64_L3_EN_ALL_SLICES | + AMD64_L3_EN_ALL_CORES; + + hwc->config |= mask; return 0; } @@ -882,23 +823,12 @@ int amd_uncore_l3_ctx_init(struct amd_uncore *uncore, unsigned int cpu) .module = THIS_MODULE, }; - if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && - boot_cpu_data.x86 >= 0x17) { + if (boot_cpu_data.x86 >= 0x17) { *l3_attr++ = &format_attr_event8.attr; *l3_attr++ = &format_attr_umask8.attr; *l3_attr++ = boot_cpu_data.x86 >= 0x19 ? &format_attr_threadmask2.attr : &format_attr_threadmask8.attr; - } else if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON && - boot_cpu_data.x86 == 0x18) { - *l3_attr++ = &format_attr_event8.attr; - *l3_attr++ = &format_attr_umask8.attr; - if (boot_cpu_data.x86_model >= 0x6 && boot_cpu_data.x86_model <= 0xf) { - *l3_attr++ = &format_attr_threadmask32.attr; - pmu->pmu.attr_update = hygon_uncore_l3_attr_update; - } else { - *l3_attr++ = &format_attr_threadmask8.attr; - } } pmu->ctx = alloc_percpu(struct amd_uncore_ctx *); @@ -911,9 +841,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; @@ -1080,8 +1008,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 39cc3bbf2b9e..4637b855d9fa 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -650,6 +650,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 3c919115598b..c36727f4438b 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -48,9 +48,6 @@ #define AMD64_EVENTSEL_EVENT \ (ARCH_PERFMON_EVENTSEL_EVENT | (0x0FULL << 32)) -#define HYGON_F18H_EVENTSEL_EVENT \ - (AMD64_EVENTSEL_EVENT | \ - GENMASK_ULL(62, 61)) #define INTEL_ARCH_EVENT_MASK \ (ARCH_PERFMON_EVENTSEL_UMASK | ARCH_PERFMON_EVENTSEL_EVENT) @@ -101,18 +98,26 @@ #define AMD64_RAW_EVENT_MASK_NB \ (AMD64_EVENTSEL_EVENT | \ ARCH_PERFMON_EVENTSEL_UMASK) -#define HYGON_F18H_M4H_EVENTSEL_UMASK_NB 0x0003FF00ULL -#define HYGON_F18H_M6H_EVENTSEL_UMASK_NB 0x000FFF00ULL -#define HYGON_F18H_RAW_EVENT_MASK_NB \ - (HYGON_F18H_EVENTSEL_EVENT | \ - ARCH_PERFMON_EVENTSEL_UMASK) -#define HYGON_F18H_M4H_RAW_EVENT_MASK_NB \ - (HYGON_F18H_EVENTSEL_EVENT | \ - HYGON_F18H_M4H_EVENTSEL_UMASK_NB) -#define HYGON_F18H_M6H_RAW_EVENT_MASK_NB \ - (HYGON_F18H_EVENTSEL_EVENT | \ - HYGON_F18H_M6H_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_EVENTSEL_EVENT_NB \ (AMD64_EVENTSEL_EVENT | \ diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index e111284e14c0..5d2777398592 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -29,6 +29,7 @@ enum cpuhp_state { CPUHP_PERF_PREPARE, 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, @@ -109,6 +110,7 @@ enum cpuhp_state { CPUHP_AP_ARM_MVEBU_COHERENCY, CPUHP_AP_MICROCODE_LOADER, 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_X86_CQM_STARTING, @@ -166,6 +168,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_X86_RAPL_ONLINE, CPUHP_AP_PERF_X86_CQM_ONLINE, -- Gitee