xref: /aosp_15_r20/external/ltp/testcases/kernel/sched/cfs-scheduler/starvation.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Copyright 2023 Mike Galbraith <efault-AT-gmx.de> */
3 /* Copyright 2023 Wei Gao <[email protected]> */
4 /*\
5  *
6  * [Description]
7  *
8  * Thread starvation test. On fauluty kernel the test timeouts.
9  *
10  * Original reproducer taken from:
11  * https://lore.kernel.org/lkml/[email protected]/
12  */
13 
14 #define _GNU_SOURCE
15 #include <stdio.h>
16 #include <signal.h>
17 #include <unistd.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include <stdlib.h>
21 #include <sched.h>
22 
23 #include "tst_test.h"
24 #include "tst_safe_clocks.h"
25 #include "tst_timer.h"
26 
27 static char *str_loop;
28 static long loop = 1000000;
29 static char *str_timeout;
30 static int timeout;
31 
32 #define CALLIBRATE_LOOPS 120000000
33 
callibrate(void)34 static int callibrate(void)
35 {
36 	int i;
37 	struct timespec start, stop;
38 	long long diff;
39 
40 	for (i = 0; i < CALLIBRATE_LOOPS; i++)
41 		__asm__ __volatile__ ("" : "+g" (i) : :);
42 
43 	SAFE_CLOCK_GETTIME(CLOCK_MONOTONIC_RAW, &start);
44 
45 	for (i = 0; i < CALLIBRATE_LOOPS; i++)
46 		__asm__ __volatile__ ("" : "+g" (i) : :);
47 
48 	SAFE_CLOCK_GETTIME(CLOCK_MONOTONIC_RAW, &stop);
49 
50 	diff = tst_timespec_diff_us(stop, start);
51 
52 	tst_res(TINFO, "CPU did %i loops in %llius", CALLIBRATE_LOOPS, diff);
53 
54 	return diff;
55 }
56 
wait_for_pid(pid_t pid)57 static int wait_for_pid(pid_t pid)
58 {
59 	int status, ret;
60 
61 again:
62 	ret = waitpid(pid, &status, 0);
63 	if (ret == -1) {
64 		if (errno == EINTR)
65 			goto again;
66 
67 		return -1;
68 	}
69 
70 	if (WIFSIGNALED(status))
71 		return 0;
72 
73 	return -1;
74 }
75 
setup(void)76 static void setup(void)
77 {
78 	cpu_set_t mask;
79 	int cpu = 0;
80 	long ncpus = tst_ncpus_conf();
81 
82 	CPU_ZERO(&mask);
83 
84 	/* Restrict test to a single cpu */
85 	if (sched_getaffinity(0, sizeof(mask), &mask) < 0)
86 		tst_brk(TBROK | TERRNO, "sched_getaffinity() failed");
87 
88 	if (CPU_COUNT(&mask) == 0)
89 		tst_brk(TBROK, "No cpus available");
90 
91 	while (CPU_ISSET(cpu, &mask) == 0 && cpu < ncpus)
92 		cpu++;
93 
94 	CPU_ZERO(&mask);
95 
96 	CPU_SET(cpu, &mask);
97 
98 	tst_res(TINFO, "Setting affinity to CPU %d", cpu);
99 
100 	if (sched_setaffinity(0, sizeof(mask), &mask) < 0)
101 		tst_brk(TBROK | TERRNO, "sched_setaffinity() failed");
102 
103 	if (tst_parse_long(str_loop, &loop, 1, LONG_MAX))
104 		tst_brk(TBROK, "Invalid number of loop number '%s'", str_loop);
105 
106 	if (tst_parse_int(str_timeout, &timeout, 1, INT_MAX))
107 		tst_brk(TBROK, "Invalid number of timeout '%s'", str_timeout);
108 	else
109 		timeout = callibrate() / 1000;
110 
111 	tst_set_max_runtime(timeout);
112 }
113 
handler(int sig LTP_ATTRIBUTE_UNUSED)114 static void handler(int sig LTP_ATTRIBUTE_UNUSED)
115 {
116 	if (loop > 0)
117 		--loop;
118 }
119 
child(void)120 static void child(void)
121 {
122 	pid_t ppid = getppid();
123 
124 	TST_CHECKPOINT_WAIT(0);
125 
126 	while (1)
127 		SAFE_KILL(ppid, SIGUSR1);
128 }
129 
do_test(void)130 static void do_test(void)
131 {
132 	pid_t child_pid;
133 
134 	child_pid = SAFE_FORK();
135 
136 	if (!child_pid)
137 		child();
138 
139 	SAFE_SIGNAL(SIGUSR1, handler);
140 	TST_CHECKPOINT_WAKE(0);
141 
142 	while (loop)
143 		sleep(1);
144 
145 	SAFE_KILL(child_pid, SIGTERM);
146 
147 	if (!tst_remaining_runtime())
148 		tst_res(TFAIL, "Scheduller starvation reproduced.");
149 	else
150 		tst_res(TPASS, "Haven't reproduced scheduller starvation.");
151 
152 	TST_EXP_PASS_SILENT(wait_for_pid(child_pid));
153 }
154 
155 static struct tst_test test = {
156 	.test_all = do_test,
157 	.setup = setup,
158 	.forks_child = 1,
159 	.options = (struct tst_option[]) {
160 		{"l:", &str_loop, "Number of loops (default 2000000)"},
161 		{"t:", &str_timeout, "Max timeout (default 240s)"},
162 		{}
163 	},
164 	.needs_checkpoints = 1,
165 };
166