162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <stdio.h>
362306a36Sopenharmony_ci#include <stdlib.h>
462306a36Sopenharmony_ci#include "util/evsel.h"
562306a36Sopenharmony_ci#include "util/env.h"
662306a36Sopenharmony_ci#include "util/pmu.h"
762306a36Sopenharmony_ci#include "util/pmus.h"
862306a36Sopenharmony_ci#include "linux/string.h"
962306a36Sopenharmony_ci#include "evsel.h"
1062306a36Sopenharmony_ci#include "util/debug.h"
1162306a36Sopenharmony_ci#include "env.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define IBS_FETCH_L3MISSONLY   (1ULL << 59)
1462306a36Sopenharmony_ci#define IBS_OP_L3MISSONLY      (1ULL << 16)
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_civoid arch_evsel__set_sample_weight(struct evsel *evsel)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	evsel__set_sample_bit(evsel, WEIGHT_STRUCT);
1962306a36Sopenharmony_ci}
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* Check whether the evsel's PMU supports the perf metrics */
2262306a36Sopenharmony_cibool evsel__sys_has_perf_metrics(const struct evsel *evsel)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	const char *pmu_name = evsel->pmu_name ? evsel->pmu_name : "cpu";
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	/*
2762306a36Sopenharmony_ci	 * The PERF_TYPE_RAW type is the core PMU type, e.g., "cpu" PMU
2862306a36Sopenharmony_ci	 * on a non-hybrid machine, "cpu_core" PMU on a hybrid machine.
2962306a36Sopenharmony_ci	 * The slots event is only available for the core PMU, which
3062306a36Sopenharmony_ci	 * supports the perf metrics feature.
3162306a36Sopenharmony_ci	 * Checking both the PERF_TYPE_RAW type and the slots event
3262306a36Sopenharmony_ci	 * should be good enough to detect the perf metrics feature.
3362306a36Sopenharmony_ci	 */
3462306a36Sopenharmony_ci	if ((evsel->core.attr.type == PERF_TYPE_RAW) &&
3562306a36Sopenharmony_ci	    perf_pmus__have_event(pmu_name, "slots"))
3662306a36Sopenharmony_ci		return true;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return false;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cibool arch_evsel__must_be_in_group(const struct evsel *evsel)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	if (!evsel__sys_has_perf_metrics(evsel) || !evsel->name ||
4462306a36Sopenharmony_ci	    strcasestr(evsel->name, "uops_retired.slots"))
4562306a36Sopenharmony_ci		return false;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return strcasestr(evsel->name, "topdown") || strcasestr(evsel->name, "slots");
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ciint arch_evsel__hw_name(struct evsel *evsel, char *bf, size_t size)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	u64 event = evsel->core.attr.config & PERF_HW_EVENT_MASK;
5362306a36Sopenharmony_ci	u64 pmu = evsel->core.attr.config >> PERF_PMU_TYPE_SHIFT;
5462306a36Sopenharmony_ci	const char *event_name;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (event < PERF_COUNT_HW_MAX && evsel__hw_names[event])
5762306a36Sopenharmony_ci		event_name = evsel__hw_names[event];
5862306a36Sopenharmony_ci	else
5962306a36Sopenharmony_ci		event_name = "unknown-hardware";
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* The PMU type is not required for the non-hybrid platform. */
6262306a36Sopenharmony_ci	if (!pmu)
6362306a36Sopenharmony_ci		return  scnprintf(bf, size, "%s", event_name);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return scnprintf(bf, size, "%s/%s/",
6662306a36Sopenharmony_ci			 evsel->pmu_name ? evsel->pmu_name : "cpu",
6762306a36Sopenharmony_ci			 event_name);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void ibs_l3miss_warn(void)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	pr_warning(
7362306a36Sopenharmony_ci"WARNING: Hw internally resets sampling period when L3 Miss Filtering is enabled\n"
7462306a36Sopenharmony_ci"and tagged operation does not cause L3 Miss. This causes sampling period skew.\n");
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_civoid arch__post_evsel_config(struct evsel *evsel, struct perf_event_attr *attr)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct perf_pmu *evsel_pmu, *ibs_fetch_pmu, *ibs_op_pmu;
8062306a36Sopenharmony_ci	static int warned_once;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (warned_once || !x86__is_amd_cpu())
8362306a36Sopenharmony_ci		return;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	evsel_pmu = evsel__find_pmu(evsel);
8662306a36Sopenharmony_ci	if (!evsel_pmu)
8762306a36Sopenharmony_ci		return;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	ibs_fetch_pmu = perf_pmus__find("ibs_fetch");
9062306a36Sopenharmony_ci	ibs_op_pmu = perf_pmus__find("ibs_op");
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (ibs_fetch_pmu && ibs_fetch_pmu->type == evsel_pmu->type) {
9362306a36Sopenharmony_ci		if (attr->config & IBS_FETCH_L3MISSONLY) {
9462306a36Sopenharmony_ci			ibs_l3miss_warn();
9562306a36Sopenharmony_ci			warned_once = 1;
9662306a36Sopenharmony_ci		}
9762306a36Sopenharmony_ci	} else if (ibs_op_pmu && ibs_op_pmu->type == evsel_pmu->type) {
9862306a36Sopenharmony_ci		if (attr->config & IBS_OP_L3MISSONLY) {
9962306a36Sopenharmony_ci			ibs_l3miss_warn();
10062306a36Sopenharmony_ci			warned_once = 1;
10162306a36Sopenharmony_ci		}
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ciint arch_evsel__open_strerror(struct evsel *evsel, char *msg, size_t size)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	if (!x86__is_amd_cpu())
10862306a36Sopenharmony_ci		return 0;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (!evsel->core.attr.precise_ip &&
11162306a36Sopenharmony_ci	    !(evsel->pmu_name && !strncmp(evsel->pmu_name, "ibs", 3)))
11262306a36Sopenharmony_ci		return 0;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* More verbose IBS errors. */
11562306a36Sopenharmony_ci	if (evsel->core.attr.exclude_kernel || evsel->core.attr.exclude_user ||
11662306a36Sopenharmony_ci	    evsel->core.attr.exclude_hv || evsel->core.attr.exclude_idle ||
11762306a36Sopenharmony_ci	    evsel->core.attr.exclude_host || evsel->core.attr.exclude_guest) {
11862306a36Sopenharmony_ci		return scnprintf(msg, size, "AMD IBS doesn't support privilege filtering. Try "
11962306a36Sopenharmony_ci				 "again without the privilege modifiers (like 'k') at the end.");
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
124