linux/tools/testing/selftests/resctrl/cat_test.c
<<
>>
Prefs
   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
  20static int count_of_bits;
  21static char cbm_mask[256];
  22static unsigned long long_mask;
  23static unsigned long cache_size;
  24
  25/*
  26 * Change schemata. Write schemata to specified
  27 * con_mon grp, mon_grp in resctrl FS.
  28 * Run 5 times in order to get average values.
  29 */
  30static int cat_setup(int num, ...)
  31{
  32        struct resctrl_val_param *p;
  33        char schemata[64];
  34        va_list param;
  35        int ret = 0;
  36
  37        va_start(param, num);
  38        p = va_arg(param, struct resctrl_val_param *);
  39        va_end(param);
  40
  41        /* Run NUM_OF_RUNS times */
  42        if (p->num_of_runs >= NUM_OF_RUNS)
  43                return -1;
  44
  45        if (p->num_of_runs == 0) {
  46                sprintf(schemata, "%lx", p->mask);
  47                ret = write_schemata(p->ctrlgrp, schemata, p->cpu_no,
  48                                     p->resctrl_val);
  49        }
  50        p->num_of_runs++;
  51
  52        return ret;
  53}
  54
  55static int check_results(struct resctrl_val_param *param)
  56{
  57        char *token_array[8], temp[512];
  58        unsigned long sum_llc_perf_miss = 0;
  59        int runs = 0, no_of_bits = 0;
  60        FILE *fp;
  61
  62        ksft_print_msg("Checking for pass/fail\n");
  63        fp = fopen(param->filename, "r");
  64        if (!fp) {
  65                perror("# Cannot open file");
  66
  67                return errno;
  68        }
  69
  70        while (fgets(temp, sizeof(temp), fp)) {
  71                char *token = strtok(temp, ":\t");
  72                int fields = 0;
  73
  74                while (token) {
  75                        token_array[fields++] = token;
  76                        token = strtok(NULL, ":\t");
  77                }
  78                /*
  79                 * Discard the first value which is inaccurate due to monitoring
  80                 * setup transition phase.
  81                 */
  82                if (runs > 0)
  83                        sum_llc_perf_miss += strtoul(token_array[3], NULL, 0);
  84                runs++;
  85        }
  86
  87        fclose(fp);
  88        no_of_bits = count_bits(param->mask);
  89
  90        return show_cache_info(sum_llc_perf_miss, no_of_bits, param->span / 64,
  91                               MAX_DIFF, MAX_DIFF_PERCENT, NUM_OF_RUNS,
  92                               !is_amd, false);
  93}
  94
  95void cat_test_cleanup(void)
  96{
  97        remove(RESULT_FILE_NAME1);
  98        remove(RESULT_FILE_NAME2);
  99}
 100
 101int cat_perf_miss_val(int cpu_no, int n, char *cache_type)
 102{
 103        unsigned long l_mask, l_mask_1;
 104        int ret, pipefd[2], sibling_cpu_no;
 105        char pipe_message;
 106        pid_t bm_pid;
 107
 108        cache_size = 0;
 109
 110        ret = remount_resctrlfs(true);
 111        if (ret)
 112                return ret;
 113
 114        /* Get default cbm mask for L3/L2 cache */
 115        ret = get_cbm_mask(cache_type, cbm_mask);
 116        if (ret)
 117                return ret;
 118
 119        long_mask = strtoul(cbm_mask, NULL, 16);
 120
 121        /* Get L3/L2 cache size */
 122        ret = get_cache_size(cpu_no, cache_type, &cache_size);
 123        if (ret)
 124                return ret;
 125        ksft_print_msg("Cache size :%lu\n", cache_size);
 126
 127        /* Get max number of bits from default-cabm mask */
 128        count_of_bits = count_bits(long_mask);
 129
 130        if (!n)
 131                n = count_of_bits / 2;
 132
 133        if (n > count_of_bits - 1) {
 134                ksft_print_msg("Invalid input value for no_of_bits n!\n");
 135                ksft_print_msg("Please enter value in range 1 to %d\n",
 136                               count_of_bits - 1);
 137                return -1;
 138        }
 139
 140        /* Get core id from same socket for running another thread */
 141        sibling_cpu_no = get_core_sibling(cpu_no);
 142        if (sibling_cpu_no < 0)
 143                return -1;
 144
 145        struct resctrl_val_param param = {
 146                .resctrl_val    = CAT_STR,
 147                .cpu_no         = cpu_no,
 148                .mum_resctrlfs  = 0,
 149                .setup          = cat_setup,
 150        };
 151
 152        l_mask = long_mask >> n;
 153        l_mask_1 = ~l_mask & long_mask;
 154
 155        /* Set param values for parent thread which will be allocated bitmask
 156         * with (max_bits - n) bits
 157         */
 158        param.span = cache_size * (count_of_bits - n) / count_of_bits;
 159        strcpy(param.ctrlgrp, "c2");
 160        strcpy(param.mongrp, "m2");
 161        strcpy(param.filename, RESULT_FILE_NAME2);
 162        param.mask = l_mask;
 163        param.num_of_runs = 0;
 164
 165        if (pipe(pipefd)) {
 166                perror("# Unable to create pipe");
 167                return errno;
 168        }
 169
 170        bm_pid = fork();
 171
 172        /* Set param values for child thread which will be allocated bitmask
 173         * with n bits
 174         */
 175        if (bm_pid == 0) {
 176                param.mask = l_mask_1;
 177                strcpy(param.ctrlgrp, "c1");
 178                strcpy(param.mongrp, "m1");
 179                param.span = cache_size * n / count_of_bits;
 180                strcpy(param.filename, RESULT_FILE_NAME1);
 181                param.num_of_runs = 0;
 182                param.cpu_no = sibling_cpu_no;
 183        }
 184
 185        remove(param.filename);
 186
 187        ret = cat_val(&param);
 188        if (ret)
 189                return ret;
 190
 191        ret = check_results(&param);
 192        if (ret)
 193                return ret;
 194
 195        if (bm_pid == 0) {
 196                /* Tell parent that child is ready */
 197                close(pipefd[0]);
 198                pipe_message = 1;
 199                if (write(pipefd[1], &pipe_message, sizeof(pipe_message)) <
 200                    sizeof(pipe_message)) {
 201                        close(pipefd[1]);
 202                        perror("# failed signaling parent process");
 203                        return errno;
 204                }
 205
 206                close(pipefd[1]);
 207                while (1)
 208                        ;
 209        } else {
 210                /* Parent waits for child to be ready. */
 211                close(pipefd[1]);
 212                pipe_message = 0;
 213                while (pipe_message != 1) {
 214                        if (read(pipefd[0], &pipe_message,
 215                                 sizeof(pipe_message)) < sizeof(pipe_message)) {
 216                                perror("# failed reading from child process");
 217                                break;
 218                        }
 219                }
 220                close(pipefd[0]);
 221                kill(bm_pid, SIGKILL);
 222        }
 223
 224        cat_test_cleanup();
 225        if (bm_pid)
 226                umount_resctrlfs();
 227
 228        return 0;
 229}
 230