/* * Copyright (C) 2013 Red Hat, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Peter Schiffer */ #include "sysfs.h" /* * Read unsigned value from file. * @param path of file * @paratm result * @return 0 if success, negative value otherwise */ short path_get_unsigned(const char *path, unsigned *result) { short ret = -1; unsigned buffer_size = 0; char **buffer = NULL; if (read_file(path, &buffer, &buffer_size) != 0 || buffer_size < 1) { ret = -2; goto done; } if (sscanf(buffer[0], "%u", result) != 1) { warn("Failed to parse file: \"%s\"; Error: %s", path, strerror(errno)); ret = -3; goto done; } ret = 0; done: free_2d_buffer(&buffer, &buffer_size); if (ret != 0) { *result = 0; } return ret; } /* * Read string value from file. * @param path of file * @paratm result * @return 0 if success, negative value otherwise */ short path_get_string(const char *path, char **result) { short ret = -1; unsigned buffer_size = 0; char **buffer = NULL; if (read_file(path, &buffer, &buffer_size) != 0 || buffer_size < 1) { ret = -2; goto done; } *result = trim(buffer[0], NULL); if (!(*result)) { warn("Failed to parse file: \"%s\"", path); ret = -3; goto done; } ret = 0; done: free_2d_buffer(&buffer, &buffer_size); if (ret != 0) { *result = NULL; } return ret; } /* * Initialize SysfsCpuCache attributes. * @param cache */ void init_sysfs_cpu_cache_struct(SysfsCpuCache *cache) { cache->id = NULL; cache->size = 0; cache->name = NULL; cache->level = 0; cache->type = NULL; cache->ways_of_assoc = 0; cache->line_size = 0; } /* * Check attributes of cache structure and fill in defaults if needed. * @param cache * @return 0 if success, negative value otherwise */ short check_sysfs_cpu_cache_attributes(SysfsCpuCache *cache) { short ret = -1; if (!cache->id) { if (!(cache->id = strdup(""))) { ret = -2; goto done; } } if (!cache->name) { if (!(cache->name = strdup(""))) { ret = -3; goto done; } } if (!cache->type) { if (!(cache->type = strdup("Unknown"))) { ret = -4; goto done; } } ret = 0; done: if (ret != 0) { warn("Failed to allocate memory."); } return ret; } short sysfs_get_cpu_caches(SysfsCpuCache **caches, unsigned *caches_nb) { short ret = -1; unsigned i, level; char *buf = NULL, *format_str, path[PATH_MAX]; DIR *dir; *caches_nb = 0; /* count caches */ char *cache_dir = SYSFS_CPU_PATH "/cpu0/cache"; dir = opendir(cache_dir); if (!dir) { warn("Failed to read directory: \"%s\"; Error: %s", cache_dir, strerror(errno)); ret = -2; goto done; } while (readdir(dir)) { (*caches_nb)++; } closedir(dir); /* do not count . and .. */ *caches_nb -= 2; /* if no cache was found */ if (*caches_nb < 1) { warn("No processor cache was found in sysfs."); ret = -3; goto done; } /* allocate memory for caches */ *caches = (SysfsCpuCache *)calloc(*caches_nb, sizeof(SysfsCpuCache)); if (!(*caches)) { warn("Failed to allocate memory."); ret = -4; goto done; } for (i = 0; i < *caches_nb; i++) { init_sysfs_cpu_cache_struct(&(*caches)[i]); /* cache ID and name */ /* cache level */ snprintf(path, PATH_MAX, SYSFS_CPU_PATH "/cpu0/cache/index%u/level", i); if (path_get_unsigned(path, &level) != 0) { ret = -5; goto done; } (*caches)[i].level = level; /* cache type */ snprintf(path, PATH_MAX, SYSFS_CPU_PATH "/cpu0/cache/index%u/type", i); if (path_get_string(path, &buf) != 0) { ret = -6; goto done; } if (strncmp(buf, "Data", 4) == 0) { format_str = "L%ud-%u"; } else if (strncmp(buf, "Instruction", 11) == 0) { format_str = "L%ui-%u"; } else { format_str = "L%u-%u"; } if (asprintf(&(*caches)[i].id, format_str, level, i) < 0) { (*caches)[i].id = NULL; warn("Failed to allocate memory."); ret = -7; goto done; } if (asprintf(&(*caches)[i].name, "Level %u %s cache", level, buf) < 0) { (*caches)[i].name = NULL; warn("Failed to allocate memory."); ret = -8; goto done; } (*caches)[i].type = buf; buf = NULL; /* cache size */ snprintf(path, PATH_MAX, SYSFS_CPU_PATH "/cpu0/cache/index%u/size", i); if (path_get_unsigned(path, &(*caches)[i].size) != 0) { (*caches)[i].size = 0; } (*caches)[i].size *= 1024; /* It's in kB, we want B */ /* ways of associativity */ snprintf(path, PATH_MAX, SYSFS_CPU_PATH "/cpu0/cache/index%u/ways_of_associativity", i); if (path_get_unsigned(path, &(*caches)[i].ways_of_assoc) != 0) { (*caches)[i].ways_of_assoc = 0; } /* line size */ snprintf(path, PATH_MAX, SYSFS_CPU_PATH "/cpu0/cache/index%u/coherency_line_size", i); if (path_get_unsigned(path, &(*caches)[i].line_size) != 0) { (*caches)[i].line_size = 0; } /* fill in default attributes if needed */ if (check_sysfs_cpu_cache_attributes(&(*caches)[i]) != 0) { ret = -9; goto done; } } ret = 0; done: free(buf); buf = NULL; if (ret != 0) { sysfs_free_cpu_caches(caches, caches_nb); } return ret; } void sysfs_free_cpu_caches(SysfsCpuCache **caches, unsigned *caches_nb) { unsigned i; if (*caches_nb > 0) { for (i = 0; i < *caches_nb; i++) { free((*caches)[i].id); (*caches)[i].id = NULL; free((*caches)[i].name); (*caches)[i].name = NULL; free((*caches)[i].type); (*caches)[i].type = NULL; } free (*caches); } *caches_nb = 0; *caches = NULL; }