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