diff options
Diffstat (limited to 'tools/testing')
| -rw-r--r-- | tools/testing/cxl/test/mem.c | 69 | ||||
| -rw-r--r-- | tools/testing/radix-tree/Makefile | 4 | ||||
| -rw-r--r-- | tools/testing/radix-tree/bitmap.c | 23 | ||||
| -rw-r--r-- | tools/testing/selftests/arm64/abi/ptrace.c | 2 | ||||
| -rw-r--r-- | tools/testing/selftests/hid/hid_bpf.c | 26 | ||||
| -rw-r--r-- | tools/testing/selftests/hid/progs/hid.c | 2 | ||||
| -rw-r--r-- | tools/testing/selftests/hid/progs/hid_bpf_helpers.h | 2 | ||||
| -rw-r--r-- | tools/testing/selftests/landlock/base_test.c | 74 | ||||
| -rw-r--r-- | tools/testing/selftests/landlock/config | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/mm/mremap_test.c | 2 | ||||
| -rw-r--r-- | tools/testing/selftests/mm/va_high_addr_switch.c | 16 | ||||
| -rw-r--r-- | tools/testing/selftests/seccomp/seccomp_bpf.c | 2 | ||||
| -rwxr-xr-x | tools/testing/selftests/turbostat/added_perf_counters.py | 178 | ||||
| -rwxr-xr-x | tools/testing/selftests/turbostat/smi_aperf_mperf.py | 157 | 
14 files changed, 496 insertions, 62 deletions
| diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index eaf091a3d331..129f179b0ac5 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -385,19 +385,21 @@ struct cxl_test_gen_media {  struct cxl_test_gen_media gen_media = {  	.id = CXL_EVENT_GEN_MEDIA_UUID,  	.rec = { -		.hdr = { -			.length = sizeof(struct cxl_test_gen_media), -			.flags[0] = CXL_EVENT_RECORD_FLAG_PERMANENT, -			/* .handle = Set dynamically */ -			.related_handle = cpu_to_le16(0), +		.media_hdr = { +			.hdr = { +				.length = sizeof(struct cxl_test_gen_media), +				.flags[0] = CXL_EVENT_RECORD_FLAG_PERMANENT, +				/* .handle = Set dynamically */ +				.related_handle = cpu_to_le16(0), +			}, +			.phys_addr = cpu_to_le64(0x2000), +			.descriptor = CXL_GMER_EVT_DESC_UNCORECTABLE_EVENT, +			.type = CXL_GMER_MEM_EVT_TYPE_DATA_PATH_ERROR, +			.transaction_type = CXL_GMER_TRANS_HOST_WRITE, +			/* .validity_flags = <set below> */ +			.channel = 1, +			.rank = 30,  		}, -		.phys_addr = cpu_to_le64(0x2000), -		.descriptor = CXL_GMER_EVT_DESC_UNCORECTABLE_EVENT, -		.type = CXL_GMER_MEM_EVT_TYPE_DATA_PATH_ERROR, -		.transaction_type = CXL_GMER_TRANS_HOST_WRITE, -		/* .validity_flags = <set below> */ -		.channel = 1, -		.rank = 30  	},  }; @@ -409,18 +411,20 @@ struct cxl_test_dram {  struct cxl_test_dram dram = {  	.id = CXL_EVENT_DRAM_UUID,  	.rec = { -		.hdr = { -			.length = sizeof(struct cxl_test_dram), -			.flags[0] = CXL_EVENT_RECORD_FLAG_PERF_DEGRADED, -			/* .handle = Set dynamically */ -			.related_handle = cpu_to_le16(0), +		.media_hdr = { +			.hdr = { +				.length = sizeof(struct cxl_test_dram), +				.flags[0] = CXL_EVENT_RECORD_FLAG_PERF_DEGRADED, +				/* .handle = Set dynamically */ +				.related_handle = cpu_to_le16(0), +			}, +			.phys_addr = cpu_to_le64(0x8000), +			.descriptor = CXL_GMER_EVT_DESC_THRESHOLD_EVENT, +			.type = CXL_GMER_MEM_EVT_TYPE_INV_ADDR, +			.transaction_type = CXL_GMER_TRANS_INTERNAL_MEDIA_SCRUB, +			/* .validity_flags = <set below> */ +			.channel = 1,  		}, -		.phys_addr = cpu_to_le64(0x8000), -		.descriptor = CXL_GMER_EVT_DESC_THRESHOLD_EVENT, -		.type = CXL_GMER_MEM_EVT_TYPE_INV_ADDR, -		.transaction_type = CXL_GMER_TRANS_INTERNAL_MEDIA_SCRUB, -		/* .validity_flags = <set below> */ -		.channel = 1,  		.bank_group = 5,  		.bank = 2,  		.column = {0xDE, 0xAD}, @@ -474,11 +478,11 @@ static int mock_set_timestamp(struct cxl_dev_state *cxlds,  static void cxl_mock_add_event_logs(struct mock_event_store *mes)  {  	put_unaligned_le16(CXL_GMER_VALID_CHANNEL | CXL_GMER_VALID_RANK, -			   &gen_media.rec.validity_flags); +			   &gen_media.rec.media_hdr.validity_flags);  	put_unaligned_le16(CXL_DER_VALID_CHANNEL | CXL_DER_VALID_BANK_GROUP |  			   CXL_DER_VALID_BANK | CXL_DER_VALID_COLUMN, -			   &dram.rec.validity_flags); +			   &dram.rec.media_hdr.validity_flags);  	mes_add_event(mes, CXL_EVENT_TYPE_INFO, &maint_needed);  	mes_add_event(mes, CXL_EVENT_TYPE_INFO, @@ -1131,27 +1135,28 @@ static bool mock_poison_dev_max_injected(struct cxl_dev_state *cxlds)  	return (count >= poison_inject_dev_max);  } -static bool mock_poison_add(struct cxl_dev_state *cxlds, u64 dpa) +static int mock_poison_add(struct cxl_dev_state *cxlds, u64 dpa)  { +	/* Return EBUSY to match the CXL driver handling */  	if (mock_poison_dev_max_injected(cxlds)) {  		dev_dbg(cxlds->dev,  			"Device poison injection limit has been reached: %d\n", -			MOCK_INJECT_DEV_MAX); -		return false; +			poison_inject_dev_max); +		return -EBUSY;  	}  	for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {  		if (!mock_poison_list[i].cxlds) {  			mock_poison_list[i].cxlds = cxlds;  			mock_poison_list[i].dpa = dpa; -			return true; +			return 0;  		}  	}  	dev_dbg(cxlds->dev,  		"Mock test poison injection limit has been reached: %d\n",  		MOCK_INJECT_TEST_MAX); -	return false; +	return -ENXIO;  }  static bool mock_poison_found(struct cxl_dev_state *cxlds, u64 dpa) @@ -1175,10 +1180,8 @@ static int mock_inject_poison(struct cxl_dev_state *cxlds,  		dev_dbg(cxlds->dev, "DPA: 0x%llx already poisoned\n", dpa);  		return 0;  	} -	if (!mock_poison_add(cxlds, dpa)) -		return -ENXIO; -	return 0; +	return mock_poison_add(cxlds, dpa);  }  static bool mock_poison_del(struct cxl_dev_state *cxlds, u64 dpa) diff --git a/tools/testing/radix-tree/Makefile b/tools/testing/radix-tree/Makefile index 7527f738b4a1..d1acd7d58850 100644 --- a/tools/testing/radix-tree/Makefile +++ b/tools/testing/radix-tree/Makefile @@ -5,8 +5,8 @@ CFLAGS += -I. -I../../include -I../../../lib -g -Og -Wall \  LDFLAGS += -fsanitize=address -fsanitize=undefined  LDLIBS+= -lpthread -lurcu  TARGETS = main idr-test multiorder xarray maple -CORE_OFILES := xarray.o radix-tree.o idr.o linux.o test.o find_bit.o bitmap.o \ -			 slab.o maple.o +LIBS := slab.o find_bit.o bitmap.o hweight.o vsprintf.o +CORE_OFILES := xarray.o radix-tree.o idr.o linux.o test.o maple.o $(LIBS)  OFILES = main.o $(CORE_OFILES) regression1.o regression2.o regression3.o \  	 regression4.o tag_check.o multiorder.o idr-test.o iteration_check.o \  	 iteration_check_2.o benchmark.o diff --git a/tools/testing/radix-tree/bitmap.c b/tools/testing/radix-tree/bitmap.c deleted file mode 100644 index 66ec4a24a203..000000000000 --- a/tools/testing/radix-tree/bitmap.c +++ /dev/null @@ -1,23 +0,0 @@ -/* lib/bitmap.c pulls in at least two other files. */ - -#include <linux/bitmap.h> - -void bitmap_clear(unsigned long *map, unsigned int start, int len) -{ -	unsigned long *p = map + BIT_WORD(start); -	const unsigned int size = start + len; -	int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); -	unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); - -	while (len - bits_to_clear >= 0) { -		*p &= ~mask_to_clear; -		len -= bits_to_clear; -		bits_to_clear = BITS_PER_LONG; -		mask_to_clear = ~0UL; -		p++; -	} -	if (len) { -		mask_to_clear &= BITMAP_LAST_WORD_MASK(size); -		*p &= ~mask_to_clear; -	} -} diff --git a/tools/testing/selftests/arm64/abi/ptrace.c b/tools/testing/selftests/arm64/abi/ptrace.c index 4c941270d8de..e4fa507cbdd0 100644 --- a/tools/testing/selftests/arm64/abi/ptrace.c +++ b/tools/testing/selftests/arm64/abi/ptrace.c @@ -156,7 +156,7 @@ static void test_hw_debug(pid_t child, int type, const char *type_name)  		/* Zero is not currently architecturally valid */  		ksft_test_result(arch, "%s_arch_set\n", type_name);  	} else { -		ksft_test_result_skip("%s_arch_set\n"); +		ksft_test_result_skip("%s_arch_set\n", type_name);  	}  } diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index dc0408a831d0..75b7b4ef6cfa 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -532,6 +532,7 @@ static void load_programs(const struct test_program programs[],  			  FIXTURE_DATA(hid_bpf) * self,  			  const FIXTURE_VARIANT(hid_bpf) * variant)  { +	struct bpf_map *iter_map;  	int err = -EINVAL;  	ASSERT_LE(progs_count, ARRAY_SIZE(self->hid_links)) @@ -564,6 +565,13 @@ static void load_programs(const struct test_program programs[],  		*ops_hid_id = self->hid_id;  	} +	/* we disable the auto-attach feature of all maps because we +	 * only want the tested one to be manually attached in the next +	 * call to bpf_map__attach_struct_ops() +	 */ +	bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj) +		bpf_map__set_autoattach(iter_map, false); +  	err = hid__load(self->skel);  	ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err); @@ -687,6 +695,24 @@ TEST_F(hid_bpf, subprog_raw_event)  }  /* + * Attach hid_first_event to the given uhid device, + * attempt at re-attaching it, we should not lock and + * return an invalid struct bpf_link + */ +TEST_F(hid_bpf, multiple_attach) +{ +	const struct test_program progs[] = { +		{ .name = "hid_first_event" }, +	}; +	struct bpf_link *link; + +	LOAD_PROGRAMS(progs); + +	link = bpf_map__attach_struct_ops(self->skel->maps.first_event); +	ASSERT_NULL(link) TH_LOG("unexpected return value when re-attaching the struct_ops"); +} + +/*   * Ensures that we can attach/detach programs   */  TEST_F(hid_bpf, test_attach_detach) diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c index ee9bbbcf751b..5ecc845ef792 100644 --- a/tools/testing/selftests/hid/progs/hid.c +++ b/tools/testing/selftests/hid/progs/hid.c @@ -455,7 +455,7 @@ struct {  	__type(value, struct elem);  } hmap SEC(".maps"); -static int wq_cb_sleepable(void *map, int *key, struct bpf_wq *work) +static int wq_cb_sleepable(void *map, int *key, void *work)  {  	__u8 buf[9] = {2, 3, 4, 5, 6, 7, 8, 9, 10};  	struct hid_bpf_ctx *hid_ctx; diff --git a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h index cfe37f491906..e5db897586bb 100644 --- a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h +++ b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h @@ -114,7 +114,7 @@ extern int hid_bpf_try_input_report(struct hid_bpf_ctx *ctx,  extern int bpf_wq_init(struct bpf_wq *wq, void *p__map, unsigned int flags) __weak __ksym;  extern int bpf_wq_start(struct bpf_wq *wq, unsigned int flags) __weak __ksym;  extern int bpf_wq_set_callback_impl(struct bpf_wq *wq, -		int (callback_fn)(void *map, int *key, struct bpf_wq *wq), +		int (callback_fn)(void *map, int *key, void *wq),  		unsigned int flags__k, void *aux__ign) __ksym;  #define bpf_wq_set_callback(timer, cb, flags) \  	bpf_wq_set_callback_impl(timer, cb, flags, NULL) diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c index 3c1e9f35b531..3b26bf3cf5b9 100644 --- a/tools/testing/selftests/landlock/base_test.c +++ b/tools/testing/selftests/landlock/base_test.c @@ -9,6 +9,7 @@  #define _GNU_SOURCE  #include <errno.h>  #include <fcntl.h> +#include <linux/keyctl.h>  #include <linux/landlock.h>  #include <string.h>  #include <sys/prctl.h> @@ -326,4 +327,77 @@ TEST(ruleset_fd_transfer)  	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));  } +TEST(cred_transfer) +{ +	struct landlock_ruleset_attr ruleset_attr = { +		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR, +	}; +	int ruleset_fd, dir_fd; +	pid_t child; +	int status; + +	drop_caps(_metadata); + +	dir_fd = open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC); +	EXPECT_LE(0, dir_fd); +	EXPECT_EQ(0, close(dir_fd)); + +	/* Denies opening directories. */ +	ruleset_fd = +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); +	ASSERT_LE(0, ruleset_fd); +	EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); +	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); +	EXPECT_EQ(0, close(ruleset_fd)); + +	/* Checks ruleset enforcement. */ +	EXPECT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)); +	EXPECT_EQ(EACCES, errno); + +	/* Needed for KEYCTL_SESSION_TO_PARENT permission checks */ +	EXPECT_NE(-1, syscall(__NR_keyctl, KEYCTL_JOIN_SESSION_KEYRING, NULL, 0, +			      0, 0)) +	{ +		TH_LOG("Failed to join session keyring: %s", strerror(errno)); +	} + +	child = fork(); +	ASSERT_LE(0, child); +	if (child == 0) { +		/* Checks ruleset enforcement. */ +		EXPECT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)); +		EXPECT_EQ(EACCES, errno); + +		/* +		 * KEYCTL_SESSION_TO_PARENT is a no-op unless we have a +		 * different session keyring in the child, so make that happen. +		 */ +		EXPECT_NE(-1, syscall(__NR_keyctl, KEYCTL_JOIN_SESSION_KEYRING, +				      NULL, 0, 0, 0)); + +		/* +		 * KEYCTL_SESSION_TO_PARENT installs credentials on the parent +		 * that never go through the cred_prepare hook, this path uses +		 * cred_transfer instead. +		 */ +		EXPECT_EQ(0, syscall(__NR_keyctl, KEYCTL_SESSION_TO_PARENT, 0, +				     0, 0, 0)); + +		/* Re-checks ruleset enforcement. */ +		EXPECT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)); +		EXPECT_EQ(EACCES, errno); + +		_exit(_metadata->exit_code); +		return; +	} + +	EXPECT_EQ(child, waitpid(child, &status, 0)); +	EXPECT_EQ(1, WIFEXITED(status)); +	EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); + +	/* Re-checks ruleset enforcement. */ +	EXPECT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)); +	EXPECT_EQ(EACCES, errno); +} +  TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config index 0086efaa7b68..29af19c4e9f9 100644 --- a/tools/testing/selftests/landlock/config +++ b/tools/testing/selftests/landlock/config @@ -2,6 +2,7 @@ CONFIG_CGROUPS=y  CONFIG_CGROUP_SCHED=y  CONFIG_INET=y  CONFIG_IPV6=y +CONFIG_KEYS=y  CONFIG_NET=y  CONFIG_NET_NS=y  CONFIG_OVERLAY_FS=y diff --git a/tools/testing/selftests/mm/mremap_test.c b/tools/testing/selftests/mm/mremap_test.c index 1b03bcfaefdf..5a3a9bcba640 100644 --- a/tools/testing/selftests/mm/mremap_test.c +++ b/tools/testing/selftests/mm/mremap_test.c @@ -22,8 +22,10 @@  #define VALIDATION_DEFAULT_THRESHOLD 4	/* 4MB */  #define VALIDATION_NO_THRESHOLD 0	/* Verify the entire region */ +#ifndef MIN  #define MIN(X, Y) ((X) < (Y) ? (X) : (Y))  #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) +#endif  #define SIZE_MB(m) ((size_t)m * (1024 * 1024))  #define SIZE_KB(k) ((size_t)k * 1024) diff --git a/tools/testing/selftests/mm/va_high_addr_switch.c b/tools/testing/selftests/mm/va_high_addr_switch.c index fa7eabfaf841..896b3f73fc53 100644 --- a/tools/testing/selftests/mm/va_high_addr_switch.c +++ b/tools/testing/selftests/mm/va_high_addr_switch.c @@ -293,6 +293,20 @@ static int run_test(struct testcase *test, int count)  	return ret;  } +#ifdef __aarch64__ +/* Check if userspace VA > 48 bits */ +static int high_address_present(void) +{ +	void *ptr = mmap((void *)(1UL << 50), 1, PROT_READ | PROT_WRITE, +			 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); +	if (ptr == MAP_FAILED) +		return 0; + +	munmap(ptr, 1); +	return 1; +} +#endif +  static int supported_arch(void)  {  #if defined(__powerpc64__) @@ -300,7 +314,7 @@ static int supported_arch(void)  #elif defined(__x86_64__)  	return 1;  #elif defined(__aarch64__) -	return 1; +	return high_address_present();  #else  	return 0;  #endif diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index e3f97f90d8db..8c3a73461475 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -60,7 +60,9 @@  #define SKIP(s, ...)	XFAIL(s, ##__VA_ARGS__)  #endif +#ifndef MIN  #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#endif  #ifndef PR_SET_PTRACER  # define PR_SET_PTRACER 0x59616d61 diff --git a/tools/testing/selftests/turbostat/added_perf_counters.py b/tools/testing/selftests/turbostat/added_perf_counters.py new file mode 100755 index 000000000000..9ab4aaf45fb8 --- /dev/null +++ b/tools/testing/selftests/turbostat/added_perf_counters.py @@ -0,0 +1,178 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +import subprocess +from shutil import which +from os import pread + +class PerfCounterInfo: +	def __init__(self, subsys, event): +		self.subsys = subsys +		self.event = event + +	def get_perf_event_name(self): +		return f'{self.subsys}/{self.event}/' + +	def get_turbostat_perf_id(self, counter_scope, counter_type, column_name): +		return f'perf/{self.subsys}/{self.event},{counter_scope},{counter_type},{column_name}' + +PERF_COUNTERS_CANDIDATES = [ +	PerfCounterInfo('msr', 'mperf'), +	PerfCounterInfo('msr', 'aperf'), +	PerfCounterInfo('msr', 'tsc'), +	PerfCounterInfo('cstate_core', 'c1-residency'), +	PerfCounterInfo('cstate_core', 'c6-residency'), +	PerfCounterInfo('cstate_core', 'c7-residency'), +	PerfCounterInfo('cstate_pkg', 'c2-residency'), +	PerfCounterInfo('cstate_pkg', 'c3-residency'), +	PerfCounterInfo('cstate_pkg', 'c6-residency'), +	PerfCounterInfo('cstate_pkg', 'c7-residency'), +	PerfCounterInfo('cstate_pkg', 'c8-residency'), +	PerfCounterInfo('cstate_pkg', 'c9-residency'), +	PerfCounterInfo('cstate_pkg', 'c10-residency'), +] +present_perf_counters = [] + +def check_perf_access(): +	perf = which('perf') +	if perf is None: +		print('SKIP: Could not find perf binary, thus could not determine perf access.') +		return False + +	def has_perf_counter_access(counter_name): +		proc_perf = subprocess.run([perf, 'stat', '-e', counter_name, '--timeout', '10'], +							 capture_output = True) + +		if proc_perf.returncode != 0: +			print(f'SKIP: Could not read {counter_name} perf counter.') +			return False + +		if b'<not supported>' in proc_perf.stderr: +			print(f'SKIP: Could not read {counter_name} perf counter.') +			return False + +		return True + +	for counter in PERF_COUNTERS_CANDIDATES: +		if has_perf_counter_access(counter.get_perf_event_name()): +			present_perf_counters.append(counter) + +	if len(present_perf_counters) == 0: +		print('SKIP: Could not read any perf counter.') +		return False + +	if len(present_perf_counters) != len(PERF_COUNTERS_CANDIDATES): +		print(f'WARN: Could not access all of the counters - some will be left untested') + +	return True + +if not check_perf_access(): +	exit(0) + +turbostat_counter_source_opts = [''] + +turbostat = which('turbostat') +if turbostat is None: +	print('Could not find turbostat binary') +	exit(1) + +timeout = which('timeout') +if timeout is None: +	print('Could not find timeout binary') +	exit(1) + +proc_turbostat = subprocess.run([turbostat, '--list'], capture_output = True) +if proc_turbostat.returncode != 0: +	print(f'turbostat failed with {proc_turbostat.returncode}') +	exit(1) + +EXPECTED_COLUMNS_DEBUG_DEFAULT = [b'usec', b'Time_Of_Day_Seconds', b'APIC', b'X2APIC'] + +expected_columns = [b'CPU'] +counters_argv = [] +for counter in present_perf_counters: +	if counter.subsys == 'cstate_core': +		counter_scope = 'core' +	elif counter.subsys == 'cstate_pkg': +		counter_scope = 'package' +	else: +		counter_scope = 'cpu' + +	counter_type = 'delta' +	column_name = counter.event + +	cparams = counter.get_turbostat_perf_id( +		counter_scope = counter_scope, +		counter_type = counter_type, +		column_name = column_name +	) +	expected_columns.append(column_name.encode()) +	counters_argv.extend(['--add', cparams]) + +expected_columns_debug = EXPECTED_COLUMNS_DEBUG_DEFAULT + expected_columns + +def gen_user_friendly_cmdline(argv_): +	argv = argv_[:] +	ret = '' + +	while len(argv) != 0: +		arg = argv.pop(0) +		arg_next = '' + +		if arg in ('-i', '--show', '--add'): +			arg_next = argv.pop(0) if len(argv) > 0 else '' + +		ret += f'{arg} {arg_next} \\\n\t' + +	# Remove the last separator and return +	return ret[:-4] + +# +# Run turbostat for some time and send SIGINT +# +timeout_argv = [timeout, '--preserve-status', '-s', 'SIGINT', '-k', '3', '0.2s'] +turbostat_argv = [turbostat, '-i', '0.50', '--show', 'CPU'] + counters_argv + +def check_columns_or_fail(expected_columns: list, actual_columns: list): +	if len(actual_columns) != len(expected_columns): +		print(f'turbostat column check failed\n{expected_columns=}\n{actual_columns=}') +		exit(1) + +	failed = False +	for expected_column in expected_columns: +		if expected_column not in actual_columns: +			print(f'turbostat column check failed: missing column {expected_column.decode()}') +			failed = True + +	if failed: +		exit(1) + +cmdline = gen_user_friendly_cmdline(turbostat_argv) +print(f'Running turbostat with:\n\t{cmdline}\n... ', end = '', flush = True) +proc_turbostat = subprocess.run(timeout_argv + turbostat_argv, capture_output = True) +if proc_turbostat.returncode != 0: +	print(f'turbostat failed with {proc_turbostat.returncode}') +	exit(1) + +actual_columns = proc_turbostat.stdout.split(b'\n')[0].split(b'\t') +check_columns_or_fail(expected_columns, actual_columns) +print('OK') + +# +# Same, but with --debug +# +# We explicitly specify '--show CPU' to make sure turbostat +# don't show a bunch of default counters instead. +# +turbostat_argv.append('--debug') + +cmdline = gen_user_friendly_cmdline(turbostat_argv) +print(f'Running turbostat (in debug mode) with:\n\t{cmdline}\n... ', end = '', flush = True) +proc_turbostat = subprocess.run(timeout_argv + turbostat_argv, capture_output = True) +if proc_turbostat.returncode != 0: +	print(f'turbostat failed with {proc_turbostat.returncode}') +	exit(1) + +actual_columns = proc_turbostat.stdout.split(b'\n')[0].split(b'\t') +check_columns_or_fail(expected_columns_debug, actual_columns) +print('OK') diff --git a/tools/testing/selftests/turbostat/smi_aperf_mperf.py b/tools/testing/selftests/turbostat/smi_aperf_mperf.py new file mode 100755 index 000000000000..6289cc47d5f0 --- /dev/null +++ b/tools/testing/selftests/turbostat/smi_aperf_mperf.py @@ -0,0 +1,157 @@ +#!/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +import subprocess +from shutil import which +from os import pread + +# CDLL calls dlopen underneath. +# Calling it with None (null), we get handle to the our own image (python interpreter). +# We hope to find sched_getcpu() inside ;] +# This is a bit ugly, but helps shipping working software, so.. +try: +	import ctypes + +	this_image = ctypes.CDLL(None) +	BASE_CPU = this_image.sched_getcpu() +except: +	BASE_CPU = 0 # If we fail, set to 0 and pray it's not offline. + +MSR_IA32_MPERF = 0x000000e7 +MSR_IA32_APERF = 0x000000e8 + +def check_perf_access(): +	perf = which('perf') +	if perf is None: +		print('SKIP: Could not find perf binary, thus could not determine perf access.') +		return False + +	def has_perf_counter_access(counter_name): +		proc_perf = subprocess.run([perf, 'stat', '-e', counter_name, '--timeout', '10'], +					    capture_output = True) + +		if proc_perf.returncode != 0: +			print(f'SKIP: Could not read {counter_name} perf counter, assuming no access.') +			return False + +		if b'<not supported>' in proc_perf.stderr: +			print(f'SKIP: Could not read {counter_name} perf counter, assuming no access.') +			return False + +		return True + +	if not has_perf_counter_access('msr/mperf/'): +		return False +	if not has_perf_counter_access('msr/aperf/'): +		return False +	if not has_perf_counter_access('msr/smi/'): +		return False + +	return True + +def check_msr_access(): +	try: +		file_msr = open(f'/dev/cpu/{BASE_CPU}/msr', 'rb') +	except: +		return False + +	if len(pread(file_msr.fileno(), 8, MSR_IA32_MPERF)) != 8: +		return False + +	if len(pread(file_msr.fileno(), 8, MSR_IA32_APERF)) != 8: +		return False + +	return True + +has_perf_access = check_perf_access() +has_msr_access = check_msr_access() + +turbostat_counter_source_opts = [''] + +if has_msr_access: +	turbostat_counter_source_opts.append('--no-perf') +else: +	print('SKIP: doesn\'t have MSR access, skipping run with --no-perf') + +if has_perf_access: +	turbostat_counter_source_opts.append('--no-msr') +else: +	print('SKIP: doesn\'t have perf access, skipping run with --no-msr') + +if not has_msr_access and not has_perf_access: +	print('SKIP: No MSR nor perf access detected. Skipping the tests entirely') +	exit(0) + +turbostat = which('turbostat') +if turbostat is None: +	print('Could not find turbostat binary') +	exit(1) + +timeout = which('timeout') +if timeout is None: +	print('Could not find timeout binary') +	exit(1) + +proc_turbostat = subprocess.run([turbostat, '--list'], capture_output = True) +if proc_turbostat.returncode != 0: +	print(f'turbostat failed with {proc_turbostat.returncode}') +	exit(1) + +EXPECTED_COLUMNS_DEBUG_DEFAULT = b'usec\tTime_Of_Day_Seconds\tAPIC\tX2APIC' + +SMI_APERF_MPERF_DEPENDENT_BICS = [ +	'SMI', +	'Avg_MHz', +	'Busy%', +	'Bzy_MHz', +] +if has_perf_access: +	SMI_APERF_MPERF_DEPENDENT_BICS.append('IPC') + +for bic in SMI_APERF_MPERF_DEPENDENT_BICS: +	for counter_source_opt in turbostat_counter_source_opts: + +		# Ugly special case, but it is what it is.. +		if counter_source_opt == '--no-perf' and bic == 'IPC': +			continue + +		expected_columns = bic.encode() +		expected_columns_debug = EXPECTED_COLUMNS_DEBUG_DEFAULT + f'\t{bic}'.encode() + +		# +		# Run turbostat for some time and send SIGINT +		# +		timeout_argv = [timeout, '--preserve-status', '-s', 'SIGINT', '-k', '3', '0.2s'] +		turbostat_argv = [turbostat, '-i', '0.50', '--show', bic] + +		if counter_source_opt: +			turbostat_argv.append(counter_source_opt) + +		print(f'Running turbostat with {turbostat_argv=}... ', end = '', flush = True) +		proc_turbostat = subprocess.run(timeout_argv + turbostat_argv, capture_output = True) +		if proc_turbostat.returncode != 0: +			print(f'turbostat failed with {proc_turbostat.returncode}') +			exit(1) + +		actual_columns = proc_turbostat.stdout.split(b'\n')[0] +		if expected_columns != actual_columns: +			print(f'turbostat column check failed\n{expected_columns=}\n{actual_columns=}') +			exit(1) +		print('OK') + +		# +		# Same, but with --debug +		# +		turbostat_argv.append('--debug') + +		print(f'Running turbostat with {turbostat_argv=}... ', end = '', flush = True) +		proc_turbostat = subprocess.run(timeout_argv + turbostat_argv, capture_output = True) +		if proc_turbostat.returncode != 0: +			print(f'turbostat failed with {proc_turbostat.returncode}') +			exit(1) + +		actual_columns = proc_turbostat.stdout.split(b'\n')[0] +		if expected_columns_debug != actual_columns: +			print(f'turbostat column check failed\n{expected_columns_debug=}\n{actual_columns=}') +			exit(1) +		print('OK') | 
