test_mutex_pi.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  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/error.h>
  81. #include <kern/init.h>
  82. #include <kern/mutex.h>
  83. #include <kern/panic.h>
  84. #include <kern/syscnt.h>
  85. #include <kern/thread.h>
  86. #include <kern/turnstile.h>
  87. #include <test/test.h>
  88. #define TEST_MIN_CPUS 5
  89. #define TEST_PRIO_A (THREAD_SCHED_RT_PRIO_MIN + 1)
  90. #define TEST_PRIO_B (TEST_PRIO_A + 1)
  91. #define TEST_PRIO_C (TEST_PRIO_B + 1)
  92. #define TEST_PRIO_D (TEST_PRIO_C + 1)
  93. #define TEST_PRIO_E (TEST_PRIO_D + 1)
  94. #define TEST_NR_LOCK_LOOPS 500
  95. #define TEST_NR_CONSUME_CPU_LOOPS 10000000
  96. static struct mutex test_mutex_1;
  97. static struct mutex test_mutex_2;
  98. static struct mutex test_mutex_3;
  99. static const char*
  100. test_thread_from_priority (uint16_t priority)
  101. {
  102. switch (priority)
  103. {
  104. case TEST_PRIO_A:
  105. return ("a");
  106. case TEST_PRIO_B:
  107. return ("b");
  108. case TEST_PRIO_C:
  109. return ("c");
  110. case TEST_PRIO_D:
  111. return ("d");
  112. case TEST_PRIO_E:
  113. return ("e");
  114. case TEST_PRIO_E + 1:
  115. return ("e+");
  116. default:
  117. panic ("invalid priority %u", priority);
  118. }
  119. }
  120. static char
  121. test_get_name (void)
  122. {
  123. const char *name = thread_self()->name;
  124. return (name[strlen (name) - 1]);
  125. }
  126. static void
  127. test_delay (void)
  128. {
  129. volatile unsigned int i;
  130. /*
  131. * Put the thread to sleep to make some CPU time available, and then
  132. * busy-wait to avoid synchronizing all threads on the clock tick.
  133. */
  134. thread_delay (1, false);
  135. for (i = 0; i < TEST_NR_CONSUME_CPU_LOOPS; i++);
  136. }
  137. static void
  138. test_check_initial_priority (void)
  139. {
  140. struct thread *thread = thread_self();
  141. uint16_t user_priority = thread_user_priority (thread),
  142. real_priority = thread_real_priority (thread);
  143. if (user_priority != real_priority)
  144. panic ("%c: invalid initial priority %hu",
  145. test_get_name (), real_priority);
  146. }
  147. static void
  148. test_for_priority_boosted (uint16_t *highest_priority)
  149. {
  150. unsigned short user_priority, real_priority;
  151. struct thread *thread = thread_self ();
  152. struct turnstile_td *td = thread_turnstile_td (thread);
  153. turnstile_td_lock (td);
  154. uint16_t user_priority = thread_user_priority (thread),
  155. real_priority = thread_real_priority (thread);
  156. if (user_priority != real_priority)
  157. {
  158. if (user_priority > real_priority)
  159. panic ("%c: invalid real priority: %hu (boosted:%u)",
  160. test_get_name (), real_priority, thread->boosted);
  161. if (real_priority > *highest_priority)
  162. {
  163. printf ("%c: real priority boosted to %s\n",
  164. test_get_name(), test_thread_from_priority (real_priority) );
  165. *highest_priority = real_priority;
  166. }
  167. }
  168. turnstile_td_unlock (td);
  169. }
  170. static void
  171. test_for_priority_deboosted (void)
  172. {
  173. struct thread *thread = thread_self ();
  174. struct turnstile_td *td = thread_turnstile_td (thread);
  175. turnstile_td_lock (td);
  176. uint16_t user_priority = thread_user_priority (thread),
  177. real_priority = thread_real_priority (thread);
  178. if (user_priority != real_priority)
  179. panic ("%c: real priority not reset (boosted:%d)",
  180. test_get_name (), thread->boosted);
  181. turnstile_td_unlock (td);
  182. }
  183. static void
  184. test_report_progress (uint32_t i)
  185. {
  186. printf ("%c:%u ", test_get_name (), i);
  187. }
  188. static void
  189. test_a (void *arg __unused)
  190. {
  191. test_check_initial_priority ();
  192. uint16_t highest_priority = 0;
  193. for (uint32_t i = 1 ; ; i++)
  194. {
  195. for (uint32_t j = 0; j < TEST_NR_LOCK_LOOPS; j++)
  196. {
  197. mutex_lock (&test_mutex_1);
  198. test_delay ();
  199. test_for_priority_boosted (&highest_priority);
  200. mutex_unlock (&test_mutex_1);
  201. test_for_priority_deboosted();
  202. test_delay ();
  203. }
  204. test_report_progress (i);
  205. }
  206. }
  207. static void
  208. test_b (void *arg)
  209. {
  210. test_check_initial_priority ();
  211. mutex_lock (&test_mutex_3);
  212. mutex_lock (&test_mutex_2);
  213. mutex_lock (&test_mutex_1);
  214. test_delay ();
  215. test_for_priority_boosted (arg);
  216. mutex_unlock (&test_mutex_1);
  217. test_delay ();
  218. mutex_unlock (&test_mutex_2);
  219. test_delay ();
  220. mutex_unlock (&test_mutex_3);
  221. /*
  222. * It would be better if the thread could immediately terminate, but
  223. * it's also the thread that locks multiple mutexes, so make sure it
  224. * was correctly deboosted. This should be cheap enough to not matter
  225. * much.
  226. */
  227. test_for_priority_deboosted ();
  228. }
  229. static void
  230. test_manage_b (void *arg __unused)
  231. {
  232. struct cpumap *cpumap;
  233. int error = cpumap_create (&cpumap);
  234. error_check (error, "cpumap_create");
  235. cpumap_zero (cpumap);
  236. cpumap_set (cpumap, 1);
  237. struct thread_attr attr;
  238. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_b");
  239. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  240. thread_attr_set_priority (&attr, TEST_PRIO_B);
  241. thread_attr_set_cpumap (&attr, cpumap);
  242. cpumap_destroy (cpumap);
  243. uint16_t highest_priority = 0;
  244. for (uint32_t i = 1 ; ; i++)
  245. {
  246. for (uint32_t j = 0; j < TEST_NR_LOCK_LOOPS; j++)
  247. {
  248. struct thread *thread_b;
  249. error = thread_create (&thread_b, &attr, test_b, &highest_priority);
  250. error_check (error, "thread_create");
  251. thread_join (thread_b);
  252. test_delay ();
  253. }
  254. printf ("b:%u ", i);
  255. syscnt_info ("thread_boosts", log_info);
  256. }
  257. }
  258. static void
  259. test_c (void *arg __unused)
  260. {
  261. test_check_initial_priority ();
  262. uint16_t highest_priority = 0;
  263. for (uint32_t i = 1 ; ; i++)
  264. {
  265. for (uint32_t j = 0; j < TEST_NR_LOCK_LOOPS; j++)
  266. {
  267. mutex_lock (&test_mutex_2);
  268. test_delay ();
  269. test_for_priority_boosted (&highest_priority);
  270. mutex_unlock (&test_mutex_2);
  271. test_for_priority_deboosted();
  272. test_delay ();
  273. }
  274. test_report_progress (i);
  275. }
  276. }
  277. static void
  278. test_chprio_c (void *arg)
  279. {
  280. struct thread *thread_c = arg;
  281. test_delay ();
  282. while (1)
  283. {
  284. thread_setscheduler (thread_c, THREAD_SCHED_POLICY_FIFO,
  285. TEST_PRIO_E + 1);
  286. thread_setscheduler (thread_c, THREAD_SCHED_POLICY_FIFO,
  287. TEST_PRIO_C);
  288. }
  289. }
  290. static void
  291. test_d (void *arg __unused)
  292. {
  293. test_check_initial_priority ();
  294. uint16_t highest_priority = 0;
  295. for (uint32_t i = 1 ; ; i++)
  296. {
  297. for (uint32_t j = 0; j < TEST_NR_LOCK_LOOPS; j++)
  298. {
  299. mutex_lock (&test_mutex_3);
  300. test_delay ();
  301. test_for_priority_boosted (&highest_priority);
  302. mutex_unlock (&test_mutex_3);
  303. test_for_priority_deboosted ();
  304. test_delay ();
  305. }
  306. test_report_progress (i);
  307. }
  308. }
  309. static void
  310. test_e (void *arg __unused)
  311. {
  312. test_check_initial_priority ();
  313. uint16_t highest_priority = 0;
  314. for (uint32_t i = 1 ; ; i++)
  315. {
  316. for (uint32_t j = 0; j < TEST_NR_LOCK_LOOPS; j++)
  317. {
  318. mutex_lock (&test_mutex_3);
  319. test_delay ();
  320. test_for_priority_boosted (&highest_priority);
  321. mutex_unlock (&test_mutex_3);
  322. test_for_priority_deboosted();
  323. test_delay ();
  324. }
  325. test_report_progress (i);
  326. }
  327. }
  328. TEST_INLINE (mutex_pi)
  329. {
  330. if (cpu_count () < TEST_MIN_CPUS)
  331. {
  332. log_err ("test: at least %u processors are required", TEST_MIN_CPUS);
  333. return (TEST_SKIPPED);
  334. }
  335. mutex_init (&test_mutex_1);
  336. mutex_init (&test_mutex_2);
  337. mutex_init (&test_mutex_3);
  338. struct cpumap *cpumap;
  339. int error = cpumap_create (&cpumap);
  340. error_check (error, "cpumap_create");
  341. cpumap_zero (cpumap);
  342. cpumap_set (cpumap, 0);
  343. struct thread_attr attr;
  344. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_a");
  345. thread_attr_set_detached (&attr);
  346. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  347. thread_attr_set_priority (&attr, TEST_PRIO_A);
  348. thread_attr_set_cpumap (&attr, cpumap);
  349. struct thread *thread;
  350. error = thread_create (&thread, &attr, test_a, NULL);
  351. error_check (error, "thread_create");
  352. cpumap_zero (cpumap);
  353. cpumap_set (cpumap, 1);
  354. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_manage_b");
  355. thread_attr_set_detached (&attr);
  356. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  357. thread_attr_set_priority (&attr, TEST_PRIO_B);
  358. thread_attr_set_cpumap (&attr, cpumap);
  359. error = thread_create (&thread, &attr, test_manage_b, NULL);
  360. error_check (error, "thread_create");
  361. cpumap_zero (cpumap);
  362. cpumap_set (cpumap, 2);
  363. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_c");
  364. thread_attr_set_detached (&attr);
  365. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  366. thread_attr_set_priority (&attr, TEST_PRIO_C);
  367. thread_attr_set_cpumap (&attr, cpumap);
  368. error = thread_create (&thread, &attr, test_c, NULL);
  369. error_check (error, "thread_create");
  370. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_chprio_c");
  371. thread_attr_set_detached (&attr);
  372. error = thread_create (&thread, &attr, test_chprio_c, thread);
  373. error_check (error, "thread_create");
  374. cpumap_zero (cpumap);
  375. cpumap_set (cpumap, 3);
  376. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_d");
  377. thread_attr_set_detached (&attr);
  378. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  379. thread_attr_set_priority (&attr, TEST_PRIO_D);
  380. thread_attr_set_cpumap (&attr, cpumap);
  381. error = thread_create (&thread, &attr, test_d, NULL);
  382. error_check (error, "thread_create");
  383. cpumap_zero (cpumap);
  384. cpumap_set (cpumap, 4);
  385. thread_attr_init (&attr, THREAD_KERNEL_PREFIX "test_e");
  386. thread_attr_set_detached (&attr);
  387. thread_attr_set_policy (&attr, THREAD_SCHED_POLICY_FIFO);
  388. thread_attr_set_priority (&attr, TEST_PRIO_E);
  389. thread_attr_set_cpumap (&attr, cpumap);
  390. error = thread_create (&thread, &attr, test_e, NULL);
  391. error_check (error, "thread_create");
  392. cpumap_destroy (cpumap);
  393. return (TEST_RUNNING);
  394. }