linux/tools/testing/selftests/resctrl/cache.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3#include <stdint.h>
   4#include "resctrl.h"
   5
   6struct read_format {
   7        __u64 nr;                       /* The number of events */
   8        struct {
   9                __u64 value;            /* The value of the event */
  10        } values[2];
  11};
  12
  13static struct perf_event_attr pea_llc_miss;
  14static struct read_format rf_cqm;
  15static int fd_lm;
  16char llc_occup_path[1024];
  17
  18static void initialize_perf_event_attr(void)
  19{
  20        pea_llc_miss.type = PERF_TYPE_HARDWARE;
  21        pea_llc_miss.size = sizeof(struct perf_event_attr);
  22        pea_llc_miss.read_format = PERF_FORMAT_GROUP;
  23        pea_llc_miss.exclude_kernel = 1;
  24        pea_llc_miss.exclude_hv = 1;
  25        pea_llc_miss.exclude_idle = 1;
  26        pea_llc_miss.exclude_callchain_kernel = 1;
  27        pea_llc_miss.inherit = 1;
  28        pea_llc_miss.exclude_guest = 1;
  29        pea_llc_miss.disabled = 1;
  30}
  31
  32static void ioctl_perf_event_ioc_reset_enable(void)
  33{
  34        ioctl(fd_lm, PERF_EVENT_IOC_RESET, 0);
  35        ioctl(fd_lm, PERF_EVENT_IOC_ENABLE, 0);
  36}
  37
  38static int perf_event_open_llc_miss(pid_t pid, int cpu_no)
  39{
  40        fd_lm = perf_event_open(&pea_llc_miss, pid, cpu_no, -1,
  41                                PERF_FLAG_FD_CLOEXEC);
  42        if (fd_lm == -1) {
  43                perror("Error opening leader");
  44                ctrlc_handler(0, NULL, NULL);
  45                return -1;
  46        }
  47
  48        return 0;
  49}
  50
  51static int initialize_llc_perf(void)
  52{
  53        memset(&pea_llc_miss, 0, sizeof(struct perf_event_attr));
  54        memset(&rf_cqm, 0, sizeof(struct read_format));
  55
  56        /* Initialize perf_event_attr structures for HW_CACHE_MISSES */
  57        initialize_perf_event_attr();
  58
  59        pea_llc_miss.config = PERF_COUNT_HW_CACHE_MISSES;
  60
  61        rf_cqm.nr = 1;
  62
  63        return 0;
  64}
  65
  66static int reset_enable_llc_perf(pid_t pid, int cpu_no)
  67{
  68        int ret = 0;
  69
  70        ret = perf_event_open_llc_miss(pid, cpu_no);
  71        if (ret < 0)
  72                return ret;
  73
  74        /* Start counters to log values */
  75        ioctl_perf_event_ioc_reset_enable();
  76
  77        return 0;
  78}
  79
  80/*
  81 * get_llc_perf:        llc cache miss through perf events
  82 * @cpu_no:             CPU number that the benchmark PID is binded to
  83 *
  84 * Perf events like HW_CACHE_MISSES could be used to validate number of
  85 * cache lines allocated.
  86 *
  87 * Return: =0 on success.  <0 on failure.
  88 */
  89static int get_llc_perf(unsigned long *llc_perf_miss)
  90{
  91        __u64 total_misses;
  92
  93        /* Stop counters after one span to get miss rate */
  94
  95        ioctl(fd_lm, PERF_EVENT_IOC_DISABLE, 0);
  96
  97        if (read(fd_lm, &rf_cqm, sizeof(struct read_format)) == -1) {
  98                perror("Could not get llc misses through perf");
  99
 100                return -1;
 101        }
 102
 103        total_misses = rf_cqm.values[0].value;
 104
 105        close(fd_lm);
 106
 107        *llc_perf_miss = total_misses;
 108
 109        return 0;
 110}
 111
 112/*
 113 * Get LLC Occupancy as reported by RESCTRL FS
 114 * For CMT,
 115 * 1. If con_mon grp and mon grp given, then read from mon grp in
 116 * con_mon grp
 117 * 2. If only con_mon grp given, then read from con_mon grp
 118 * 3. If both not given, then read from root con_mon grp
 119 * For CAT,
 120 * 1. If con_mon grp given, then read from it
 121 * 2. If con_mon grp not given, then read from root con_mon grp
 122 *
 123 * Return: =0 on success.  <0 on failure.
 124 */
 125static int get_llc_occu_resctrl(unsigned long *llc_occupancy)
 126{
 127        FILE *fp;
 128
 129        fp = fopen(llc_occup_path, "r");
 130        if (!fp) {
 131                perror("Failed to open results file");
 132
 133                return errno;
 134        }
 135        if (fscanf(fp, "%lu", llc_occupancy) <= 0) {
 136                perror("Could not get llc occupancy");
 137                fclose(fp);
 138
 139                return -1;
 140        }
 141        fclose(fp);
 142
 143        return 0;
 144}
 145
 146/*
 147 * print_results_cache: the cache results are stored in a file
 148 * @filename:           file that stores the results
 149 * @bm_pid:             child pid that runs benchmark
 150 * @llc_value:          perf miss value /
 151 *                      llc occupancy value reported by resctrl FS
 152 *
 153 * Return:              0 on success. non-zero on failure.
 154 */
 155static int print_results_cache(char *filename, int bm_pid,
 156                               unsigned long llc_value)
 157{
 158        FILE *fp;
 159
 160        if (strcmp(filename, "stdio") == 0 || strcmp(filename, "stderr") == 0) {
 161                printf("Pid: %d \t LLC_value: %lu\n", bm_pid,
 162                       llc_value);
 163        } else {
 164                fp = fopen(filename, "a");
 165                if (!fp) {
 166                        perror("Cannot open results file");
 167
 168                        return errno;
 169                }
 170                fprintf(fp, "Pid: %d \t llc_value: %lu\n", bm_pid, llc_value);
 171                fclose(fp);
 172        }
 173
 174        return 0;
 175}
 176
 177int measure_cache_vals(struct resctrl_val_param *param, int bm_pid)
 178{
 179        unsigned long llc_perf_miss = 0, llc_occu_resc = 0, llc_value = 0;
 180        int ret;
 181
 182        /*
 183         * Measure cache miss from perf.
 184         */
 185        if (!strncmp(param->resctrl_val, CAT_STR, sizeof(CAT_STR))) {
 186                ret = get_llc_perf(&llc_perf_miss);
 187                if (ret < 0)
 188                        return ret;
 189                llc_value = llc_perf_miss;
 190        }
 191
 192        /*
 193         * Measure llc occupancy from resctrl.
 194         */
 195        if (!strncmp(param->resctrl_val, CMT_STR, sizeof(CMT_STR))) {
 196                ret = get_llc_occu_resctrl(&llc_occu_resc);
 197                if (ret < 0)
 198                        return ret;
 199                llc_value = llc_occu_resc;
 200        }
 201        ret = print_results_cache(param->filename, bm_pid, llc_value);
 202        if (ret)
 203                return ret;
 204
 205        return 0;
 206}
 207
 208/*
 209 * cache_val:           execute benchmark and measure LLC occupancy resctrl
 210 * and perf cache miss for the benchmark
 211 * @param:              parameters passed to cache_val()
 212 *
 213 * Return:              0 on success. non-zero on failure.
 214 */
 215int cat_val(struct resctrl_val_param *param)
 216{
 217        int malloc_and_init_memory = 1, memflush = 1, operation = 0, ret = 0;
 218        char *resctrl_val = param->resctrl_val;
 219        pid_t bm_pid;
 220
 221        if (strcmp(param->filename, "") == 0)
 222                sprintf(param->filename, "stdio");
 223
 224        bm_pid = getpid();
 225
 226        /* Taskset benchmark to specified cpu */
 227        ret = taskset_benchmark(bm_pid, param->cpu_no);
 228        if (ret)
 229                return ret;
 230
 231        /* Write benchmark to specified con_mon grp, mon_grp in resctrl FS*/
 232        ret = write_bm_pid_to_resctrl(bm_pid, param->ctrlgrp, param->mongrp,
 233                                      resctrl_val);
 234        if (ret)
 235                return ret;
 236
 237        if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) {
 238                ret = initialize_llc_perf();
 239                if (ret)
 240                        return ret;
 241        }
 242
 243        /* Test runs until the callback setup() tells the test to stop. */
 244        while (1) {
 245                if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) {
 246                        ret = param->setup(1, param);
 247                        if (ret) {
 248                                ret = 0;
 249                                break;
 250                        }
 251                        ret = reset_enable_llc_perf(bm_pid, param->cpu_no);
 252                        if (ret)
 253                                break;
 254
 255                        if (run_fill_buf(param->span, malloc_and_init_memory,
 256                                         memflush, operation, resctrl_val)) {
 257                                fprintf(stderr, "Error-running fill buffer\n");
 258                                ret = -1;
 259                                break;
 260                        }
 261
 262                        sleep(1);
 263                        ret = measure_cache_vals(param, bm_pid);
 264                        if (ret)
 265                                break;
 266                } else {
 267                        break;
 268                }
 269        }
 270
 271        return ret;
 272}
 273
 274/*
 275 * show_cache_info:     show cache test result information
 276 * @sum_llc_val:        sum of LLC cache result data
 277 * @no_of_bits:         number of bits
 278 * @cache_span:         cache span in bytes for CMT or in lines for CAT
 279 * @max_diff:           max difference
 280 * @max_diff_percent:   max difference percentage
 281 * @num_of_runs:        number of runs
 282 * @platform:           show test information on this platform
 283 * @cmt:                CMT test or CAT test
 284 *
 285 * Return:              0 on success. non-zero on failure.
 286 */
 287int show_cache_info(unsigned long sum_llc_val, int no_of_bits,
 288                    unsigned long cache_span, unsigned long max_diff,
 289                    unsigned long max_diff_percent, unsigned long num_of_runs,
 290                    bool platform, bool cmt)
 291{
 292        unsigned long avg_llc_val = 0;
 293        float diff_percent;
 294        long avg_diff = 0;
 295        int ret;
 296
 297        avg_llc_val = sum_llc_val / (num_of_runs - 1);
 298        avg_diff = (long)abs(cache_span - avg_llc_val);
 299        diff_percent = ((float)cache_span - avg_llc_val) / cache_span * 100;
 300
 301        ret = platform && abs((int)diff_percent) > max_diff_percent &&
 302              (cmt ? (abs(avg_diff) > max_diff) : true);
 303
 304        ksft_print_msg("%s Check cache miss rate within %d%%\n",
 305                       ret ? "Fail:" : "Pass:", max_diff_percent);
 306
 307        ksft_print_msg("Percent diff=%d\n", abs((int)diff_percent));
 308        ksft_print_msg("Number of bits: %d\n", no_of_bits);
 309        ksft_print_msg("Average LLC val: %lu\n", avg_llc_val);
 310        ksft_print_msg("Cache span (%s): %lu\n", cmt ? "bytes" : "lines",
 311                       cache_span);
 312
 313        return ret;
 314}
 315