1 /* Generic test for CPU affinity functions, multi-threaded variant.
2 Copyright (C) 2015-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
19 /* Before including this file, a test has to declare the helper
20 getaffinity and setaffinity functions described in
21 tst-skeleton-affinity.c, which is included below. */
27 #include <support/xthread.h>
31 static bool early_test (struct conf
*);
33 /* Arbitrary run time for each pass. */
34 #define PASS_TIMEOUT 2
36 /* There are two passes (one with sched_yield, one without), and we
37 double the timeout to be on the safe side. */
38 #define TIMEOUT (2 * PASS_TIMEOUT * 2)
40 #include "tst-skeleton-affinity.c"
42 /* 0 if still running, 1 of stopping requested. */
43 static int still_running
;
45 /* 0 if no scheduling failures, 1 if failures are encountered. */
49 thread_burn_one_cpu (void *closure
)
51 int cpu
= (uintptr_t) closure
;
52 while (__atomic_load_n (&still_running
, __ATOMIC_RELAXED
) == 0)
54 int current
= sched_getcpu ();
55 if (sched_getcpu () != cpu
)
57 printf ("error: Pinned thread %d ran on impossible cpu %d\n",
59 __atomic_store_n (&failed
, 1, __ATOMIC_RELAXED
);
60 /* Terminate early. */
61 __atomic_store_n (&still_running
, 1, __ATOMIC_RELAXED
);
71 cpu_set_t
*initial_set
;
77 thread_burn_any_cpu (void *closure
)
79 struct burn_thread
*param
= closure
;
81 /* Schedule this thread around a bit to see if it lands on another
82 CPU. Run this for 2 seconds, once with sched_yield, once
84 for (int pass
= 1; pass
<= 2; ++pass
)
86 time_t start
= time (NULL
);
87 while (time (NULL
) - start
<= PASS_TIMEOUT
)
89 int cpu
= sched_getcpu ();
90 if (cpu
> param
->conf
->last_cpu
91 || !CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (param
->conf
->set_size
),
94 printf ("error: Unpinned thread %d ran on impossible CPU %d\n",
96 __atomic_store_n (&failed
, 1, __ATOMIC_RELAXED
);
99 CPU_SET_S (cpu
, CPU_ALLOC_SIZE (param
->conf
->set_size
),
109 stop_and_join_threads (struct conf
*conf
, cpu_set_t
*set
,
110 pthread_t
*pinned_first
, pthread_t
*pinned_last
,
111 struct burn_thread
*other_first
,
112 struct burn_thread
*other_last
)
114 __atomic_store_n (&still_running
, 1, __ATOMIC_RELAXED
);
115 for (pthread_t
*p
= pinned_first
; p
< pinned_last
; ++p
)
117 int cpu
= p
- pinned_first
;
118 if (!CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), set
))
121 int ret
= pthread_join (*p
, NULL
);
124 printf ("error: Failed to join thread %d: %s\n", cpu
, strerror (ret
));
126 /* Cannot shut down cleanly with threads still running. */
131 for (struct burn_thread
*p
= other_first
; p
< other_last
; ++p
)
133 int cpu
= p
- other_first
;
134 if (!CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), set
))
137 int ret
= pthread_join (p
->self
, NULL
);
140 printf ("error: Failed to join thread %d: %s\n", cpu
, strerror (ret
));
142 /* Cannot shut down cleanly with threads still running. */
148 /* Tries to check that the initial set of CPUs is complete and that
149 the main thread will not run on any other threads. */
151 early_test (struct conf
*conf
)
153 pthread_t
*pinned_threads
154 = calloc (conf
->last_cpu
+ 1, sizeof (*pinned_threads
));
155 struct burn_thread
*other_threads
156 = calloc (conf
->last_cpu
+ 1, sizeof (*other_threads
));
157 cpu_set_t
*initial_set
= CPU_ALLOC (conf
->set_size
);
158 cpu_set_t
*scratch_set
= CPU_ALLOC (conf
->set_size
);
160 if (pinned_threads
== NULL
|| other_threads
== NULL
161 || initial_set
== NULL
|| scratch_set
== NULL
)
163 puts ("error: Memory allocation failure");
166 if (getaffinity (CPU_ALLOC_SIZE (conf
->set_size
), initial_set
) < 0)
168 printf ("error: pthread_getaffinity_np failed: %m\n");
171 for (int cpu
= 0; cpu
<= conf
->last_cpu
; ++cpu
)
173 if (!CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), initial_set
))
175 other_threads
[cpu
].conf
= conf
;
176 other_threads
[cpu
].initial_set
= initial_set
;
177 other_threads
[cpu
].thread
= cpu
;
178 other_threads
[cpu
].seen_set
= CPU_ALLOC (conf
->set_size
);
179 if (other_threads
[cpu
].seen_set
== NULL
)
181 puts ("error: Memory allocation failure");
184 CPU_ZERO_S (CPU_ALLOC_SIZE (conf
->set_size
),
185 other_threads
[cpu
].seen_set
);
189 int ret
= pthread_attr_init (&attr
);
192 printf ("error: pthread_attr_init failed: %s\n", strerror (ret
));
195 support_set_small_thread_stack_size (&attr
);
197 /* Spawn a thread pinned to each available CPU. */
198 for (int cpu
= 0; cpu
<= conf
->last_cpu
; ++cpu
)
200 if (!CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), initial_set
))
202 CPU_ZERO_S (CPU_ALLOC_SIZE (conf
->set_size
), scratch_set
);
203 CPU_SET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), scratch_set
);
204 ret
= pthread_attr_setaffinity_np
205 (&attr
, CPU_ALLOC_SIZE (conf
->set_size
), scratch_set
);
208 printf ("error: pthread_attr_setaffinity_np for CPU %d failed: %s\n",
209 cpu
, strerror (ret
));
210 stop_and_join_threads (conf
, initial_set
,
211 pinned_threads
, pinned_threads
+ cpu
,
215 ret
= pthread_create (pinned_threads
+ cpu
, &attr
,
216 thread_burn_one_cpu
, (void *) (uintptr_t) cpu
);
219 printf ("error: pthread_create for CPU %d failed: %s\n",
220 cpu
, strerror (ret
));
221 stop_and_join_threads (conf
, initial_set
,
222 pinned_threads
, pinned_threads
+ cpu
,
228 /* Spawn another set of threads running on all CPUs. */
229 for (int cpu
= 0; cpu
<= conf
->last_cpu
; ++cpu
)
231 if (!CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), initial_set
))
233 ret
= pthread_create (&other_threads
[cpu
].self
,
234 support_small_stack_thread_attribute (),
235 thread_burn_any_cpu
, other_threads
+ cpu
);
238 printf ("error: pthread_create for thread %d failed: %s\n",
239 cpu
, strerror (ret
));
240 stop_and_join_threads (conf
, initial_set
,
242 pinned_threads
+ conf
->last_cpu
+ 1,
243 other_threads
, other_threads
+ cpu
);
249 struct burn_thread main_thread
;
250 main_thread
.conf
= conf
;
251 main_thread
.initial_set
= initial_set
;
252 main_thread
.seen_set
= scratch_set
;
253 main_thread
.thread
= -1;
254 CPU_ZERO_S (CPU_ALLOC_SIZE (conf
->set_size
), main_thread
.seen_set
);
255 thread_burn_any_cpu (&main_thread
);
256 stop_and_join_threads (conf
, initial_set
,
258 pinned_threads
+ conf
->last_cpu
+ 1,
259 other_threads
, other_threads
+ conf
->last_cpu
+ 1);
261 printf ("info: Main thread ran on %d CPU(s) of %d available CPU(s)\n",
262 CPU_COUNT_S (CPU_ALLOC_SIZE (conf
->set_size
), scratch_set
),
263 CPU_COUNT_S (CPU_ALLOC_SIZE (conf
->set_size
), initial_set
));
264 CPU_ZERO_S (CPU_ALLOC_SIZE (conf
->set_size
), scratch_set
);
265 for (int cpu
= 0; cpu
<= conf
->last_cpu
; ++cpu
)
267 if (!CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), initial_set
))
269 CPU_OR_S (CPU_ALLOC_SIZE (conf
->set_size
),
270 scratch_set
, scratch_set
, other_threads
[cpu
].seen_set
);
271 CPU_FREE (other_threads
[cpu
].seen_set
);
273 printf ("info: Other threads ran on %d CPU(s)\n",
274 CPU_COUNT_S (CPU_ALLOC_SIZE (conf
->set_size
), scratch_set
));;
277 pthread_attr_destroy (&attr
);
278 CPU_FREE (scratch_set
);
279 CPU_FREE (initial_set
);
280 free (pinned_threads
);
281 free (other_threads
);