mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
Update the selftests so they are executed for legacy (32 bytes RSEQ region)
and optimized RSEQ ABI v2 mode.
Fixes: d6200245c7 ("rseq: Allow registering RSEQ with slice extension")
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Dmitry Vyukov <dvyukov@google.com>
Tested-by: Dmitry Vyukov <dvyukov@google.com>
Link: https://patch.msgid.link/20260428224428.009121296%40kernel.org
Cc: stable@vger.kernel.org
222 lines
4.9 KiB
C
222 lines
4.9 KiB
C
// SPDX-License-Identifier: LGPL-2.1
|
|
#define _GNU_SOURCE
|
|
#include <assert.h>
|
|
#include <pthread.h>
|
|
#include <sched.h>
|
|
#include <signal.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <syscall.h>
|
|
#include <unistd.h>
|
|
|
|
#include <linux/prctl.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/time.h>
|
|
|
|
#include "rseq.h"
|
|
|
|
#include "../kselftest_harness.h"
|
|
|
|
#ifndef __NR_rseq_slice_yield
|
|
# define __NR_rseq_slice_yield 471
|
|
#endif
|
|
|
|
#define BITS_PER_INT 32
|
|
#define BITS_PER_BYTE 8
|
|
|
|
#ifndef PR_RSEQ_SLICE_EXTENSION
|
|
# define PR_RSEQ_SLICE_EXTENSION 79
|
|
# define PR_RSEQ_SLICE_EXTENSION_GET 1
|
|
# define PR_RSEQ_SLICE_EXTENSION_SET 2
|
|
# define PR_RSEQ_SLICE_EXT_ENABLE 0x01
|
|
#endif
|
|
|
|
#ifndef RSEQ_SLICE_EXT_REQUEST_BIT
|
|
# define RSEQ_SLICE_EXT_REQUEST_BIT 0
|
|
# define RSEQ_SLICE_EXT_GRANTED_BIT 1
|
|
#endif
|
|
|
|
#ifndef asm_inline
|
|
# define asm_inline asm __inline
|
|
#endif
|
|
|
|
#define NSEC_PER_SEC 1000000000L
|
|
#define NSEC_PER_USEC 1000L
|
|
|
|
struct noise_params {
|
|
int64_t noise_nsecs;
|
|
int64_t sleep_nsecs;
|
|
int64_t run;
|
|
};
|
|
|
|
FIXTURE(slice_ext)
|
|
{
|
|
pthread_t noise_thread;
|
|
struct noise_params noise_params;
|
|
};
|
|
|
|
FIXTURE_VARIANT(slice_ext)
|
|
{
|
|
int64_t total_nsecs;
|
|
int64_t slice_nsecs;
|
|
int64_t noise_nsecs;
|
|
int64_t sleep_nsecs;
|
|
bool no_yield;
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(slice_ext, n2_2_50)
|
|
{
|
|
.total_nsecs = 5LL * NSEC_PER_SEC,
|
|
.slice_nsecs = 2LL * NSEC_PER_USEC,
|
|
.noise_nsecs = 2LL * NSEC_PER_USEC,
|
|
.sleep_nsecs = 50LL * NSEC_PER_USEC,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(slice_ext, n50_2_50)
|
|
{
|
|
.total_nsecs = 5LL * NSEC_PER_SEC,
|
|
.slice_nsecs = 50LL * NSEC_PER_USEC,
|
|
.noise_nsecs = 2LL * NSEC_PER_USEC,
|
|
.sleep_nsecs = 50LL * NSEC_PER_USEC,
|
|
};
|
|
|
|
FIXTURE_VARIANT_ADD(slice_ext, n2_2_50_no_yield)
|
|
{
|
|
.total_nsecs = 5LL * NSEC_PER_SEC,
|
|
.slice_nsecs = 2LL * NSEC_PER_USEC,
|
|
.noise_nsecs = 2LL * NSEC_PER_USEC,
|
|
.sleep_nsecs = 50LL * NSEC_PER_USEC,
|
|
.no_yield = true,
|
|
};
|
|
|
|
|
|
static inline bool elapsed(struct timespec *start, struct timespec *now,
|
|
int64_t span)
|
|
{
|
|
int64_t delta = now->tv_sec - start->tv_sec;
|
|
|
|
delta *= NSEC_PER_SEC;
|
|
delta += now->tv_nsec - start->tv_nsec;
|
|
return delta >= span;
|
|
}
|
|
|
|
static void *noise_thread(void *arg)
|
|
{
|
|
struct noise_params *p = arg;
|
|
|
|
while (RSEQ_READ_ONCE(p->run)) {
|
|
struct timespec ts_start, ts_now;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts_start);
|
|
do {
|
|
clock_gettime(CLOCK_MONOTONIC, &ts_now);
|
|
} while (!elapsed(&ts_start, &ts_now, p->noise_nsecs));
|
|
|
|
ts_start.tv_sec = 0;
|
|
ts_start.tv_nsec = p->sleep_nsecs;
|
|
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts_start, NULL);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
FIXTURE_SETUP(slice_ext)
|
|
{
|
|
cpu_set_t affinity;
|
|
|
|
if (__rseq_register_current_thread(true, false))
|
|
SKIP(return, "RSEQ not supported\n");
|
|
|
|
if (prctl(PR_RSEQ_SLICE_EXTENSION, PR_RSEQ_SLICE_EXTENSION_SET,
|
|
PR_RSEQ_SLICE_EXT_ENABLE, 0, 0))
|
|
SKIP(return, "Time slice extension not supported\n");
|
|
|
|
ASSERT_EQ(sched_getaffinity(0, sizeof(affinity), &affinity), 0);
|
|
|
|
/* Pin it on a single CPU. Avoid CPU 0 */
|
|
for (int i = 1; i < CPU_SETSIZE; i++) {
|
|
if (!CPU_ISSET(i, &affinity))
|
|
continue;
|
|
|
|
CPU_ZERO(&affinity);
|
|
CPU_SET(i, &affinity);
|
|
ASSERT_EQ(sched_setaffinity(0, sizeof(affinity), &affinity), 0);
|
|
break;
|
|
}
|
|
|
|
self->noise_params.noise_nsecs = variant->noise_nsecs;
|
|
self->noise_params.sleep_nsecs = variant->sleep_nsecs;
|
|
self->noise_params.run = 1;
|
|
|
|
ASSERT_EQ(pthread_create(&self->noise_thread, NULL, noise_thread, &self->noise_params), 0);
|
|
}
|
|
|
|
FIXTURE_TEARDOWN(slice_ext)
|
|
{
|
|
self->noise_params.run = 0;
|
|
pthread_join(self->noise_thread, NULL);
|
|
}
|
|
|
|
TEST_F(slice_ext, slice_test)
|
|
{
|
|
unsigned long success = 0, yielded = 0, scheduled = 0, raced = 0;
|
|
unsigned long total = 0, aborted = 0;
|
|
struct rseq_abi *rs = rseq_get_abi();
|
|
struct timespec ts_start, ts_now;
|
|
|
|
ASSERT_NE(rs, NULL);
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts_start);
|
|
do {
|
|
struct timespec ts_cs;
|
|
bool req = false;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts_cs);
|
|
|
|
total++;
|
|
RSEQ_WRITE_ONCE(rs->slice_ctrl.request, 1);
|
|
do {
|
|
clock_gettime(CLOCK_MONOTONIC, &ts_now);
|
|
} while (!elapsed(&ts_cs, &ts_now, variant->slice_nsecs));
|
|
|
|
/*
|
|
* request can be cleared unconditionally, but for making
|
|
* the stats work this is actually checking it first
|
|
*/
|
|
if (RSEQ_READ_ONCE(rs->slice_ctrl.request)) {
|
|
RSEQ_WRITE_ONCE(rs->slice_ctrl.request, 0);
|
|
/* Race between check and clear! */
|
|
req = true;
|
|
success++;
|
|
}
|
|
|
|
if (RSEQ_READ_ONCE(rs->slice_ctrl.granted)) {
|
|
/* The above raced against a late grant */
|
|
if (req)
|
|
success--;
|
|
if (variant->no_yield) {
|
|
syscall(__NR_getpid);
|
|
aborted++;
|
|
} else {
|
|
yielded++;
|
|
if (!syscall(__NR_rseq_slice_yield))
|
|
raced++;
|
|
}
|
|
} else {
|
|
if (!req)
|
|
scheduled++;
|
|
}
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts_now);
|
|
} while (!elapsed(&ts_start, &ts_now, variant->total_nsecs));
|
|
|
|
printf("# Total %12ld\n", total);
|
|
printf("# Success %12ld\n", success);
|
|
printf("# Yielded %12ld\n", yielded);
|
|
printf("# Aborted %12ld\n", aborted);
|
|
printf("# Scheduled %12ld\n", scheduled);
|
|
printf("# Raced %12ld\n", raced);
|
|
}
|
|
|
|
TEST_HARNESS_MAIN
|