18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include "tests/tests.h"
38c2ecf20Sopenharmony_ci#include "cloexec.h"
48c2ecf20Sopenharmony_ci#include "debug.h"
58c2ecf20Sopenharmony_ci#include "evlist.h"
68c2ecf20Sopenharmony_ci#include "evsel.h"
78c2ecf20Sopenharmony_ci#include "arch-tests.h"
88c2ecf20Sopenharmony_ci#include <internal/lib.h> // page_size
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <signal.h>
118c2ecf20Sopenharmony_ci#include <sys/mman.h>
128c2ecf20Sopenharmony_ci#include <sys/wait.h>
138c2ecf20Sopenharmony_ci#include <errno.h>
148c2ecf20Sopenharmony_ci#include <string.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic pid_t spawn(void)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	pid_t pid;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	pid = fork();
218c2ecf20Sopenharmony_ci	if (pid)
228c2ecf20Sopenharmony_ci		return pid;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	while(1)
258c2ecf20Sopenharmony_ci		sleep(5);
268c2ecf20Sopenharmony_ci	return 0;
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * Create an event group that contains both a sampled hardware
318c2ecf20Sopenharmony_ci * (cpu-cycles) and software (intel_cqm/llc_occupancy/) event. We then
328c2ecf20Sopenharmony_ci * wait for the hardware perf counter to overflow and generate a PMI,
338c2ecf20Sopenharmony_ci * which triggers an event read for both of the events in the group.
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * Since reading Intel CQM event counters requires sending SMP IPIs, the
368c2ecf20Sopenharmony_ci * CQM pmu needs to handle the above situation gracefully, and return
378c2ecf20Sopenharmony_ci * the last read counter value to avoid triggering a WARN_ON_ONCE() in
388c2ecf20Sopenharmony_ci * smp_call_function_many() caused by sending IPIs from NMI context.
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_ciint test__intel_cqm_count_nmi_context(struct test *test __maybe_unused, int subtest __maybe_unused)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct evlist *evlist = NULL;
438c2ecf20Sopenharmony_ci	struct evsel *evsel = NULL;
448c2ecf20Sopenharmony_ci	struct perf_event_attr pe;
458c2ecf20Sopenharmony_ci	int i, fd[2], flag, ret;
468c2ecf20Sopenharmony_ci	size_t mmap_len;
478c2ecf20Sopenharmony_ci	void *event;
488c2ecf20Sopenharmony_ci	pid_t pid;
498c2ecf20Sopenharmony_ci	int err = TEST_FAIL;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	flag = perf_event_open_cloexec_flag();
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	evlist = evlist__new();
548c2ecf20Sopenharmony_ci	if (!evlist) {
558c2ecf20Sopenharmony_ci		pr_debug("perf_evlist__new failed\n");
568c2ecf20Sopenharmony_ci		return TEST_FAIL;
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	ret = parse_events(evlist, "intel_cqm/llc_occupancy/", NULL);
608c2ecf20Sopenharmony_ci	if (ret) {
618c2ecf20Sopenharmony_ci		pr_debug("parse_events failed, is \"intel_cqm/llc_occupancy/\" available?\n");
628c2ecf20Sopenharmony_ci		err = TEST_SKIP;
638c2ecf20Sopenharmony_ci		goto out;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	evsel = evlist__first(evlist);
678c2ecf20Sopenharmony_ci	if (!evsel) {
688c2ecf20Sopenharmony_ci		pr_debug("evlist__first failed\n");
698c2ecf20Sopenharmony_ci		goto out;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	memset(&pe, 0, sizeof(pe));
738c2ecf20Sopenharmony_ci	pe.size = sizeof(pe);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	pe.type = PERF_TYPE_HARDWARE;
768c2ecf20Sopenharmony_ci	pe.config = PERF_COUNT_HW_CPU_CYCLES;
778c2ecf20Sopenharmony_ci	pe.read_format = PERF_FORMAT_GROUP;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	pe.sample_period = 128;
808c2ecf20Sopenharmony_ci	pe.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_READ;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	pid = spawn();
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	fd[0] = sys_perf_event_open(&pe, pid, -1, -1, flag);
858c2ecf20Sopenharmony_ci	if (fd[0] < 0) {
868c2ecf20Sopenharmony_ci		pr_debug("failed to open event\n");
878c2ecf20Sopenharmony_ci		goto out;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	memset(&pe, 0, sizeof(pe));
918c2ecf20Sopenharmony_ci	pe.size = sizeof(pe);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	pe.type = evsel->attr.type;
948c2ecf20Sopenharmony_ci	pe.config = evsel->attr.config;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	fd[1] = sys_perf_event_open(&pe, pid, -1, fd[0], flag);
978c2ecf20Sopenharmony_ci	if (fd[1] < 0) {
988c2ecf20Sopenharmony_ci		pr_debug("failed to open event\n");
998c2ecf20Sopenharmony_ci		goto out;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/*
1038c2ecf20Sopenharmony_ci	 * Pick a power-of-two number of pages + 1 for the meta-data
1048c2ecf20Sopenharmony_ci	 * page (struct perf_event_mmap_page). See tools/perf/design.txt.
1058c2ecf20Sopenharmony_ci	 */
1068c2ecf20Sopenharmony_ci	mmap_len = page_size * 65;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	event = mmap(NULL, mmap_len, PROT_READ, MAP_SHARED, fd[0], 0);
1098c2ecf20Sopenharmony_ci	if (event == (void *)(-1)) {
1108c2ecf20Sopenharmony_ci		pr_debug("failed to mmap %d\n", errno);
1118c2ecf20Sopenharmony_ci		goto out;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	sleep(1);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	err = TEST_OK;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	munmap(event, mmap_len);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++)
1218c2ecf20Sopenharmony_ci		close(fd[i]);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	kill(pid, SIGKILL);
1248c2ecf20Sopenharmony_ci	wait(NULL);
1258c2ecf20Sopenharmony_ciout:
1268c2ecf20Sopenharmony_ci	evlist__delete(evlist);
1278c2ecf20Sopenharmony_ci	return err;
1288c2ecf20Sopenharmony_ci}
129