162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include "tests/tests.h"
362306a36Sopenharmony_ci#include "cloexec.h"
462306a36Sopenharmony_ci#include "debug.h"
562306a36Sopenharmony_ci#include "evlist.h"
662306a36Sopenharmony_ci#include "evsel.h"
762306a36Sopenharmony_ci#include "arch-tests.h"
862306a36Sopenharmony_ci#include <internal/lib.h> // page_size
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <signal.h>
1162306a36Sopenharmony_ci#include <sys/mman.h>
1262306a36Sopenharmony_ci#include <sys/wait.h>
1362306a36Sopenharmony_ci#include <errno.h>
1462306a36Sopenharmony_ci#include <string.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic pid_t spawn(void)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	pid_t pid;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	pid = fork();
2162306a36Sopenharmony_ci	if (pid)
2262306a36Sopenharmony_ci		return pid;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	while(1)
2562306a36Sopenharmony_ci		sleep(5);
2662306a36Sopenharmony_ci	return 0;
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * Create an event group that contains both a sampled hardware
3162306a36Sopenharmony_ci * (cpu-cycles) and software (intel_cqm/llc_occupancy/) event. We then
3262306a36Sopenharmony_ci * wait for the hardware perf counter to overflow and generate a PMI,
3362306a36Sopenharmony_ci * which triggers an event read for both of the events in the group.
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * Since reading Intel CQM event counters requires sending SMP IPIs, the
3662306a36Sopenharmony_ci * CQM pmu needs to handle the above situation gracefully, and return
3762306a36Sopenharmony_ci * the last read counter value to avoid triggering a WARN_ON_ONCE() in
3862306a36Sopenharmony_ci * smp_call_function_many() caused by sending IPIs from NMI context.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ciint test__intel_cqm_count_nmi_context(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct evlist *evlist = NULL;
4362306a36Sopenharmony_ci	struct evsel *evsel = NULL;
4462306a36Sopenharmony_ci	struct perf_event_attr pe;
4562306a36Sopenharmony_ci	int i, fd[2], flag, ret;
4662306a36Sopenharmony_ci	size_t mmap_len;
4762306a36Sopenharmony_ci	void *event;
4862306a36Sopenharmony_ci	pid_t pid;
4962306a36Sopenharmony_ci	int err = TEST_FAIL;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	flag = perf_event_open_cloexec_flag();
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	evlist = evlist__new();
5462306a36Sopenharmony_ci	if (!evlist) {
5562306a36Sopenharmony_ci		pr_debug("evlist__new failed\n");
5662306a36Sopenharmony_ci		return TEST_FAIL;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	ret = parse_event(evlist, "intel_cqm/llc_occupancy/");
6062306a36Sopenharmony_ci	if (ret) {
6162306a36Sopenharmony_ci		pr_debug("parse_events failed, is \"intel_cqm/llc_occupancy/\" available?\n");
6262306a36Sopenharmony_ci		err = TEST_SKIP;
6362306a36Sopenharmony_ci		goto out;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	evsel = evlist__first(evlist);
6762306a36Sopenharmony_ci	if (!evsel) {
6862306a36Sopenharmony_ci		pr_debug("evlist__first failed\n");
6962306a36Sopenharmony_ci		goto out;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	memset(&pe, 0, sizeof(pe));
7362306a36Sopenharmony_ci	pe.size = sizeof(pe);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	pe.type = PERF_TYPE_HARDWARE;
7662306a36Sopenharmony_ci	pe.config = PERF_COUNT_HW_CPU_CYCLES;
7762306a36Sopenharmony_ci	pe.read_format = PERF_FORMAT_GROUP;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	pe.sample_period = 128;
8062306a36Sopenharmony_ci	pe.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_READ;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	pid = spawn();
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	fd[0] = sys_perf_event_open(&pe, pid, -1, -1, flag);
8562306a36Sopenharmony_ci	if (fd[0] < 0) {
8662306a36Sopenharmony_ci		pr_debug("failed to open event\n");
8762306a36Sopenharmony_ci		goto out;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	memset(&pe, 0, sizeof(pe));
9162306a36Sopenharmony_ci	pe.size = sizeof(pe);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	pe.type = evsel->attr.type;
9462306a36Sopenharmony_ci	pe.config = evsel->attr.config;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	fd[1] = sys_perf_event_open(&pe, pid, -1, fd[0], flag);
9762306a36Sopenharmony_ci	if (fd[1] < 0) {
9862306a36Sopenharmony_ci		pr_debug("failed to open event\n");
9962306a36Sopenharmony_ci		goto out;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/*
10362306a36Sopenharmony_ci	 * Pick a power-of-two number of pages + 1 for the meta-data
10462306a36Sopenharmony_ci	 * page (struct perf_event_mmap_page). See tools/perf/design.txt.
10562306a36Sopenharmony_ci	 */
10662306a36Sopenharmony_ci	mmap_len = page_size * 65;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	event = mmap(NULL, mmap_len, PROT_READ, MAP_SHARED, fd[0], 0);
10962306a36Sopenharmony_ci	if (event == (void *)(-1)) {
11062306a36Sopenharmony_ci		pr_debug("failed to mmap %d\n", errno);
11162306a36Sopenharmony_ci		goto out;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	sleep(1);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	err = TEST_OK;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	munmap(event, mmap_len);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	for (i = 0; i < 2; i++)
12162306a36Sopenharmony_ci		close(fd[i]);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	kill(pid, SIGKILL);
12462306a36Sopenharmony_ci	wait(NULL);
12562306a36Sopenharmony_ciout:
12662306a36Sopenharmony_ci	evlist__delete(evlist);
12762306a36Sopenharmony_ci	return err;
12862306a36Sopenharmony_ci}
129