projects/linux·blob master · refs

221 lines · 4.9 KiB

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 if (__rseq_register_current_thread(true, false))
128 SKIP(return, "RSEQ not supported\n");
129
130 if (prctl(PR_RSEQ_SLICE_EXTENSION, PR_RSEQ_SLICE_EXTENSION_SET,
131 PR_RSEQ_SLICE_EXT_ENABLE, 0, 0))
132 SKIP(return, "Time slice extension not supported\n");
133
134 ASSERT_EQ(sched_getaffinity(0, sizeof(affinity), &affinity), 0);
135
136 /* Pin it on a single CPU. Avoid CPU 0 */
137 for (int i = 1; i < CPU_SETSIZE; i++) {
138 if (!CPU_ISSET(i, &affinity))
139 continue;
140
141 CPU_ZERO(&affinity);
142 CPU_SET(i, &affinity);
143 ASSERT_EQ(sched_setaffinity(0, sizeof(affinity), &affinity), 0);
144 break;
145 }
146
147 self->noise_params.noise_nsecs = variant->noise_nsecs;
148 self->noise_params.sleep_nsecs = variant->sleep_nsecs;
149 self->noise_params.run = 1;
150
151 ASSERT_EQ(pthread_create(&self->noise_thread, NULL, noise_thread, &self->noise_params), 0);
152}
153
154FIXTURE_TEARDOWN(slice_ext)
155{
156 self->noise_params.run = 0;
157 pthread_join(self->noise_thread, NULL);
158}
159
160TEST_F(slice_ext, slice_test)
161{
162 unsigned long success = 0, yielded = 0, scheduled = 0, raced = 0;
163 unsigned long total = 0, aborted = 0;
164 struct rseq_abi *rs = rseq_get_abi();
165 struct timespec ts_start, ts_now;
166
167 ASSERT_NE(rs, NULL);
168
169 clock_gettime(CLOCK_MONOTONIC, &ts_start);
170 do {
171 struct timespec ts_cs;
172 bool req = false;
173
174 clock_gettime(CLOCK_MONOTONIC, &ts_cs);
175
176 total++;
177 RSEQ_WRITE_ONCE(rs->slice_ctrl.request, 1);
178 do {
179 clock_gettime(CLOCK_MONOTONIC, &ts_now);
180 } while (!elapsed(&ts_cs, &ts_now, variant->slice_nsecs));
181
182 /*
183 * request can be cleared unconditionally, but for making
184 * the stats work this is actually checking it first
185 */
186 if (RSEQ_READ_ONCE(rs->slice_ctrl.request)) {
187 RSEQ_WRITE_ONCE(rs->slice_ctrl.request, 0);
188 /* Race between check and clear! */
189 req = true;
190 success++;
191 }
192
193 if (RSEQ_READ_ONCE(rs->slice_ctrl.granted)) {
194 /* The above raced against a late grant */
195 if (req)
196 success--;
197 if (variant->no_yield) {
198 syscall(__NR_getpid);
199 aborted++;
200 } else {
201 yielded++;
202 if (!syscall(__NR_rseq_slice_yield))
203 raced++;
204 }
205 } else {
206 if (!req)
207 scheduled++;
208 }
209
210 clock_gettime(CLOCK_MONOTONIC, &ts_now);
211 } while (!elapsed(&ts_start, &ts_now, variant->total_nsecs));
212
213 printf("# Total %12ld\n", total);
214 printf("# Success %12ld\n", success);
215 printf("# Yielded %12ld\n", yielded);
216 printf("# Aborted %12ld\n", aborted);
217 printf("# Scheduled %12ld\n", scheduled);
218 printf("# Raced %12ld\n", raced);
219}
220
221TEST_HARNESS_MAIN