| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Cache Monitoring Technology (CMT) test | |
| * | |
| * Copyright (C) 2018 Intel Corporation | |
| * | |
| * Authors: | |
| * Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>, | |
| * Fenghua Yu <fenghua.yu@intel.com> | |
| */ | |
| #include "resctrl.h" | |
| #include <unistd.h> | |
| #define RESULT_FILE_NAME "result_cmt" | |
| #define NUM_OF_RUNS 5 | |
| #define MAX_DIFF 2000000 | |
| #define MAX_DIFF_PERCENT 15 | |
| #define CON_MON_LCC_OCCUP_PATH \ | |
| "%s/%s/mon_data/mon_L3_%02d/llc_occupancy" | |
| /* | |
| * Initialize capacity bitmasks (CBMs) of: | |
| * - control group being tested per test parameters, | |
| * - default resource group as inverse of control group being tested to prevent | |
| * other tasks from interfering with test, | |
| * - L2 resource of control group being tested to minimize allocations into | |
| * L2 if possible to better predict L3 occupancy. | |
| */ | |
| static int cmt_init(const struct resctrl_test *test, | |
| const struct user_params *uparams, | |
| const struct resctrl_val_param *param, int domain_id) | |
| { | |
| unsigned long full_mask; | |
| char schemata[64]; | |
| int ret; | |
| sprintf(llc_occup_path, CON_MON_LCC_OCCUP_PATH, RESCTRL_PATH, | |
| param->ctrlgrp, domain_id); | |
| ret = get_full_cbm(test->resource, &full_mask); | |
| if (ret) | |
| return ret; | |
| snprintf(schemata, sizeof(schemata), "%lx", ~param->mask & full_mask); | |
| ret = write_schemata("", schemata, uparams->cpu, test->resource); | |
| if (ret) | |
| return ret; | |
| snprintf(schemata, sizeof(schemata), "%lx", param->mask); | |
| ret = write_schemata(param->ctrlgrp, schemata, uparams->cpu, test->resource); | |
| if (ret) | |
| return ret; | |
| return minimize_l2_occupancy(test, uparams, param); | |
| } | |
| static int cmt_setup(const struct resctrl_test *test, | |
| const struct user_params *uparams, | |
| struct resctrl_val_param *p) | |
| { | |
| /* Run NUM_OF_RUNS times */ | |
| if (p->num_of_runs >= NUM_OF_RUNS) | |
| return END_OF_TESTS; | |
| p->num_of_runs++; | |
| return 0; | |
| } | |
| static int cmt_measure(const struct user_params *uparams, | |
| struct resctrl_val_param *param, pid_t bm_pid) | |
| { | |
| sleep(1); | |
| return measure_llc_resctrl(param->filename, bm_pid); | |
| } | |
| static int show_results_info(unsigned long sum_llc_val, int no_of_bits, | |
| unsigned long cache_span, unsigned long max_diff, | |
| unsigned long max_diff_percent, unsigned long num_of_runs, | |
| bool platform) | |
| { | |
| unsigned long avg_llc_val = 0; | |
| float diff_percent; | |
| long avg_diff = 0; | |
| int ret; | |
| avg_llc_val = sum_llc_val / num_of_runs; | |
| avg_diff = (long)(cache_span - avg_llc_val); | |
| diff_percent = ((float)cache_span - avg_llc_val) / cache_span * 100; | |
| ret = platform && abs((int)diff_percent) > max_diff_percent && | |
| labs(avg_diff) > max_diff; | |
| ksft_print_msg("%s Check cache miss rate within %lu%%\n", | |
| ret ? "Fail:" : "Pass:", max_diff_percent); | |
| ksft_print_msg("Percent diff=%d\n", abs((int)diff_percent)); | |
| show_cache_info(no_of_bits, avg_llc_val, cache_span, false); | |
| return ret; | |
| } | |
| static int check_results(struct resctrl_val_param *param, size_t span, int no_of_bits) | |
| { | |
| char *token_array[8], temp[512]; | |
| unsigned long sum_llc_occu_resc = 0; | |
| int runs = 0; | |
| FILE *fp; | |
| ksft_print_msg("Checking for pass/fail\n"); | |
| fp = fopen(param->filename, "r"); | |
| if (!fp) { | |
| ksft_perror("Error in opening file"); | |
| return -1; | |
| } | |
| while (fgets(temp, sizeof(temp), fp)) { | |
| char *token = strtok(temp, ":\t"); | |
| int fields = 0; | |
| while (token) { | |
| token_array[fields++] = token; | |
| token = strtok(NULL, ":\t"); | |
| } | |
| /* Field 3 is llc occ resc value */ | |
| sum_llc_occu_resc += strtoul(token_array[3], NULL, 0); | |
| runs++; | |
| } | |
| fclose(fp); | |
| return show_results_info(sum_llc_occu_resc, no_of_bits, span, | |
| MAX_DIFF, MAX_DIFF_PERCENT, runs, true); | |
| } | |
| static void cmt_test_cleanup(void) | |
| { | |
| remove(RESULT_FILE_NAME); | |
| } | |
| static int cmt_run_test(const struct resctrl_test *test, const struct user_params *uparams) | |
| { | |
| struct fill_buf_param fill_buf = {}; | |
| unsigned long cache_total_size = 0; | |
| int n = uparams->bits ? : 5; | |
| unsigned long long_mask; | |
| int count_of_bits; | |
| size_t span; | |
| int ret; | |
| ret = get_full_cbm("L3", &long_mask); | |
| if (ret) | |
| return ret; | |
| ret = get_cache_size(uparams->cpu, "L3", &cache_total_size); | |
| if (ret) | |
| return ret; | |
| ksft_print_msg("Cache size :%lu\n", cache_total_size); | |
| count_of_bits = count_bits(long_mask); | |
| if (n < 1 || n > count_of_bits) { | |
| ksft_print_msg("Invalid input value for numbr_of_bits n!\n"); | |
| ksft_print_msg("Please enter value in range 1 to %d\n", count_of_bits); | |
| return -1; | |
| } | |
| struct resctrl_val_param param = { | |
| .ctrlgrp = "c1", | |
| .filename = RESULT_FILE_NAME, | |
| .mask = ~(long_mask << n) & long_mask, | |
| .num_of_runs = 0, | |
| .init = cmt_init, | |
| .setup = cmt_setup, | |
| .measure = cmt_measure, | |
| }; | |
| span = cache_portion_size(cache_total_size, param.mask, long_mask); | |
| if (uparams->fill_buf) { | |
| fill_buf.buf_size = span * 2; | |
| fill_buf.memflush = uparams->fill_buf->memflush; | |
| param.fill_buf = &fill_buf; | |
| } else if (!uparams->benchmark_cmd[0]) { | |
| fill_buf.buf_size = span * 2; | |
| fill_buf.memflush = true; | |
| param.fill_buf = &fill_buf; | |
| } | |
| remove(RESULT_FILE_NAME); | |
| ret = resctrl_val(test, uparams, ¶m); | |
| if (ret) | |
| return ret; | |
| ret = check_results(¶m, span, n); | |
| if (ret && (get_vendor() == ARCH_INTEL) && !snc_kernel_support()) | |
| ksft_print_msg("Kernel doesn't support Sub-NUMA Clustering but it is enabled on the system.\n"); | |
| return ret; | |
| } | |
| static bool cmt_feature_check(const struct resctrl_test *test) | |
| { | |
| return test_resource_feature_check(test) && | |
| resctrl_mon_feature_exists("L3_MON", "llc_occupancy"); | |
| } | |
| struct resctrl_test cmt_test = { | |
| .name = "CMT", | |
| .resource = "L3", | |
| .feature_check = cmt_feature_check, | |
| .run_test = cmt_run_test, | |
| .cleanup = cmt_test_cleanup, | |
| }; |