tst-skeleton-thread-affinity.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. /* Generic test for CPU affinity functions, multi-threaded variant.
  2. Copyright (C) 2015-2019 Free Software Foundation, Inc.
  3. This file is part of the GNU C Library.
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. The GNU C Library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with the GNU C Library; if not, see
  14. <http://www.gnu.org/licenses/>. */
  15. /* Before including this file, a test has to declare the helper
  16. getaffinity and setaffinity functions described in
  17. tst-skeleton-affinity.c, which is included below. */
  18. #include <errno.h>
  19. #include <pthread.h>
  20. #include <stdbool.h>
  21. #include <stdlib.h>
  22. #include <sys/time.h>
  23. struct conf;
  24. static bool early_test (struct conf *);
  25. /* Arbitrary run time for each pass. */
  26. #define PASS_TIMEOUT 2
  27. /* There are two passes (one with sched_yield, one without), and we
  28. double the timeout to be on the safe side. */
  29. #define TIMEOUT (2 * PASS_TIMEOUT * 2)
  30. #include "tst-skeleton-affinity.c"
  31. /* 0 if still running, 1 of stopping requested. */
  32. static int still_running;
  33. /* 0 if no scheduling failures, 1 if failures are encountered. */
  34. static int failed;
  35. static void *
  36. thread_burn_one_cpu (void *closure)
  37. {
  38. int cpu = (uintptr_t) closure;
  39. while (__atomic_load_n (&still_running, __ATOMIC_RELAXED) == 0)
  40. {
  41. int current = sched_getcpu ();
  42. if (sched_getcpu () != cpu)
  43. {
  44. printf ("error: Pinned thread %d ran on impossible cpu %d\n",
  45. cpu, current);
  46. __atomic_store_n (&failed, 1, __ATOMIC_RELAXED);
  47. /* Terminate early. */
  48. __atomic_store_n (&still_running, 1, __ATOMIC_RELAXED);
  49. }
  50. }
  51. return NULL;
  52. }
  53. struct burn_thread
  54. {
  55. pthread_t self;
  56. struct conf *conf;
  57. cpu_set_t *initial_set;
  58. cpu_set_t *seen_set;
  59. int thread;
  60. };
  61. static void *
  62. thread_burn_any_cpu (void *closure)
  63. {
  64. struct burn_thread *param = closure;
  65. /* Schedule this thread around a bit to see if it lands on another
  66. CPU. Run this for 2 seconds, once with sched_yield, once
  67. without. */
  68. for (int pass = 1; pass <= 2; ++pass)
  69. {
  70. time_t start = time (NULL);
  71. while (time (NULL) - start <= PASS_TIMEOUT)
  72. {
  73. int cpu = sched_getcpu ();
  74. if (cpu > param->conf->last_cpu
  75. || !CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (param->conf->set_size),
  76. param->initial_set))
  77. {
  78. printf ("error: Unpinned thread %d ran on impossible CPU %d\n",
  79. param->thread, cpu);
  80. __atomic_store_n (&failed, 1, __ATOMIC_RELAXED);
  81. return NULL;
  82. }
  83. CPU_SET_S (cpu, CPU_ALLOC_SIZE (param->conf->set_size),
  84. param->seen_set);
  85. if (pass == 1)
  86. sched_yield ();
  87. }
  88. }
  89. return NULL;
  90. }
  91. static void
  92. stop_and_join_threads (struct conf *conf, cpu_set_t *set,
  93. pthread_t *pinned_first, pthread_t *pinned_last,
  94. struct burn_thread *other_first,
  95. struct burn_thread *other_last)
  96. {
  97. __atomic_store_n (&still_running, 1, __ATOMIC_RELAXED);
  98. for (pthread_t *p = pinned_first; p < pinned_last; ++p)
  99. {
  100. int cpu = p - pinned_first;
  101. if (!CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), set))
  102. continue;
  103. int ret = pthread_join (*p, NULL);
  104. if (ret != 0)
  105. {
  106. printf ("error: Failed to join thread %d: %s\n", cpu, strerror (ret));
  107. fflush (stdout);
  108. /* Cannot shut down cleanly with threads still running. */
  109. abort ();
  110. }
  111. }
  112. for (struct burn_thread *p = other_first; p < other_last; ++p)
  113. {
  114. int cpu = p - other_first;
  115. if (!CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), set))
  116. continue;
  117. int ret = pthread_join (p->self, NULL);
  118. if (ret != 0)
  119. {
  120. printf ("error: Failed to join thread %d: %s\n", cpu, strerror (ret));
  121. fflush (stdout);
  122. /* Cannot shut down cleanly with threads still running. */
  123. abort ();
  124. }
  125. }
  126. }
  127. /* Tries to check that the initial set of CPUs is complete and that
  128. the main thread will not run on any other threads. */
  129. static bool
  130. early_test (struct conf *conf)
  131. {
  132. pthread_t *pinned_threads
  133. = calloc (conf->last_cpu + 1, sizeof (*pinned_threads));
  134. struct burn_thread *other_threads
  135. = calloc (conf->last_cpu + 1, sizeof (*other_threads));
  136. cpu_set_t *initial_set = CPU_ALLOC (conf->set_size);
  137. cpu_set_t *scratch_set = CPU_ALLOC (conf->set_size);
  138. if (pinned_threads == NULL || other_threads == NULL
  139. || initial_set == NULL || scratch_set == NULL)
  140. {
  141. puts ("error: Memory allocation failure");
  142. return false;
  143. }
  144. if (getaffinity (CPU_ALLOC_SIZE (conf->set_size), initial_set) < 0)
  145. {
  146. printf ("error: pthread_getaffinity_np failed: %m\n");
  147. return false;
  148. }
  149. for (int cpu = 0; cpu <= conf->last_cpu; ++cpu)
  150. {
  151. if (!CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), initial_set))
  152. continue;
  153. other_threads[cpu].conf = conf;
  154. other_threads[cpu].initial_set = initial_set;
  155. other_threads[cpu].thread = cpu;
  156. other_threads[cpu].seen_set = CPU_ALLOC (conf->set_size);
  157. if (other_threads[cpu].seen_set == NULL)
  158. {
  159. puts ("error: Memory allocation failure");
  160. return false;
  161. }
  162. CPU_ZERO_S (CPU_ALLOC_SIZE (conf->set_size),
  163. other_threads[cpu].seen_set);
  164. }
  165. pthread_attr_t attr;
  166. int ret = pthread_attr_init (&attr);
  167. if (ret != 0)
  168. {
  169. printf ("error: pthread_attr_init failed: %s\n", strerror (ret));
  170. return false;
  171. }
  172. /* Spawn a thread pinned to each available CPU. */
  173. for (int cpu = 0; cpu <= conf->last_cpu; ++cpu)
  174. {
  175. if (!CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), initial_set))
  176. continue;
  177. CPU_ZERO_S (CPU_ALLOC_SIZE (conf->set_size), scratch_set);
  178. CPU_SET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), scratch_set);
  179. ret = pthread_attr_setaffinity_np
  180. (&attr, CPU_ALLOC_SIZE (conf->set_size), scratch_set);
  181. if (ret != 0)
  182. {
  183. printf ("error: pthread_attr_setaffinity_np for CPU %d failed: %s\n",
  184. cpu, strerror (ret));
  185. stop_and_join_threads (conf, initial_set,
  186. pinned_threads, pinned_threads + cpu,
  187. NULL, NULL);
  188. return false;
  189. }
  190. ret = pthread_create (pinned_threads + cpu, &attr,
  191. thread_burn_one_cpu, (void *) (uintptr_t) cpu);
  192. if (ret != 0)
  193. {
  194. printf ("error: pthread_create for CPU %d failed: %s\n",
  195. cpu, strerror (ret));
  196. stop_and_join_threads (conf, initial_set,
  197. pinned_threads, pinned_threads + cpu,
  198. NULL, NULL);
  199. return false;
  200. }
  201. }
  202. /* Spawn another set of threads running on all CPUs. */
  203. for (int cpu = 0; cpu <= conf->last_cpu; ++cpu)
  204. {
  205. if (!CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), initial_set))
  206. continue;
  207. ret = pthread_create (&other_threads[cpu].self, NULL,
  208. thread_burn_any_cpu, other_threads + cpu);
  209. if (ret != 0)
  210. {
  211. printf ("error: pthread_create for thread %d failed: %s\n",
  212. cpu, strerror (ret));
  213. stop_and_join_threads (conf, initial_set,
  214. pinned_threads,
  215. pinned_threads + conf->last_cpu + 1,
  216. other_threads, other_threads + cpu);
  217. return false;
  218. }
  219. }
  220. /* Main thread. */
  221. struct burn_thread main_thread;
  222. main_thread.conf = conf;
  223. main_thread.initial_set = initial_set;
  224. main_thread.seen_set = scratch_set;
  225. main_thread.thread = -1;
  226. CPU_ZERO_S (CPU_ALLOC_SIZE (conf->set_size), main_thread.seen_set);
  227. thread_burn_any_cpu (&main_thread);
  228. stop_and_join_threads (conf, initial_set,
  229. pinned_threads,
  230. pinned_threads + conf->last_cpu + 1,
  231. other_threads, other_threads + conf->last_cpu + 1);
  232. printf ("info: Main thread ran on %d CPU(s) of %d available CPU(s)\n",
  233. CPU_COUNT_S (CPU_ALLOC_SIZE (conf->set_size), scratch_set),
  234. CPU_COUNT_S (CPU_ALLOC_SIZE (conf->set_size), initial_set));
  235. CPU_ZERO_S (CPU_ALLOC_SIZE (conf->set_size), scratch_set);
  236. for (int cpu = 0; cpu <= conf->last_cpu; ++cpu)
  237. {
  238. if (!CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), initial_set))
  239. continue;
  240. CPU_OR_S (CPU_ALLOC_SIZE (conf->set_size),
  241. scratch_set, scratch_set, other_threads[cpu].seen_set);
  242. CPU_FREE (other_threads[cpu].seen_set);
  243. }
  244. printf ("info: Other threads ran on %d CPU(s)\n",
  245. CPU_COUNT_S (CPU_ALLOC_SIZE (conf->set_size), scratch_set));;
  246. pthread_attr_destroy (&attr);
  247. CPU_FREE (scratch_set);
  248. CPU_FREE (initial_set);
  249. free (pinned_threads);
  250. free (other_threads);
  251. return failed == 0;
  252. }