Skip to content

Commit

Permalink
selftests/resctrl: Adjust effective L3 cache size with SNC enabled
Browse files Browse the repository at this point in the history
Sub-NUMA Cluster divides CPUs sharing an L3 cache into separate NUMA
nodes. Systems may support splitting into either two, three, four or six
nodes. When SNC mode is enabled the effective amount of L3 cache
available for allocation is divided by the number of nodes per L3.

It's possible to detect which SNC mode is active by comparing the number
of CPUs that share a cache with CPU0, with the number of CPUs on node0.

Detect SNC mode once and let other tests inherit that information.

Update CFLAGS after including lib.mk in the Makefile so that fallthrough
macro can be used.

To check if SNC detection is reliable one can check the
/sys/devices/system/cpu/offline file. If it's empty, it means all cores
are operational and the ratio should be calculated correctly. If it has
any contents, it means the detected SNC mode can't be trusted and should
be disabled.

Check if detection was not reliable due to offline cpus. If it was skip
running tests since the results couldn't be trusted.

Co-developed-by: Tony Luck <tony.luck@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Reviewed-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
  • Loading branch information
Maciej Wieczor-Retman authored and Shuah Khan committed Jan 15, 2025
1 parent 89ae643 commit a1cd99e
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 1 deletion.
1 change: 1 addition & 0 deletions tools/testing/selftests/resctrl/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ TEST_GEN_PROGS := resctrl_tests
LOCAL_HDRS += $(wildcard *.h)

include ../lib.mk
CFLAGS += -I$(top_srcdir)/tools/include

$(OUTPUT)/resctrl_tests: $(wildcard *.c)
5 changes: 5 additions & 0 deletions tools/testing/selftests/resctrl/resctrl.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <signal.h>
#include <dirent.h>
#include <stdbool.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
Expand All @@ -21,6 +22,7 @@
#include <sys/eventfd.h>
#include <asm/unistd.h>
#include <linux/perf_event.h>
#include <linux/compiler.h>
#include "../kselftest.h"

#define MB (1024 * 1024)
Expand Down Expand Up @@ -156,8 +158,11 @@ struct perf_event_read {
*/
extern volatile int *value_sink;

extern int snc_unreliable;

extern char llc_occup_path[1024];

int snc_nodes_per_l3_cache(void);
int get_vendor(void);
bool check_resctrlfs_support(void);
int filter_dmesg(void);
Expand Down
9 changes: 8 additions & 1 deletion tools/testing/selftests/resctrl/resctrl_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ static bool test_vendor_specific_check(const struct resctrl_test *test)

static void run_single_test(const struct resctrl_test *test, const struct user_params *uparams)
{
int ret;
int ret, snc_mode;

if (test->disabled)
return;
Expand All @@ -128,8 +128,15 @@ static void run_single_test(const struct resctrl_test *test, const struct user_p
return;
}

snc_mode = snc_nodes_per_l3_cache();

ksft_print_msg("Starting %s test ...\n", test->name);

if (snc_mode == 1 && snc_unreliable && get_vendor() == ARCH_INTEL) {
ksft_test_result_skip("SNC detection unreliable due to offline CPUs. Test results may not be accurate if SNC enabled.\n");
return;
}

if (test_prepare(test)) {
ksft_exit_fail_msg("Abnormal failure when preparing for the test\n");
return;
Expand Down
105 changes: 105 additions & 0 deletions tools/testing/selftests/resctrl/resctrlfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

#include "resctrl.h"

int snc_unreliable;

static int find_resctrl_mount(char *buffer)
{
FILE *mounts;
Expand Down Expand Up @@ -156,6 +158,98 @@ int get_domain_id(const char *resource, int cpu_no, int *domain_id)
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;

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
Expand Down Expand Up @@ -211,6 +305,17 @@ int get_cache_size(int cpu_no, const 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;
}

Expand Down

0 comments on commit a1cd99e

Please sign in to comment.