Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
47struct noise_params {
48 int64_t noise_nsecs;
49 int64_t sleep_nsecs;
50 int64_t run;
51};
52
53FIXTURE(slice_ext)
54{
55 pthread_t noise_thread;
56 struct noise_params noise_params;
57};
58
59FIXTURE_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
68FIXTURE_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
76FIXTURE_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
84FIXTURE_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
94static 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
104static 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
123FIXTURE_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
152FIXTURE_TEARDOWN(slice_ext)
153{
154 self->noise_params.run = 0;
155 pthread_join(self->noise_thread, NULL);
156}
157
158TEST_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
219TEST_HARNESS_MAIN