forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
229 lines
4.9 KiB
229 lines
4.9 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Cache Allocation Technology (CAT) test |
|
* |
|
* Copyright (C) 2018 Intel Corporation |
|
* |
|
* Authors: |
|
* Sai Praneeth Prakhya <[email protected]>, |
|
* Fenghua Yu <[email protected]> |
|
*/ |
|
#include "resctrl.h" |
|
#include <unistd.h> |
|
|
|
#define RESULT_FILE_NAME1 "result_cat1" |
|
#define RESULT_FILE_NAME2 "result_cat2" |
|
#define NUM_OF_RUNS 5 |
|
#define MAX_DIFF_PERCENT 4 |
|
#define MAX_DIFF 1000000 |
|
|
|
static int count_of_bits; |
|
static char cbm_mask[256]; |
|
static unsigned long long_mask; |
|
static unsigned long cache_size; |
|
|
|
/* |
|
* Change schemata. Write schemata to specified |
|
* con_mon grp, mon_grp in resctrl FS. |
|
* Run 5 times in order to get average values. |
|
*/ |
|
static int cat_setup(int num, ...) |
|
{ |
|
struct resctrl_val_param *p; |
|
char schemata[64]; |
|
va_list param; |
|
int ret = 0; |
|
|
|
va_start(param, num); |
|
p = va_arg(param, struct resctrl_val_param *); |
|
va_end(param); |
|
|
|
/* Run NUM_OF_RUNS times */ |
|
if (p->num_of_runs >= NUM_OF_RUNS) |
|
return -1; |
|
|
|
if (p->num_of_runs == 0) { |
|
sprintf(schemata, "%lx", p->mask); |
|
ret = write_schemata(p->ctrlgrp, schemata, p->cpu_no, |
|
p->resctrl_val); |
|
} |
|
p->num_of_runs++; |
|
|
|
return ret; |
|
} |
|
|
|
static int check_results(struct resctrl_val_param *param) |
|
{ |
|
char *token_array[8], temp[512]; |
|
unsigned long sum_llc_perf_miss = 0; |
|
int runs = 0, no_of_bits = 0; |
|
FILE *fp; |
|
|
|
ksft_print_msg("Checking for pass/fail\n"); |
|
fp = fopen(param->filename, "r"); |
|
if (!fp) { |
|
perror("# Cannot open file"); |
|
|
|
return errno; |
|
} |
|
|
|
while (fgets(temp, sizeof(temp), fp)) { |
|
char *token = strtok(temp, ":\t"); |
|
int fields = 0; |
|
|
|
while (token) { |
|
token_array[fields++] = token; |
|
token = strtok(NULL, ":\t"); |
|
} |
|
/* |
|
* Discard the first value which is inaccurate due to monitoring |
|
* setup transition phase. |
|
*/ |
|
if (runs > 0) |
|
sum_llc_perf_miss += strtoul(token_array[3], NULL, 0); |
|
runs++; |
|
} |
|
|
|
fclose(fp); |
|
no_of_bits = count_bits(param->mask); |
|
|
|
return show_cache_info(sum_llc_perf_miss, no_of_bits, param->span / 64, |
|
MAX_DIFF, MAX_DIFF_PERCENT, NUM_OF_RUNS, |
|
!is_amd, false); |
|
} |
|
|
|
void cat_test_cleanup(void) |
|
{ |
|
remove(RESULT_FILE_NAME1); |
|
remove(RESULT_FILE_NAME2); |
|
} |
|
|
|
int cat_perf_miss_val(int cpu_no, int n, char *cache_type) |
|
{ |
|
unsigned long l_mask, l_mask_1; |
|
int ret, pipefd[2], sibling_cpu_no; |
|
char pipe_message; |
|
pid_t bm_pid; |
|
|
|
cache_size = 0; |
|
|
|
ret = remount_resctrlfs(true); |
|
if (ret) |
|
return ret; |
|
|
|
/* Get default cbm mask for L3/L2 cache */ |
|
ret = get_cbm_mask(cache_type, cbm_mask); |
|
if (ret) |
|
return ret; |
|
|
|
long_mask = strtoul(cbm_mask, NULL, 16); |
|
|
|
/* Get L3/L2 cache size */ |
|
ret = get_cache_size(cpu_no, cache_type, &cache_size); |
|
if (ret) |
|
return ret; |
|
ksft_print_msg("Cache size :%lu\n", cache_size); |
|
|
|
/* Get max number of bits from default-cabm mask */ |
|
count_of_bits = count_bits(long_mask); |
|
|
|
if (!n) |
|
n = count_of_bits / 2; |
|
|
|
if (n > count_of_bits - 1) { |
|
ksft_print_msg("Invalid input value for no_of_bits n!\n"); |
|
ksft_print_msg("Please enter value in range 1 to %d\n", |
|
count_of_bits - 1); |
|
return -1; |
|
} |
|
|
|
/* Get core id from same socket for running another thread */ |
|
sibling_cpu_no = get_core_sibling(cpu_no); |
|
if (sibling_cpu_no < 0) |
|
return -1; |
|
|
|
struct resctrl_val_param param = { |
|
.resctrl_val = CAT_STR, |
|
.cpu_no = cpu_no, |
|
.mum_resctrlfs = 0, |
|
.setup = cat_setup, |
|
}; |
|
|
|
l_mask = long_mask >> n; |
|
l_mask_1 = ~l_mask & long_mask; |
|
|
|
/* Set param values for parent thread which will be allocated bitmask |
|
* with (max_bits - n) bits |
|
*/ |
|
param.span = cache_size * (count_of_bits - n) / count_of_bits; |
|
strcpy(param.ctrlgrp, "c2"); |
|
strcpy(param.mongrp, "m2"); |
|
strcpy(param.filename, RESULT_FILE_NAME2); |
|
param.mask = l_mask; |
|
param.num_of_runs = 0; |
|
|
|
if (pipe(pipefd)) { |
|
perror("# Unable to create pipe"); |
|
return errno; |
|
} |
|
|
|
bm_pid = fork(); |
|
|
|
/* Set param values for child thread which will be allocated bitmask |
|
* with n bits |
|
*/ |
|
if (bm_pid == 0) { |
|
param.mask = l_mask_1; |
|
strcpy(param.ctrlgrp, "c1"); |
|
strcpy(param.mongrp, "m1"); |
|
param.span = cache_size * n / count_of_bits; |
|
strcpy(param.filename, RESULT_FILE_NAME1); |
|
param.num_of_runs = 0; |
|
param.cpu_no = sibling_cpu_no; |
|
} |
|
|
|
remove(param.filename); |
|
|
|
ret = cat_val(¶m); |
|
if (ret) |
|
return ret; |
|
|
|
ret = check_results(¶m); |
|
if (ret) |
|
return ret; |
|
|
|
if (bm_pid == 0) { |
|
/* Tell parent that child is ready */ |
|
close(pipefd[0]); |
|
pipe_message = 1; |
|
if (write(pipefd[1], &pipe_message, sizeof(pipe_message)) < |
|
sizeof(pipe_message)) { |
|
close(pipefd[1]); |
|
perror("# failed signaling parent process"); |
|
return errno; |
|
} |
|
|
|
close(pipefd[1]); |
|
while (1) |
|
; |
|
} else { |
|
/* Parent waits for child to be ready. */ |
|
close(pipefd[1]); |
|
pipe_message = 0; |
|
while (pipe_message != 1) { |
|
if (read(pipefd[0], &pipe_message, |
|
sizeof(pipe_message)) < sizeof(pipe_message)) { |
|
perror("# failed reading from child process"); |
|
break; |
|
} |
|
} |
|
close(pipefd[0]); |
|
kill(bm_pid, SIGKILL); |
|
} |
|
|
|
cat_test_cleanup(); |
|
if (bm_pid) |
|
umount_resctrlfs(); |
|
|
|
return 0; |
|
}
|
|
|