"Fossies" - the Fresh Open Source Software Archive

Member "stress-ng-0.09.56/core-cache.c" (15 Mar 2019, 13097 Bytes) of package /linux/privat/stress-ng-0.09.56.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "core-cache.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 0.09.52_vs_0.09.54.

    1 /*
    2  * Copyright (C) 2016-2017 Intel, Ltd.
    3  * Copyright (C) 2016-2019 Canonical, Ltd.
    4  *
    5  * This program is free software; you can redistribute it and/or
    6  * modify it under the terms of the GNU General Public License
    7  * as published by the Free Software Foundation; either version 2
    8  * of the License, or (at your option) any later version.
    9  *
   10  * This program is distributed in the hope that it will be useful,
   11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13  * GNU General Public License for more details.
   14  *
   15  * You should have received a copy of the GNU General Public License
   16  * along with this program; if not, write to the Free Software
   17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   18  *
   19  */
   20 #include "stress-ng.h"
   21 
   22 #if defined(__linux__)
   23 
   24 typedef struct {
   25     const char  *name;      /* cache type name */
   26     const uint32_t  value;      /* cache type ID */
   27 } generic_map_t;
   28 
   29 #include <glob.h>
   30 
   31 #if defined(__linux__)
   32 #define SYS_CPU_PREFIX               "/sys/devices/system/cpu"
   33 #define GLOB_PATTERN SYS_CPU_PREFIX  "/cpu[0-9]*"
   34 #define SYS_CPU_CACHE_DIR            "/cache"
   35 #define GLOB_PATTERN_INDEX_PREFIX    "/index[0-9]*"
   36 #endif
   37 
   38 static const generic_map_t cache_type_map[] = {
   39     {"data"        , CACHE_TYPE_DATA},
   40     {"instruction" , CACHE_TYPE_INSTRUCTION},
   41     {"unified"     , CACHE_TYPE_UNIFIED},
   42     { NULL         , CACHE_TYPE_UNKNOWN}
   43 };
   44 
   45 /*
   46  * append @element to array @path (which has len @len)
   47  */
   48 static inline void mk_path(char *path, size_t len, const char *element)
   49 {
   50     const size_t e_len = strlen(element);
   51 
   52     path[len] = '\0';
   53     (void)strncpy((path) + len, element, e_len + 1);
   54     path[len + e_len] = '\0';
   55 }
   56 
   57 /**
   58  *
   59  * cache_get_cpu()
   60  *
   61  **/
   62 static inline unsigned int cache_get_cpu(const cpus_t *cpus)
   63 {
   64     const unsigned int cpu = stress_get_cpu();
   65 
   66     return (cpu >= cpus->count) ? 0 : cpu;
   67 }
   68 
   69 /**
   70  * file_exists()
   71  * @path: file to check.
   72  * Determine if specified file exists.
   73  *
   74  * Returns: file type if @path exists, else 0.
   75  **/
   76 static int file_exists(const char *path)
   77 {
   78     struct stat st;
   79 
   80     if (!path) {
   81         pr_dbg("%s: empty path specified\n", __func__);
   82         return 0;
   83     }
   84     if (stat(path, &st) < 0)
   85         return 0;
   86 
   87     return (st.st_mode & S_IFMT);
   88 }
   89 
   90 /*
   91  * get_contents()
   92  * @path: file to read.
   93  * Reads the contents of @file, returning the value as a string.
   94  *
   95  * Returns: dynamically-allocated copy of the contents of @path,
   96  * or NULL on error.
   97  */
   98 static char *get_contents(const char *path)
   99 {
  100     FILE         *fp = NULL;
  101     char         *contents = NULL;
  102     struct stat   st;
  103     size_t        size;
  104 
  105     if (!path) {
  106         pr_dbg("%s: empty path specified\n", __func__);
  107         return NULL;
  108     }
  109 
  110     fp = fopen(path, "r");
  111     if (!fp)
  112         return NULL;
  113 
  114     if (fstat(fileno(fp), &st) < 0)
  115         goto err_close;
  116 
  117     size = st.st_size;
  118 
  119     contents = malloc(size);
  120     if (!contents)
  121         goto err_close;
  122 
  123     if (!fgets(contents, size, fp))
  124         goto err;
  125 
  126     (void)fclose(fp);
  127     return contents;
  128 
  129 err:
  130     free(contents);
  131 err_close:
  132     (void)fclose(fp);
  133     return NULL;
  134 }
  135 
  136 /*
  137  * get_string_from_file()
  138  * @path: file to read contents of.
  139  *
  140  * Returns: dynamically-allocated copy of the contents of @path,
  141  * or NULL on error.
  142  */
  143 static char *get_string_from_file(const char *path)
  144 {
  145     char   *str;
  146     ssize_t  len;
  147 
  148     str = get_contents(path);
  149     if (!str)
  150         return NULL;
  151 
  152     len = strlen(str) - 1;
  153     if ((len >= 0) && (str[len] == '\n'))
  154         str[len] = '\0';
  155 
  156     return str;
  157 }
  158 
  159 /*
  160  * size_to_bytes()
  161  * Convert human-readable integer sizes (such as "32K", "4M") into bytes.
  162  *
  163  * Supports:
  164  *
  165  * - bytes ('B').
  166  * - kibibytes ('K' - aka KiB).
  167  * - mebibytes ('M' - aka MiB).
  168  * - gibibytes ('G' - aka GiB).
  169  *
  170  * Returns: size in bytes, or 0 on error.
  171  */
  172 static uint64_t size_to_bytes(const char *str)
  173 {
  174     uint64_t            bytes;
  175     uint64_t            multiplier;
  176     unsigned long int   value;
  177     int                 ret;
  178     char               *s;
  179 
  180     if (!str) {
  181         pr_dbg("%s: empty string specified\n", __func__);
  182         return 0;
  183     }
  184 
  185     ret = sscanf(str, "%lu%ms", &value, &s);
  186     if ((ret != 2) || !s) {
  187         pr_dbg("%s: failed to parse suffix from \"%s\"\n",
  188             __func__, str);
  189         return 0;
  190     }
  191 
  192     switch (*s) {
  193     case 'B':
  194         multiplier = 1;
  195         break;
  196     case 'K':
  197         multiplier = KB;
  198         break;
  199     case 'M':
  200         multiplier = MB;
  201         break;
  202     case 'G':
  203         multiplier = GB;
  204         break;
  205     default:
  206         pr_err("unable to convert string to bytes: %s\n", str);
  207         bytes = 0;
  208         goto out;
  209     }
  210 
  211     bytes = value * multiplier;
  212 
  213 out:
  214     free(s);
  215     return bytes;
  216 }
  217 
  218 /*
  219  * get_cache_type()
  220  * @name: human-readable cache type.
  221  * Convert a human-readable cache type into a cache_type_t.
  222  *
  223  * Returns: cache_type_t or CACHE_TYPE_UNKNOWN on error.
  224  */
  225 static cache_type_t get_cache_type(const char *name)
  226 {
  227     const generic_map_t *p;
  228 
  229     if (!name) {
  230         pr_dbg("%s: no cache type specified\n", __func__);
  231         goto out;
  232     }
  233 
  234     for (p = cache_type_map; p && p->name; p++) {
  235         if (!strcasecmp(p->name, name))
  236             return p->value;
  237     }
  238 
  239 out:
  240     return CACHE_TYPE_UNKNOWN;
  241 }
  242 
  243 /*
  244  * add_cpu_cache_detail()
  245  * @cache: cpu_cache_t pointer.
  246  * @index_path: full /sys path to the particular cpu cache which is to
  247  *   be represented by @cache.
  248  * Populate the specified @cache based on the given cache index.
  249  *
  250  * Returns: EXIT_FAILURE or EXIT_SUCCESS.
  251  */
  252 static int add_cpu_cache_detail(cpu_cache_t *cache, const char *index_path)
  253 {
  254     const size_t index_posn = index_path ? strlen(index_path) : 0;
  255     const size_t path_len = index_posn + 32;
  256     char path[path_len];
  257     char *contents = NULL;
  258     int ret = EXIT_FAILURE;
  259 
  260     (void)memset(path, 0, sizeof(path));
  261     if (!cache) {
  262         pr_dbg("%s: invalid cache specified\n", __func__);
  263         goto out;
  264     }
  265     if (!index_path) {
  266         pr_dbg("%s: invalid index specified\n", __func__);
  267         goto out;
  268     }
  269 
  270     (void)strncpy(path, index_path, index_posn + 1);
  271     mk_path(path, index_posn, "/type");
  272     contents = get_string_from_file(path);
  273     if (!contents)
  274         goto out;
  275 
  276     cache->type = (cache_type_t)get_cache_type(contents);
  277     if (cache->type == CACHE_TYPE_UNKNOWN)
  278         goto out;
  279     free(contents);
  280 
  281     mk_path(path, index_posn, "/size");
  282     contents = get_string_from_file(path);
  283     if (!contents)
  284         goto out;
  285 
  286     cache->size = size_to_bytes(contents);
  287     free(contents);
  288 
  289     mk_path(path, index_posn, "/level");
  290     contents = get_string_from_file(path);
  291     if (!contents)
  292         goto out;
  293 
  294     cache->level = (uint16_t)atoi(contents);
  295     free(contents);
  296 
  297     mk_path(path, index_posn, "/coherency_line_size");
  298     contents = get_string_from_file(path);
  299     if (!contents)
  300         goto out;
  301 
  302     cache->line_size = (uint32_t)atoi(contents);
  303     free(contents);
  304 
  305     mk_path(path, index_posn, "/ways_of_associativity");
  306     contents = get_string_from_file(path);
  307 
  308     /* Don't error if file is not readable: cache may not be
  309      * way-based.
  310      */
  311     cache->ways = contents ? atoi(contents) : 0;
  312 
  313     ret = EXIT_SUCCESS;
  314 
  315 out:
  316     if (contents)
  317         free(contents);
  318 
  319     return ret;
  320 }
  321 
  322 /*
  323  * get_cache_by_cpu()
  324  * @cpu: cpu to consider.
  325  * @cache_level: numeric cache level (1-indexed).
  326  * Obtain the cpu cache indexed by @cache_level.
  327  *
  328  * POTENTIAL BUG: assumes only 1 data cache per CPU cache level.
  329  *
  330  * Returns: cpu_cache_t, or NULL on error.
  331  */
  332 static cpu_cache_t * get_cache_by_cpu(const cpu_t *cpu, const int cache_level)
  333 {
  334     uint32_t  i;
  335 
  336     if (!cpu || !cache_level)
  337         return NULL;
  338 
  339     for (i = 0; i < cpu->cache_count; i++) {
  340         cpu_cache_t *p;
  341 
  342         p = &cpu->caches[i];
  343         if (p->level != cache_level)
  344             continue;
  345 
  346         /* we want a data cache */
  347         if (p->type != CACHE_TYPE_INSTRUCTION)
  348             return p;
  349     }
  350     return NULL;
  351 }
  352 
  353 /*
  354  * get_max_cache_level()
  355  * @cpus: array of cpus to query.
  356  * Determine the maximum cache level available on the system.
  357  *
  358  * Returns: 1-index value denoting highest cache level, or 0 on error.
  359  */
  360 uint16_t get_max_cache_level(const cpus_t *cpus)
  361 {
  362     cpu_t    *cpu;
  363     uint32_t  i;
  364     uint16_t  max = 0;
  365 
  366     if (!cpus) {
  367         pr_dbg("%s: invalid cpus parameter\n", __func__);
  368         return 0;
  369     }
  370 
  371     cpu = &cpus->cpus[cache_get_cpu(cpus)];
  372 
  373     for (i = 0; i < cpu->cache_count; i++) {
  374         cpu_cache_t *cache;
  375 
  376         cache = &cpu->caches[i];
  377         max = cache->level > max ? cache->level : max;
  378     }
  379 
  380     return max;
  381 }
  382 
  383 /*
  384  * get_cpu_cache()
  385  * @cpus: array of cpus to query.
  386  * @cache_level: numeric cache level (1-indexed).
  387  * Obtain a cpu cache of level @cache_level.
  388  *
  389  * Returns: cpu_cache_t pointer, or NULL on error.
  390  */
  391 cpu_cache_t * get_cpu_cache(const cpus_t *cpus, const uint16_t cache_level)
  392 {
  393     cpu_t *cpu;
  394 
  395     if (!cpus) {
  396         pr_dbg("%s: invalid cpus parameter\n", __func__);
  397         return NULL;
  398     }
  399 
  400     if (!cache_level) {
  401         pr_dbg("%s: invalid cache_level: %d\n",
  402             __func__, cache_level);
  403         return NULL;
  404     }
  405 
  406     cpu = &cpus->cpus[cache_get_cpu(cpus)];
  407 
  408     return get_cache_by_cpu(cpu, cache_level);
  409 }
  410 
  411 /*
  412  * get_cpu_cache_details()
  413  * @cpu: cpu to fill in.
  414  * @cpu_path: Full /sys path to cpu which will be represented by @cpu.
  415  * Populate @cpu with details from @cpu_path.
  416  *
  417  * Returns: EXIT_FAILURE or EXIT_SUCCESS.
  418  */
  419 static int get_cpu_cache_details(cpu_t *cpu, const char *cpu_path)
  420 {
  421     uint32_t   i;
  422     size_t     len = cpu_path ? strlen(cpu_path) : 0;
  423     size_t     len2 = strlen(SYS_CPU_CACHE_DIR) + 1;
  424     glob_t     globbuf;
  425     char       glob_path[len + len2];
  426     char     **results;
  427     int        ret = EXIT_FAILURE;
  428     int        ret2;
  429 
  430     (void)memset(glob_path, 0, sizeof(glob_path));
  431     (void)memset(&globbuf, 0, sizeof(globbuf));
  432 
  433     if (!cpu) {
  434         pr_dbg("%s: invalid cpu parameter\n", __func__);
  435         return ret;
  436     }
  437     if (!cpu_path) {
  438         pr_dbg("%s: invalid cpu path parameter\n", __func__);
  439         return ret;
  440     }
  441 
  442     (void)strncat(glob_path, cpu_path, len);
  443     (void)strncat(glob_path, SYS_CPU_CACHE_DIR, len2);
  444     len += len2;
  445 
  446     ret2 = file_exists(glob_path);
  447     if (!ret2) {
  448         /*
  449          * Not an error since some platforms don't provide cache
  450          * details * via /sys (ARM).
  451          */
  452         /*
  453         if (warn_once(WARN_ONCE_NO_CACHE))
  454             pr_dbg("%s does not exist\n", glob_path);
  455          */
  456         return ret;
  457     }
  458 
  459     if (ret2 != S_IFDIR) {
  460         if (warn_once(WARN_ONCE_NO_CACHE))
  461             pr_err("file %s is not a directory\n",
  462                 glob_path);
  463         return ret;
  464     }
  465 
  466     (void)strncat(glob_path, GLOB_PATTERN_INDEX_PREFIX,
  467         sizeof(glob_path) - len - 1);
  468     ret2 = glob(glob_path, GLOB_ONLYDIR, NULL, &globbuf);
  469     if (ret2 != 0) {
  470         if (warn_once(WARN_ONCE_NO_CACHE))
  471             pr_err("glob on regex \"%s\" failed: %d\n",
  472                 glob_path, ret);
  473         return ret;
  474     }
  475 
  476     results = globbuf.gl_pathv;
  477     cpu->cache_count = globbuf.gl_pathc;
  478 
  479     if (!cpu->cache_count) {
  480         if (warn_once(WARN_ONCE_NO_CACHE))
  481             pr_err("no CPU caches found\n");
  482         goto err;
  483     }
  484 
  485     cpu->caches = calloc(cpu->cache_count, sizeof(cpu_cache_t));
  486     if (!cpu->caches) {
  487         size_t cache_bytes = cpu->cache_count * sizeof(cpu_cache_t);
  488 
  489         pr_err("failed to allocate %zu bytes for cpu caches\n",
  490             cache_bytes);
  491         goto err;
  492     }
  493 
  494     for (i = 0; i < cpu->cache_count; i++) {
  495         ret2 = add_cpu_cache_detail(&cpu->caches[i], results[i]);
  496         if (ret2 != EXIT_SUCCESS)
  497             goto err;
  498     }
  499 
  500     ret = EXIT_SUCCESS;
  501 
  502 err:
  503     globfree(&globbuf);
  504 
  505     /* reset */
  506     glob_path[0] = '\0';
  507 
  508     return ret;
  509 }
  510 
  511 /*
  512  * get_all_cpu_cache_details()
  513  * Obtain information on all cpus caches on the system.
  514  *
  515  * Returns: dynamically-allocated cpus_t object, or NULL on error.
  516  */
  517 cpus_t * get_all_cpu_cache_details(void)
  518 {
  519     uint32_t   i;
  520     int        ret;
  521     glob_t     globbuf;
  522     char     **results;
  523     cpus_t    *cpus = NULL;
  524     size_t     cpu_count;
  525 
  526     (void)memset(&globbuf, 0, sizeof(globbuf));
  527 
  528     ret = file_exists(SYS_CPU_PREFIX);
  529     if (!ret) {
  530         pr_err("%s does not exist\n", SYS_CPU_PREFIX);
  531         return NULL;
  532     }
  533     if (ret != S_IFDIR) {
  534         pr_err("file %s is not a directory\n", SYS_CPU_PREFIX);
  535         return NULL;
  536     }
  537 
  538     ret = glob(GLOB_PATTERN, GLOB_ONLYDIR, NULL, &globbuf);
  539     if (ret != 0) {
  540         pr_err("glob on regex \"%s\" failed: %d\n",
  541             GLOB_PATTERN, ret);
  542         return NULL;
  543     }
  544 
  545     results = globbuf.gl_pathv;
  546     cpu_count = globbuf.gl_pathc;
  547     if (!cpu_count) {
  548         /* Maybe we should check this? */
  549         pr_err("no CPUs found - is /sys mounted?\n");
  550         goto out;
  551     }
  552 
  553     cpus = calloc(1, sizeof(cpus_t));
  554     if (!cpus)
  555         goto out;
  556 
  557     cpus->cpus = calloc(cpu_count, sizeof(cpu_t));
  558     if (!cpus->cpus) {
  559         free(cpus);
  560         cpus = NULL;
  561         goto out;
  562     }
  563     cpus->count = cpu_count;
  564 
  565     for (i = 0; i < cpus->count; i++) {
  566         char *contents = NULL;
  567         cpu_t *cpu;
  568 
  569         cpu = &cpus->cpus[i];
  570         cpu->num = i;
  571 
  572         if (i == 0) {
  573             /* 1st CPU cannot be taken offline */
  574             cpu->online = 1;
  575         } else {
  576             const size_t len = strlen(results[i]);
  577             char path[len + 32];
  578 
  579             (void)memset(path, 0, sizeof(path));
  580             (void)strncpy(path, results[i], len);
  581             mk_path(path, len, "/online");
  582 
  583             contents = get_string_from_file(path);
  584             if (!contents)
  585                 goto out;
  586             cpu->online = atoi(contents);
  587             free(contents);
  588         }
  589 
  590         ret = get_cpu_cache_details(&cpus->cpus[i], results[i]);
  591         if (ret != EXIT_SUCCESS) {
  592             free(cpus->cpus);
  593             free(cpus);
  594             cpus = NULL;
  595             goto out;
  596         }
  597     }
  598 
  599 out:
  600     globfree(&globbuf);
  601 
  602     return cpus;
  603 }
  604 
  605 /*
  606  * free_cpu_caches()
  607  * @cpus: value returned by get_all_cpu_cache_details().
  608  *
  609  * Undo the action of get_all_cpu_cache_details() by freeing all
  610  * associated resources.
  611  */
  612 void free_cpu_caches(cpus_t *cpus)
  613 {
  614     uint32_t  i;
  615 
  616     if (!cpus)
  617         return;
  618 
  619     for (i = 0; i < cpus->count; i++) {
  620         cpu_t *cpu = &cpus->cpus[i];
  621 
  622         free(cpu->caches);
  623     }
  624     free(cpus->cpus);
  625     free(cpus);
  626 }
  627 
  628 #endif