xref: /openbmc/linux/tools/testing/selftests/resctrl/cat_test.c (revision 7e24a55b2122746c2eef192296fc84624354f895)
1  // SPDX-License-Identifier: GPL-2.0
2  /*
3   * Cache Allocation Technology (CAT) test
4   *
5   * Copyright (C) 2018 Intel Corporation
6   *
7   * Authors:
8   *    Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>,
9   *    Fenghua Yu <fenghua.yu@intel.com>
10   */
11  #include "resctrl.h"
12  #include <unistd.h>
13  
14  #define RESULT_FILE_NAME1	"result_cat1"
15  #define RESULT_FILE_NAME2	"result_cat2"
16  #define NUM_OF_RUNS		5
17  #define MAX_DIFF_PERCENT	4
18  #define MAX_DIFF		1000000
19  
20  /*
21   * Change schemata. Write schemata to specified
22   * con_mon grp, mon_grp in resctrl FS.
23   * Run 5 times in order to get average values.
24   */
cat_setup(struct resctrl_val_param * p)25  static int cat_setup(struct resctrl_val_param *p)
26  {
27  	char schemata[64];
28  	int ret = 0;
29  
30  	/* Run NUM_OF_RUNS times */
31  	if (p->num_of_runs >= NUM_OF_RUNS)
32  		return END_OF_TESTS;
33  
34  	if (p->num_of_runs == 0) {
35  		sprintf(schemata, "%lx", p->mask);
36  		ret = write_schemata(p->ctrlgrp, schemata, p->cpu_no,
37  				     p->resctrl_val);
38  	}
39  	p->num_of_runs++;
40  
41  	return ret;
42  }
43  
check_results(struct resctrl_val_param * param,size_t span)44  static int check_results(struct resctrl_val_param *param, size_t span)
45  {
46  	char *token_array[8], temp[512];
47  	unsigned long sum_llc_perf_miss = 0;
48  	int runs = 0, no_of_bits = 0;
49  	FILE *fp;
50  
51  	ksft_print_msg("Checking for pass/fail\n");
52  	fp = fopen(param->filename, "r");
53  	if (!fp) {
54  		ksft_perror("Cannot open file");
55  
56  		return errno;
57  	}
58  
59  	while (fgets(temp, sizeof(temp), fp)) {
60  		char *token = strtok(temp, ":\t");
61  		int fields = 0;
62  
63  		while (token) {
64  			token_array[fields++] = token;
65  			token = strtok(NULL, ":\t");
66  		}
67  		/*
68  		 * Discard the first value which is inaccurate due to monitoring
69  		 * setup transition phase.
70  		 */
71  		if (runs > 0)
72  			sum_llc_perf_miss += strtoul(token_array[3], NULL, 0);
73  		runs++;
74  	}
75  
76  	fclose(fp);
77  	no_of_bits = count_bits(param->mask);
78  
79  	return show_cache_info(sum_llc_perf_miss, no_of_bits, span / 64,
80  			       MAX_DIFF, MAX_DIFF_PERCENT, runs - 1,
81  			       get_vendor() == ARCH_INTEL, false);
82  }
83  
cat_test_cleanup(void)84  void cat_test_cleanup(void)
85  {
86  	remove(RESULT_FILE_NAME1);
87  	remove(RESULT_FILE_NAME2);
88  }
89  
cat_perf_miss_val(int cpu_no,int n,char * cache_type)90  int cat_perf_miss_val(int cpu_no, int n, char *cache_type)
91  {
92  	unsigned long l_mask, l_mask_1;
93  	int ret, pipefd[2], sibling_cpu_no;
94  	unsigned long cache_size = 0;
95  	unsigned long long_mask;
96  	char cbm_mask[256];
97  	int count_of_bits;
98  	char pipe_message;
99  	size_t span;
100  
101  	/* Get default cbm mask for L3/L2 cache */
102  	ret = get_cbm_mask(cache_type, cbm_mask);
103  	if (ret)
104  		return ret;
105  
106  	long_mask = strtoul(cbm_mask, NULL, 16);
107  
108  	/* Get L3/L2 cache size */
109  	ret = get_cache_size(cpu_no, cache_type, &cache_size);
110  	if (ret)
111  		return ret;
112  	ksft_print_msg("Cache size :%lu\n", cache_size);
113  
114  	/* Get max number of bits from default-cabm mask */
115  	count_of_bits = count_bits(long_mask);
116  
117  	if (!n)
118  		n = count_of_bits / 2;
119  
120  	if (n > count_of_bits - 1) {
121  		ksft_print_msg("Invalid input value for no_of_bits n!\n");
122  		ksft_print_msg("Please enter value in range 1 to %d\n",
123  			       count_of_bits - 1);
124  		return -1;
125  	}
126  
127  	/* Get core id from same socket for running another thread */
128  	sibling_cpu_no = get_core_sibling(cpu_no);
129  	if (sibling_cpu_no < 0)
130  		return -1;
131  
132  	struct resctrl_val_param param = {
133  		.resctrl_val	= CAT_STR,
134  		.cpu_no		= cpu_no,
135  		.setup		= cat_setup,
136  	};
137  
138  	l_mask = long_mask >> n;
139  	l_mask_1 = ~l_mask & long_mask;
140  
141  	/* Set param values for parent thread which will be allocated bitmask
142  	 * with (max_bits - n) bits
143  	 */
144  	span = cache_size * (count_of_bits - n) / count_of_bits;
145  	strcpy(param.ctrlgrp, "c2");
146  	strcpy(param.mongrp, "m2");
147  	strcpy(param.filename, RESULT_FILE_NAME2);
148  	param.mask = l_mask;
149  	param.num_of_runs = 0;
150  
151  	if (pipe(pipefd)) {
152  		ksft_perror("Unable to create pipe");
153  		return errno;
154  	}
155  
156  	fflush(stdout);
157  	bm_pid = fork();
158  
159  	/* Set param values for child thread which will be allocated bitmask
160  	 * with n bits
161  	 */
162  	if (bm_pid == 0) {
163  		param.mask = l_mask_1;
164  		strcpy(param.ctrlgrp, "c1");
165  		strcpy(param.mongrp, "m1");
166  		span = cache_size * n / count_of_bits;
167  		strcpy(param.filename, RESULT_FILE_NAME1);
168  		param.num_of_runs = 0;
169  		param.cpu_no = sibling_cpu_no;
170  	}
171  
172  	remove(param.filename);
173  
174  	ret = cat_val(&param, span);
175  	if (ret == 0)
176  		ret = check_results(&param, span);
177  
178  	if (bm_pid == 0) {
179  		/* Tell parent that child is ready */
180  		close(pipefd[0]);
181  		pipe_message = 1;
182  		if (write(pipefd[1], &pipe_message, sizeof(pipe_message)) <
183  		    sizeof(pipe_message))
184  			/*
185  			 * Just print the error message.
186  			 * Let while(1) run and wait for itself to be killed.
187  			 */
188  			ksft_perror("Failed signaling parent process");
189  
190  		close(pipefd[1]);
191  		while (1)
192  			;
193  	} else {
194  		/* Parent waits for child to be ready. */
195  		close(pipefd[1]);
196  		pipe_message = 0;
197  		while (pipe_message != 1) {
198  			if (read(pipefd[0], &pipe_message,
199  				 sizeof(pipe_message)) < sizeof(pipe_message)) {
200  				ksft_perror("Failed reading from child process");
201  				break;
202  			}
203  		}
204  		close(pipefd[0]);
205  		kill(bm_pid, SIGKILL);
206  	}
207  
208  	cat_test_cleanup();
209  
210  	return ret;
211  }
212