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 CQM,
 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 (!strcmp(param->resctrl_val, "cat")) {
 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 (!strcmp(param->resctrl_val, "cqm")) {
 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 ((strcmp(resctrl_val, "cat") == 0)) {
 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 (strcmp(resctrl_val, "cat") == 0) {
 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