mirror of
https://github.com/torvalds/linux.git
synced 2026-01-25 15:03:52 +08:00
Merge tag 'linux_kselftest-next-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
Pull kselftest updates from Shuah Khan: - fixes, reporting improvements, and cleanup changes to several tests - add support for DT_GNU_HASH to selftests/vDSO * tag 'linux_kselftest-next-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: selftests/rseq: Fix handling of glibc without rseq support selftests/resctrl: Discover SNC kernel support and adjust messages selftests/resctrl: Adjust effective L3 cache size with SNC enabled selftests/ftrace: Make uprobe test more robust against binary name selftests/ftrace: Fix to use remount when testing mount GID option selftests: tmpfs: Add kselftest support to tmpfs selftests: tmpfs: Add Test-skip if not run as root selftests: harness: fix printing of mismatch values in __EXPECT() selftests/ring-buffer: Add test for out-of-bound pgoff mapping selftests/run_kselftest.sh: Fix help string for --per-test-log selftests: acct: Add ksft_exit_skip if not running as root selftests: kselftest: Fix the wrong format specifier selftests: timers: clocksource-switch: Adapt progress to kselftest framework selftests/zram: gitignore output file selftests/filesystems: Add missing gitignore file selftests: Warn about skipped tests in result summary selftests: kselftest: Add ksft_test_result_xpass selftests/vDSO: support DT_GNU_HASH selftests/ipc: Remove unused variables selftest: media_tests: fix trivial UAF typo
This commit is contained in:
@@ -24,7 +24,7 @@ int main(void)
|
||||
|
||||
// Check if test is run a root
|
||||
if (geteuid()) {
|
||||
ksft_test_result_skip("This test needs root to run!\n");
|
||||
ksft_exit_skip("This test needs root to run!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
statmount_test_ns
|
||||
/*_test
|
||||
|
||||
@@ -15,11 +15,11 @@ find_alternate_gid() {
|
||||
tac /etc/group | grep -v ":$original_gid:" | head -1 | cut -d: -f3
|
||||
}
|
||||
|
||||
mount_tracefs_with_options() {
|
||||
remount_tracefs_with_options() {
|
||||
local mount_point="$1"
|
||||
local options="$2"
|
||||
|
||||
mount -t tracefs -o "$options" nodev "$mount_point"
|
||||
mount -t tracefs -o "remount,$options" nodev "$mount_point"
|
||||
|
||||
setup
|
||||
}
|
||||
@@ -81,7 +81,7 @@ test_gid_mount_option() {
|
||||
|
||||
# Unmount existing tracefs instance and mount with new GID
|
||||
unmount_tracefs "$mount_point"
|
||||
mount_tracefs_with_options "$mount_point" "$new_options"
|
||||
remount_tracefs_with_options "$mount_point" "$new_options"
|
||||
|
||||
check_gid "$mount_point" "$other_group"
|
||||
|
||||
@@ -92,7 +92,7 @@ test_gid_mount_option() {
|
||||
|
||||
# Unmount and remount with the original GID
|
||||
unmount_tracefs "$mount_point"
|
||||
mount_tracefs_with_options "$mount_point" "$mount_options"
|
||||
remount_tracefs_with_options "$mount_point" "$mount_options"
|
||||
check_gid "$mount_point" "$original_group"
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
echo 0 > events/enable
|
||||
echo > dynamic_events
|
||||
|
||||
REALBIN=`readlink -f /bin/sh`
|
||||
|
||||
echo 'cat /proc/$$/maps' | /bin/sh | \
|
||||
grep "r-xp .*/bin/.*sh$" | \
|
||||
grep "r-xp .*${REALBIN}$" | \
|
||||
awk '{printf "p:myevent %s:0x%s\n", $6,$3 }' >> uprobe_events
|
||||
|
||||
grep -q myevent uprobe_events
|
||||
|
||||
@@ -194,7 +194,7 @@ int fill_msgque(struct msgque_data *msgque)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int msg, pid, err;
|
||||
int err;
|
||||
struct msgque_data msgque;
|
||||
|
||||
if (getuid() != 0)
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
* ksft_print_msg(fmt, ...);
|
||||
* ksft_perror(msg);
|
||||
*
|
||||
* and finally report the pass/fail/skip/xfail state of the test with one of:
|
||||
* and finally report the pass/fail/skip/xfail/xpass state of the test
|
||||
* with one of:
|
||||
*
|
||||
* ksft_test_result(condition, fmt, ...);
|
||||
* ksft_test_result_report(result, fmt, ...);
|
||||
@@ -26,6 +27,7 @@
|
||||
* ksft_test_result_fail(fmt, ...);
|
||||
* ksft_test_result_skip(fmt, ...);
|
||||
* ksft_test_result_xfail(fmt, ...);
|
||||
* ksft_test_result_xpass(fmt, ...);
|
||||
* ksft_test_result_error(fmt, ...);
|
||||
* ksft_test_result_code(exit_code, test_name, fmt, ...);
|
||||
*
|
||||
@@ -147,6 +149,11 @@ static inline void ksft_set_plan(unsigned int plan)
|
||||
|
||||
static inline void ksft_print_cnts(void)
|
||||
{
|
||||
if (ksft_cnt.ksft_xskip > 0)
|
||||
printf(
|
||||
"# %u skipped test(s) detected. Consider enabling relevant config options to improve coverage.\n",
|
||||
ksft_cnt.ksft_xskip
|
||||
);
|
||||
if (ksft_plan != ksft_test_num())
|
||||
printf("# Planned tests != run tests (%u != %u)\n",
|
||||
ksft_plan, ksft_test_num());
|
||||
@@ -227,6 +234,20 @@ static inline __printf(1, 2) void ksft_test_result_xfail(const char *msg, ...)
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static inline __printf(1, 2) void ksft_test_result_xpass(const char *msg, ...)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
va_list args;
|
||||
|
||||
ksft_cnt.ksft_xpass++;
|
||||
|
||||
va_start(args, msg);
|
||||
printf("ok %u # XPASS ", ksft_test_num());
|
||||
errno = saved_errno;
|
||||
vprintf(msg, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static inline __printf(1, 2) void ksft_test_result_skip(const char *msg, ...)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
@@ -318,6 +339,9 @@ void ksft_test_result_code(int exit_code, const char *test_name,
|
||||
case KSFT_XFAIL: \
|
||||
ksft_test_result_xfail(fmt, ##__VA_ARGS__); \
|
||||
break; \
|
||||
case KSFT_XPASS: \
|
||||
ksft_test_result_xpass(fmt, ##__VA_ARGS__); \
|
||||
break; \
|
||||
case KSFT_SKIP: \
|
||||
ksft_test_result_skip(fmt, ##__VA_ARGS__); \
|
||||
break; \
|
||||
@@ -403,7 +427,7 @@ static inline __noreturn __printf(1, 2) void ksft_exit_skip(const char *msg, ...
|
||||
*/
|
||||
if (ksft_plan || ksft_test_num()) {
|
||||
ksft_cnt.ksft_xskip++;
|
||||
printf("ok %d # SKIP ", 1 + ksft_test_num());
|
||||
printf("ok %u # SKIP ", 1 + ksft_test_num());
|
||||
} else {
|
||||
printf("1..0 # SKIP ");
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ def set_plan(num_tests):
|
||||
|
||||
|
||||
def print_cnts():
|
||||
if ksft_cnt['skip'] > 0:
|
||||
print(f"# {ksft_cnt['skip']} skipped test(s) detected. Consider enabling relevant config options to improve coverage.")
|
||||
|
||||
print(
|
||||
f"# Totals: pass:{ksft_cnt['pass']} fail:{ksft_cnt['fail']} xfail:0 xpass:0 skip:{ksft_cnt['skip']} error:0"
|
||||
)
|
||||
|
||||
@@ -118,5 +118,9 @@ ktap_finished() {
|
||||
}
|
||||
|
||||
ktap_print_totals() {
|
||||
if [ "$KTAP_CNT_SKIP" -gt 0 ]; then
|
||||
echo "# $KTAP_CNT_SKIP skipped test(s) detected. " \
|
||||
"Consider enabling relevant config options to improve coverage."
|
||||
fi
|
||||
echo "# Totals: pass:$KTAP_CNT_PASS fail:$KTAP_CNT_FAIL xfail:$KTAP_CNT_XFAIL xpass:0 skip:$KTAP_CNT_SKIP error:0"
|
||||
}
|
||||
|
||||
@@ -760,33 +760,33 @@
|
||||
/* Report with actual signedness to avoid weird output. */ \
|
||||
switch (is_signed_type(__exp) * 2 + is_signed_type(__seen)) { \
|
||||
case 0: { \
|
||||
unsigned long long __exp_print = (uintptr_t)__exp; \
|
||||
unsigned long long __seen_print = (uintptr_t)__seen; \
|
||||
__TH_LOG("Expected %s (%llu) %s %s (%llu)", \
|
||||
uintmax_t __exp_print = (uintmax_t)__exp; \
|
||||
uintmax_t __seen_print = (uintmax_t)__seen; \
|
||||
__TH_LOG("Expected %s (%ju) %s %s (%ju)", \
|
||||
_expected_str, __exp_print, #_t, \
|
||||
_seen_str, __seen_print); \
|
||||
break; \
|
||||
} \
|
||||
case 1: { \
|
||||
unsigned long long __exp_print = (uintptr_t)__exp; \
|
||||
long long __seen_print = (intptr_t)__seen; \
|
||||
__TH_LOG("Expected %s (%llu) %s %s (%lld)", \
|
||||
uintmax_t __exp_print = (uintmax_t)__exp; \
|
||||
intmax_t __seen_print = (intmax_t)__seen; \
|
||||
__TH_LOG("Expected %s (%ju) %s %s (%jd)", \
|
||||
_expected_str, __exp_print, #_t, \
|
||||
_seen_str, __seen_print); \
|
||||
break; \
|
||||
} \
|
||||
case 2: { \
|
||||
long long __exp_print = (intptr_t)__exp; \
|
||||
unsigned long long __seen_print = (uintptr_t)__seen; \
|
||||
__TH_LOG("Expected %s (%lld) %s %s (%llu)", \
|
||||
intmax_t __exp_print = (intmax_t)__exp; \
|
||||
uintmax_t __seen_print = (uintmax_t)__seen; \
|
||||
__TH_LOG("Expected %s (%jd) %s %s (%ju)", \
|
||||
_expected_str, __exp_print, #_t, \
|
||||
_seen_str, __seen_print); \
|
||||
break; \
|
||||
} \
|
||||
case 3: { \
|
||||
long long __exp_print = (intptr_t)__exp; \
|
||||
long long __seen_print = (intptr_t)__seen; \
|
||||
__TH_LOG("Expected %s (%lld) %s %s (%lld)", \
|
||||
intmax_t __exp_print = (intmax_t)__exp; \
|
||||
intmax_t __seen_print = (intmax_t)__seen; \
|
||||
__TH_LOG("Expected %s (%jd) %s %s (%jd)", \
|
||||
_expected_str, __exp_print, #_t, \
|
||||
_seen_str, __seen_print); \
|
||||
break; \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Testing for regressions in Media Controller API register, ioctl, syscall,
|
||||
and unregister paths. There have a few problems that result in user-after
|
||||
and unregister paths. There have a few problems that result in use-after
|
||||
free on media_device, media_devnode, and cdev pointers when the driver is
|
||||
unbound while ioctl is in progress.
|
||||
|
||||
@@ -15,11 +15,11 @@ Build media_device_test
|
||||
cd tools/testing/selftests/media_tests
|
||||
make
|
||||
|
||||
Regressions test for cdev user-after free error on /dev/mediaX when driver
|
||||
Regressions test for cdev use-after-free error on /dev/mediaX when driver
|
||||
is unbound:
|
||||
|
||||
Start media_device_test to regression test media devnode dynamic alloc
|
||||
and cdev user-after-free fixes. This opens media dev files and sits in
|
||||
and cdev use-after-free fixes. This opens media dev files and sits in
|
||||
a loop running media ioctl MEDIA_IOC_DEVICE_INFO command once every 10
|
||||
seconds. The idea is when device file goes away, media devnode and cdev
|
||||
should stick around until this test exits.
|
||||
@@ -40,4 +40,4 @@ keep ioctls going while bind/unbind runs.
|
||||
Copy bind_unbind_sample.txt and make changes to specify the driver name
|
||||
and number to run bind and unbind. Start the bind_unbind.sh
|
||||
|
||||
Run dmesg looking for any user-after free errors or mutex lock errors.
|
||||
Run dmesg looking for any use-after-free errors or mutex lock errors.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -169,8 +169,8 @@ static int cmt_run_test(const struct resctrl_test *test, const struct user_param
|
||||
return ret;
|
||||
|
||||
ret = check_results(¶m, span, n);
|
||||
if (ret && (get_vendor() == ARCH_INTEL))
|
||||
ksft_print_msg("Intel CMT may be inaccurate when Sub-NUMA Clustering is enabled. Check BIOS configuration.\n");
|
||||
if (ret && (get_vendor() == ARCH_INTEL) && !snc_kernel_support())
|
||||
ksft_print_msg("Kernel doesn't support Sub-NUMA Clustering but it is enabled on the system.\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -201,6 +201,8 @@ static int mba_run_test(const struct resctrl_test *test, const struct user_param
|
||||
return ret;
|
||||
|
||||
ret = check_results();
|
||||
if (ret && (get_vendor() == ARCH_INTEL) && !snc_kernel_support())
|
||||
ksft_print_msg("Kernel doesn't support Sub-NUMA Clustering but it is enabled on the system.\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -160,8 +160,8 @@ static int mbm_run_test(const struct resctrl_test *test, const struct user_param
|
||||
return ret;
|
||||
|
||||
ret = check_results(param.fill_buf ? param.fill_buf->buf_size : 0);
|
||||
if (ret && (get_vendor() == ARCH_INTEL))
|
||||
ksft_print_msg("Intel MBM may be inaccurate when Sub-NUMA Clustering is enabled. Check BIOS configuration.\n");
|
||||
if (ret && (get_vendor() == ARCH_INTEL) && !snc_kernel_support())
|
||||
ksft_print_msg("Kernel doesn't support Sub-NUMA Clustering but it is enabled on the system.\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
@@ -198,6 +203,7 @@ void ctrlc_handler(int signum, siginfo_t *info, void *ptr);
|
||||
int signal_handler_register(const struct resctrl_test *test);
|
||||
void signal_handler_unregister(void);
|
||||
unsigned int count_bits(unsigned long n);
|
||||
int snc_kernel_support(void);
|
||||
|
||||
void perf_event_attr_initialize(struct perf_event_attr *pea, __u64 config);
|
||||
void perf_event_initialize_read_format(struct perf_event_read *pe_read);
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
#include "resctrl.h"
|
||||
|
||||
int snc_unreliable;
|
||||
|
||||
static int find_resctrl_mount(char *buffer)
|
||||
{
|
||||
FILE *mounts;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -852,3 +957,35 @@ unsigned int count_bits(unsigned long n)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -233,12 +233,18 @@ TEST_F(map, data_mmap)
|
||||
ASSERT_NE(data, MAP_FAILED);
|
||||
munmap(data, data_len);
|
||||
|
||||
/* Overflow the available subbufs by 1 */
|
||||
/* Offset within ring-buffer bounds, mapping size overflow */
|
||||
meta_len += desc->meta->subbuf_size * 2;
|
||||
data = mmap(NULL, data_len, PROT_READ, MAP_SHARED,
|
||||
desc->cpu_fd, meta_len);
|
||||
ASSERT_EQ(data, MAP_FAILED);
|
||||
|
||||
/* Offset outside ring-buffer bounds */
|
||||
data_len = desc->meta->subbuf_size * desc->meta->nr_subbufs;
|
||||
data = mmap(NULL, data_len, PROT_READ, MAP_SHARED,
|
||||
desc->cpu_fd, data_len + (desc->meta->subbuf_size * 2));
|
||||
ASSERT_EQ(data, MAP_FAILED);
|
||||
|
||||
/* Verify meta-page padding */
|
||||
if (desc->meta->meta_page_size > getpagesize()) {
|
||||
data_len = desc->meta->meta_page_size;
|
||||
|
||||
@@ -61,7 +61,6 @@ unsigned int rseq_size = -1U;
|
||||
unsigned int rseq_flags;
|
||||
|
||||
static int rseq_ownership;
|
||||
static int rseq_reg_success; /* At least one rseq registration has succeded. */
|
||||
|
||||
/* Allocate a large area for the TLS. */
|
||||
#define RSEQ_THREAD_AREA_ALLOC_SIZE 1024
|
||||
@@ -152,14 +151,27 @@ int rseq_register_current_thread(void)
|
||||
}
|
||||
rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), 0, RSEQ_SIG);
|
||||
if (rc) {
|
||||
if (RSEQ_READ_ONCE(rseq_reg_success)) {
|
||||
/*
|
||||
* After at least one thread has registered successfully
|
||||
* (rseq_size > 0), the registration of other threads should
|
||||
* never fail.
|
||||
*/
|
||||
if (RSEQ_READ_ONCE(rseq_size) > 0) {
|
||||
/* Incoherent success/failure within process. */
|
||||
abort();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
assert(rseq_current_cpu_raw() >= 0);
|
||||
RSEQ_WRITE_ONCE(rseq_reg_success, 1);
|
||||
|
||||
/*
|
||||
* The first thread to register sets the rseq_size to mimic the libc
|
||||
* behavior.
|
||||
*/
|
||||
if (RSEQ_READ_ONCE(rseq_size) == 0) {
|
||||
RSEQ_WRITE_ONCE(rseq_size, get_rseq_kernel_feature_size());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -235,12 +247,18 @@ void rseq_init(void)
|
||||
return;
|
||||
}
|
||||
rseq_ownership = 1;
|
||||
if (!rseq_available()) {
|
||||
rseq_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Calculate the offset of the rseq area from the thread pointer. */
|
||||
rseq_offset = (void *)&__rseq_abi - rseq_thread_pointer();
|
||||
|
||||
/* rseq flags are deprecated, always set to 0. */
|
||||
rseq_flags = 0;
|
||||
|
||||
/*
|
||||
* Set the size to 0 until at least one thread registers to mimic the
|
||||
* libc behavior.
|
||||
*/
|
||||
rseq_size = 0;
|
||||
}
|
||||
|
||||
static __attribute__((destructor))
|
||||
|
||||
@@ -60,7 +60,14 @@
|
||||
extern ptrdiff_t rseq_offset;
|
||||
|
||||
/*
|
||||
* Size of the registered rseq area. 0 if the registration was
|
||||
* The rseq ABI is composed of extensible feature fields. The extensions
|
||||
* are done by appending additional fields at the end of the structure.
|
||||
* The rseq_size defines the size of the active feature set which can be
|
||||
* used by the application for the current rseq registration. Features
|
||||
* starting at offset >= rseq_size are inactive and should not be used.
|
||||
*
|
||||
* The rseq_size is the intersection between the available allocation
|
||||
* size for the rseq area and the feature size supported by the kernel.
|
||||
* unsuccessful.
|
||||
*/
|
||||
extern unsigned int rseq_size;
|
||||
|
||||
@@ -21,7 +21,7 @@ usage()
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
-s | --summary Print summary with detailed log in output.log (conflict with -p)
|
||||
-p | --per_test_log Print test log in /tmp with each test name (conflict with -s)
|
||||
-p | --per-test-log Print test log in /tmp with each test name (conflict with -s)
|
||||
-t | --test COLLECTION:TEST Run TEST from COLLECTION
|
||||
-c | --collection COLLECTION Run all tests from COLLECTION
|
||||
-l | --list List the available collection:test entries
|
||||
|
||||
@@ -156,8 +156,8 @@ int main(int argc, char **argv)
|
||||
/* Check everything is sane before we start switching asynchronously */
|
||||
if (do_sanity_check) {
|
||||
for (i = 0; i < count; i++) {
|
||||
printf("Validating clocksource %s\n",
|
||||
clocksource_list[i]);
|
||||
ksft_print_msg("Validating clocksource %s\n",
|
||||
clocksource_list[i]);
|
||||
if (change_clocksource(clocksource_list[i])) {
|
||||
status = -1;
|
||||
goto out;
|
||||
@@ -169,7 +169,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
printf("Running Asynchronous Switching Tests...\n");
|
||||
ksft_print_msg("Running Asynchronous Switching Tests...\n");
|
||||
pid = fork();
|
||||
if (!pid)
|
||||
return run_tests(runtime);
|
||||
|
||||
@@ -23,45 +23,56 @@
|
||||
#include <sys/mount.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../kselftest.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (unshare(CLONE_NEWNS) == -1) {
|
||||
if (errno == ENOSYS || errno == EPERM) {
|
||||
fprintf(stderr, "error: unshare, errno %d\n", errno);
|
||||
return 4;
|
||||
}
|
||||
fprintf(stderr, "error: unshare, errno %d\n", errno);
|
||||
// Setting up kselftest framework
|
||||
ksft_print_header();
|
||||
ksft_set_plan(1);
|
||||
|
||||
// Check if test is run as root
|
||||
if (geteuid()) {
|
||||
ksft_exit_skip("This test needs root to run!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unshare(CLONE_NEWNS) == -1) {
|
||||
if (errno == ENOSYS || errno == EPERM) {
|
||||
ksft_exit_skip("unshare() error: unshare, errno %d\n", errno);
|
||||
} else {
|
||||
ksft_exit_fail_msg("unshare() error: unshare, errno %d\n", errno);
|
||||
}
|
||||
}
|
||||
|
||||
if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) == -1) {
|
||||
fprintf(stderr, "error: mount '/', errno %d\n", errno);
|
||||
return 1;
|
||||
ksft_exit_fail_msg("mount() error: Root filesystem private mount: Fail %d\n", errno);
|
||||
}
|
||||
|
||||
/* Our heroes: 1 root inode, 1 O_TMPFILE inode, 1 permanent inode. */
|
||||
if (mount(NULL, "/tmp", "tmpfs", 0, "nr_inodes=3") == -1) {
|
||||
fprintf(stderr, "error: mount tmpfs, errno %d\n", errno);
|
||||
return 1;
|
||||
ksft_exit_fail_msg("mount() error: Mounting tmpfs on /tmp: Fail %d\n", errno);
|
||||
}
|
||||
|
||||
fd = openat(AT_FDCWD, "/tmp", O_WRONLY|O_TMPFILE, 0600);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "error: open 1, errno %d\n", errno);
|
||||
return 1;
|
||||
ksft_exit_fail_msg("openat() error: Open first temporary file: Fail %d\n", errno);
|
||||
}
|
||||
|
||||
if (linkat(fd, "", AT_FDCWD, "/tmp/1", AT_EMPTY_PATH) == -1) {
|
||||
fprintf(stderr, "error: linkat, errno %d\n", errno);
|
||||
return 1;
|
||||
ksft_exit_fail_msg("linkat() error: Linking the temporary file: Fail %d\n", errno);
|
||||
/* Ensure fd is closed on failure */
|
||||
close(fd);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
fd = openat(AT_FDCWD, "/tmp", O_WRONLY|O_TMPFILE, 0600);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "error: open 2, errno %d\n", errno);
|
||||
return 1;
|
||||
ksft_exit_fail_msg("openat() error: Opening the second temporary file: Fail %d\n", errno);
|
||||
}
|
||||
|
||||
ksft_test_result_pass(" ");
|
||||
ksft_exit_pass();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ static struct vdso_info
|
||||
/* Symbol table */
|
||||
ELF(Sym) *symtab;
|
||||
const char *symstrings;
|
||||
ELF(Word) *gnu_hash;
|
||||
ELF_HASH_ENTRY *bucket, *chain;
|
||||
ELF_HASH_ENTRY nbucket, nchain;
|
||||
|
||||
@@ -81,6 +82,16 @@ static unsigned long elf_hash(const char *name)
|
||||
return h;
|
||||
}
|
||||
|
||||
static uint32_t gnu_hash(const char *name)
|
||||
{
|
||||
const unsigned char *s = (void *)name;
|
||||
uint32_t h = 5381;
|
||||
|
||||
for (; *s; s++)
|
||||
h += h * 32 + *s;
|
||||
return h;
|
||||
}
|
||||
|
||||
void vdso_init_from_sysinfo_ehdr(uintptr_t base)
|
||||
{
|
||||
size_t i;
|
||||
@@ -123,6 +134,7 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
|
||||
*/
|
||||
ELF_HASH_ENTRY *hash = 0;
|
||||
vdso_info.symstrings = 0;
|
||||
vdso_info.gnu_hash = 0;
|
||||
vdso_info.symtab = 0;
|
||||
vdso_info.versym = 0;
|
||||
vdso_info.verdef = 0;
|
||||
@@ -143,6 +155,11 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
|
||||
((uintptr_t)dyn[i].d_un.d_ptr
|
||||
+ vdso_info.load_offset);
|
||||
break;
|
||||
case DT_GNU_HASH:
|
||||
vdso_info.gnu_hash =
|
||||
(ELF(Word) *)((uintptr_t)dyn[i].d_un.d_ptr +
|
||||
vdso_info.load_offset);
|
||||
break;
|
||||
case DT_VERSYM:
|
||||
vdso_info.versym = (ELF(Versym) *)
|
||||
((uintptr_t)dyn[i].d_un.d_ptr
|
||||
@@ -155,17 +172,27 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!vdso_info.symstrings || !vdso_info.symtab || !hash)
|
||||
if (!vdso_info.symstrings || !vdso_info.symtab ||
|
||||
(!hash && !vdso_info.gnu_hash))
|
||||
return; /* Failed */
|
||||
|
||||
if (!vdso_info.verdef)
|
||||
vdso_info.versym = 0;
|
||||
|
||||
/* Parse the hash table header. */
|
||||
vdso_info.nbucket = hash[0];
|
||||
vdso_info.nchain = hash[1];
|
||||
vdso_info.bucket = &hash[2];
|
||||
vdso_info.chain = &hash[vdso_info.nbucket + 2];
|
||||
if (vdso_info.gnu_hash) {
|
||||
vdso_info.nbucket = vdso_info.gnu_hash[0];
|
||||
/* The bucket array is located after the header (4 uint32) and the bloom
|
||||
* filter (size_t array of gnu_hash[2] elements).
|
||||
*/
|
||||
vdso_info.bucket = vdso_info.gnu_hash + 4 +
|
||||
sizeof(size_t) / 4 * vdso_info.gnu_hash[2];
|
||||
} else {
|
||||
vdso_info.nbucket = hash[0];
|
||||
vdso_info.nchain = hash[1];
|
||||
vdso_info.bucket = &hash[2];
|
||||
vdso_info.chain = &hash[vdso_info.nbucket + 2];
|
||||
}
|
||||
|
||||
/* That's all we need. */
|
||||
vdso_info.valid = true;
|
||||
@@ -209,6 +236,26 @@ static bool vdso_match_version(ELF(Versym) ver,
|
||||
&& !strcmp(name, vdso_info.symstrings + aux->vda_name);
|
||||
}
|
||||
|
||||
static bool check_sym(ELF(Sym) *sym, ELF(Word) i, const char *name,
|
||||
const char *version, unsigned long ver_hash)
|
||||
{
|
||||
/* Check for a defined global or weak function w/ right name. */
|
||||
if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
|
||||
return false;
|
||||
if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
|
||||
ELF64_ST_BIND(sym->st_info) != STB_WEAK)
|
||||
return false;
|
||||
if (strcmp(name, vdso_info.symstrings + sym->st_name))
|
||||
return false;
|
||||
|
||||
/* Check symbol version. */
|
||||
if (vdso_info.versym &&
|
||||
!vdso_match_version(vdso_info.versym[i], version, ver_hash))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void *vdso_sym(const char *version, const char *name)
|
||||
{
|
||||
unsigned long ver_hash;
|
||||
@@ -216,29 +263,36 @@ void *vdso_sym(const char *version, const char *name)
|
||||
return 0;
|
||||
|
||||
ver_hash = elf_hash(version);
|
||||
ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket];
|
||||
ELF(Word) i;
|
||||
|
||||
for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) {
|
||||
ELF(Sym) *sym = &vdso_info.symtab[chain];
|
||||
if (vdso_info.gnu_hash) {
|
||||
uint32_t h1 = gnu_hash(name), h2, *hashval;
|
||||
|
||||
/* Check for a defined global or weak function w/ right name. */
|
||||
if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
|
||||
continue;
|
||||
if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
|
||||
ELF64_ST_BIND(sym->st_info) != STB_WEAK)
|
||||
continue;
|
||||
if (sym->st_shndx == SHN_UNDEF)
|
||||
continue;
|
||||
if (strcmp(name, vdso_info.symstrings + sym->st_name))
|
||||
continue;
|
||||
|
||||
/* Check symbol version. */
|
||||
if (vdso_info.versym
|
||||
&& !vdso_match_version(vdso_info.versym[chain],
|
||||
version, ver_hash))
|
||||
continue;
|
||||
|
||||
return (void *)(vdso_info.load_offset + sym->st_value);
|
||||
i = vdso_info.bucket[h1 % vdso_info.nbucket];
|
||||
if (i == 0)
|
||||
return 0;
|
||||
h1 |= 1;
|
||||
hashval = vdso_info.bucket + vdso_info.nbucket +
|
||||
(i - vdso_info.gnu_hash[1]);
|
||||
for (;; i++) {
|
||||
ELF(Sym) *sym = &vdso_info.symtab[i];
|
||||
h2 = *hashval++;
|
||||
if (h1 == (h2 | 1) &&
|
||||
check_sym(sym, i, name, version, ver_hash))
|
||||
return (void *)(vdso_info.load_offset +
|
||||
sym->st_value);
|
||||
if (h2 & 1)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
i = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket];
|
||||
for (; i; i = vdso_info.chain[i]) {
|
||||
ELF(Sym) *sym = &vdso_info.symtab[i];
|
||||
if (sym->st_shndx != SHN_UNDEF &&
|
||||
check_sym(sym, i, name, version, ver_hash))
|
||||
return (void *)(vdso_info.load_offset +
|
||||
sym->st_value);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
2
tools/testing/selftests/zram/.gitignore
vendored
Normal file
2
tools/testing/selftests/zram/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
err.log
|
||||
Reference in New Issue
Block a user