1 /* Generic test for CPU affinity functions, multi-threaded variant.
2 Copyright (C) 2015-2016 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 <http://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. */
30 static bool early_test (struct conf
*);
32 /* Arbitrary run time for each pass. */
33 #define PASS_TIMEOUT 2
35 /* There are two passes (one with sched_yield, one without), and we
36 double the timeout to be on the safe side. */
37 #define TIMEOUT (2 * PASS_TIMEOUT * 2)
39 #include "tst-skeleton-affinity.c"
41 /* 0 if still running, 1 of stopping requested. */
42 static int still_running
;
44 /* 0 if no scheduling failures, 1 if failures are encountered. */
48 thread_burn_one_cpu (void *closure
)
50 int cpu
= (uintptr_t) closure
;
51 while (__atomic_load_n (&still_running
, __ATOMIC_RELAXED
) == 0)
53 int current
= sched_getcpu ();
54 if (sched_getcpu () != cpu
)
56 printf ("error: Pinned thread %d ran on impossible cpu %d\n",
58 __atomic_store_n (&failed
, 1, __ATOMIC_RELAXED
);
59 /* Terminate early. */
60 __atomic_store_n (&still_running
, 1, __ATOMIC_RELAXED
);
70 cpu_set_t
*initial_set
;
76 thread_burn_any_cpu (void *closure
)
78 struct burn_thread
*param
= closure
;
80 /* Schedule this thread around a bit to see if it lands on another
81 CPU. Run this for 2 seconds, once with sched_yield, once
83 for (int pass
= 1; pass
<= 2; ++pass
)
85 time_t start
= time (NULL
);
86 while (time (NULL
) - start
<= PASS_TIMEOUT
)
88 int cpu
= sched_getcpu ();
89 if (cpu
> param
->conf
->last_cpu
90 || !CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (param
->conf
->set_size
),
93 printf ("error: Unpinned thread %d ran on impossible CPU %d\n",
95 __atomic_store_n (&failed
, 1, __ATOMIC_RELAXED
);
98 CPU_SET_S (cpu
, CPU_ALLOC_SIZE (param
->conf
->set_size
),
108 stop_and_join_threads (struct conf
*conf
, cpu_set_t
*set
,
109 pthread_t
*pinned_first
, pthread_t
*pinned_last
,
110 struct burn_thread
*other_first
,
111 struct burn_thread
*other_last
)
113 __atomic_store_n (&still_running
, 1, __ATOMIC_RELAXED
);
114 for (pthread_t
*p
= pinned_first
; p
< pinned_last
; ++p
)
116 int cpu
= p
- pinned_first
;
117 if (!CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), set
))
120 int ret
= pthread_join (*p
, NULL
);
123 printf ("error: Failed to join thread %d: %s\n", cpu
, strerror (ret
));
125 /* Cannot shut down cleanly with threads still running. */
130 for (struct burn_thread
*p
= other_first
; p
< other_last
; ++p
)
132 int cpu
= p
- other_first
;
133 if (!CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), set
))
136 int ret
= pthread_join (p
->self
, NULL
);
139 printf ("error: Failed to join thread %d: %s\n", cpu
, strerror (ret
));
141 /* Cannot shut down cleanly with threads still running. */
147 /* Tries to check that the initial set of CPUs is complete and that
148 the main thread will not run on any other threads. */
150 early_test (struct conf
*conf
)
152 pthread_t
*pinned_threads
153 = calloc (conf
->last_cpu
+ 1, sizeof (*pinned_threads
));
154 struct burn_thread
*other_threads
155 = calloc (conf
->last_cpu
+ 1, sizeof (*other_threads
));
156 cpu_set_t
*initial_set
= CPU_ALLOC (conf
->set_size
);
157 cpu_set_t
*scratch_set
= CPU_ALLOC (conf
->set_size
);
159 if (pinned_threads
== NULL
|| other_threads
== NULL
160 || initial_set
== NULL
|| scratch_set
== NULL
)
162 puts ("error: Memory allocation failure");
165 if (getaffinity (CPU_ALLOC_SIZE (conf
->set_size
), initial_set
) < 0)
167 printf ("error: pthread_getaffinity_np failed: %m\n");
170 for (int cpu
= 0; cpu
<= conf
->last_cpu
; ++cpu
)
172 if (!CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), initial_set
))
174 other_threads
[cpu
].conf
= conf
;
175 other_threads
[cpu
].initial_set
= initial_set
;
176 other_threads
[cpu
].thread
= cpu
;
177 other_threads
[cpu
].seen_set
= CPU_ALLOC (conf
->set_size
);
178 if (other_threads
[cpu
].seen_set
== NULL
)
180 puts ("error: Memory allocation failure");
183 CPU_ZERO_S (CPU_ALLOC_SIZE (conf
->set_size
),
184 other_threads
[cpu
].seen_set
);
188 int ret
= pthread_attr_init (&attr
);
191 printf ("error: pthread_attr_init failed: %s\n", strerror (ret
));
195 /* Spawn a thread pinned to each available CPU. */
196 for (int cpu
= 0; cpu
<= conf
->last_cpu
; ++cpu
)
198 if (!CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), initial_set
))
200 CPU_ZERO_S (CPU_ALLOC_SIZE (conf
->set_size
), scratch_set
);
201 CPU_SET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), scratch_set
);
202 ret
= pthread_attr_setaffinity_np
203 (&attr
, CPU_ALLOC_SIZE (conf
->set_size
), scratch_set
);
206 printf ("error: pthread_attr_setaffinity_np for CPU %d failed: %s\n",
207 cpu
, strerror (ret
));
208 stop_and_join_threads (conf
, initial_set
,
209 pinned_threads
, pinned_threads
+ cpu
,
213 ret
= pthread_create (pinned_threads
+ cpu
, &attr
,
214 thread_burn_one_cpu
, (void *) (uintptr_t) cpu
);
217 printf ("error: pthread_create for CPU %d failed: %s\n",
218 cpu
, strerror (ret
));
219 stop_and_join_threads (conf
, initial_set
,
220 pinned_threads
, pinned_threads
+ cpu
,
226 /* Spawn another set of threads running on all CPUs. */
227 for (int cpu
= 0; cpu
<= conf
->last_cpu
; ++cpu
)
229 if (!CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), initial_set
))
231 ret
= pthread_create (&other_threads
[cpu
].self
, NULL
,
232 thread_burn_any_cpu
, other_threads
+ cpu
);
235 printf ("error: pthread_create for thread %d failed: %s\n",
236 cpu
, strerror (ret
));
237 stop_and_join_threads (conf
, initial_set
,
239 pinned_threads
+ conf
->last_cpu
+ 1,
240 other_threads
, other_threads
+ cpu
);
246 struct burn_thread main_thread
;
247 main_thread
.conf
= conf
;
248 main_thread
.initial_set
= initial_set
;
249 main_thread
.seen_set
= scratch_set
;
250 main_thread
.thread
= -1;
251 CPU_ZERO_S (CPU_ALLOC_SIZE (conf
->set_size
), main_thread
.seen_set
);
252 thread_burn_any_cpu (&main_thread
);
253 stop_and_join_threads (conf
, initial_set
,
255 pinned_threads
+ conf
->last_cpu
+ 1,
256 other_threads
, other_threads
+ conf
->last_cpu
+ 1);
258 printf ("info: Main thread ran on %d CPU(s) of %d available CPU(s)\n",
259 CPU_COUNT_S (CPU_ALLOC_SIZE (conf
->set_size
), scratch_set
),
260 CPU_COUNT_S (CPU_ALLOC_SIZE (conf
->set_size
), initial_set
));
261 CPU_ZERO_S (CPU_ALLOC_SIZE (conf
->set_size
), scratch_set
);
262 for (int cpu
= 0; cpu
<= conf
->last_cpu
; ++cpu
)
264 if (!CPU_ISSET_S (cpu
, CPU_ALLOC_SIZE (conf
->set_size
), initial_set
))
266 CPU_OR_S (CPU_ALLOC_SIZE (conf
->set_size
),
267 scratch_set
, scratch_set
, other_threads
[cpu
].seen_set
);
268 CPU_FREE (other_threads
[cpu
].seen_set
);
270 printf ("info: Other threads ran on %d CPU(s)\n",
271 CPU_COUNT_S (CPU_ALLOC_SIZE (conf
->set_size
), scratch_set
));;
274 pthread_attr_destroy (&attr
);
275 CPU_FREE (scratch_set
);
276 CPU_FREE (initial_set
);
277 free (pinned_threads
);
278 free (other_threads
);