riscv: Fix alignment-ignorant memcpy implementation
[glibc.git] / sysdeps / unix / sysv / linux / tst-skeleton-thread-affinity.c
blob5a1e84431a30132d038dcc45ca3bda33f729e895
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. */
23 #include <errno.h>
24 #include <pthread.h>
25 #include <stdbool.h>
26 #include <stdlib.h>
27 #include <support/xthread.h>
28 #include <sys/time.h>
30 struct conf;
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. */
46 static int failed;
48 static void *
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",
58 cpu, current);
59 __atomic_store_n (&failed, 1, __ATOMIC_RELAXED);
60 /* Terminate early. */
61 __atomic_store_n (&still_running, 1, __ATOMIC_RELAXED);
64 return NULL;
67 struct burn_thread
69 pthread_t self;
70 struct conf *conf;
71 cpu_set_t *initial_set;
72 cpu_set_t *seen_set;
73 int thread;
76 static void *
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
83 without. */
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),
92 param->initial_set))
94 printf ("error: Unpinned thread %d ran on impossible CPU %d\n",
95 param->thread, cpu);
96 __atomic_store_n (&failed, 1, __ATOMIC_RELAXED);
97 return NULL;
99 CPU_SET_S (cpu, CPU_ALLOC_SIZE (param->conf->set_size),
100 param->seen_set);
101 if (pass == 1)
102 sched_yield ();
105 return NULL;
108 static void
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))
119 continue;
121 int ret = pthread_join (*p, NULL);
122 if (ret != 0)
124 printf ("error: Failed to join thread %d: %s\n", cpu, strerror (ret));
125 fflush (stdout);
126 /* Cannot shut down cleanly with threads still running. */
127 abort ();
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))
135 continue;
137 int ret = pthread_join (p->self, NULL);
138 if (ret != 0)
140 printf ("error: Failed to join thread %d: %s\n", cpu, strerror (ret));
141 fflush (stdout);
142 /* Cannot shut down cleanly with threads still running. */
143 abort ();
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. */
150 static bool
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");
164 return false;
166 if (getaffinity (CPU_ALLOC_SIZE (conf->set_size), initial_set) < 0)
168 printf ("error: pthread_getaffinity_np failed: %m\n");
169 return false;
171 for (int cpu = 0; cpu <= conf->last_cpu; ++cpu)
173 if (!CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), initial_set))
174 continue;
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");
182 return false;
184 CPU_ZERO_S (CPU_ALLOC_SIZE (conf->set_size),
185 other_threads[cpu].seen_set);
188 pthread_attr_t attr;
189 int ret = pthread_attr_init (&attr);
190 if (ret != 0)
192 printf ("error: pthread_attr_init failed: %s\n", strerror (ret));
193 return false;
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))
201 continue;
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);
206 if (ret != 0)
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,
212 NULL, NULL);
213 return false;
215 ret = pthread_create (pinned_threads + cpu, &attr,
216 thread_burn_one_cpu, (void *) (uintptr_t) cpu);
217 if (ret != 0)
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,
223 NULL, NULL);
224 return false;
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))
232 continue;
233 ret = pthread_create (&other_threads[cpu].self,
234 support_small_stack_thread_attribute (),
235 thread_burn_any_cpu, other_threads + cpu);
236 if (ret != 0)
238 printf ("error: pthread_create for thread %d failed: %s\n",
239 cpu, strerror (ret));
240 stop_and_join_threads (conf, initial_set,
241 pinned_threads,
242 pinned_threads + conf->last_cpu + 1,
243 other_threads, other_threads + cpu);
244 return false;
248 /* Main thread. */
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,
257 pinned_threads,
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))
268 continue;
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);
282 return failed == 0;