xref: /linux/tools/testing/selftests/sched_ext/nohz_tick.c (revision 298bb2b8903323f6ef2eab4819a2e477765f0ff1)
1*cfe950d7SAndrea Righi // SPDX-License-Identifier: GPL-2.0
2*cfe950d7SAndrea Righi /*
3*cfe950d7SAndrea Righi  * Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES
4*cfe950d7SAndrea Righi  *
5*cfe950d7SAndrea Righi  * Validate that a finite-slice EXT task restarts the scheduler tick when it
6*cfe950d7SAndrea Righi  * follows an infinite-slice EXT task and an idle interval on a NOHZ_FULL CPU.
7*cfe950d7SAndrea Righi  */
8*cfe950d7SAndrea Righi #define _GNU_SOURCE
9*cfe950d7SAndrea Righi 
10*cfe950d7SAndrea Righi #include <bpf/bpf.h>
11*cfe950d7SAndrea Righi #include <errno.h>
12*cfe950d7SAndrea Righi #include <sched.h>
13*cfe950d7SAndrea Righi #include <signal.h>
14*cfe950d7SAndrea Righi #include <stdbool.h>
15*cfe950d7SAndrea Righi #include <stdio.h>
16*cfe950d7SAndrea Righi #include <stdlib.h>
17*cfe950d7SAndrea Righi #include <sys/prctl.h>
18*cfe950d7SAndrea Righi #include <sys/wait.h>
19*cfe950d7SAndrea Righi #include <unistd.h>
20*cfe950d7SAndrea Righi 
21*cfe950d7SAndrea Righi #include <scx/common.h>
22*cfe950d7SAndrea Righi 
23*cfe950d7SAndrea Righi #include "nohz_tick.bpf.skel.h"
24*cfe950d7SAndrea Righi #include "scx_test.h"
25*cfe950d7SAndrea Righi 
26*cfe950d7SAndrea Righi #ifndef SCHED_EXT
27*cfe950d7SAndrea Righi #define SCHED_EXT 7
28*cfe950d7SAndrea Righi #endif
29*cfe950d7SAndrea Righi 
30*cfe950d7SAndrea Righi #define MIN_FINITE_TICKS 3
31*cfe950d7SAndrea Righi #define PHASE_TIMEOUT_MS 1000
32*cfe950d7SAndrea Righi 
33*cfe950d7SAndrea Righi struct nohz_tick_ctx {
34*cfe950d7SAndrea Righi 	struct nohz_tick *skel;
35*cfe950d7SAndrea Righi 	cpu_set_t original_mask;
36*cfe950d7SAndrea Righi 	int test_cpu;
37*cfe950d7SAndrea Righi };
38*cfe950d7SAndrea Righi 
first_allowed_cpu(const cpu_set_t * mask,int first,int last)39*cfe950d7SAndrea Righi static int first_allowed_cpu(const cpu_set_t *mask, int first, int last)
40*cfe950d7SAndrea Righi {
41*cfe950d7SAndrea Righi 	int cpu;
42*cfe950d7SAndrea Righi 
43*cfe950d7SAndrea Righi 	for (cpu = first; cpu <= last && cpu < CPU_SETSIZE; cpu++)
44*cfe950d7SAndrea Righi 		if (CPU_ISSET(cpu, mask))
45*cfe950d7SAndrea Righi 			return cpu;
46*cfe950d7SAndrea Righi 
47*cfe950d7SAndrea Righi 	return -1;
48*cfe950d7SAndrea Righi }
49*cfe950d7SAndrea Righi 
find_nohz_full_cpu(const cpu_set_t * allowed)50*cfe950d7SAndrea Righi static int find_nohz_full_cpu(const cpu_set_t *allowed)
51*cfe950d7SAndrea Righi {
52*cfe950d7SAndrea Righi 	char buf[4096], *cur, *end;
53*cfe950d7SAndrea Righi 	FILE *file;
54*cfe950d7SAndrea Righi 
55*cfe950d7SAndrea Righi 	file = fopen("/sys/devices/system/cpu/nohz_full", "r");
56*cfe950d7SAndrea Righi 	if (!file)
57*cfe950d7SAndrea Righi 		return -1;
58*cfe950d7SAndrea Righi 	if (!fgets(buf, sizeof(buf), file)) {
59*cfe950d7SAndrea Righi 		fclose(file);
60*cfe950d7SAndrea Righi 		return -1;
61*cfe950d7SAndrea Righi 	}
62*cfe950d7SAndrea Righi 	fclose(file);
63*cfe950d7SAndrea Righi 
64*cfe950d7SAndrea Righi 	cur = buf;
65*cfe950d7SAndrea Righi 	while (*cur) {
66*cfe950d7SAndrea Righi 		long first, last;
67*cfe950d7SAndrea Righi 		int cpu;
68*cfe950d7SAndrea Righi 
69*cfe950d7SAndrea Righi 		while (*cur == ' ' || *cur == '\t' || *cur == ',')
70*cfe950d7SAndrea Righi 			cur++;
71*cfe950d7SAndrea Righi 		if (*cur < '0' || *cur > '9')
72*cfe950d7SAndrea Righi 			break;
73*cfe950d7SAndrea Righi 
74*cfe950d7SAndrea Righi 		errno = 0;
75*cfe950d7SAndrea Righi 		first = strtol(cur, &end, 10);
76*cfe950d7SAndrea Righi 		if (errno || end == cur || first < 0 || first >= CPU_SETSIZE)
77*cfe950d7SAndrea Righi 			return -1;
78*cfe950d7SAndrea Righi 		cur = end;
79*cfe950d7SAndrea Righi 		last = first;
80*cfe950d7SAndrea Righi 		if (*cur == '-') {
81*cfe950d7SAndrea Righi 			cur++;
82*cfe950d7SAndrea Righi 			errno = 0;
83*cfe950d7SAndrea Righi 			last = strtol(cur, &end, 10);
84*cfe950d7SAndrea Righi 			if (errno || end == cur || last < first)
85*cfe950d7SAndrea Righi 				return -1;
86*cfe950d7SAndrea Righi 			cur = end;
87*cfe950d7SAndrea Righi 		}
88*cfe950d7SAndrea Righi 
89*cfe950d7SAndrea Righi 		cpu = first_allowed_cpu(allowed, first, last);
90*cfe950d7SAndrea Righi 		if (cpu >= 0)
91*cfe950d7SAndrea Righi 			return cpu;
92*cfe950d7SAndrea Righi 	}
93*cfe950d7SAndrea Righi 
94*cfe950d7SAndrea Righi 	return -1;
95*cfe950d7SAndrea Righi }
96*cfe950d7SAndrea Righi 
start_worker(int cpu)97*cfe950d7SAndrea Righi static pid_t start_worker(int cpu)
98*cfe950d7SAndrea Righi {
99*cfe950d7SAndrea Righi 	struct sched_param param = {};
100*cfe950d7SAndrea Righi 	cpu_set_t mask;
101*cfe950d7SAndrea Righi 	pid_t parent;
102*cfe950d7SAndrea Righi 	pid_t pid;
103*cfe950d7SAndrea Righi 
104*cfe950d7SAndrea Righi 	parent = getpid();
105*cfe950d7SAndrea Righi 	pid = fork();
106*cfe950d7SAndrea Righi 	if (pid != 0)
107*cfe950d7SAndrea Righi 		return pid;
108*cfe950d7SAndrea Righi 	if (prctl(PR_SET_PDEATHSIG, SIGKILL) || getppid() != parent)
109*cfe950d7SAndrea Righi 		_exit(1);
110*cfe950d7SAndrea Righi 
111*cfe950d7SAndrea Righi 	/*
112*cfe950d7SAndrea Righi 	 * Become EXT before touching the target so it stays idle until wakeup.
113*cfe950d7SAndrea Righi 	 */
114*cfe950d7SAndrea Righi 	if (sched_setscheduler(0, SCHED_EXT, &param))
115*cfe950d7SAndrea Righi 		_exit(1);
116*cfe950d7SAndrea Righi 
117*cfe950d7SAndrea Righi 	CPU_ZERO(&mask);
118*cfe950d7SAndrea Righi 	CPU_SET(cpu, &mask);
119*cfe950d7SAndrea Righi 	if (sched_setaffinity(0, sizeof(mask), &mask))
120*cfe950d7SAndrea Righi 		_exit(1);
121*cfe950d7SAndrea Righi 
122*cfe950d7SAndrea Righi 	for (;;)
123*cfe950d7SAndrea Righi 		asm volatile("" ::: "memory");
124*cfe950d7SAndrea Righi }
125*cfe950d7SAndrea Righi 
stop_worker(pid_t pid)126*cfe950d7SAndrea Righi static void stop_worker(pid_t pid)
127*cfe950d7SAndrea Righi {
128*cfe950d7SAndrea Righi 	if (pid <= 0)
129*cfe950d7SAndrea Righi 		return;
130*cfe950d7SAndrea Righi 
131*cfe950d7SAndrea Righi 	kill(pid, SIGKILL);
132*cfe950d7SAndrea Righi 	waitpid(pid, NULL, 0);
133*cfe950d7SAndrea Righi }
134*cfe950d7SAndrea Righi 
pause_worker(pid_t pid)135*cfe950d7SAndrea Righi static int pause_worker(pid_t pid)
136*cfe950d7SAndrea Righi {
137*cfe950d7SAndrea Righi 	int status;
138*cfe950d7SAndrea Righi 
139*cfe950d7SAndrea Righi 	if (kill(pid, SIGSTOP))
140*cfe950d7SAndrea Righi 		return -errno;
141*cfe950d7SAndrea Righi 	if (waitpid(pid, &status, WUNTRACED) != pid)
142*cfe950d7SAndrea Righi 		return -errno;
143*cfe950d7SAndrea Righi 	if (!WIFSTOPPED(status))
144*cfe950d7SAndrea Righi 		return -ECHILD;
145*cfe950d7SAndrea Righi 
146*cfe950d7SAndrea Righi 	return 0;
147*cfe950d7SAndrea Righi }
148*cfe950d7SAndrea Righi 
wait_for_counter(const u64 * counter,u64 value,int timeout_ms)149*cfe950d7SAndrea Righi static bool wait_for_counter(const u64 *counter, u64 value, int timeout_ms)
150*cfe950d7SAndrea Righi {
151*cfe950d7SAndrea Righi 	int elapsed;
152*cfe950d7SAndrea Righi 
153*cfe950d7SAndrea Righi 	for (elapsed = 0; elapsed < timeout_ms; elapsed++) {
154*cfe950d7SAndrea Righi 		if (__atomic_load_n(counter, __ATOMIC_RELAXED) >= value)
155*cfe950d7SAndrea Righi 			return true;
156*cfe950d7SAndrea Righi 		usleep(1000);
157*cfe950d7SAndrea Righi 	}
158*cfe950d7SAndrea Righi 
159*cfe950d7SAndrea Righi 	return false;
160*cfe950d7SAndrea Righi }
161*cfe950d7SAndrea Righi 
setup(void ** ctx_ptr)162*cfe950d7SAndrea Righi static enum scx_test_status setup(void **ctx_ptr)
163*cfe950d7SAndrea Righi {
164*cfe950d7SAndrea Righi 	struct nohz_tick_ctx *ctx;
165*cfe950d7SAndrea Righi 	cpu_set_t controller_mask;
166*cfe950d7SAndrea Righi 	int cpu;
167*cfe950d7SAndrea Righi 
168*cfe950d7SAndrea Righi 	ctx = calloc(1, sizeof(*ctx));
169*cfe950d7SAndrea Righi 	SCX_FAIL_IF(!ctx, "Failed to allocate context");
170*cfe950d7SAndrea Righi 	if (sched_getaffinity(0, sizeof(ctx->original_mask),
171*cfe950d7SAndrea Righi 			      &ctx->original_mask)) {
172*cfe950d7SAndrea Righi 		free(ctx);
173*cfe950d7SAndrea Righi 		SCX_FAIL("Failed to get affinity (%d)", errno);
174*cfe950d7SAndrea Righi 	}
175*cfe950d7SAndrea Righi 
176*cfe950d7SAndrea Righi 	cpu = find_nohz_full_cpu(&ctx->original_mask);
177*cfe950d7SAndrea Righi 	if (cpu < 0) {
178*cfe950d7SAndrea Righi 		fprintf(stderr, "SKIP: no allowed NOHZ_FULL CPU\n");
179*cfe950d7SAndrea Righi 		free(ctx);
180*cfe950d7SAndrea Righi 		return SCX_TEST_SKIP;
181*cfe950d7SAndrea Righi 	}
182*cfe950d7SAndrea Righi 
183*cfe950d7SAndrea Righi 	controller_mask = ctx->original_mask;
184*cfe950d7SAndrea Righi 	CPU_CLR(cpu, &controller_mask);
185*cfe950d7SAndrea Righi 	if (CPU_COUNT(&controller_mask) == 0) {
186*cfe950d7SAndrea Righi 		fprintf(stderr, "SKIP: no housekeeping CPU available\n");
187*cfe950d7SAndrea Righi 		free(ctx);
188*cfe950d7SAndrea Righi 		return SCX_TEST_SKIP;
189*cfe950d7SAndrea Righi 	}
190*cfe950d7SAndrea Righi 
191*cfe950d7SAndrea Righi 	ctx->test_cpu = cpu;
192*cfe950d7SAndrea Righi 	ctx->skel = nohz_tick__open();
193*cfe950d7SAndrea Righi 	if (!ctx->skel) {
194*cfe950d7SAndrea Righi 		free(ctx);
195*cfe950d7SAndrea Righi 		SCX_FAIL("Failed to open skeleton");
196*cfe950d7SAndrea Righi 	}
197*cfe950d7SAndrea Righi 
198*cfe950d7SAndrea Righi 	SCX_ENUM_INIT(ctx->skel);
199*cfe950d7SAndrea Righi 	ctx->skel->rodata->test_cpu = cpu;
200*cfe950d7SAndrea Righi 	ctx->skel->struct_ops.nohz_tick_ops->flags |= SCX_OPS_SWITCH_PARTIAL |
201*cfe950d7SAndrea Righi 							   SCX_OPS_ENQ_LAST;
202*cfe950d7SAndrea Righi 	if (nohz_tick__load(ctx->skel)) {
203*cfe950d7SAndrea Righi 		nohz_tick__destroy(ctx->skel);
204*cfe950d7SAndrea Righi 		free(ctx);
205*cfe950d7SAndrea Righi 		SCX_FAIL("Failed to load skeleton");
206*cfe950d7SAndrea Righi 	}
207*cfe950d7SAndrea Righi 
208*cfe950d7SAndrea Righi 	if (sched_setaffinity(0, sizeof(controller_mask), &controller_mask)) {
209*cfe950d7SAndrea Righi 		nohz_tick__destroy(ctx->skel);
210*cfe950d7SAndrea Righi 		free(ctx);
211*cfe950d7SAndrea Righi 		SCX_FAIL("Failed to move controller off CPU %d (%d)", cpu, errno);
212*cfe950d7SAndrea Righi 	}
213*cfe950d7SAndrea Righi 
214*cfe950d7SAndrea Righi 	*ctx_ptr = ctx;
215*cfe950d7SAndrea Righi 	return SCX_TEST_PASS;
216*cfe950d7SAndrea Righi }
217*cfe950d7SAndrea Righi 
run(void * ctx_ptr)218*cfe950d7SAndrea Righi static enum scx_test_status run(void *ctx_ptr)
219*cfe950d7SAndrea Righi {
220*cfe950d7SAndrea Righi 	struct nohz_tick_ctx *ctx = ctx_ptr;
221*cfe950d7SAndrea Righi 	struct nohz_tick *skel = ctx->skel;
222*cfe950d7SAndrea Righi 	struct bpf_link *link = NULL;
223*cfe950d7SAndrea Righi 	enum scx_test_status status = SCX_TEST_FAIL;
224*cfe950d7SAndrea Righi 	pid_t finite_worker = -1;
225*cfe950d7SAndrea Righi 	pid_t inf_worker = -1;
226*cfe950d7SAndrea Righi 	u64 finite_running;
227*cfe950d7SAndrea Righi 	u64 finite_ticks;
228*cfe950d7SAndrea Righi 	int ret;
229*cfe950d7SAndrea Righi 
230*cfe950d7SAndrea Righi 	link = bpf_map__attach_struct_ops(skel->maps.nohz_tick_ops);
231*cfe950d7SAndrea Righi 	if (!link) {
232*cfe950d7SAndrea Righi 		SCX_ERR("Failed to attach scheduler");
233*cfe950d7SAndrea Righi 		goto out;
234*cfe950d7SAndrea Righi 	}
235*cfe950d7SAndrea Righi 
236*cfe950d7SAndrea Righi 	/*
237*cfe950d7SAndrea Righi 	 * Establish SCX_RQ_CAN_STOP_TICK with an infinite-slice task.
238*cfe950d7SAndrea Righi 	 */
239*cfe950d7SAndrea Righi 	inf_worker = start_worker(ctx->test_cpu);
240*cfe950d7SAndrea Righi 	if (inf_worker < 0) {
241*cfe950d7SAndrea Righi 		SCX_ERR("Failed to start infinite-slice worker (%d)", errno);
242*cfe950d7SAndrea Righi 		goto out;
243*cfe950d7SAndrea Righi 	}
244*cfe950d7SAndrea Righi 	if (!wait_for_counter(&skel->bss->nr_inf_running, 1,
245*cfe950d7SAndrea Righi 			      PHASE_TIMEOUT_MS)) {
246*cfe950d7SAndrea Righi 		SCX_ERR("Infinite-slice worker was not scheduled");
247*cfe950d7SAndrea Righi 		goto out;
248*cfe950d7SAndrea Righi 	}
249*cfe950d7SAndrea Righi 
250*cfe950d7SAndrea Righi 	/* Block without exiting so the rq retains the infinite-slice state. */
251*cfe950d7SAndrea Righi 	ret = pause_worker(inf_worker);
252*cfe950d7SAndrea Righi 	if (ret) {
253*cfe950d7SAndrea Righi 		SCX_ERR("Failed to stop infinite-slice worker (%d)", ret);
254*cfe950d7SAndrea Righi 		goto out;
255*cfe950d7SAndrea Righi 	}
256*cfe950d7SAndrea Righi 
257*cfe950d7SAndrea Righi 	/* Let the target enter idle with its tick stopped. */
258*cfe950d7SAndrea Righi 	usleep(100000);
259*cfe950d7SAndrea Righi 
260*cfe950d7SAndrea Righi 	/*
261*cfe950d7SAndrea Righi 	 * The next EXT task receives a finite slice and must restart the tick.
262*cfe950d7SAndrea Righi 	 */
263*cfe950d7SAndrea Righi 	__atomic_store_n(&skel->bss->finite_phase, true, __ATOMIC_RELEASE);
264*cfe950d7SAndrea Righi 	finite_worker = start_worker(ctx->test_cpu);
265*cfe950d7SAndrea Righi 	if (finite_worker < 0) {
266*cfe950d7SAndrea Righi 		SCX_ERR("Failed to start finite-slice worker (%d)", errno);
267*cfe950d7SAndrea Righi 		goto out;
268*cfe950d7SAndrea Righi 	}
269*cfe950d7SAndrea Righi 	if (!wait_for_counter(&skel->bss->nr_finite_running, 1,
270*cfe950d7SAndrea Righi 			      PHASE_TIMEOUT_MS)) {
271*cfe950d7SAndrea Righi 		SCX_ERR("Finite-slice worker was not scheduled");
272*cfe950d7SAndrea Righi 		goto out;
273*cfe950d7SAndrea Righi 	}
274*cfe950d7SAndrea Righi 	if (!wait_for_counter(&skel->bss->nr_finite_ticks, MIN_FINITE_TICKS,
275*cfe950d7SAndrea Righi 			      PHASE_TIMEOUT_MS)) {
276*cfe950d7SAndrea Righi 		SCX_ERR("Finite-slice worker received only %llu scheduler ticks",
277*cfe950d7SAndrea Righi 			(unsigned long long)skel->bss->nr_finite_ticks);
278*cfe950d7SAndrea Righi 		goto out;
279*cfe950d7SAndrea Righi 	}
280*cfe950d7SAndrea Righi 	stop_worker(finite_worker);
281*cfe950d7SAndrea Righi 	finite_worker = -1;
282*cfe950d7SAndrea Righi 
283*cfe950d7SAndrea Righi 	/*
284*cfe950d7SAndrea Righi 	 * Leave the CPU idle after a finite-slice task. The next finite-slice
285*cfe950d7SAndrea Righi 	 * task must restart the tick even though the slice type is unchanged.
286*cfe950d7SAndrea Righi 	 */
287*cfe950d7SAndrea Righi 	usleep(100000);
288*cfe950d7SAndrea Righi 	finite_running = __atomic_load_n(&skel->bss->nr_finite_running,
289*cfe950d7SAndrea Righi 					 __ATOMIC_RELAXED);
290*cfe950d7SAndrea Righi 	finite_ticks = __atomic_load_n(&skel->bss->nr_finite_ticks,
291*cfe950d7SAndrea Righi 				       __ATOMIC_RELAXED);
292*cfe950d7SAndrea Righi 
293*cfe950d7SAndrea Righi 	finite_worker = start_worker(ctx->test_cpu);
294*cfe950d7SAndrea Righi 	if (finite_worker < 0) {
295*cfe950d7SAndrea Righi 		SCX_ERR("Failed to start second finite-slice worker (%d)", errno);
296*cfe950d7SAndrea Righi 		goto out;
297*cfe950d7SAndrea Righi 	}
298*cfe950d7SAndrea Righi 	if (!wait_for_counter(&skel->bss->nr_finite_running,
299*cfe950d7SAndrea Righi 			      finite_running + 1, PHASE_TIMEOUT_MS)) {
300*cfe950d7SAndrea Righi 		SCX_ERR("Second finite-slice worker was not scheduled");
301*cfe950d7SAndrea Righi 		goto out;
302*cfe950d7SAndrea Righi 	}
303*cfe950d7SAndrea Righi 	if (!wait_for_counter(&skel->bss->nr_finite_ticks,
304*cfe950d7SAndrea Righi 			      finite_ticks + MIN_FINITE_TICKS,
305*cfe950d7SAndrea Righi 			      PHASE_TIMEOUT_MS)) {
306*cfe950d7SAndrea Righi 		SCX_ERR("Second finite-slice worker received only %llu scheduler ticks",
307*cfe950d7SAndrea Righi 			(unsigned long long)(skel->bss->nr_finite_ticks -
308*cfe950d7SAndrea Righi 					     finite_ticks));
309*cfe950d7SAndrea Righi 		goto out;
310*cfe950d7SAndrea Righi 	}
311*cfe950d7SAndrea Righi 
312*cfe950d7SAndrea Righi 	if (skel->data->uei.kind != EXIT_KIND(SCX_EXIT_NONE)) {
313*cfe950d7SAndrea Righi 		SCX_ERR("Scheduler exited unexpectedly (kind=%llu code=%lld)",
314*cfe950d7SAndrea Righi 			(unsigned long long)skel->data->uei.kind,
315*cfe950d7SAndrea Righi 			(long long)skel->data->uei.exit_code);
316*cfe950d7SAndrea Righi 		goto out;
317*cfe950d7SAndrea Righi 	}
318*cfe950d7SAndrea Righi 
319*cfe950d7SAndrea Righi 	fprintf(stderr, "CPU %d received %llu finite-slice ticks\n",
320*cfe950d7SAndrea Righi 		ctx->test_cpu,
321*cfe950d7SAndrea Righi 		(unsigned long long)skel->bss->nr_finite_ticks);
322*cfe950d7SAndrea Righi 	status = SCX_TEST_PASS;
323*cfe950d7SAndrea Righi out:
324*cfe950d7SAndrea Righi 	stop_worker(finite_worker);
325*cfe950d7SAndrea Righi 	stop_worker(inf_worker);
326*cfe950d7SAndrea Righi 	if (link)
327*cfe950d7SAndrea Righi 		bpf_link__destroy(link);
328*cfe950d7SAndrea Righi 	return status;
329*cfe950d7SAndrea Righi }
330*cfe950d7SAndrea Righi 
cleanup(void * ctx_ptr)331*cfe950d7SAndrea Righi static void cleanup(void *ctx_ptr)
332*cfe950d7SAndrea Righi {
333*cfe950d7SAndrea Righi 	struct nohz_tick_ctx *ctx = ctx_ptr;
334*cfe950d7SAndrea Righi 
335*cfe950d7SAndrea Righi 	sched_setaffinity(0, sizeof(ctx->original_mask), &ctx->original_mask);
336*cfe950d7SAndrea Righi 	nohz_tick__destroy(ctx->skel);
337*cfe950d7SAndrea Righi 	free(ctx);
338*cfe950d7SAndrea Righi }
339*cfe950d7SAndrea Righi 
340*cfe950d7SAndrea Righi struct scx_test nohz_tick = {
341*cfe950d7SAndrea Righi 	.name = "nohz_tick",
342*cfe950d7SAndrea Righi 	.description = "Verify finite EXT slices restart the NOHZ_FULL tick",
343*cfe950d7SAndrea Righi 	.setup = setup,
344*cfe950d7SAndrea Righi 	.run = run,
345*cfe950d7SAndrea Righi 	.cleanup = cleanup,
346*cfe950d7SAndrea Righi };
347*cfe950d7SAndrea Righi REGISTER_SCX_TEST(&nohz_tick)
348