]>
Commit | Line | Data |
---|---|---|
2359035a | 1 | /* Generic test case for CPU affinity functions. |
d614a753 | 2 | Copyright (C) 2015-2020 Free Software Foundation, Inc. |
2359035a FW |
3 | This file is part of the GNU C Library. |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Lesser General Public | |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | Lesser General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Lesser General Public | |
16 | License along with the GNU C Library; if not, see | |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
2359035a FW |
18 | |
19 | /* This file is included by the tst-affinity*.c files to test the two | |
20 | variants of the functions, under different conditions. The | |
21 | following functions have to be definied: | |
22 | ||
23 | static int getaffinity (size_t, cpu_set_t *); | |
24 | static int setaffinity (size_t, const cpu_set_t *); | |
25 | static bool early_test (struct conf *); | |
26 | ||
27 | The first two functions shall affect the affinity mask for the | |
28 | current thread and return 0 for success, -1 for error (with an | |
29 | error code in errno). | |
30 | ||
31 | early_test is invoked before the tests in this file affect the | |
32 | affinity masks. If it returns true, testing continues, otherwise | |
33 | no more tests run and the overall test exits with status 1. | |
34 | */ | |
35 | ||
36 | #include <errno.h> | |
37 | #include <limits.h> | |
38 | #include <sched.h> | |
39 | #include <stdbool.h> | |
40 | #include <stdio.h> | |
41 | ||
42 | /* CPU set configuration determined. Can be used from early_test. */ | |
43 | struct conf | |
44 | { | |
45 | int set_size; /* in bits */ | |
46 | int last_cpu; | |
47 | }; | |
48 | ||
49 | static int | |
50 | find_set_size (void) | |
51 | { | |
52 | /* There is considerable controversy about how to determine the size | |
53 | of the kernel CPU mask. The probing loop below is only intended | |
54 | for testing purposes. */ | |
55 | for (int num_cpus = 64; num_cpus <= INT_MAX / 2; ++num_cpus) | |
56 | { | |
57 | cpu_set_t *set = CPU_ALLOC (num_cpus); | |
58 | size_t size = CPU_ALLOC_SIZE (num_cpus); | |
59 | ||
60 | if (set == NULL) | |
61 | { | |
62 | printf ("error: CPU_ALLOC (%d) failed\n", num_cpus); | |
63 | return -1; | |
64 | } | |
65 | if (getaffinity (size, set) == 0) | |
66 | { | |
67 | CPU_FREE (set); | |
68 | return num_cpus; | |
69 | } | |
70 | if (errno != EINVAL) | |
71 | { | |
72 | printf ("error: getaffinity for %d CPUs: %m\n", num_cpus); | |
73 | CPU_FREE (set); | |
74 | return -1; | |
75 | } | |
76 | CPU_FREE (set); | |
77 | } | |
78 | puts ("error: Cannot find maximum CPU number"); | |
79 | return -1; | |
80 | } | |
81 | ||
82 | static int | |
83 | find_last_cpu (const cpu_set_t *set, size_t size) | |
84 | { | |
85 | /* We need to determine the set size with CPU_COUNT_S and the | |
86 | cpus_found counter because there is no direct way to obtain the | |
87 | actual CPU set size, in bits, from the value of | |
88 | CPU_ALLOC_SIZE. */ | |
89 | size_t cpus_found = 0; | |
90 | size_t total_cpus = CPU_COUNT_S (size, set); | |
91 | int last_cpu = -1; | |
92 | ||
93 | for (int cpu = 0; cpus_found < total_cpus; ++cpu) | |
94 | { | |
95 | if (CPU_ISSET_S (cpu, size, set)) | |
96 | { | |
97 | last_cpu = cpu; | |
98 | ++cpus_found; | |
99 | } | |
100 | } | |
101 | return last_cpu; | |
102 | } | |
103 | ||
104 | static void | |
105 | setup_conf (struct conf *conf) | |
106 | { | |
107 | *conf = (struct conf) {-1, -1}; | |
108 | conf->set_size = find_set_size (); | |
109 | if (conf->set_size > 0) | |
110 | { | |
111 | cpu_set_t *set = CPU_ALLOC (conf->set_size); | |
112 | ||
113 | if (set == NULL) | |
114 | { | |
115 | printf ("error: CPU_ALLOC (%d) failed\n", conf->set_size); | |
116 | CPU_FREE (set); | |
117 | return; | |
118 | } | |
119 | if (getaffinity (CPU_ALLOC_SIZE (conf->set_size), set) < 0) | |
120 | { | |
121 | printf ("error: getaffinity failed: %m\n"); | |
122 | CPU_FREE (set); | |
123 | return; | |
124 | } | |
125 | conf->last_cpu = find_last_cpu (set, CPU_ALLOC_SIZE (conf->set_size)); | |
126 | if (conf->last_cpu < 0) | |
127 | puts ("info: No test CPU found"); | |
128 | CPU_FREE (set); | |
129 | } | |
130 | } | |
131 | ||
132 | static bool | |
133 | test_size (const struct conf *conf, size_t size) | |
134 | { | |
135 | if (size < conf->set_size) | |
136 | { | |
137 | printf ("info: Test not run for CPU set size %zu\n", size); | |
138 | return true; | |
139 | } | |
140 | ||
141 | cpu_set_t *initial_set = CPU_ALLOC (size); | |
142 | cpu_set_t *set2 = CPU_ALLOC (size); | |
143 | cpu_set_t *active_cpu_set = CPU_ALLOC (size); | |
144 | ||
145 | if (initial_set == NULL || set2 == NULL || active_cpu_set == NULL) | |
146 | { | |
147 | printf ("error: size %zu: CPU_ALLOC failed\n", size); | |
148 | return false; | |
149 | } | |
150 | size_t kernel_size = CPU_ALLOC_SIZE (size); | |
151 | ||
152 | if (getaffinity (kernel_size, initial_set) < 0) | |
153 | { | |
154 | printf ("error: size %zu: getaffinity: %m\n", size); | |
155 | return false; | |
156 | } | |
157 | if (setaffinity (kernel_size, initial_set) < 0) | |
158 | { | |
159 | printf ("error: size %zu: setaffinity: %m\n", size); | |
160 | return true; | |
161 | } | |
162 | ||
163 | /* Use one-CPU set to test switching between CPUs. */ | |
164 | int last_active_cpu = -1; | |
165 | for (int cpu = 0; cpu <= conf->last_cpu; ++cpu) | |
166 | { | |
167 | int active_cpu = sched_getcpu (); | |
168 | if (last_active_cpu >= 0 && last_active_cpu != active_cpu) | |
169 | { | |
170 | printf ("error: Unexpected CPU %d, expected %d\n", | |
171 | active_cpu, last_active_cpu); | |
172 | return false; | |
173 | } | |
174 | ||
175 | if (!CPU_ISSET_S (cpu, kernel_size, initial_set)) | |
176 | continue; | |
177 | last_active_cpu = cpu; | |
178 | ||
179 | CPU_ZERO_S (kernel_size, active_cpu_set); | |
180 | CPU_SET_S (cpu, kernel_size, active_cpu_set); | |
181 | if (setaffinity (kernel_size, active_cpu_set) < 0) | |
182 | { | |
183 | printf ("error: size %zu: setaffinity (%d): %m\n", size, cpu); | |
184 | return false; | |
185 | } | |
186 | active_cpu = sched_getcpu (); | |
187 | if (active_cpu != cpu) | |
188 | { | |
189 | printf ("error: Unexpected CPU %d, expected %d\n", active_cpu, cpu); | |
190 | return false; | |
191 | } | |
a092ca94 L |
192 | unsigned int numa_cpu, numa_node; |
193 | if (getcpu (&numa_cpu, &numa_node) != 0) | |
194 | { | |
195 | printf ("error: getcpu: %m\n"); | |
196 | return false; | |
197 | } | |
198 | if ((unsigned int) active_cpu != numa_cpu) | |
199 | { | |
200 | printf ("error: Unexpected CPU %d, expected %d\n", | |
201 | active_cpu, numa_cpu); | |
202 | return false; | |
203 | } | |
2359035a FW |
204 | if (getaffinity (kernel_size, set2) < 0) |
205 | { | |
206 | printf ("error: size %zu: getaffinity (2): %m\n", size); | |
207 | return false; | |
208 | } | |
209 | if (!CPU_EQUAL_S (kernel_size, active_cpu_set, set2)) | |
210 | { | |
211 | printf ("error: size %zu: CPU sets do not match\n", size); | |
212 | return false; | |
213 | } | |
214 | } | |
215 | ||
216 | /* Test setting the all-ones set. */ | |
217 | for (int cpu = 0; cpu < size; ++cpu) | |
218 | CPU_SET_S (cpu, kernel_size, set2); | |
219 | if (setaffinity (kernel_size, set2) < 0) | |
220 | { | |
221 | printf ("error: size %zu: setaffinity (3): %m\n", size); | |
222 | return false; | |
223 | } | |
224 | ||
225 | if (setaffinity (kernel_size, initial_set) < 0) | |
226 | { | |
227 | printf ("error: size %zu: setaffinity (4): %m\n", size); | |
228 | return false; | |
229 | } | |
230 | if (getaffinity (kernel_size, set2) < 0) | |
231 | { | |
232 | printf ("error: size %zu: getaffinity (3): %m\n", size); | |
233 | return false; | |
234 | } | |
235 | if (!CPU_EQUAL_S (kernel_size, initial_set, set2)) | |
236 | { | |
237 | printf ("error: size %zu: CPU sets do not match (2)\n", size); | |
238 | return false; | |
239 | } | |
240 | ||
241 | CPU_FREE (initial_set); | |
242 | CPU_FREE (set2); | |
243 | CPU_FREE (active_cpu_set); | |
244 | ||
245 | return true; | |
246 | } | |
247 | ||
248 | static int | |
249 | do_test (void) | |
250 | { | |
251 | { | |
252 | cpu_set_t set; | |
253 | if (getaffinity (sizeof (set), &set) < 0 && errno == ENOSYS) | |
254 | { | |
255 | puts ("warning: getaffinity not supported, test cannot run"); | |
256 | return 0; | |
257 | } | |
258 | if (sched_getcpu () < 0 && errno == ENOSYS) | |
259 | { | |
260 | puts ("warning: sched_getcpu not supported, test cannot run"); | |
261 | return 0; | |
262 | } | |
263 | } | |
264 | ||
265 | struct conf conf; | |
266 | setup_conf (&conf); | |
9e3e27c4 FW |
267 | /* Note: The CPU set size in bits can be less than the CPU count |
268 | (and the maximum test CPU) because the userspace interface rounds | |
269 | up the bit count, and the rounded-up buffer size is passed into | |
270 | the kernel. The kernel does not know that some of the buffer are | |
271 | actually padding, and writes data there. */ | |
2359035a FW |
272 | printf ("info: Detected CPU set size (in bits): %d\n", conf.set_size); |
273 | printf ("info: Maximum test CPU: %d\n", conf.last_cpu); | |
274 | if (conf.set_size < 0 || conf.last_cpu < 0) | |
275 | return 1; | |
276 | ||
277 | if (!early_test (&conf)) | |
278 | return 1; | |
279 | ||
280 | bool error = false; | |
281 | error |= !test_size (&conf, 1024); | |
282 | error |= !test_size (&conf, conf.set_size); | |
283 | error |= !test_size (&conf, 2); | |
284 | error |= !test_size (&conf, 32); | |
285 | error |= !test_size (&conf, 40); | |
286 | error |= !test_size (&conf, 64); | |
287 | error |= !test_size (&conf, 96); | |
288 | error |= !test_size (&conf, 128); | |
289 | error |= !test_size (&conf, 256); | |
290 | error |= !test_size (&conf, 8192); | |
291 | return error; | |
292 | } | |
293 | ||
294 | #define TEST_FUNCTION do_test () | |
295 | #include "../test-skeleton.c" |