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