root/tools/testing/selftests/resctrl/resctrlfs.c
// 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)
{
        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
 * @old_affinity:       When not NULL, set to old CPU affinity
 *
 * Return: 0 on success, < 0 on error.
 */
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)) {
                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;
        }

        return 0;
}

/*
 * create_grp - Create a group only if one doesn't exist
 * @grp_name:   Name of the group
 * @grp:        Full path and name of the group
 * @parent_grp: Full path and name of the parent group
 *
 * 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)
{
        int found_grp = 0;
        struct dirent *ep;
        DIR *dp;

        if (!grp_name)
                return 0;

        /* Check if requested grp exists or not */
        dp = opendir(parent_grp);
        if (dp) {
                while ((ep = readdir(dp)) != NULL) {
                        if (strcmp(ep->d_name, grp_name) == 0)
                                found_grp = 1;
                }
                closedir(dp);
        } else {
                ksft_perror("Unable to open resctrl for group");

                return -1;
        }

        /* Requested grp doesn't exist, hence create it */
        if (found_grp == 0) {
                if (mkdir(grp, 0) == -1) {
                        ksft_perror("Unable to create group");

                        return -1;
                }
        }

        return 0;
}

static int write_pid_to_tasks(char *tasks, pid_t pid)
{
        FILE *fp;

        fp = fopen(tasks, "w");
        if (!fp) {
                ksft_perror("Failed to open tasks file");

                return -1;
        }
        if (fprintf(fp, "%d\n", (int)pid) < 0) {
                ksft_print_msg("Failed to write pid to tasks file\n");
                fclose(fp);

                return -1;
        }
        fclose(fp);

        return 0;
}

/*
 * write_bm_pid_to_resctrl - Write a PID (i.e. benchmark) to resctrl FS
 * @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)
 *
 * If a con_mon grp is requested, create it and write pid to it, otherwise
 * write pid to root con_mon grp.
 * If a mon grp is requested, create it and write pid to it, otherwise
 * 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, < 0 on error.
 */
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 (ctrlgrp)
                sprintf(controlgroup, "%s/%s", RESCTRL_PATH, ctrlgrp);
        else
                sprintf(controlgroup, "%s", RESCTRL_PATH);

        /* Create control and monitoring group and write pid into it */
        ret = create_grp(ctrlgrp, controlgroup, RESCTRL_PATH);
        if (ret)
                goto out;
        sprintf(tasks, "%s/tasks", controlgroup);
        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)
                ksft_print_msg("Failed writing to resctrlfs\n");

        return ret;
}

/*
 * write_schemata - Update schemata of a con_mon grp
 * @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
 * @resource:           Resctrl resource (Eg: MB, L3, L2, etc.)
 *
 * Update schemata of a con_mon grp *only* if requested resctrl resource is
 * allocation type
 *
 * Return: 0 on success, < 0 on error.
 */
int write_schemata(const char *ctrlgrp, char *schemata, int cpu_no,
                   const char *resource)
{
        char controlgroup[1024], reason[128], schema[1024] = {};
        int domain_id, fd, schema_len, ret = 0;

        if (!schemata) {
                ksft_print_msg("Skipping empty schemata update\n");

                return -1;
        }

        if (get_domain_id(resource, cpu_no, &domain_id) < 0) {
                sprintf(reason, "Failed to get domain ID");
                ret = -1;

                goto out;
        }

        if (ctrlgrp)
                sprintf(controlgroup, "%s/%s/schemata", RESCTRL_PATH, ctrlgrp);
        else
                sprintf(controlgroup, "%s/schemata", RESCTRL_PATH);

        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);
                ret = -1;
                goto out;
        }

        fd = open(controlgroup, O_WRONLY);
        if (fd < 0) {
                snprintf(reason, sizeof(reason),
                         "open() failed : %s", strerror(errno));
                ret = -1;

                goto err_schema_not_empty;
        }
        if (write(fd, schema, schema_len) < 0) {
                snprintf(reason, sizeof(reason),
                         "write() failed : %s", strerror(errno));
                close(fd);
                ret = -1;

                goto err_schema_not_empty;
        }
        close(fd);

err_schema_not_empty:
        schema[schema_len - 1] = 0;
out:
        ksft_print_msg("Write schema \"%s\" to resctrl FS%s%s\n",
                       schema, ret ? " # " : "",
                       ret ? reason : "");

        return ret;
}

bool check_resctrlfs_support(void)
{
        FILE *inf = fopen("/proc/filesystems", "r");
        DIR *dp;
        char *res;
        bool ret = false;

        if (!inf)
                return false;

        res = fgrep(inf, "nodev\tresctrl\n");

        if (res) {
                ret = true;
                free(res);
        }

        fclose(inf);

        ksft_print_msg("%s Check kernel supports resctrl filesystem\n",
                       ret ? "Pass:" : "Fail:");

        if (!ret)
                return ret;

        dp = opendir(RESCTRL_PATH);
        ksft_print_msg("%s Check resctrl mountpoint \"%s\" exists\n",
                       dp ? "Pass:" : "Fail:", RESCTRL_PATH);
        if (dp)
                closedir(dp);

        ksft_print_msg("resctrl filesystem %s mounted\n",
                       find_resctrl_mount(NULL) ? "not" : "is");

        return ret;
}

char *fgrep(FILE *inf, const char *str)
{
        char line[256];
        int slen = strlen(str);

        while (!feof(inf)) {
                if (!fgets(line, 256, inf))
                        break;
                if (strncmp(line, str, slen))
                        continue;

                return strdup(line);
        }

        return NULL;
}

/*
 * 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 is supported, else false. False is
 *         also returned if resctrl FS is not mounted.
 */
bool resctrl_resource_exists(const char *resource)
{
        char res_path[PATH_MAX];
        struct stat statbuf;
        int ret;

        if (!resource)
                return false;

        ret = find_resctrl_mount(NULL);
        if (ret)
                return false;

        snprintf(res_path, sizeof(res_path), "%s/%s", INFO_PATH, resource);

        if (stat(res_path, &statbuf))
                return false;

        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");
        if (!inf)
                return false;

        res = fgrep(inf, feature);
        free(res);
        fclose(inf);

        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];
        FILE *fp;
        int pipefds[2];
        pid_t pid;
        int ret;

        ret = pipe(pipefds);
        if (ret) {
                ksft_perror("pipe");
                return ret;
        }
        fflush(stdout);
        pid = fork();
        if (pid == 0) {
                close(pipefds[0]);
                dup2(pipefds[1], STDOUT_FILENO);
                execlp("dmesg", "dmesg", NULL);
                ksft_perror("Executing dmesg");
                exit(1);
        }
        close(pipefds[1]);
        fp = fdopen(pipefds[0], "r");
        if (!fp) {
                ksft_perror("fdopen(pipe)");
                kill(pid, SIGTERM);

                return -1;
        }

        while (fgets(line, 1024, fp)) {
                if (strstr(line, "intel_rdt:"))
                        ksft_print_msg("dmesg: %s", line);
                if (strstr(line, "resctrl:"))
                        ksft_print_msg("dmesg: %s", line);
        }
        fclose(fp);
        waitpid(pid, NULL, 0);

        return 0;
}

int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu,
                    int group_fd, unsigned long flags)
{
        int ret;

        ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
                      group_fd, flags);
        return ret;
}

unsigned int count_bits(unsigned long n)
{
        unsigned int count = 0;

        while (n) {
                count += n & 1;
                n >>= 1;
        }

        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;
}