]>
Commit | Line | Data |
---|---|---|
4f19048f | 1 | // SPDX-License-Identifier: GPL-2.0-only |
0bc4b0cf FW |
2 | /* |
3 | * Copyright (C) 2013 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com> | |
4 | * | |
0bc4b0cf FW |
5 | * Selftests for a few posix timers interface. |
6 | * | |
7 | * Kernel loop code stolen from Steven Rostedt <srostedt@redhat.com> | |
8 | */ | |
9 | ||
10 | #include <sys/time.h> | |
11 | #include <stdio.h> | |
12 | #include <signal.h> | |
13 | #include <unistd.h> | |
14 | #include <time.h> | |
15 | #include <pthread.h> | |
16 | ||
88471649 SK |
17 | #include "../kselftest.h" |
18 | ||
0bc4b0cf FW |
19 | #define DELAY 2 |
20 | #define USECS_PER_SEC 1000000 | |
21 | ||
22 | static volatile int done; | |
23 | ||
24 | /* Busy loop in userspace to elapse ITIMER_VIRTUAL */ | |
25 | static void user_loop(void) | |
26 | { | |
27 | while (!done); | |
28 | } | |
29 | ||
30 | /* | |
31 | * Try to spend as much time as possible in kernelspace | |
32 | * to elapse ITIMER_PROF. | |
33 | */ | |
34 | static void kernel_loop(void) | |
35 | { | |
36 | void *addr = sbrk(0); | |
2430ec65 | 37 | int err = 0; |
0bc4b0cf | 38 | |
2430ec65 JS |
39 | while (!done && !err) { |
40 | err = brk(addr + 4096); | |
41 | err |= brk(addr); | |
0bc4b0cf FW |
42 | } |
43 | } | |
44 | ||
45 | /* | |
46 | * Sleep until ITIMER_REAL expiration. | |
47 | */ | |
48 | static void idle_loop(void) | |
49 | { | |
50 | pause(); | |
51 | } | |
52 | ||
53 | static void sig_handler(int nr) | |
54 | { | |
55 | done = 1; | |
56 | } | |
57 | ||
58 | /* | |
59 | * Check the expected timer expiration matches the GTOD elapsed delta since | |
60 | * we armed the timer. Keep a 0.5 sec error margin due to various jitter. | |
61 | */ | |
62 | static int check_diff(struct timeval start, struct timeval end) | |
63 | { | |
64 | long long diff; | |
65 | ||
66 | diff = end.tv_usec - start.tv_usec; | |
67 | diff += (end.tv_sec - start.tv_sec) * USECS_PER_SEC; | |
68 | ||
69 | if (abs(diff - DELAY * USECS_PER_SEC) > USECS_PER_SEC / 2) { | |
70 | printf("Diff too high: %lld..", diff); | |
71 | return -1; | |
72 | } | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
77 | static int check_itimer(int which) | |
78 | { | |
79 | int err; | |
80 | struct timeval start, end; | |
81 | struct itimerval val = { | |
82 | .it_value.tv_sec = DELAY, | |
83 | }; | |
84 | ||
85 | printf("Check itimer "); | |
86 | ||
87 | if (which == ITIMER_VIRTUAL) | |
88 | printf("virtual... "); | |
89 | else if (which == ITIMER_PROF) | |
90 | printf("prof... "); | |
91 | else if (which == ITIMER_REAL) | |
92 | printf("real... "); | |
93 | ||
94 | fflush(stdout); | |
95 | ||
96 | done = 0; | |
97 | ||
98 | if (which == ITIMER_VIRTUAL) | |
99 | signal(SIGVTALRM, sig_handler); | |
100 | else if (which == ITIMER_PROF) | |
101 | signal(SIGPROF, sig_handler); | |
102 | else if (which == ITIMER_REAL) | |
103 | signal(SIGALRM, sig_handler); | |
104 | ||
105 | err = gettimeofday(&start, NULL); | |
106 | if (err < 0) { | |
107 | perror("Can't call gettimeofday()\n"); | |
108 | return -1; | |
109 | } | |
110 | ||
111 | err = setitimer(which, &val, NULL); | |
112 | if (err < 0) { | |
113 | perror("Can't set timer\n"); | |
114 | return -1; | |
115 | } | |
116 | ||
117 | if (which == ITIMER_VIRTUAL) | |
118 | user_loop(); | |
119 | else if (which == ITIMER_PROF) | |
120 | kernel_loop(); | |
121 | else if (which == ITIMER_REAL) | |
122 | idle_loop(); | |
123 | ||
c3cb8356 | 124 | err = gettimeofday(&end, NULL); |
0bc4b0cf FW |
125 | if (err < 0) { |
126 | perror("Can't call gettimeofday()\n"); | |
127 | return -1; | |
128 | } | |
129 | ||
130 | if (!check_diff(start, end)) | |
131 | printf("[OK]\n"); | |
132 | else | |
133 | printf("[FAIL]\n"); | |
134 | ||
135 | return 0; | |
136 | } | |
137 | ||
138 | static int check_timer_create(int which) | |
139 | { | |
140 | int err; | |
141 | timer_t id; | |
142 | struct timeval start, end; | |
143 | struct itimerspec val = { | |
144 | .it_value.tv_sec = DELAY, | |
145 | }; | |
146 | ||
147 | printf("Check timer_create() "); | |
148 | if (which == CLOCK_THREAD_CPUTIME_ID) { | |
149 | printf("per thread... "); | |
150 | } else if (which == CLOCK_PROCESS_CPUTIME_ID) { | |
151 | printf("per process... "); | |
152 | } | |
153 | fflush(stdout); | |
154 | ||
155 | done = 0; | |
c88b05b2 | 156 | err = timer_create(which, NULL, &id); |
0bc4b0cf FW |
157 | if (err < 0) { |
158 | perror("Can't create timer\n"); | |
159 | return -1; | |
160 | } | |
161 | signal(SIGALRM, sig_handler); | |
162 | ||
163 | err = gettimeofday(&start, NULL); | |
164 | if (err < 0) { | |
165 | perror("Can't call gettimeofday()\n"); | |
166 | return -1; | |
167 | } | |
168 | ||
169 | err = timer_settime(id, 0, &val, NULL); | |
170 | if (err < 0) { | |
171 | perror("Can't set timer\n"); | |
172 | return -1; | |
173 | } | |
174 | ||
175 | user_loop(); | |
176 | ||
c3cb8356 | 177 | err = gettimeofday(&end, NULL); |
0bc4b0cf FW |
178 | if (err < 0) { |
179 | perror("Can't call gettimeofday()\n"); | |
180 | return -1; | |
181 | } | |
182 | ||
183 | if (!check_diff(start, end)) | |
184 | printf("[OK]\n"); | |
185 | else | |
186 | printf("[FAIL]\n"); | |
187 | ||
188 | return 0; | |
189 | } | |
190 | ||
191 | int main(int argc, char **argv) | |
192 | { | |
0bc4b0cf FW |
193 | printf("Testing posix timers. False negative may happen on CPU execution \n"); |
194 | printf("based timers if other threads run on the CPU...\n"); | |
195 | ||
196 | if (check_itimer(ITIMER_VIRTUAL) < 0) | |
88471649 | 197 | return ksft_exit_fail(); |
0bc4b0cf FW |
198 | |
199 | if (check_itimer(ITIMER_PROF) < 0) | |
88471649 | 200 | return ksft_exit_fail(); |
0bc4b0cf FW |
201 | |
202 | if (check_itimer(ITIMER_REAL) < 0) | |
88471649 | 203 | return ksft_exit_fail(); |
0bc4b0cf FW |
204 | |
205 | if (check_timer_create(CLOCK_THREAD_CPUTIME_ID) < 0) | |
88471649 | 206 | return ksft_exit_fail(); |
0bc4b0cf FW |
207 | |
208 | /* | |
209 | * It's unfortunately hard to reliably test a timer expiration | |
210 | * on parallel multithread cputime. We could arm it to expire | |
211 | * on DELAY * nr_threads, with nr_threads busy looping, then wait | |
212 | * the normal DELAY since the time is elapsing nr_threads faster. | |
213 | * But for that we need to ensure we have real physical free CPUs | |
214 | * to ensure true parallelism. So test only one thread until we | |
215 | * find a better solution. | |
216 | */ | |
217 | if (check_timer_create(CLOCK_PROCESS_CPUTIME_ID) < 0) | |
88471649 | 218 | return ksft_exit_fail(); |
0bc4b0cf | 219 | |
88471649 | 220 | return ksft_exit_pass(); |
0bc4b0cf | 221 | } |