1/* Generic test case for CPU affinity functions.
2 Copyright (C) 2015-2024 Free Software Foundation, Inc.
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
17 <https://www.gnu.org/licenses/>. */
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 defined:
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. */
43struct conf
44{
45 int set_size; /* in bits */
46 int last_cpu;
47};
48
49static int
50find_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 (format: "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 (format: "error: getaffinity for %d CPUs: %m\n", num_cpus);
73 CPU_FREE (set);
74 return -1;
75 }
76 CPU_FREE (set);
77 }
78 puts (s: "error: Cannot find maximum CPU number");
79 return -1;
80}
81
82static int
83find_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
104static void
105setup_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 (format: "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 (format: "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 (s: "info: No test CPU found");
128 CPU_FREE (set);
129 }
130}
131
132static bool
133test_size (const struct conf *conf, size_t size)
134{
135 if (size < conf->set_size)
136 {
137 printf (format: "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 (format: "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 (size: kernel_size, set: initial_set) < 0)
153 {
154 printf (format: "error: size %zu: getaffinity: %m\n", size);
155 return false;
156 }
157 if (setaffinity (size: kernel_size, set: initial_set) < 0)
158 {
159 printf (format: "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 (format: "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 (size: kernel_size, set: active_cpu_set) < 0)
182 {
183 printf (format: "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 (format: "error: Unexpected CPU %d, expected %d\n", active_cpu, cpu);
190 return false;
191 }
192 unsigned int numa_cpu, numa_node;
193 if (getcpu (&numa_cpu, &numa_node) != 0)
194 {
195 printf (format: "error: getcpu: %m\n");
196 return false;
197 }
198 if ((unsigned int) active_cpu != numa_cpu)
199 {
200 printf (format: "error: Unexpected CPU %d, expected %d\n",
201 active_cpu, numa_cpu);
202 return false;
203 }
204 if (getaffinity (size: kernel_size, set: set2) < 0)
205 {
206 printf (format: "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 (format: "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 (size: kernel_size, set: set2) < 0)
220 {
221 printf (format: "error: size %zu: setaffinity (3): %m\n", size);
222 return false;
223 }
224
225 if (setaffinity (size: kernel_size, set: initial_set) < 0)
226 {
227 printf (format: "error: size %zu: setaffinity (4): %m\n", size);
228 return false;
229 }
230 if (getaffinity (size: kernel_size, set: set2) < 0)
231 {
232 printf (format: "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 (format: "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
248static int
249do_test (void)
250{
251 {
252 cpu_set_t set;
253 if (getaffinity (size: sizeof (set), set: &set) < 0 && errno == ENOSYS)
254 {
255 puts (s: "warning: getaffinity not supported, test cannot run");
256 return 0;
257 }
258 if (sched_getcpu () < 0 && errno == ENOSYS)
259 {
260 puts (s: "warning: sched_getcpu not supported, test cannot run");
261 return 0;
262 }
263 }
264
265 struct conf conf;
266 setup_conf (&conf);
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. */
272 printf (format: "info: Detected CPU set size (in bits): %d\n", conf.set_size);
273 printf (format: "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: &conf, size: 1024);
282 error |= !test_size (conf: &conf, size: conf.set_size);
283 error |= !test_size (conf: &conf, size: 2);
284 error |= !test_size (conf: &conf, size: 32);
285 error |= !test_size (conf: &conf, size: 40);
286 error |= !test_size (conf: &conf, size: 64);
287 error |= !test_size (conf: &conf, size: 96);
288 error |= !test_size (conf: &conf, size: 128);
289 error |= !test_size (conf: &conf, size: 256);
290 error |= !test_size (conf: &conf, size: 8192);
291 return error;
292}
293
294#define TEST_FUNCTION do_test ()
295#include "../test-skeleton.c"
296

source code of glibc/sysdeps/unix/sysv/linux/tst-skeleton-affinity.c