162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <stdint.h>
462306a36Sopenharmony_ci#include "resctrl.h"
562306a36Sopenharmony_ci
662306a36Sopenharmony_cistruct read_format {
762306a36Sopenharmony_ci	__u64 nr;			/* The number of events */
862306a36Sopenharmony_ci	struct {
962306a36Sopenharmony_ci		__u64 value;		/* The value of the event */
1062306a36Sopenharmony_ci	} values[2];
1162306a36Sopenharmony_ci};
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic struct perf_event_attr pea_llc_miss;
1462306a36Sopenharmony_cistatic struct read_format rf_cqm;
1562306a36Sopenharmony_cistatic int fd_lm;
1662306a36Sopenharmony_cichar llc_occup_path[1024];
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic void initialize_perf_event_attr(void)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	pea_llc_miss.type = PERF_TYPE_HARDWARE;
2162306a36Sopenharmony_ci	pea_llc_miss.size = sizeof(struct perf_event_attr);
2262306a36Sopenharmony_ci	pea_llc_miss.read_format = PERF_FORMAT_GROUP;
2362306a36Sopenharmony_ci	pea_llc_miss.exclude_kernel = 1;
2462306a36Sopenharmony_ci	pea_llc_miss.exclude_hv = 1;
2562306a36Sopenharmony_ci	pea_llc_miss.exclude_idle = 1;
2662306a36Sopenharmony_ci	pea_llc_miss.exclude_callchain_kernel = 1;
2762306a36Sopenharmony_ci	pea_llc_miss.inherit = 1;
2862306a36Sopenharmony_ci	pea_llc_miss.exclude_guest = 1;
2962306a36Sopenharmony_ci	pea_llc_miss.disabled = 1;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic void ioctl_perf_event_ioc_reset_enable(void)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	ioctl(fd_lm, PERF_EVENT_IOC_RESET, 0);
3562306a36Sopenharmony_ci	ioctl(fd_lm, PERF_EVENT_IOC_ENABLE, 0);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic int perf_event_open_llc_miss(pid_t pid, int cpu_no)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	fd_lm = perf_event_open(&pea_llc_miss, pid, cpu_no, -1,
4162306a36Sopenharmony_ci				PERF_FLAG_FD_CLOEXEC);
4262306a36Sopenharmony_ci	if (fd_lm == -1) {
4362306a36Sopenharmony_ci		perror("Error opening leader");
4462306a36Sopenharmony_ci		ctrlc_handler(0, NULL, NULL);
4562306a36Sopenharmony_ci		return -1;
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	return 0;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic void initialize_llc_perf(void)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	memset(&pea_llc_miss, 0, sizeof(struct perf_event_attr));
5462306a36Sopenharmony_ci	memset(&rf_cqm, 0, sizeof(struct read_format));
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* Initialize perf_event_attr structures for HW_CACHE_MISSES */
5762306a36Sopenharmony_ci	initialize_perf_event_attr();
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	pea_llc_miss.config = PERF_COUNT_HW_CACHE_MISSES;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	rf_cqm.nr = 1;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic int reset_enable_llc_perf(pid_t pid, int cpu_no)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	int ret = 0;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	ret = perf_event_open_llc_miss(pid, cpu_no);
6962306a36Sopenharmony_ci	if (ret < 0)
7062306a36Sopenharmony_ci		return ret;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* Start counters to log values */
7362306a36Sopenharmony_ci	ioctl_perf_event_ioc_reset_enable();
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return 0;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/*
7962306a36Sopenharmony_ci * get_llc_perf:	llc cache miss through perf events
8062306a36Sopenharmony_ci * @llc_perf_miss:	LLC miss counter that is filled on success
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * Perf events like HW_CACHE_MISSES could be used to validate number of
8362306a36Sopenharmony_ci * cache lines allocated.
8462306a36Sopenharmony_ci *
8562306a36Sopenharmony_ci * Return: =0 on success.  <0 on failure.
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_cistatic int get_llc_perf(unsigned long *llc_perf_miss)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	__u64 total_misses;
9062306a36Sopenharmony_ci	int ret;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* Stop counters after one span to get miss rate */
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	ioctl(fd_lm, PERF_EVENT_IOC_DISABLE, 0);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	ret = read(fd_lm, &rf_cqm, sizeof(struct read_format));
9762306a36Sopenharmony_ci	if (ret == -1) {
9862306a36Sopenharmony_ci		perror("Could not get llc misses through perf");
9962306a36Sopenharmony_ci		return -1;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	total_misses = rf_cqm.values[0].value;
10362306a36Sopenharmony_ci	*llc_perf_miss = total_misses;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return 0;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/*
10962306a36Sopenharmony_ci * Get LLC Occupancy as reported by RESCTRL FS
11062306a36Sopenharmony_ci * For CMT,
11162306a36Sopenharmony_ci * 1. If con_mon grp and mon grp given, then read from mon grp in
11262306a36Sopenharmony_ci * con_mon grp
11362306a36Sopenharmony_ci * 2. If only con_mon grp given, then read from con_mon grp
11462306a36Sopenharmony_ci * 3. If both not given, then read from root con_mon grp
11562306a36Sopenharmony_ci * For CAT,
11662306a36Sopenharmony_ci * 1. If con_mon grp given, then read from it
11762306a36Sopenharmony_ci * 2. If con_mon grp not given, then read from root con_mon grp
11862306a36Sopenharmony_ci *
11962306a36Sopenharmony_ci * Return: =0 on success.  <0 on failure.
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_cistatic int get_llc_occu_resctrl(unsigned long *llc_occupancy)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	FILE *fp;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	fp = fopen(llc_occup_path, "r");
12662306a36Sopenharmony_ci	if (!fp) {
12762306a36Sopenharmony_ci		perror("Failed to open results file");
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		return errno;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci	if (fscanf(fp, "%lu", llc_occupancy) <= 0) {
13262306a36Sopenharmony_ci		perror("Could not get llc occupancy");
13362306a36Sopenharmony_ci		fclose(fp);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		return -1;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci	fclose(fp);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return 0;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/*
14362306a36Sopenharmony_ci * print_results_cache:	the cache results are stored in a file
14462306a36Sopenharmony_ci * @filename:		file that stores the results
14562306a36Sopenharmony_ci * @bm_pid:		child pid that runs benchmark
14662306a36Sopenharmony_ci * @llc_value:		perf miss value /
14762306a36Sopenharmony_ci *			llc occupancy value reported by resctrl FS
14862306a36Sopenharmony_ci *
14962306a36Sopenharmony_ci * Return:		0 on success. non-zero on failure.
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_cistatic int print_results_cache(char *filename, int bm_pid,
15262306a36Sopenharmony_ci			       unsigned long llc_value)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	FILE *fp;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (strcmp(filename, "stdio") == 0 || strcmp(filename, "stderr") == 0) {
15762306a36Sopenharmony_ci		printf("Pid: %d \t LLC_value: %lu\n", bm_pid,
15862306a36Sopenharmony_ci		       llc_value);
15962306a36Sopenharmony_ci	} else {
16062306a36Sopenharmony_ci		fp = fopen(filename, "a");
16162306a36Sopenharmony_ci		if (!fp) {
16262306a36Sopenharmony_ci			perror("Cannot open results file");
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci			return errno;
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci		fprintf(fp, "Pid: %d \t llc_value: %lu\n", bm_pid, llc_value);
16762306a36Sopenharmony_ci		fclose(fp);
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return 0;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ciint measure_cache_vals(struct resctrl_val_param *param, int bm_pid)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	unsigned long llc_perf_miss = 0, llc_occu_resc = 0, llc_value = 0;
17662306a36Sopenharmony_ci	int ret;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/*
17962306a36Sopenharmony_ci	 * Measure cache miss from perf.
18062306a36Sopenharmony_ci	 */
18162306a36Sopenharmony_ci	if (!strncmp(param->resctrl_val, CAT_STR, sizeof(CAT_STR))) {
18262306a36Sopenharmony_ci		ret = get_llc_perf(&llc_perf_miss);
18362306a36Sopenharmony_ci		if (ret < 0)
18462306a36Sopenharmony_ci			return ret;
18562306a36Sopenharmony_ci		llc_value = llc_perf_miss;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/*
18962306a36Sopenharmony_ci	 * Measure llc occupancy from resctrl.
19062306a36Sopenharmony_ci	 */
19162306a36Sopenharmony_ci	if (!strncmp(param->resctrl_val, CMT_STR, sizeof(CMT_STR))) {
19262306a36Sopenharmony_ci		ret = get_llc_occu_resctrl(&llc_occu_resc);
19362306a36Sopenharmony_ci		if (ret < 0)
19462306a36Sopenharmony_ci			return ret;
19562306a36Sopenharmony_ci		llc_value = llc_occu_resc;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci	ret = print_results_cache(param->filename, bm_pid, llc_value);
19862306a36Sopenharmony_ci	if (ret)
19962306a36Sopenharmony_ci		return ret;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return 0;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/*
20562306a36Sopenharmony_ci * cache_val:		execute benchmark and measure LLC occupancy resctrl
20662306a36Sopenharmony_ci * and perf cache miss for the benchmark
20762306a36Sopenharmony_ci * @param:		parameters passed to cache_val()
20862306a36Sopenharmony_ci * @span:		buffer size for the benchmark
20962306a36Sopenharmony_ci *
21062306a36Sopenharmony_ci * Return:		0 on success. non-zero on failure.
21162306a36Sopenharmony_ci */
21262306a36Sopenharmony_ciint cat_val(struct resctrl_val_param *param, size_t span)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	int memflush = 1, operation = 0, ret = 0;
21562306a36Sopenharmony_ci	char *resctrl_val = param->resctrl_val;
21662306a36Sopenharmony_ci	pid_t bm_pid;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (strcmp(param->filename, "") == 0)
21962306a36Sopenharmony_ci		sprintf(param->filename, "stdio");
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	bm_pid = getpid();
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* Taskset benchmark to specified cpu */
22462306a36Sopenharmony_ci	ret = taskset_benchmark(bm_pid, param->cpu_no);
22562306a36Sopenharmony_ci	if (ret)
22662306a36Sopenharmony_ci		return ret;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* Write benchmark to specified con_mon grp, mon_grp in resctrl FS*/
22962306a36Sopenharmony_ci	ret = write_bm_pid_to_resctrl(bm_pid, param->ctrlgrp, param->mongrp,
23062306a36Sopenharmony_ci				      resctrl_val);
23162306a36Sopenharmony_ci	if (ret)
23262306a36Sopenharmony_ci		return ret;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	initialize_llc_perf();
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* Test runs until the callback setup() tells the test to stop. */
23762306a36Sopenharmony_ci	while (1) {
23862306a36Sopenharmony_ci		ret = param->setup(param);
23962306a36Sopenharmony_ci		if (ret == END_OF_TESTS) {
24062306a36Sopenharmony_ci			ret = 0;
24162306a36Sopenharmony_ci			break;
24262306a36Sopenharmony_ci		}
24362306a36Sopenharmony_ci		if (ret < 0)
24462306a36Sopenharmony_ci			break;
24562306a36Sopenharmony_ci		ret = reset_enable_llc_perf(bm_pid, param->cpu_no);
24662306a36Sopenharmony_ci		if (ret)
24762306a36Sopenharmony_ci			break;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		if (run_fill_buf(span, memflush, operation, true)) {
25062306a36Sopenharmony_ci			fprintf(stderr, "Error-running fill buffer\n");
25162306a36Sopenharmony_ci			ret = -1;
25262306a36Sopenharmony_ci			goto pe_close;
25362306a36Sopenharmony_ci		}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		sleep(1);
25662306a36Sopenharmony_ci		ret = measure_cache_vals(param, bm_pid);
25762306a36Sopenharmony_ci		if (ret)
25862306a36Sopenharmony_ci			goto pe_close;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return ret;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cipe_close:
26462306a36Sopenharmony_ci	close(fd_lm);
26562306a36Sopenharmony_ci	return ret;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci/*
26962306a36Sopenharmony_ci * show_cache_info:	show cache test result information
27062306a36Sopenharmony_ci * @sum_llc_val:	sum of LLC cache result data
27162306a36Sopenharmony_ci * @no_of_bits:		number of bits
27262306a36Sopenharmony_ci * @cache_span:		cache span in bytes for CMT or in lines for CAT
27362306a36Sopenharmony_ci * @max_diff:		max difference
27462306a36Sopenharmony_ci * @max_diff_percent:	max difference percentage
27562306a36Sopenharmony_ci * @num_of_runs:	number of runs
27662306a36Sopenharmony_ci * @platform:		show test information on this platform
27762306a36Sopenharmony_ci * @cmt:		CMT test or CAT test
27862306a36Sopenharmony_ci *
27962306a36Sopenharmony_ci * Return:		0 on success. non-zero on failure.
28062306a36Sopenharmony_ci */
28162306a36Sopenharmony_ciint show_cache_info(unsigned long sum_llc_val, int no_of_bits,
28262306a36Sopenharmony_ci		    size_t cache_span, unsigned long max_diff,
28362306a36Sopenharmony_ci		    unsigned long max_diff_percent, unsigned long num_of_runs,
28462306a36Sopenharmony_ci		    bool platform, bool cmt)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	unsigned long avg_llc_val = 0;
28762306a36Sopenharmony_ci	float diff_percent;
28862306a36Sopenharmony_ci	long avg_diff = 0;
28962306a36Sopenharmony_ci	int ret;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	avg_llc_val = sum_llc_val / num_of_runs;
29262306a36Sopenharmony_ci	avg_diff = (long)abs(cache_span - avg_llc_val);
29362306a36Sopenharmony_ci	diff_percent = ((float)cache_span - avg_llc_val) / cache_span * 100;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	ret = platform && abs((int)diff_percent) > max_diff_percent &&
29662306a36Sopenharmony_ci	      (cmt ? (abs(avg_diff) > max_diff) : true);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	ksft_print_msg("%s Check cache miss rate within %d%%\n",
29962306a36Sopenharmony_ci		       ret ? "Fail:" : "Pass:", max_diff_percent);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	ksft_print_msg("Percent diff=%d\n", abs((int)diff_percent));
30262306a36Sopenharmony_ci	ksft_print_msg("Number of bits: %d\n", no_of_bits);
30362306a36Sopenharmony_ci	ksft_print_msg("Average LLC val: %lu\n", avg_llc_val);
30462306a36Sopenharmony_ci	ksft_print_msg("Cache span (%s): %zu\n", cmt ? "bytes" : "lines",
30562306a36Sopenharmony_ci		       cache_span);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return ret;
30862306a36Sopenharmony_ci}
309