test_mutex_pi.c 14 KB

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