// SPDX-License-Identifier: GPL-2.0/* * Basic resctrl file system operations * * Copyright (C) 2018 Intel Corporation * * Authors: * Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>, * Fenghua Yu <fenghua.yu@intel.com> */#include <fcntl.h>#include <limits.h> #include "resctrl.h" int snc_unreliable; static int find_resctrl_mount(char *buffer){ FILE *mounts; char line[256], *fs, *mntpoint; mounts = fopen("/proc/mounts", "r"); if (!mounts) { ksft_perror("/proc/mounts"); return -ENXIO; } while (!feof(mounts)) { if (!fgets(line, 256, mounts)) break; fs = strtok(line, " \t"); if (!fs) continue; mntpoint = strtok(NULL, " \t"); if (!mntpoint) continue; fs = strtok(NULL, " \t"); if (!fs) continue; if (strcmp(fs, "resctrl")) continue; fclose(mounts); if (buffer) strncpy(buffer, mntpoint, 256); return 0; } fclose(mounts); return -ENOENT;} /* * mount_resctrlfs - Mount resctrl FS at /sys/fs/resctrl * * Mounts resctrl FS. Fails if resctrl FS is already mounted to avoid * pre-existing settings interfering with the test results. * * Return: 0 on success, < 0 on error. */int mount_resctrlfs(void){ int ret; ret = find_resctrl_mount(NULL); if (ret != -ENOENT) return -1; ksft_print_msg("Mounting resctrl to \"%s\"\n", RESCTRL_PATH); ret = mount("resctrl", RESCTRL_PATH, "resctrl", 0, NULL); if (ret) ksft_perror("mount"); return ret;} int umount_resctrlfs(void){ char mountpoint[256]; int ret; ret = find_resctrl_mount(mountpoint); if (ret == -ENOENT) return 0; if (ret) return ret; if (umount(mountpoint)) { ksft_perror("Unable to umount resctrl"); return -1; } return 0;} /* * get_cache_level - Convert cache level from string to integer * @cache_type: Cache level as string * * Return: cache level as integer or -1 if @cache_type is invalid. */static int get_cache_level(const char *cache_type){ if (!strcmp(cache_type, "L3")) return 3; if (!strcmp(cache_type, "L2")) return 2; ksft_print_msg("Invalid cache level\n"); return -1;} static int get_resource_cache_level(const char *resource){ /* "MB" use L3 (LLC) as resource */ if (!strcmp(resource, "MB")) return 3; return get_cache_level(resource);} /* * get_domain_id - Get resctrl domain ID for a specified CPU * @resource: resource name * @cpu_no: CPU number * @domain_id: domain ID (cache ID; for MB, L3 cache ID) * * Return: >= 0 on success, < 0 on failure. */int get_domain_id(const char *resource, int cpu_no, int *domain_id){ char phys_pkg_path[1024]; int cache_num; FILE *fp; cache_num = get_resource_cache_level(resource); if (cache_num < 0) return cache_num; sprintf(phys_pkg_path, "%s%d/cache/index%d/id", PHYS_ID_PATH, cpu_no, cache_num); fp = fopen(phys_pkg_path, "r"); if (!fp) { ksft_perror("Failed to open cache id file"); return -1; } if (fscanf(fp, "%d", domain_id) <= 0) { ksft_perror("Could not get domain ID"); fclose(fp); return -1; } fclose(fp); return 0;} /* * Count number of CPUs in a /sys bitmap */static unsigned int count_sys_bitmap_bits(char *name){ FILE *fp = fopen(name, "r"); int count = 0, c; if (!fp) return 0; while ((c = fgetc(fp)) != EOF) { if (!isxdigit(c)) continue; switch (c) { case 'f': count++; fallthrough; case '7': case 'b': case 'd': case 'e': count++; fallthrough; case '3': case '5': case '6': case '9': case 'a': case 'c': count++; fallthrough; case '1': case '2': case '4': case '8': count++; break; } } fclose(fp); return count;} static bool cpus_offline_empty(void){ char offline_cpus_str[64]; FILE *fp; fp = fopen("/sys/devices/system/cpu/offline", "r"); if (!fp) { ksft_perror("Could not open /sys/devices/system/cpu/offline"); return 0; } if (fscanf(fp, "%63s", offline_cpus_str) < 0) { if (!errno) { fclose(fp); return 1; } ksft_perror("Could not read /sys/devices/system/cpu/offline"); } fclose(fp); return 0;} /* * Detect SNC by comparing #CPUs in node0 with #CPUs sharing LLC with CPU0. * If any CPUs are offline declare the detection as unreliable. */int snc_nodes_per_l3_cache(void){ int node_cpus, cache_cpus; static int snc_mode; if (!snc_mode) { snc_mode = 1; if (!cpus_offline_empty()) { ksft_print_msg("Runtime SNC detection unreliable due to offline CPUs.\n"); ksft_print_msg("Setting SNC mode to disabled.\n"); snc_unreliable = 1; return snc_mode; } node_cpus = count_sys_bitmap_bits("/sys/devices/system/node/node0/cpumap"); cache_cpus = count_sys_bitmap_bits("/sys/devices/system/cpu/cpu0/cache/index3/shared_cpu_map"); if (!node_cpus || !cache_cpus) { ksft_print_msg("Could not determine Sub-NUMA Cluster mode.\n"); snc_unreliable = 1; return snc_mode; } snc_mode = cache_cpus / node_cpus; /* * On some platforms (e.g. Hygon), * cache_cpus < node_cpus, the calculated snc_mode is 0. * * Set snc_mode = 1 to indicate that SNC mode is not * supported on the platform. */ if (!snc_mode) snc_mode = 1; if (snc_mode > 1) ksft_print_msg("SNC-%d mode discovered.\n", snc_mode); } return snc_mode;} /* * get_cache_size - Get cache size for a specified CPU * @cpu_no: CPU number * @cache_type: Cache level L2/L3 * @cache_size: pointer to cache_size * * Return: = 0 on success, < 0 on failure. */int get_cache_size(int cpu_no, const char *cache_type, unsigned long *cache_size){ char cache_path[1024], cache_str[64]; int length, i, cache_num; FILE *fp; cache_num = get_cache_level(cache_type); if (cache_num < 0) return cache_num; sprintf(cache_path, "/sys/bus/cpu/devices/cpu%d/cache/index%d/size", cpu_no, cache_num); fp = fopen(cache_path, "r"); if (!fp) { ksft_perror("Failed to open cache size"); return -1; } if (fscanf(fp, "%63s", cache_str) <= 0) { ksft_perror("Could not get cache_size"); fclose(fp); return -1; } fclose(fp); length = (int)strlen(cache_str); *cache_size = 0; for (i = 0; i < length; i++) { if ((cache_str[i] >= '0') && (cache_str[i] <= '9')) *cache_size = *cache_size * 10 + (cache_str[i] - '0'); else if (cache_str[i] == 'K') *cache_size = *cache_size * 1024; else if (cache_str[i] == 'M') *cache_size = *cache_size * 1024 * 1024; else break; } /* * The amount of cache represented by each bit in the masks * in the schemata file is reduced by a factor equal to SNC * nodes per L3 cache. * E.g. on a SNC-2 system with a 100MB L3 cache a test that * allocates memory from its local SNC node (default behavior * without using libnuma) will only see 50 MB llc_occupancy * with a fully populated L3 mask in the schemata file. */ if (cache_num == 3) *cache_size /= snc_nodes_per_l3_cache(); return 0;} #define CORE_SIBLINGS_PATH "/sys/bus/cpu/devices/cpu" /* * get_bit_mask - Get bit mask from given file * @filename: File containing the mask * @mask: The bit mask returned as unsigned long * * Return: = 0 on success, < 0 on failure. */static int get_bit_mask(const char *filename, unsigned long *mask){ FILE *fp; if (!filename || !mask) return -1; fp = fopen(filename, "r"); if (!fp) { ksft_print_msg("Failed to open bit mask file '%s': %s\n", filename, strerror(errno)); return -1; } if (fscanf(fp, "%lx", mask) <= 0) { ksft_print_msg("Could not read bit mask file '%s': %s\n", filename, strerror(errno)); fclose(fp); return -1; } fclose(fp); return 0;} /* * resource_info_unsigned_get - Read an unsigned value from * /sys/fs/resctrl/info/@resource/@filename * @resource: Resource name that matches directory name in * /sys/fs/resctrl/info * @filename: File in /sys/fs/resctrl/info/@resource * @val: Contains read value on success. * * Return: = 0 on success, < 0 on failure. On success the read * value is saved into @val. */int resource_info_unsigned_get(const char *resource, const char *filename, unsigned int *val){ char file_path[PATH_MAX]; FILE *fp; snprintf(file_path, sizeof(file_path), "%s/%s/%s", INFO_PATH, resource, filename); fp = fopen(file_path, "r"); if (!fp) { ksft_print_msg("Error opening %s: %m\n", file_path); return -1; } if (fscanf(fp, "%u", val) <= 0) { ksft_print_msg("Could not get contents of %s: %m\n", file_path); fclose(fp); return -1; } fclose(fp); return 0;} /* * create_bit_mask- Create bit mask from start, len pair * @start: LSB of the mask * @len Number of bits in the mask */unsigned long create_bit_mask(unsigned int start, unsigned int len){ return ((1UL << len) - 1UL) << start;} /* * count_contiguous_bits - Returns the longest train of bits in a bit mask * @val A bit mask * @start The location of the least-significant bit of the longest train * * Return: The length of the contiguous bits in the longest train of bits */unsigned int count_contiguous_bits(unsigned long val, unsigned int *start){ unsigned long last_val; unsigned int count = 0; while (val) { last_val = val; val &= (val >> 1); count++; } if (start) { if (count) *start = ffsl(last_val) - 1; else *start = 0; } return count;} /* * get_full_cbm - Get full Cache Bit Mask (CBM) * @cache_type: Cache type as "L2" or "L3" * @mask: Full cache bit mask representing the maximal portion of cache * available for allocation, returned as unsigned long. * * Return: = 0 on success, < 0 on failure. */int get_full_cbm(const char *cache_type, unsigned long *mask){ char cbm_path[PATH_MAX]; int ret; if (!cache_type) return -1; snprintf(cbm_path, sizeof(cbm_path), "%s/%s/cbm_mask", INFO_PATH, cache_type); ret = get_bit_mask(cbm_path, mask); if (ret || !*mask) return -1; return 0;} /* * get_shareable_mask - Get shareable mask from shareable_bits * @cache_type: Cache type as "L2" or "L3" * @shareable_mask: Shareable mask returned as unsigned long * * Return: = 0 on success, < 0 on failure. */static int get_shareable_mask(const char *cache_type, unsigned long *shareable_mask){ char mask_path[PATH_MAX]; if (!cache_type) return -1; snprintf(mask_path, sizeof(mask_path), "%s/%s/shareable_bits", INFO_PATH, cache_type); return get_bit_mask(mask_path, shareable_mask);} /* * get_mask_no_shareable - Get Cache Bit Mask (CBM) without shareable bits * @cache_type: Cache type as "L2" or "L3" * @mask: The largest exclusive portion of the cache out of the * full CBM, returned as unsigned long * * Parts of a cache may be shared with other devices such as GPU. This function * calculates the largest exclusive portion of the cache where no other devices * besides CPU have access to the cache portion. * * Return: = 0 on success, < 0 on failure. */int get_mask_no_shareable(const char *cache_type, unsigned long *mask){Lines 1–500 of 1001. Load more
// SPDX-License-Identifier: GPL-2.0 /* * Basic resctrl file system operations * * Copyright (C) 2018 Intel Corporation * * Authors: * Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>, * Fenghua Yu <fenghua.yu@intel.com> */ #include <fcntl.h> #include <limits.h> #include "resctrl.h" int snc_unreliable; static int find_resctrl_mount(char *buffer) { FILE *mounts; char line[256], *fs, *mntpoint; mounts = fopen("/proc/mounts", "r"); if (!mounts) { ksft_perror("/proc/mounts"); return -ENXIO; } while (!feof(mounts)) { if (!fgets(line, 256, mounts)) break; fs = strtok(line, " \t"); if (!fs) continue; mntpoint = strtok(NULL, " \t"); if (!mntpoint) continue; fs = strtok(NULL, " \t"); if (!fs) continue; if (strcmp(fs, "resctrl")) continue; fclose(mounts); if (buffer) strncpy(buffer, mntpoint, 256); return 0; } fclose(mounts); return -ENOENT; } /* * mount_resctrlfs - Mount resctrl FS at /sys/fs/resctrl * * Mounts resctrl FS. Fails if resctrl FS is already mounted to avoid * pre-existing settings interfering with the test results. * * Return: 0 on success, < 0 on error. */ int mount_resctrlfs(void) { int ret; ret = find_resctrl_mount(NULL); if (ret != -ENOENT) return -1; ksft_print_msg("Mounting resctrl to \"%s\"\n", RESCTRL_PATH); ret = mount("resctrl", RESCTRL_PATH, "resctrl", 0, NULL); if (ret) ksft_perror("mount"); return ret; } int umount_resctrlfs(void) { char mountpoint[256]; int ret; ret = find_resctrl_mount(mountpoint); if (ret == -ENOENT) return 0; if (ret) return ret; if (umount(mountpoint)) { ksft_perror("Unable to umount resctrl"); return -1; } return 0; } /* * get_cache_level - Convert cache level from string to integer * @cache_type: Cache level as string * * Return: cache level as integer or -1 if @cache_type is invalid. */ static int get_cache_level(const char *cache_type) { if (!strcmp(cache_type, "L3")) return 3; if (!strcmp(cache_type, "L2")) return 2; ksft_print_msg("Invalid cache level\n"); return -1; } static int get_resource_cache_level(const char *resource) { /* "MB" use L3 (LLC) as resource */ if (!strcmp(resource, "MB")) return 3; return get_cache_level(resource); } /* * get_domain_id - Get resctrl domain ID for a specified CPU * @resource: resource name * @cpu_no: CPU number * @domain_id: domain ID (cache ID; for MB, L3 cache ID) * * Return: >= 0 on success, < 0 on failure. */ int get_domain_id(const char *resource, int cpu_no, int *domain_id) { char phys_pkg_path[1024]; int cache_num; FILE *fp; cache_num = get_resource_cache_level(resource); if (cache_num < 0) return cache_num; sprintf(phys_pkg_path, "%s%d/cache/index%d/id", PHYS_ID_PATH, cpu_no, cache_num); fp = fopen(phys_pkg_path, "r"); if (!fp) { ksft_perror("Failed to open cache id file"); return -1; } if (fscanf(fp, "%d", domain_id) <= 0) { ksft_perror("Could not get domain ID"); fclose(fp); return -1; } fclose(fp); return 0; } /* * Count number of CPUs in a /sys bitmap */ static unsigned int count_sys_bitmap_bits(char *name) { FILE *fp = fopen(name, "r"); int count = 0, c; if (!fp) return 0; while ((c = fgetc(fp)) != EOF) { if (!isxdigit(c)) continue; switch (c) { case 'f': count++; fallthrough; case '7': case 'b': case 'd': case 'e': count++; fallthrough; case '3': case '5': case '6': case '9': case 'a': case 'c': count++; fallthrough; case '1': case '2': case '4': case '8': count++; break; } } fclose(fp); return count; } static bool cpus_offline_empty(void) { char offline_cpus_str[64]; FILE *fp; fp = fopen("/sys/devices/system/cpu/offline", "r"); if (!fp) { ksft_perror("Could not open /sys/devices/system/cpu/offline"); return 0; } if (fscanf(fp, "%63s", offline_cpus_str) < 0) { if (!errno) { fclose(fp); return 1; } ksft_perror("Could not read /sys/devices/system/cpu/offline"); } fclose(fp); return 0; } /* * Detect SNC by comparing #CPUs in node0 with #CPUs sharing LLC with CPU0. * If any CPUs are offline declare the detection as unreliable. */ int snc_nodes_per_l3_cache(void) { int node_cpus, cache_cpus; static int snc_mode; if (!snc_mode) { snc_mode = 1; if (!cpus_offline_empty()) { ksft_print_msg("Runtime SNC detection unreliable due to offline CPUs.\n"); ksft_print_msg("Setting SNC mode to disabled.\n"); snc_unreliable = 1; return snc_mode; } node_cpus = count_sys_bitmap_bits("/sys/devices/system/node/node0/cpumap"); cache_cpus = count_sys_bitmap_bits("/sys/devices/system/cpu/cpu0/cache/index3/shared_cpu_map"); if (!node_cpus || !cache_cpus) { ksft_print_msg("Could not determine Sub-NUMA Cluster mode.\n"); snc_unreliable = 1; return snc_mode; } snc_mode = cache_cpus / node_cpus; /* * On some platforms (e.g. Hygon), * cache_cpus < node_cpus, the calculated snc_mode is 0. * * Set snc_mode = 1 to indicate that SNC mode is not * supported on the platform. */ if (!snc_mode) snc_mode = 1; if (snc_mode > 1) ksft_print_msg("SNC-%d mode discovered.\n", snc_mode); } return snc_mode; } /* * get_cache_size - Get cache size for a specified CPU * @cpu_no: CPU number * @cache_type: Cache level L2/L3 * @cache_size: pointer to cache_size * * Return: = 0 on success, < 0 on failure. */ int get_cache_size(int cpu_no, const char *cache_type, unsigned long *cache_size) { char cache_path[1024], cache_str[64]; int length, i, cache_num; FILE *fp; cache_num = get_cache_level(cache_type); if (cache_num < 0) return cache_num; sprintf(cache_path, "/sys/bus/cpu/devices/cpu%d/cache/index%d/size", cpu_no, cache_num); fp = fopen(cache_path, "r"); if (!fp) { ksft_perror("Failed to open cache size"); return -1; } if (fscanf(fp, "%63s", cache_str) <= 0) { ksft_perror("Could not get cache_size"); fclose(fp); return -1; } fclose(fp); length = (int)strlen(cache_str); *cache_size = 0; for (i = 0; i < length; i++) { if ((cache_str[i] >= '0') && (cache_str[i] <= '9')) *cache_size = *cache_size * 10 + (cache_str[i] - '0'); else if (cache_str[i] == 'K') *cache_size = *cache_size * 1024; else if (cache_str[i] == 'M') *cache_size = *cache_size * 1024 * 1024; else break; } /* * The amount of cache represented by each bit in the masks * in the schemata file is reduced by a factor equal to SNC * nodes per L3 cache. * E.g. on a SNC-2 system with a 100MB L3 cache a test that * allocates memory from its local SNC node (default behavior * without using libnuma) will only see 50 MB llc_occupancy * with a fully populated L3 mask in the schemata file. */ if (cache_num == 3) *cache_size /= snc_nodes_per_l3_cache(); return 0; } #define CORE_SIBLINGS_PATH "/sys/bus/cpu/devices/cpu" /* * get_bit_mask - Get bit mask from given file * @filename: File containing the mask * @mask: The bit mask returned as unsigned long * * Return: = 0 on success, < 0 on failure. */ static int get_bit_mask(const char *filename, unsigned long *mask) { FILE *fp; if (!filename || !mask) return -1; fp = fopen(filename, "r"); if (!fp) { ksft_print_msg("Failed to open bit mask file '%s': %s\n", filename, strerror(errno)); return -1; } if (fscanf(fp, "%lx", mask) <= 0) { ksft_print_msg("Could not read bit mask file '%s': %s\n", filename, strerror(errno)); fclose(fp); return -1; } fclose(fp); return 0; } /* * resource_info_unsigned_get - Read an unsigned value from * /sys/fs/resctrl/info/@resource/@filename * @resource: Resource name that matches directory name in * /sys/fs/resctrl/info * @filename: File in /sys/fs/resctrl/info/@resource * @val: Contains read value on success. * * Return: = 0 on success, < 0 on failure. On success the read * value is saved into @val. */ int resource_info_unsigned_get(const char *resource, const char *filename, unsigned int *val) { char file_path[PATH_MAX]; FILE *fp; snprintf(file_path, sizeof(file_path), "%s/%s/%s", INFO_PATH, resource, filename); fp = fopen(file_path, "r"); if (!fp) { ksft_print_msg("Error opening %s: %m\n", file_path); return -1; } if (fscanf(fp, "%u", val) <= 0) { ksft_print_msg("Could not get contents of %s: %m\n", file_path); fclose(fp); return -1; } fclose(fp); return 0; } /* * create_bit_mask- Create bit mask from start, len pair * @start: LSB of the mask * @len Number of bits in the mask */ unsigned long create_bit_mask(unsigned int start, unsigned int len) { return ((1UL << len) - 1UL) << start; } /* * count_contiguous_bits - Returns the longest train of bits in a bit mask * @val A bit mask * @start The location of the least-significant bit of the longest train * * Return: The length of the contiguous bits in the longest train of bits */ unsigned int count_contiguous_bits(unsigned long val, unsigned int *start) { unsigned long last_val; unsigned int count = 0; while (val) { last_val = val; val &= (val >> 1); count++; } if (start) { if (count) *start = ffsl(last_val) - 1; else *start = 0; } return count; } /* * get_full_cbm - Get full Cache Bit Mask (CBM) * @cache_type: Cache type as "L2" or "L3" * @mask: Full cache bit mask representing the maximal portion of cache * available for allocation, returned as unsigned long. * * Return: = 0 on success, < 0 on failure. */ int get_full_cbm(const char *cache_type, unsigned long *mask) { char cbm_path[PATH_MAX]; int ret; if (!cache_type) return -1; snprintf(cbm_path, sizeof(cbm_path), "%s/%s/cbm_mask", INFO_PATH, cache_type); ret = get_bit_mask(cbm_path, mask); if (ret || !*mask) return -1; return 0; } /* * get_shareable_mask - Get shareable mask from shareable_bits * @cache_type: Cache type as "L2" or "L3" * @shareable_mask: Shareable mask returned as unsigned long * * Return: = 0 on success, < 0 on failure. */ static int get_shareable_mask(const char *cache_type, unsigned long *shareable_mask) { char mask_path[PATH_MAX]; if (!cache_type) return -1; snprintf(mask_path, sizeof(mask_path), "%s/%s/shareable_bits", INFO_PATH, cache_type); return get_bit_mask(mask_path, shareable_mask); } /* * get_mask_no_shareable - Get Cache Bit Mask (CBM) without shareable bits * @cache_type: Cache type as "L2" or "L3" * @mask: The largest exclusive portion of the cache out of the * full CBM, returned as unsigned long * * Parts of a cache may be shared with other devices such as GPU. This function * calculates the largest exclusive portion of the cache where no other devices * besides CPU have access to the cache portion. * * Return: = 0 on success, < 0 on failure. */ int get_mask_no_shareable(const char *cache_type, unsigned long *mask) {