diff options
Diffstat (limited to 'tools/testing/selftests/resctrl/resctrlfs.c')
-rw-r--r-- | tools/testing/selftests/resctrl/resctrlfs.c | 612 |
1 files changed, 456 insertions, 156 deletions
diff --git a/tools/testing/selftests/resctrl/resctrlfs.c b/tools/testing/selftests/resctrl/resctrlfs.c index 5ebd43683876..195f04c4d158 100644 --- a/tools/testing/selftests/resctrl/resctrlfs.c +++ b/tools/testing/selftests/resctrl/resctrlfs.c @@ -13,6 +13,8 @@ #include "resctrl.h" +int snc_unreliable; + static int find_resctrl_mount(char *buffer) { FILE *mounts; @@ -20,7 +22,7 @@ static int find_resctrl_mount(char *buffer) mounts = fopen("/proc/mounts", "r"); if (!mounts) { - perror("/proc/mounts"); + ksft_perror("/proc/mounts"); return -ENXIO; } while (!feof(mounts)) { @@ -56,7 +58,7 @@ static int find_resctrl_mount(char *buffer) * Mounts resctrl FS. Fails if resctrl FS is already mounted to avoid * pre-existing settings interfering with the test results. * - * Return: 0 on success, non-zero on failure + * Return: 0 on success, < 0 on error. */ int mount_resctrlfs(void) { @@ -69,7 +71,7 @@ int mount_resctrlfs(void) ksft_print_msg("Mounting resctrl to \"%s\"\n", RESCTRL_PATH); ret = mount("resctrl", RESCTRL_PATH, "resctrl", 0, NULL); if (ret) - perror("# mount"); + ksft_perror("mount"); return ret; } @@ -86,41 +88,67 @@ int umount_resctrlfs(void) return ret; if (umount(mountpoint)) { - perror("# Unable to umount resctrl"); + ksft_perror("Unable to umount resctrl"); - return errno; + return -1; } return 0; } /* - * get_resource_id - Get socket number/l3 id for a specified CPU + * 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 - * @resource_id: Socket number or l3_id + * @domain_id: domain ID (cache ID; for MB, L3 cache ID) * * Return: >= 0 on success, < 0 on failure. */ -int get_resource_id(int cpu_no, int *resource_id) +int get_domain_id(const char *resource, int cpu_no, int *domain_id) { char phys_pkg_path[1024]; + int cache_num; FILE *fp; - if (get_vendor() == ARCH_AMD) - sprintf(phys_pkg_path, "%s%d/cache/index3/id", - PHYS_ID_PATH, cpu_no); - else - sprintf(phys_pkg_path, "%s%d/topology/physical_package_id", - PHYS_ID_PATH, cpu_no); + 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) { - perror("Failed to open physical_package_id"); + ksft_perror("Failed to open cache id file"); return -1; } - if (fscanf(fp, "%d", resource_id) <= 0) { - perror("Could not get socket number or l3 id"); + if (fscanf(fp, "%d", domain_id) <= 0) { + ksft_perror("Could not get domain ID"); fclose(fp); return -1; @@ -131,6 +159,98 @@ int get_resource_id(int cpu_no, int *resource_id) } /* + * 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; + + 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 @@ -138,31 +258,26 @@ int get_resource_id(int cpu_no, int *resource_id) * * Return: = 0 on success, < 0 on failure. */ -int get_cache_size(int cpu_no, char *cache_type, unsigned long *cache_size) +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; - if (!strcmp(cache_type, "L3")) { - cache_num = 3; - } else if (!strcmp(cache_type, "L2")) { - cache_num = 2; - } else { - perror("Invalid cache level"); - return -1; - } + 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) { - perror("Failed to open cache size"); + ksft_perror("Failed to open cache size"); return -1; } - if (fscanf(fp, "%s", cache_str) <= 0) { - perror("Could not get cache_size"); + if (fscanf(fp, "%63s", cache_str) <= 0) { + ksft_perror("Could not get cache_size"); fclose(fp); return -1; @@ -190,36 +305,46 @@ int get_cache_size(int cpu_no, char *cache_type, unsigned long *cache_size) 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_cbm_mask - Get cbm mask for given cache - * @cache_type: Cache level L2/L3 - * @cbm_mask: cbm_mask returned as a string + * 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. */ -int get_cbm_mask(char *cache_type, char *cbm_mask) +static int get_bit_mask(const char *filename, unsigned long *mask) { - char cbm_mask_path[1024]; FILE *fp; - if (!cbm_mask) + if (!filename || !mask) return -1; - sprintf(cbm_mask_path, "%s/%s/cbm_mask", INFO_PATH, cache_type); - - fp = fopen(cbm_mask_path, "r"); + fp = fopen(filename, "r"); if (!fp) { - perror("Failed to open cache level"); - + ksft_print_msg("Failed to open bit mask file '%s': %s\n", + filename, strerror(errno)); return -1; } - if (fscanf(fp, "%s", cbm_mask) <= 0) { - perror("Could not get max cbm_mask"); + + 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; @@ -230,64 +355,200 @@ int get_cbm_mask(char *cache_type, char *cbm_mask) } /* - * get_core_sibling - Get sibling core id from the same socket for given CPU - * @cpu_no: CPU number + * 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. + * Return: = 0 on success, < 0 on failure. On success the read + * value is saved into @val. */ -int get_core_sibling(int cpu_no) +int resource_info_unsigned_get(const char *resource, const char *filename, + unsigned int *val) { - char core_siblings_path[1024], cpu_list_str[64]; - int sibling_cpu_no = -1; + char file_path[PATH_MAX]; FILE *fp; - sprintf(core_siblings_path, "%s%d/topology/core_siblings_list", - CORE_SIBLINGS_PATH, cpu_no); + snprintf(file_path, sizeof(file_path), "%s/%s/%s", INFO_PATH, resource, + filename); - fp = fopen(core_siblings_path, "r"); + fp = fopen(file_path, "r"); if (!fp) { - perror("Failed to open core siblings path"); - + ksft_print_msg("Error opening %s: %m\n", file_path); return -1; } - if (fscanf(fp, "%s", cpu_list_str) <= 0) { - perror("Could not get core_siblings list"); - fclose(fp); + 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; +} - char *token = strtok(cpu_list_str, "-,"); +/* + * 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; +} - while (token) { - sibling_cpu_no = atoi(token); - /* Skipping core 0 as we don't want to run test on core 0 */ - if (sibling_cpu_no != 0 && sibling_cpu_no != cpu_no) - break; - token = strtok(NULL, "-,"); +/* + * 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++; } - return sibling_cpu_no; + 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) +{ + unsigned long full_mask, shareable_mask; + unsigned int start, len; + + if (get_full_cbm(cache_type, &full_mask) < 0) + return -1; + if (get_shareable_mask(cache_type, &shareable_mask) < 0) + return -1; + + len = count_contiguous_bits(full_mask & ~shareable_mask, &start); + if (!len) + return -1; + + *mask = create_bit_mask(start, len); + + return 0; } /* * taskset_benchmark - Taskset PID (i.e. benchmark) to a specified cpu - * @bm_pid: PID that should be binded - * @cpu_no: CPU number at which the PID would be binded + * @bm_pid: PID that should be binded + * @cpu_no: CPU number at which the PID would be binded + * @old_affinity: When not NULL, set to old CPU affinity * - * Return: 0 on success, non-zero on failure + * Return: 0 on success, < 0 on error. */ -int taskset_benchmark(pid_t bm_pid, int cpu_no) +int taskset_benchmark(pid_t bm_pid, int cpu_no, cpu_set_t *old_affinity) { cpu_set_t my_set; + if (old_affinity) { + CPU_ZERO(old_affinity); + if (sched_getaffinity(bm_pid, sizeof(*old_affinity), + old_affinity)) { + ksft_perror("Unable to read CPU affinity"); + return -1; + } + } + CPU_ZERO(&my_set); CPU_SET(cpu_no, &my_set); if (sched_setaffinity(bm_pid, sizeof(cpu_set_t), &my_set)) { - perror("Unable to taskset benchmark"); + ksft_perror("Unable to taskset benchmark"); + + return -1; + } + return 0; +} + +/* + * taskset_restore - Taskset PID to the earlier CPU affinity + * @bm_pid: PID that should be reset + * @old_affinity: The old CPU affinity to restore + * + * Return: 0 on success, < 0 on error. + */ +int taskset_restore(pid_t bm_pid, cpu_set_t *old_affinity) +{ + if (sched_setaffinity(bm_pid, sizeof(*old_affinity), old_affinity)) { + ksft_perror("Unable to restore CPU affinity"); return -1; } @@ -300,7 +561,10 @@ int taskset_benchmark(pid_t bm_pid, int cpu_no) * @grp: Full path and name of the group * @parent_grp: Full path and name of the parent group * - * Return: 0 on success, non-zero on failure + * Creates a group @grp_name if it does not exist yet. If @grp_name is NULL, + * it is interpreted as the root group which always results in success. + * + * Return: 0 on success, < 0 on error. */ static int create_grp(const char *grp_name, char *grp, const char *parent_grp) { @@ -308,12 +572,7 @@ static int create_grp(const char *grp_name, char *grp, const char *parent_grp) struct dirent *ep; DIR *dp; - /* - * At this point, we are guaranteed to have resctrl FS mounted and if - * length of grp_name == 0, it means, user wants to use root con_mon - * grp, so do nothing - */ - if (strlen(grp_name) == 0) + if (!grp_name) return 0; /* Check if requested grp exists or not */ @@ -325,7 +584,7 @@ static int create_grp(const char *grp_name, char *grp, const char *parent_grp) } closedir(dp); } else { - perror("Unable to open resctrl for group"); + ksft_perror("Unable to open resctrl for group"); return -1; } @@ -333,7 +592,7 @@ static int create_grp(const char *grp_name, char *grp, const char *parent_grp) /* Requested grp doesn't exist, hence create it */ if (found_grp == 0) { if (mkdir(grp, 0) == -1) { - perror("Unable to create group"); + ksft_perror("Unable to create group"); return -1; } @@ -348,12 +607,12 @@ static int write_pid_to_tasks(char *tasks, pid_t pid) fp = fopen(tasks, "w"); if (!fp) { - perror("Failed to open tasks file"); + ksft_perror("Failed to open tasks file"); return -1; } - if (fprintf(fp, "%d\n", pid) < 0) { - perror("Failed to wr pid to tasks file"); + if (fprintf(fp, "%d\n", (int)pid) < 0) { + ksft_print_msg("Failed to write pid to tasks file\n"); fclose(fp); return -1; @@ -368,7 +627,6 @@ static int write_pid_to_tasks(char *tasks, pid_t pid) * @bm_pid: PID that should be written * @ctrlgrp: Name of the control monitor group (con_mon grp) * @mongrp: Name of the monitor group (mon grp) - * @resctrl_val: Resctrl feature (Eg: mbm, mba.. etc) * * If a con_mon grp is requested, create it and write pid to it, otherwise * write pid to root con_mon grp. @@ -376,16 +634,15 @@ static int write_pid_to_tasks(char *tasks, pid_t pid) * pid is not written, this means that pid is in con_mon grp and hence * should consult con_mon grp's mon_data directory for results. * - * Return: 0 on success, non-zero on failure + * Return: 0 on success, < 0 on error. */ -int write_bm_pid_to_resctrl(pid_t bm_pid, char *ctrlgrp, char *mongrp, - char *resctrl_val) +int write_bm_pid_to_resctrl(pid_t bm_pid, const char *ctrlgrp, const char *mongrp) { char controlgroup[128], monitorgroup[512], monitorgroup_p[256]; char tasks[1024]; int ret = 0; - if (strlen(ctrlgrp)) + if (ctrlgrp) sprintf(controlgroup, "%s/%s", RESCTRL_PATH, ctrlgrp); else sprintf(controlgroup, "%s", RESCTRL_PATH); @@ -399,28 +656,25 @@ int write_bm_pid_to_resctrl(pid_t bm_pid, char *ctrlgrp, char *mongrp, if (ret) goto out; - /* Create mon grp and write pid into it for "mbm" and "cmt" test */ - if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)) || - !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) { - if (strlen(mongrp)) { - sprintf(monitorgroup_p, "%s/mon_groups", controlgroup); - sprintf(monitorgroup, "%s/%s", monitorgroup_p, mongrp); - ret = create_grp(mongrp, monitorgroup, monitorgroup_p); - if (ret) - goto out; - - sprintf(tasks, "%s/mon_groups/%s/tasks", - controlgroup, mongrp); - ret = write_pid_to_tasks(tasks, bm_pid); - if (ret) - goto out; - } + /* Create monitor group and write pid into if it is used */ + if (mongrp) { + sprintf(monitorgroup_p, "%s/mon_groups", controlgroup); + sprintf(monitorgroup, "%s/%s", monitorgroup_p, mongrp); + ret = create_grp(mongrp, monitorgroup, monitorgroup_p); + if (ret) + goto out; + + sprintf(tasks, "%s/mon_groups/%s/tasks", + controlgroup, mongrp); + ret = write_pid_to_tasks(tasks, bm_pid); + if (ret) + goto out; } out: ksft_print_msg("Writing benchmark parameters to resctrl FS\n"); if (ret) - perror("# writing to resctrlfs"); + ksft_print_msg("Failed writing to resctrlfs\n"); return ret; } @@ -430,23 +684,18 @@ out: * @ctrlgrp: Name of the con_mon grp * @schemata: Schemata that should be updated to * @cpu_no: CPU number that the benchmark PID is binded to - * @resctrl_val: Resctrl feature (Eg: mbm, mba.. etc) + * @resource: Resctrl resource (Eg: MB, L3, L2, etc.) * - * Update schemata of a con_mon grp *only* if requested resctrl feature is + * Update schemata of a con_mon grp *only* if requested resctrl resource is * allocation type * - * Return: 0 on success, non-zero on failure + * Return: 0 on success, < 0 on error. */ -int write_schemata(char *ctrlgrp, char *schemata, int cpu_no, char *resctrl_val) +int write_schemata(const char *ctrlgrp, char *schemata, int cpu_no, + const char *resource) { char controlgroup[1024], reason[128], schema[1024] = {}; - int resource_id, fd, schema_len = -1, ret = 0; - - if (strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR)) && - strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR)) && - strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR)) && - strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) - return -ENOENT; + int domain_id, fd, schema_len, ret = 0; if (!schemata) { ksft_print_msg("Skipping empty schemata update\n"); @@ -454,26 +703,20 @@ int write_schemata(char *ctrlgrp, char *schemata, int cpu_no, char *resctrl_val) return -1; } - if (get_resource_id(cpu_no, &resource_id) < 0) { - sprintf(reason, "Failed to get resource id"); + if (get_domain_id(resource, cpu_no, &domain_id) < 0) { + sprintf(reason, "Failed to get domain ID"); ret = -1; goto out; } - if (strlen(ctrlgrp) != 0) + if (ctrlgrp) sprintf(controlgroup, "%s/%s/schemata", RESCTRL_PATH, ctrlgrp); else sprintf(controlgroup, "%s/schemata", RESCTRL_PATH); - if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR)) || - !strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR))) - schema_len = snprintf(schema, sizeof(schema), "%s%d%c%s\n", - "L3:", resource_id, '=', schemata); - if (!strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR)) || - !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) - schema_len = snprintf(schema, sizeof(schema), "%s%d%c%s\n", - "MB:", resource_id, '=', schemata); + schema_len = snprintf(schema, sizeof(schema), "%s:%d=%s\n", + resource, domain_id, schemata); if (schema_len < 0 || schema_len >= sizeof(schema)) { snprintf(reason, sizeof(reason), "snprintf() failed with return value : %d", schema_len); @@ -564,20 +807,16 @@ char *fgrep(FILE *inf, const char *str) } /* - * validate_resctrl_feature_request - Check if requested feature is valid. - * @resource: Required resource (e.g., MB, L3, L2, L3_MON, etc.) - * @feature: Required monitor feature (in mon_features file). Can only be - * set for L3_MON. Must be NULL for all other resources. + * resctrl_resource_exists - Check if a resource is supported. + * @resource: Resctrl resource (e.g., MB, L3, L2, L3_MON, etc.) * - * Return: True if the resource/feature is supported, else false. False is + * Return: True if the resource is supported, else false. False is * also returned if resctrl FS is not mounted. */ -bool validate_resctrl_feature_request(const char *resource, const char *feature) +bool resctrl_resource_exists(const char *resource) { char res_path[PATH_MAX]; struct stat statbuf; - char *res; - FILE *inf; int ret; if (!resource) @@ -592,8 +831,25 @@ bool validate_resctrl_feature_request(const char *resource, const char *feature) if (stat(res_path, &statbuf)) return false; - if (!feature) - return true; + return true; +} + +/* + * resctrl_mon_feature_exists - Check if requested monitoring feature is valid. + * @resource: Resource that uses the mon_features file. Currently only L3_MON + * is valid. + * @feature: Required monitor feature (in mon_features file). + * + * Return: True if the feature is supported, else false. + */ +bool resctrl_mon_feature_exists(const char *resource, const char *feature) +{ + char res_path[PATH_MAX]; + char *res; + FILE *inf; + + if (!feature || !resource) + return false; snprintf(res_path, sizeof(res_path), "%s/%s/mon_features", INFO_PATH, resource); inf = fopen(res_path, "r"); @@ -607,6 +863,36 @@ bool validate_resctrl_feature_request(const char *resource, const char *feature) return !!res; } +/* + * resource_info_file_exists - Check if a file is present inside + * /sys/fs/resctrl/info/@resource. + * @resource: Required resource (Eg: MB, L3, L2, etc.) + * @file: Required file. + * + * Return: True if the /sys/fs/resctrl/info/@resource/@file exists, else false. + */ +bool resource_info_file_exists(const char *resource, const char *file) +{ + char res_path[PATH_MAX]; + struct stat statbuf; + + if (!file || !resource) + return false; + + snprintf(res_path, sizeof(res_path), "%s/%s/%s", INFO_PATH, resource, + file); + + if (stat(res_path, &statbuf)) + return false; + + return true; +} + +bool test_resource_feature_check(const struct resctrl_test *test) +{ + return resctrl_resource_exists(test->resource); +} + int filter_dmesg(void) { char line[1024]; @@ -617,7 +903,7 @@ int filter_dmesg(void) ret = pipe(pipefds); if (ret) { - perror("pipe"); + ksft_perror("pipe"); return ret; } fflush(stdout); @@ -626,13 +912,13 @@ int filter_dmesg(void) close(pipefds[0]); dup2(pipefds[1], STDOUT_FILENO); execlp("dmesg", "dmesg", NULL); - perror("executing dmesg"); + ksft_perror("Executing dmesg"); exit(1); } close(pipefds[1]); fp = fdopen(pipefds[0], "r"); if (!fp) { - perror("fdopen(pipe)"); + ksft_perror("fdopen(pipe)"); kill(pid, SIGTERM); return -1; @@ -650,24 +936,6 @@ int filter_dmesg(void) return 0; } -int validate_bw_report_request(char *bw_report) -{ - if (strcmp(bw_report, "reads") == 0) - return 0; - if (strcmp(bw_report, "writes") == 0) - return 0; - if (strcmp(bw_report, "nt-writes") == 0) { - strcpy(bw_report, "writes"); - return 0; - } - if (strcmp(bw_report, "total") == 0) - return 0; - - fprintf(stderr, "Requested iMC B/W report type unavailable\n"); - - return -1; -} - int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) { @@ -689,3 +957,35 @@ unsigned int count_bits(unsigned long n) return count; } + +/** + * snc_kernel_support - Check for existence of mon_sub_L3_00 file that indicates + * SNC resctrl support on the kernel side. + * + * Return: 0 if not supported, 1 if SNC is disabled or SNC discovery is + * unreliable or SNC is both enabled and supported. + */ +int snc_kernel_support(void) +{ + char node_path[PATH_MAX]; + struct stat statbuf; + int ret; + + ret = snc_nodes_per_l3_cache(); + /* + * If SNC is disabled then its kernel support isn't important. If SNC + * got disabled because the discovery process was unreliable the + * snc_unreliable variable was set. It can be used to verify the SNC + * discovery reliability elsewhere in the selftest. + */ + if (ret == 1) + return ret; + + snprintf(node_path, sizeof(node_path), "%s/%s", RESCTRL_PATH, + "mon_data/mon_L3_00/mon_sub_L3_00"); + + if (!stat(node_path, &statbuf)) + return 1; + + return 0; +} |