test_futex.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. * Copyright (c) 2023 Agustina Arzille.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. * This module aims to test the full futex API. It is composed of 4
  18. * sub-tests, each one dedicated to testing particular features:
  19. *
  20. * - The first sub-test checks the basic futex API: waiting, waiting
  21. * and requeuing using regular futexes (local, non-robust, non-PI).
  22. *
  23. * - The second sub-test checks that shared futexes work correctly. It
  24. * creates a second task and a thread in it. This new thread will map
  25. * memory from the same object as the initial thread. This way, the
  26. * shared memory mapping can be used to place a futex there and check
  27. * that it works correctly.
  28. *
  29. * - The third sub-test checks that PI futexes allow waiting threads to
  30. * propagate their priorities to lower-priority threads. The initial
  31. * thread creates a futex, acquires it by writing its TID to it and
  32. * then creates 2 real-time threads that will wait on the futex. The
  33. * first thread then checks that it has inherited their priority and
  34. * is running boosted.
  35. *
  36. * - The fourth sub-test checks that robust futexes are handled correctly
  37. * when a thread exits while holding one or more of them. It creates the
  38. * linked list of futexes (plus its 'pending' one), sets the futex word
  39. * to its TID and the FUTEX_WAITERS bit and then exits without unlocking
  40. * them. Another thread waits for it to exit and then waits on the futexes,
  41. * checking that the FUTEX_OWNER_DIED bit has been set.
  42. */
  43. #include <stdio.h>
  44. #include <kern/futex.h>
  45. #include <kern/task.h>
  46. #include <kern/user.h>
  47. #include <test/test.h>
  48. #include <vm/map.h>
  49. #include <vm/object.h>
  50. struct test_futex_obj
  51. {
  52. struct futex_robust_list head;
  53. uint64_t prev; // Unused.
  54. };
  55. struct test_futex_data
  56. {
  57. struct thread *thr;
  58. struct futex_td td;
  59. struct test_futex_obj objs[3];
  60. };
  61. static void
  62. test_futex_helper (void *arg)
  63. {
  64. int error = futex_wait (arg, 0, 0, 0);
  65. test_assert_or (error == 0, error == EAGAIN);
  66. }
  67. static void
  68. test_futex_local (void *arg __unused)
  69. {
  70. void *addr;
  71. int error = vm_map_anon_alloc (&addr, vm_map_self (), 1);
  72. test_assert_zero (error);
  73. // Don't touch the address' contents so we may test page faults here.
  74. error = futex_wait (addr, 1, 0, 0);
  75. test_assert_eq (error, EAGAIN);
  76. error = futex_wait (addr, 0, FUTEX_TIMED, 10);
  77. test_assert_eq (error, ETIMEDOUT);
  78. struct thread *thread;
  79. struct thread_attr attr;
  80. thread_attr_init (&attr, "futex/1");
  81. thread_create (&thread, &attr, test_futex_helper, addr);
  82. test_thread_wait_state (thread, THREAD_SLEEPING);
  83. futex_wake (addr, FUTEX_MUTATE, 1);
  84. test_assert_eq (*(int *)addr, 1);
  85. thread_join (thread);
  86. *(int *)addr = 0;
  87. struct thread *thrs[4];
  88. for (size_t i = 0; i < ARRAY_SIZE (thrs); ++i)
  89. {
  90. char name[16];
  91. sprintf (name, "futex-L/%d", (int)(i + 1));
  92. thread_attr_init (&attr, name);
  93. error = thread_create (&thrs[i], &attr, test_futex_helper,
  94. (int *)addr + (i & 1));
  95. test_assert_eq (error, 0);
  96. test_thread_wait_state (thrs[i], THREAD_SLEEPING);
  97. }
  98. error = futex_requeue (addr, (int *)addr + 1, 0, FUTEX_BROADCAST);
  99. test_assert_zero (error);
  100. *(int *)addr = 1;
  101. error = futex_wake (addr, FUTEX_BROADCAST, 1);
  102. test_assert_zero (error);
  103. for (size_t i = 0; i < ARRAY_SIZE (thrs); ++i)
  104. thread_join (thrs[i]);
  105. *(int *)addr = 0;
  106. for (size_t i = 0; i < ARRAY_SIZE (thrs); ++i)
  107. {
  108. char name[16];
  109. sprintf (name, "futex-M/%d", (int)(i + 1));
  110. thread_attr_init (&attr, name);
  111. thread_create (&thrs[i], &attr, test_futex_helper,
  112. (int *)addr + (i & 1));
  113. test_thread_wait_state (thrs[i], THREAD_SLEEPING);
  114. }
  115. *(int *)addr = 1;
  116. error = futex_requeue (addr, (int *)addr + 1, 1, 0);
  117. test_assert_zero (error);
  118. error = futex_wake (addr, FUTEX_BROADCAST, 0);
  119. test_assert_zero (error);
  120. // Wake the remaining thread.
  121. error = futex_wake ((int *)addr + 1, 0, 0);
  122. test_assert_zero (error);
  123. for (size_t i = 0; i < ARRAY_SIZE (thrs); ++i)
  124. thread_join (thrs[i]);
  125. }
  126. static void
  127. test_futex_shared_helper (void *arg)
  128. {
  129. struct vm_map_entry *entry = arg;
  130. uintptr_t start = PAGE_SIZE * 100;
  131. int flags = VM_MAP_FLAGS (VM_PROT_RDWR, VM_PROT_RDWR, VM_INHERIT_SHARE,
  132. VM_ADV_DEFAULT, 0);
  133. int error = vm_map_enter (vm_map_self (), &start, PAGE_SIZE,
  134. flags, entry->object, entry->offset);
  135. test_assert_zero (error);
  136. void *addr = (void *)start;
  137. error = futex_wait (addr, 0, FUTEX_SHARED, 0);
  138. test_assert_or (error == 0, error == EAGAIN);
  139. test_assert_eq (*(int *)addr, 1);
  140. }
  141. static void
  142. test_futex_shared (void *arg __unused)
  143. {
  144. void *addr;
  145. int error = vm_map_anon_alloc (&addr, vm_map_self (), 1);
  146. test_assert_zero (error);
  147. _Auto entry = vm_map_find (vm_map_self (), (uintptr_t)addr);
  148. test_assert_nonnull (entry);
  149. struct thread *thr;
  150. error = test_util_create_thr (&thr, test_futex_shared_helper,
  151. entry, "futex-sh-fork");
  152. test_thread_wait_state (thr, THREAD_SLEEPING);
  153. error = futex_wake (addr, FUTEX_MUTATE | FUTEX_SHARED, 1);
  154. test_assert_zero (error);
  155. thread_join (thr);
  156. vm_map_entry_put (entry);
  157. }
  158. static void
  159. test_futex_pi_helper (void *arg)
  160. {
  161. int *futex = arg;
  162. int error = futex_wait (futex, (*futex & FUTEX_TID_MASK), FUTEX_PI, 0);
  163. if (! error)
  164. test_assert_eq ((*futex & FUTEX_TID_MASK),
  165. (uint32_t)thread_id (thread_self ()));
  166. else
  167. test_assert_eq (error, EAGAIN);
  168. }
  169. static bool
  170. test_futex_pi_wait_sched (void)
  171. {
  172. for (int i = 0; i < 100; ++i)
  173. if (thread_real_sched_policy (thread_self ()) == THREAD_SCHED_POLICY_FIFO)
  174. return (true);
  175. else
  176. thread_yield ();
  177. return (false);
  178. }
  179. static void
  180. test_futex_pi (void *arg __unused)
  181. {
  182. void *addr;
  183. int error = vm_map_anon_alloc (&addr, vm_map_self (), 1);
  184. test_assert_zero (error);
  185. int *futex = addr;
  186. *futex = thread_id (thread_self ());
  187. struct thread *thrs[2];
  188. struct thread_attr attr;
  189. thread_attr_init (&attr, "futex-pi/1");
  190. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  191. thread_attr_set_priority (&attr, THREAD_SCHED_RT_PRIO_MAX / 2);
  192. error = thread_create (&thrs[0], &attr, test_futex_pi_helper, futex);
  193. test_assert_eq (error, 0);
  194. thread_attr_init (&attr, "futex-pi/2");
  195. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  196. thread_attr_set_priority (&attr, THREAD_SCHED_RT_PRIO_MAX / 2);
  197. error = thread_create (&thrs[1], &attr, test_futex_pi_helper, futex);
  198. test_assert_zero (error);
  199. test_thread_wait_state (thrs[0], THREAD_SLEEPING);
  200. test_thread_wait_state (thrs[1], THREAD_SLEEPING);
  201. test_assert_eq (test_futex_pi_wait_sched (), true);
  202. error = futex_wake (futex, FUTEX_PI | FUTEX_BROADCAST | FUTEX_MUTATE, 0);
  203. test_assert_zero (error);
  204. thread_join (thrs[0]);
  205. thread_join (thrs[1]);
  206. }
  207. static void
  208. test_futex_robust_helper (void *arg)
  209. {
  210. struct test_futex_data *data = arg;
  211. _Auto td = &data->td;
  212. int val = thread_id (thread_self ()) | FUTEX_WAITERS;
  213. futex_td_init (td);
  214. for (size_t i = 0; i < ARRAY_SIZE (data->objs); ++i)
  215. data->objs[i].head.futex = val;
  216. td->pending = &data->objs[0].head;
  217. data->objs[1].head.next = (uint64_t)(uintptr_t)&data->objs[2].head;
  218. data->objs[2].head.next = 0;
  219. td->list = &data->objs[1].head;
  220. test_thread_wait_state (data->thr, THREAD_SLEEPING);
  221. thread_self()->futex_td = td;
  222. }
  223. static void
  224. test_futex_robust (void *arg __unused)
  225. {
  226. void *addr;
  227. int error = vm_map_anon_alloc (&addr, vm_map_self (), 1);
  228. test_assert_zero (error);
  229. struct test_futex_data *data = addr;
  230. data->thr = thread_self ();
  231. struct thread *thr;
  232. struct thread_attr attr;
  233. thread_attr_init (&attr, "futex-robust/1");
  234. error = thread_create (&thr, &attr, test_futex_robust_helper, addr);
  235. test_assert_zero (error);
  236. error = futex_wait (&data->objs[2].head.futex, FUTEX_WAITERS |
  237. thread_id (thr), 0, 0);
  238. test_assert_or (error == 0, error == EAGAIN);
  239. thread_join (thr);
  240. for (size_t i = 0; i < ARRAY_SIZE (data->objs); ++i)
  241. test_assert_ne ((data->objs[i].head.futex & FUTEX_OWNER_DIED), 0);
  242. }
  243. TEST_DEFERRED (futex)
  244. {
  245. struct thread *thread;
  246. int error;
  247. error = test_util_create_thr (&thread, test_futex_local, NULL, "futex");
  248. test_assert_zero (error);
  249. thread_join (thread);
  250. error = test_util_create_thr (&thread, test_futex_shared, NULL, "futex-sh");
  251. test_assert_zero (error);
  252. thread_join (thread);
  253. error = test_util_create_thr (&thread, test_futex_pi, NULL, "futex-pi");
  254. test_assert_zero (error);
  255. thread_join (thread);
  256. error = test_util_create_thr (&thread, test_futex_robust,
  257. NULL, "futex-robust");
  258. test_assert_zero (error);
  259. thread_join (thread);
  260. return (TEST_OK);
  261. }