Merge tag 'x86-asm-2024-03-11' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
[linux-2.6-block.git] / tools / testing / selftests / resctrl / resctrlfs.c
index 5ebd436838769561bfd426a8b9c398347dc210e5..1cade75176eb1d01bf43113414e24a5d7b3190fa 100644 (file)
@@ -20,7 +20,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 +56,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 +69,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 +86,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;
@@ -138,31 +164,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");
+               ksft_perror("Could not get cache_size");
                fclose(fp);
 
                return -1;
@@ -196,30 +217,29 @@ int get_cache_size(int cpu_no, char *cache_type, unsigned long *cache_size)
 #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 +250,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++;
+       }
+
+       if (start) {
+               if (count)
+                       *start = ffsl(last_val) - 1;
+               else
+                       *start = 0;
        }
 
-       return sibling_cpu_no;
+       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 +456,7 @@ 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
+ * Return: 0 on success, < 0 on error.
  */
 static int create_grp(const char *grp_name, char *grp, const char *parent_grp)
 {
@@ -325,7 +481,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 +489,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 +504,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");
+               ksft_print_msg("Failed to write pid to tasks file\n");
                fclose(fp);
 
                return -1;
@@ -376,7 +532,7 @@ 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)
@@ -420,7 +576,7 @@ int write_bm_pid_to_resctrl(pid_t bm_pid, char *ctrlgrp, char *mongrp,
 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 +586,17 @@ 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(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,8 +604,8 @@ 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;
@@ -466,14 +616,8 @@ int write_schemata(char *ctrlgrp, char *schemata, int cpu_no, char *resctrl_val)
        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 +708,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 +732,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 +764,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 +804,7 @@ int filter_dmesg(void)
 
        ret = pipe(pipefds);
        if (ret) {
-               perror("pipe");
+               ksft_perror("pipe");
                return ret;
        }
        fflush(stdout);
@@ -626,13 +813,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;