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.
*/ staticint get_cache_level(constchar *cache_type)
{ if (!strcmp(cache_type, "L3")) return 3; if (!strcmp(cache_type, "L2")) return 2;
staticint get_resource_cache_level(constchar *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(constchar *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;
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; staticint 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 (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, constchar *cache_type, unsignedlong *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'))
/* * 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;
}
/* * 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.
*/ staticint get_bit_mask(constchar *filename, unsignedlong *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(constchar *resource, constchar *filename, unsignedint *val)
{ char file_path[PATH_MAX];
FILE *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;
}
/* * create_bit_mask- Create bit mask from start, len pair * @start: LSB of the mask * @len Number of bits in the mask
*/ unsignedlong create_bit_mask(unsignedint start, unsignedint 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
*/ unsignedint count_contiguous_bits(unsignedlong val, unsignedint *start)
{ unsignedlong last_val; unsignedint 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(constchar *cache_type, unsignedlong *mask)
{ char cbm_path[PATH_MAX]; int ret;
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.
*/ staticint get_shareable_mask(constchar *cache_type, unsignedlong *shareable_mask)
{ char mask_path[PATH_MAX];
/* * 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(constchar *cache_type, unsignedlong *mask)
{ unsignedlong full_mask, shareable_mask; unsignedint 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.
*/ staticint create_grp(constchar *grp_name, char *grp, constchar *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");
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, constchar *ctrlgrp, constchar *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(constchar *ctrlgrp, char *schemata, int cpu_no, constchar *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;
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(constchar *resource)
{ char res_path[PATH_MAX]; struct stat statbuf; int ret;
if (!resource) returnfalse;
ret = find_resctrl_mount(NULL); if (ret) returnfalse;
/** * 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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.