Skip to content

Commit 830969e

Browse files
KAGA-KOKOPeter Zijlstra
authored andcommitted
selftests/rseq: Implement time slice extension test
Provide an initial test case to evaluate the functionality. This needs to be extended to cover the ABI violations and expose the race condition between observing granted and arriving in rseq_slice_yield(). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://patch.msgid.link/20251215155709.320325431@linutronix.de
1 parent 3c78aae commit 830969e

File tree

4 files changed

+251
-1
lines changed

4 files changed

+251
-1
lines changed

tools/testing/selftests/rseq/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ param_test_mm_cid
1010
param_test_mm_cid_benchmark
1111
param_test_mm_cid_compare_twice
1212
syscall_errors_test
13+
slice_test

tools/testing/selftests/rseq/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ OVERRIDE_TARGETS = 1
1717
TEST_GEN_PROGS = basic_test basic_percpu_ops_test basic_percpu_ops_mm_cid_test param_test \
1818
param_test_benchmark param_test_compare_twice param_test_mm_cid \
1919
param_test_mm_cid_benchmark param_test_mm_cid_compare_twice \
20-
syscall_errors_test
20+
syscall_errors_test slice_test
2121

2222
TEST_GEN_PROGS_EXTENDED = librseq.so
2323

@@ -59,3 +59,6 @@ $(OUTPUT)/param_test_mm_cid_compare_twice: param_test.c $(TEST_GEN_PROGS_EXTENDE
5959
$(OUTPUT)/syscall_errors_test: syscall_errors_test.c $(TEST_GEN_PROGS_EXTENDED) \
6060
rseq.h rseq-*.h
6161
$(CC) $(CFLAGS) $< $(LDLIBS) -lrseq -o $@
62+
63+
$(OUTPUT)/slice_test: slice_test.c $(TEST_GEN_PROGS_EXTENDED) rseq.h rseq-*.h
64+
$(CC) $(CFLAGS) $< $(LDLIBS) -lrseq -o $@

tools/testing/selftests/rseq/rseq-abi.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,27 @@ struct rseq_abi_cs {
5353
__u64 abort_ip;
5454
} __attribute__((aligned(4 * sizeof(__u64))));
5555

56+
/**
57+
* rseq_abi_slice_ctrl - Time slice extension control structure
58+
* @all: Compound value
59+
* @request: Request for a time slice extension
60+
* @granted: Granted time slice extension
61+
*
62+
* @request is set by user space and can be cleared by user space or kernel
63+
* space. @granted is set and cleared by the kernel and must only be read
64+
* by user space.
65+
*/
66+
struct rseq_abi_slice_ctrl {
67+
union {
68+
__u32 all;
69+
struct {
70+
__u8 request;
71+
__u8 granted;
72+
__u16 __reserved;
73+
};
74+
};
75+
};
76+
5677
/*
5778
* struct rseq_abi is aligned on 4 * 8 bytes to ensure it is always
5879
* contained within a single cache-line.
@@ -164,6 +185,12 @@ struct rseq_abi {
164185
*/
165186
__u32 mm_cid;
166187

188+
/*
189+
* Time slice extension control structure. CPU local updates from
190+
* kernel and user space.
191+
*/
192+
struct rseq_abi_slice_ctrl slice_ctrl;
193+
167194
/*
168195
* Flexible array member at end of structure, after last feature field.
169196
*/
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
// SPDX-License-Identifier: LGPL-2.1
2+
#define _GNU_SOURCE
3+
#include <assert.h>
4+
#include <pthread.h>
5+
#include <sched.h>
6+
#include <signal.h>
7+
#include <stdbool.h>
8+
#include <stdio.h>
9+
#include <string.h>
10+
#include <syscall.h>
11+
#include <unistd.h>
12+
13+
#include <linux/prctl.h>
14+
#include <sys/prctl.h>
15+
#include <sys/time.h>
16+
17+
#include "rseq.h"
18+
19+
#include "../kselftest_harness.h"
20+
21+
#ifndef __NR_rseq_slice_yield
22+
# define __NR_rseq_slice_yield 471
23+
#endif
24+
25+
#define BITS_PER_INT 32
26+
#define BITS_PER_BYTE 8
27+
28+
#ifndef PR_RSEQ_SLICE_EXTENSION
29+
# define PR_RSEQ_SLICE_EXTENSION 79
30+
# define PR_RSEQ_SLICE_EXTENSION_GET 1
31+
# define PR_RSEQ_SLICE_EXTENSION_SET 2
32+
# define PR_RSEQ_SLICE_EXT_ENABLE 0x01
33+
#endif
34+
35+
#ifndef RSEQ_SLICE_EXT_REQUEST_BIT
36+
# define RSEQ_SLICE_EXT_REQUEST_BIT 0
37+
# define RSEQ_SLICE_EXT_GRANTED_BIT 1
38+
#endif
39+
40+
#ifndef asm_inline
41+
# define asm_inline asm __inline
42+
#endif
43+
44+
#define NSEC_PER_SEC 1000000000L
45+
#define NSEC_PER_USEC 1000L
46+
47+
struct noise_params {
48+
int64_t noise_nsecs;
49+
int64_t sleep_nsecs;
50+
int64_t run;
51+
};
52+
53+
FIXTURE(slice_ext)
54+
{
55+
pthread_t noise_thread;
56+
struct noise_params noise_params;
57+
};
58+
59+
FIXTURE_VARIANT(slice_ext)
60+
{
61+
int64_t total_nsecs;
62+
int64_t slice_nsecs;
63+
int64_t noise_nsecs;
64+
int64_t sleep_nsecs;
65+
bool no_yield;
66+
};
67+
68+
FIXTURE_VARIANT_ADD(slice_ext, n2_2_50)
69+
{
70+
.total_nsecs = 5LL * NSEC_PER_SEC,
71+
.slice_nsecs = 2LL * NSEC_PER_USEC,
72+
.noise_nsecs = 2LL * NSEC_PER_USEC,
73+
.sleep_nsecs = 50LL * NSEC_PER_USEC,
74+
};
75+
76+
FIXTURE_VARIANT_ADD(slice_ext, n50_2_50)
77+
{
78+
.total_nsecs = 5LL * NSEC_PER_SEC,
79+
.slice_nsecs = 50LL * NSEC_PER_USEC,
80+
.noise_nsecs = 2LL * NSEC_PER_USEC,
81+
.sleep_nsecs = 50LL * NSEC_PER_USEC,
82+
};
83+
84+
FIXTURE_VARIANT_ADD(slice_ext, n2_2_50_no_yield)
85+
{
86+
.total_nsecs = 5LL * NSEC_PER_SEC,
87+
.slice_nsecs = 2LL * NSEC_PER_USEC,
88+
.noise_nsecs = 2LL * NSEC_PER_USEC,
89+
.sleep_nsecs = 50LL * NSEC_PER_USEC,
90+
.no_yield = true,
91+
};
92+
93+
94+
static inline bool elapsed(struct timespec *start, struct timespec *now,
95+
int64_t span)
96+
{
97+
int64_t delta = now->tv_sec - start->tv_sec;
98+
99+
delta *= NSEC_PER_SEC;
100+
delta += now->tv_nsec - start->tv_nsec;
101+
return delta >= span;
102+
}
103+
104+
static void *noise_thread(void *arg)
105+
{
106+
struct noise_params *p = arg;
107+
108+
while (RSEQ_READ_ONCE(p->run)) {
109+
struct timespec ts_start, ts_now;
110+
111+
clock_gettime(CLOCK_MONOTONIC, &ts_start);
112+
do {
113+
clock_gettime(CLOCK_MONOTONIC, &ts_now);
114+
} while (!elapsed(&ts_start, &ts_now, p->noise_nsecs));
115+
116+
ts_start.tv_sec = 0;
117+
ts_start.tv_nsec = p->sleep_nsecs;
118+
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts_start, NULL);
119+
}
120+
return NULL;
121+
}
122+
123+
FIXTURE_SETUP(slice_ext)
124+
{
125+
cpu_set_t affinity;
126+
127+
ASSERT_EQ(sched_getaffinity(0, sizeof(affinity), &affinity), 0);
128+
129+
/* Pin it on a single CPU. Avoid CPU 0 */
130+
for (int i = 1; i < CPU_SETSIZE; i++) {
131+
if (!CPU_ISSET(i, &affinity))
132+
continue;
133+
134+
CPU_ZERO(&affinity);
135+
CPU_SET(i, &affinity);
136+
ASSERT_EQ(sched_setaffinity(0, sizeof(affinity), &affinity), 0);
137+
break;
138+
}
139+
140+
ASSERT_EQ(rseq_register_current_thread(), 0);
141+
142+
ASSERT_EQ(prctl(PR_RSEQ_SLICE_EXTENSION, PR_RSEQ_SLICE_EXTENSION_SET,
143+
PR_RSEQ_SLICE_EXT_ENABLE, 0, 0), 0);
144+
145+
self->noise_params.noise_nsecs = variant->noise_nsecs;
146+
self->noise_params.sleep_nsecs = variant->sleep_nsecs;
147+
self->noise_params.run = 1;
148+
149+
ASSERT_EQ(pthread_create(&self->noise_thread, NULL, noise_thread, &self->noise_params), 0);
150+
}
151+
152+
FIXTURE_TEARDOWN(slice_ext)
153+
{
154+
self->noise_params.run = 0;
155+
pthread_join(self->noise_thread, NULL);
156+
}
157+
158+
TEST_F(slice_ext, slice_test)
159+
{
160+
unsigned long success = 0, yielded = 0, scheduled = 0, raced = 0;
161+
unsigned long total = 0, aborted = 0;
162+
struct rseq_abi *rs = rseq_get_abi();
163+
struct timespec ts_start, ts_now;
164+
165+
ASSERT_NE(rs, NULL);
166+
167+
clock_gettime(CLOCK_MONOTONIC, &ts_start);
168+
do {
169+
struct timespec ts_cs;
170+
bool req = false;
171+
172+
clock_gettime(CLOCK_MONOTONIC, &ts_cs);
173+
174+
total++;
175+
RSEQ_WRITE_ONCE(rs->slice_ctrl.request, 1);
176+
do {
177+
clock_gettime(CLOCK_MONOTONIC, &ts_now);
178+
} while (!elapsed(&ts_cs, &ts_now, variant->slice_nsecs));
179+
180+
/*
181+
* request can be cleared unconditionally, but for making
182+
* the stats work this is actually checking it first
183+
*/
184+
if (RSEQ_READ_ONCE(rs->slice_ctrl.request)) {
185+
RSEQ_WRITE_ONCE(rs->slice_ctrl.request, 0);
186+
/* Race between check and clear! */
187+
req = true;
188+
success++;
189+
}
190+
191+
if (RSEQ_READ_ONCE(rs->slice_ctrl.granted)) {
192+
/* The above raced against a late grant */
193+
if (req)
194+
success--;
195+
if (variant->no_yield) {
196+
syscall(__NR_getpid);
197+
aborted++;
198+
} else {
199+
yielded++;
200+
if (!syscall(__NR_rseq_slice_yield))
201+
raced++;
202+
}
203+
} else {
204+
if (!req)
205+
scheduled++;
206+
}
207+
208+
clock_gettime(CLOCK_MONOTONIC, &ts_now);
209+
} while (!elapsed(&ts_start, &ts_now, variant->total_nsecs));
210+
211+
printf("# Total %12ld\n", total);
212+
printf("# Success %12ld\n", success);
213+
printf("# Yielded %12ld\n", yielded);
214+
printf("# Aborted %12ld\n", aborted);
215+
printf("# Scheduled %12ld\n", scheduled);
216+
printf("# Raced %12ld\n", raced);
217+
}
218+
219+
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)