test_mutex_pi.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /*
  2. * Copyright (c) 2017-2019 Richard Braun.
  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. *
  18. * This test module is a stress test, expected to never terminate, of
  19. * priority inheritance with mutexes. It creates a priority inheritance
  20. * tree by starting multiple threads manipulating multiple mutexes.
  21. *
  22. * Here is one intended state of the priority inheritance tree :
  23. *
  24. *
  25. * C->M2-+
  26. * |
  27. * D--+ +->B->M1->A
  28. * | |
  29. * +->M3-+
  30. * |
  31. * E--+
  32. *
  33. *
  34. * M1,M2,M3,etc... are mutexes and A,B,C,etc... are threads. The thread
  35. * priorities p(thread) are ordered such that p(A) < p(B) < p(C) etc...
  36. * An arrow from a mutex to a thread indicates ownership, such that
  37. * M1->A means that A owns M1. An arrow from a thread to a mutex indicates
  38. * waiting, such that B->M1 means that B is waiting for M1 to be unlocked.
  39. *
  40. * In addition, thread B is actually many threads, each terminating after
  41. * unlocking their mutexes. Also, the priority of thread C is regularly
  42. * increased to p(E) + 1 and later restored to p(C).
  43. *
  44. * Here is the list of all the cases this test must cover :
  45. * - Priority inheritance: All threads can get their real priority boosted.
  46. * C can be boosted if it owns M2 and B waits for it while inheriting
  47. * from E, and E can be boosted when p(C) is temporarily increased.
  48. * - Return to normal priority: after releasing all locks, a thread must
  49. * have reset its real priority to its user priority.
  50. * - Priority changes: When the priority of C is increased, that priority
  51. * must take precedence over all others.
  52. * - Thread destruction resilience: Check that priority propagation never
  53. * accesses a destroyed thread.
  54. *
  55. * Note that this test doesn't check that priority propagation correctly
  56. * adjusts the top priority after lowering the priority of thread C back
  57. * to p(C).
  58. *
  59. * In order to artificially create priority inversions, all threads run on
  60. * separate processors, making this test require 5 processors.
  61. *
  62. * The test should output a couple of messages about thread priorities
  63. * being boosted, and then frequent updates from each thread to show
  64. * they're all making progress. Thread B suffers from contention the most
  65. * so its report frequency should be lower. Thread A suffers from contention
  66. * the least and should be the most frequent to report progress. Because of
  67. * contention from B, D and E on M3, D rarely gets boosted. The reason is
  68. * that, when B owns the mutex, E is likely to wait on M3 soon enough that
  69. * it will be awoken before D, preventing the conditions for priority
  70. * inheritance to occur.
  71. *
  72. * Note that the test uses regular mutexes instead of real-time mutexes,
  73. * so that its behaviour can be analyzed for both types depending on
  74. * build options.
  75. */
  76. #include <stddef.h>
  77. #include <stdio.h>
  78. #include <string.h>
  79. #include <kern/cpumap.h>
  80. #include <kern/init.h>
  81. #include <kern/mutex.h>
  82. #include <kern/panic.h>
  83. #include <kern/syscnt.h>
  84. #include <kern/thread.h>
  85. #include <kern/turnstile.h>
  86. #include <test/test.h>
  87. #define TEST_MIN_CPUS 5
  88. #define TEST_PRIO_A (THREAD_SCHED_RT_PRIO_MIN + 1)
  89. #define TEST_PRIO_B (TEST_PRIO_A + 1)
  90. #define TEST_PRIO_C (TEST_PRIO_B + 1)
  91. #define TEST_PRIO_D (TEST_PRIO_C + 1)
  92. #define TEST_PRIO_E (TEST_PRIO_D + 1)
  93. #define TEST_NR_LOCK_LOOPS 500
  94. #define TEST_NR_CONSUME_CPU_LOOPS 10000000
  95. static struct mutex test_mutex_1;
  96. static struct mutex test_mutex_2;
  97. static struct mutex test_mutex_3;
  98. static const char*
  99. test_thread_from_priority (uint16_t priority)
  100. {
  101. switch (priority)
  102. {
  103. case TEST_PRIO_A:
  104. return ("a");
  105. case TEST_PRIO_B:
  106. return ("b");
  107. case TEST_PRIO_C:
  108. return ("c");
  109. case TEST_PRIO_D:
  110. return ("d");
  111. case TEST_PRIO_E:
  112. return ("e");
  113. case TEST_PRIO_E + 1:
  114. return ("e+");
  115. default:
  116. panic ("invalid priority %u", priority);
  117. }
  118. }
  119. static char
  120. test_get_name (void)
  121. {
  122. const char *name = thread_self()->name;
  123. return (name[strlen (name) - 1]);
  124. }
  125. static void
  126. test_delay (void)
  127. {
  128. volatile unsigned int i;
  129. /*
  130. * Put the thread to sleep to make some CPU time available, and then
  131. * busy-wait to avoid synchronizing all threads on the clock tick.
  132. */
  133. thread_delay (1, false);
  134. for (i = 0; i < TEST_NR_CONSUME_CPU_LOOPS; i++);
  135. }
  136. static void
  137. test_check_initial_priority (void)
  138. {
  139. struct thread *thread = thread_self();
  140. uint16_t user_priority = thread_user_priority (thread),
  141. real_priority = thread_real_priority (thread);
  142. if (user_priority != real_priority)
  143. panic ("%c: invalid initial priority %hu",
  144. test_get_name (), real_priority);
  145. }
  146. static void
  147. test_for_priority_boosted (uint16_t *highest_priority)
  148. {
  149. struct thread *thread = thread_self ();
  150. struct turnstile_td *td = thread_turnstile_td (thread);
  151. turnstile_td_lock (td);
  152. uint16_t user_priority = thread_user_priority (thread),
  153. real_priority = thread_real_priority (thread);
  154. if (user_priority != real_priority)
  155. {
  156. if (user_priority > real_priority)
  157. panic ("%c: invalid real priority: %hu (boosted:%u)",
  158. test_get_name (), real_priority, thread->boosted);
  159. if (real_priority > *highest_priority)
  160. {
  161. printf ("%c: real priority boosted to %s\n",
  162. test_get_name(), test_thread_from_priority (real_priority) );
  163. *highest_priority = real_priority;
  164. }
  165. }
  166. turnstile_td_unlock (td);
  167. }
  168. static void
  169. test_for_priority_deboosted (void)
  170. {
  171. struct thread *thread = thread_self ();
  172. struct turnstile_td *td = thread_turnstile_td (thread);
  173. turnstile_td_lock (td);
  174. uint16_t user_priority = thread_user_priority (thread),
  175. real_priority = thread_real_priority (thread);
  176. if (user_priority != real_priority)
  177. panic ("%c: real priority not reset (boosted:%d)",
  178. test_get_name (), thread->boosted);
  179. turnstile_td_unlock (td);
  180. }
  181. static void
  182. test_report_progress (uint32_t i)
  183. {
  184. printf ("%c:%u ", test_get_name (), i);
  185. }
  186. static void
  187. test_a (void *arg __unused)
  188. {
  189. test_check_initial_priority ();
  190. uint16_t highest_priority = 0;
  191. for (uint32_t i = 1 ; ; i++)
  192. {
  193. for (uint32_t j = 0; j < TEST_NR_LOCK_LOOPS; j++)
  194. {
  195. mutex_lock (&test_mutex_1);
  196. test_delay ();
  197. test_for_priority_boosted (&highest_priority);
  198. mutex_unlock (&test_mutex_1);
  199. test_for_priority_deboosted();
  200. test_delay ();
  201. }
  202. test_report_progress (i);
  203. }
  204. }
  205. static void
  206. test_b (void *arg)
  207. {
  208. test_check_initial_priority ();
  209. mutex_lock (&test_mutex_3);
  210. mutex_lock (&test_mutex_2);
  211. mutex_lock (&test_mutex_1);
  212. test_delay ();
  213. test_for_priority_boosted (arg);
  214. mutex_unlock (&test_mutex_1);
  215. test_delay ();
  216. mutex_unlock (&test_mutex_2);
  217. test_delay ();
  218. mutex_unlock (&test_mutex_3);
  219. /*
  220. * It would be better if the thread could immediately terminate, but
  221. * it's also the thread that locks multiple mutexes, so make sure it
  222. * was correctly deboosted. This should be cheap enough to not matter
  223. * much.
  224. */
  225. test_for_priority_deboosted ();
  226. }
  227. static void
  228. test_manage_b (void *arg __unused)
  229. {
  230. struct cpumap *cpumap;
  231. int error = cpumap_create (&cpumap);
  232. test_assert_zero (error);
  233. cpumap_zero (cpumap);
  234. cpumap_set (cpumap, 1);
  235. struct thread_attr attr;
  236. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_b");
  237. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  238. thread_attr_set_priority (&attr, TEST_PRIO_B);
  239. thread_attr_set_cpumap (&attr, cpumap);
  240. cpumap_destroy (cpumap);
  241. uint16_t highest_priority = 0;
  242. for (uint32_t i = 1 ; ; i++)
  243. {
  244. for (uint32_t j = 0; j < TEST_NR_LOCK_LOOPS; j++)
  245. {
  246. struct thread *thread_b;
  247. error = thread_create (&thread_b, &attr, test_b, &highest_priority);
  248. test_assert_zero (error);
  249. thread_join (thread_b);
  250. test_delay ();
  251. }
  252. printf ("b:%u ", i);
  253. syscnt_info ("thread_boosts", log_stream_info ());
  254. }
  255. }
  256. static void
  257. test_c (void *arg __unused)
  258. {
  259. test_check_initial_priority ();
  260. uint16_t highest_priority = 0;
  261. for (uint32_t i = 1 ; ; i++)
  262. {
  263. for (uint32_t j = 0; j < TEST_NR_LOCK_LOOPS; j++)
  264. {
  265. mutex_lock (&test_mutex_2);
  266. test_delay ();
  267. test_for_priority_boosted (&highest_priority);
  268. mutex_unlock (&test_mutex_2);
  269. test_for_priority_deboosted();
  270. test_delay ();
  271. }
  272. test_report_progress (i);
  273. }
  274. }
  275. static void
  276. test_chprio_c (void *arg)
  277. {
  278. struct thread *thread_c = arg;
  279. test_delay ();
  280. while (1)
  281. {
  282. thread_setscheduler (thread_c, THREAD_SCHED_POLICY_FIFO,
  283. TEST_PRIO_E + 1);
  284. thread_setscheduler (thread_c, THREAD_SCHED_POLICY_FIFO,
  285. TEST_PRIO_C);
  286. }
  287. }
  288. static void
  289. test_d (void *arg __unused)
  290. {
  291. test_check_initial_priority ();
  292. uint16_t highest_priority = 0;
  293. for (uint32_t i = 1 ; ; i++)
  294. {
  295. for (uint32_t j = 0; j < TEST_NR_LOCK_LOOPS; j++)
  296. {
  297. mutex_lock (&test_mutex_3);
  298. test_delay ();
  299. test_for_priority_boosted (&highest_priority);
  300. mutex_unlock (&test_mutex_3);
  301. test_for_priority_deboosted ();
  302. test_delay ();
  303. }
  304. test_report_progress (i);
  305. }
  306. }
  307. static void
  308. test_e (void *arg __unused)
  309. {
  310. test_check_initial_priority ();
  311. uint16_t highest_priority = 0;
  312. for (uint32_t i = 1 ; ; i++)
  313. {
  314. for (uint32_t j = 0; j < TEST_NR_LOCK_LOOPS; j++)
  315. {
  316. mutex_lock (&test_mutex_3);
  317. test_delay ();
  318. test_for_priority_boosted (&highest_priority);
  319. mutex_unlock (&test_mutex_3);
  320. test_for_priority_deboosted();
  321. test_delay ();
  322. }
  323. test_report_progress (i);
  324. }
  325. }
  326. TEST_INLINE (mutex_pi)
  327. {
  328. if (cpu_count () < TEST_MIN_CPUS)
  329. {
  330. log_err ("test: at least %u processors are required", TEST_MIN_CPUS);
  331. return (TEST_SKIPPED);
  332. }
  333. mutex_init (&test_mutex_1);
  334. mutex_init (&test_mutex_2);
  335. mutex_init (&test_mutex_3);
  336. struct cpumap *cpumap;
  337. int error = cpumap_create (&cpumap);
  338. test_assert_zero (error);
  339. cpumap_zero (cpumap);
  340. cpumap_set (cpumap, 0);
  341. struct thread_attr attr;
  342. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_a");
  343. thread_attr_set_detached (&attr);
  344. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  345. thread_attr_set_priority (&attr, TEST_PRIO_A);
  346. thread_attr_set_cpumap (&attr, cpumap);
  347. struct thread *thread;
  348. error = thread_create (&thread, &attr, test_a, NULL);
  349. test_assert_zero (error);
  350. cpumap_zero (cpumap);
  351. cpumap_set (cpumap, 1);
  352. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_manage_b");
  353. thread_attr_set_detached (&attr);
  354. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  355. thread_attr_set_priority (&attr, TEST_PRIO_B);
  356. thread_attr_set_cpumap (&attr, cpumap);
  357. error = thread_create (&thread, &attr, test_manage_b, NULL);
  358. test_assert_zero (error);
  359. cpumap_zero (cpumap);
  360. cpumap_set (cpumap, 2);
  361. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_c");
  362. thread_attr_set_detached (&attr);
  363. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  364. thread_attr_set_priority (&attr, TEST_PRIO_C);
  365. thread_attr_set_cpumap (&attr, cpumap);
  366. error = thread_create (&thread, &attr, test_c, NULL);
  367. test_assert_zero (error);
  368. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_chprio_c");
  369. thread_attr_set_detached (&attr);
  370. error = thread_create (&thread, &attr, test_chprio_c, thread);
  371. test_assert_zero (error);
  372. cpumap_zero (cpumap);
  373. cpumap_set (cpumap, 3);
  374. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_d");
  375. thread_attr_set_detached (&attr);
  376. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  377. thread_attr_set_priority (&attr, TEST_PRIO_D);
  378. thread_attr_set_cpumap (&attr, cpumap);
  379. error = thread_create (&thread, &attr, test_d, NULL);
  380. test_assert_zero (error);
  381. cpumap_zero (cpumap);
  382. cpumap_set (cpumap, 4);
  383. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_e");
  384. thread_attr_set_detached (&attr);
  385. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  386. thread_attr_set_priority (&attr, TEST_PRIO_E);
  387. thread_attr_set_cpumap (&attr, cpumap);
  388. error = thread_create (&thread, &attr, test_e, NULL);
  389. test_assert_zero (error);
  390. cpumap_destroy (cpumap);
  391. return (TEST_RUNNING);
  392. }