1 /*
2 * Copyright (c) 2019, 2020 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/ztest.h>
8 #include <zephyr/irq_offload.h>
9 #include <zephyr/sys/mutex.h>
10
11
12 /**
13 * @brief Tests for Kernel Futex objects
14 * @defgroup kernel_futex_tests Futex
15 * @ingroup all_tests
16 * @{
17 * @}
18 */
19
20 /* Macro declarations */
21 #define TOTAL_THREADS_WAITING (3)
22 #define PRIO_WAIT (CONFIG_ZTEST_THREAD_PRIORITY - 1)
23 #define PRIO_WAKE (CONFIG_ZTEST_THREAD_PRIORITY - 2)
24 #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
25 #define PRIORITY 5
26
27 /******************************************************************************/
28 /* declaration */
29 K_THREAD_STACK_DEFINE(stack_1, STACK_SIZE);
30 K_THREAD_STACK_DEFINE(futex_wake_stack, STACK_SIZE);
31 K_THREAD_STACK_ARRAY_DEFINE(multiple_stack,
32 TOTAL_THREADS_WAITING, STACK_SIZE);
33 K_THREAD_STACK_ARRAY_DEFINE(multiple_wake_stack,
34 TOTAL_THREADS_WAITING, STACK_SIZE);
35
36 ZTEST_BMEM int woken;
37 ZTEST_BMEM int timeout;
38 ZTEST_BMEM int index[TOTAL_THREADS_WAITING];
39 ZTEST_BMEM struct k_futex simple_futex;
40 ZTEST_BMEM struct k_futex multiple_futex[TOTAL_THREADS_WAITING];
41 struct k_futex no_access_futex;
42 ZTEST_BMEM atomic_t not_a_futex;
43 ZTEST_BMEM struct sys_mutex also_not_a_futex;
44
45 struct k_thread futex_tid;
46 struct k_thread futex_wake_tid;
47 struct k_thread multiple_tid[TOTAL_THREADS_WAITING];
48 struct k_thread multiple_wake_tid[TOTAL_THREADS_WAITING];
49
50 /******************************************************************************/
51 /* Helper functions */
futex_isr_wake(const void * futex)52 static void futex_isr_wake(const void *futex)
53 {
54 k_futex_wake((struct k_futex *)futex, false);
55 }
56
futex_wake_from_isr(struct k_futex * futex)57 static void futex_wake_from_isr(struct k_futex *futex)
58 {
59 irq_offload(futex_isr_wake, (const void *)futex);
60 }
61
62 /* test futex wait, no futex wake */
futex_wait_task(void * p1,void * p2,void * p3)63 static void futex_wait_task(void *p1, void *p2, void *p3)
64 {
65 int32_t ret_value;
66 k_ticks_t time_val = *(int *)p1;
67
68 zassert_true(time_val >= (int)K_TICKS_FOREVER,
69 "invalid timeout parameter");
70
71 ret_value = k_futex_wait(&simple_futex,
72 atomic_get(&simple_futex.val), K_TICKS(time_val));
73
74 switch (time_val) {
75 case K_TICKS_FOREVER:
76 zassert_true(ret_value == 0,
77 "k_futex_wait failed when it shouldn't have");
78 zassert_false(ret_value == 0,
79 "futex wait task wakeup when it shouldn't have");
80 break;
81 case 0:
82 zassert_true(ret_value == -ETIMEDOUT,
83 "k_futex_wait failed when it shouldn't have");
84 atomic_sub(&simple_futex.val, 1);
85 break;
86 default:
87 zassert_true(ret_value == -ETIMEDOUT,
88 "k_futex_wait failed when it shouldn't have");
89 atomic_sub(&simple_futex.val, 1);
90 break;
91 }
92 }
93
futex_wake_task(void * p1,void * p2,void * p3)94 static void futex_wake_task(void *p1, void *p2, void *p3)
95 {
96 int32_t ret_value;
97 int woken_num = *(int *)p1;
98
99 ret_value = k_futex_wake(&simple_futex,
100 woken_num == 1 ? false : true);
101 zassert_true(ret_value == woken_num,
102 "k_futex_wake failed when it shouldn't have");
103 }
104
futex_wait_wake_task(void * p1,void * p2,void * p3)105 static void futex_wait_wake_task(void *p1, void *p2, void *p3)
106 {
107 int32_t ret_value;
108 int time_val = *(int *)p1;
109
110 zassert_true(time_val >= (int)K_TICKS_FOREVER, "invalid timeout parameter");
111
112 ret_value = k_futex_wait(&simple_futex,
113 atomic_get(&simple_futex.val), K_TICKS(time_val));
114
115 switch (time_val) {
116 case K_TICKS_FOREVER:
117 zassert_true(ret_value == 0,
118 "k_futex_wait failed when it shouldn't have");
119 break;
120 case 0:
121 zassert_true(ret_value == -ETIMEDOUT,
122 "k_futex_wait failed when it shouldn't have");
123 break;
124 default:
125 zassert_true(ret_value == 0,
126 "k_futex_wait failed when it shouldn't have");
127 break;
128 }
129
130 atomic_sub(&simple_futex.val, 1);
131 }
132
futex_multiple_wake_task(void * p1,void * p2,void * p3)133 static void futex_multiple_wake_task(void *p1, void *p2, void *p3)
134 {
135 int32_t ret_value;
136 int woken_num = *(int *)p1;
137 int idx = *(int *)p2;
138
139 zassert_true(woken_num > 0, "invalid woken number");
140
141 ret_value = k_futex_wake(&multiple_futex[idx],
142 woken_num == 1 ? false : true);
143 zassert_true(ret_value == woken_num,
144 "k_futex_wake failed when it shouldn't have");
145 }
146
futex_multiple_wait_wake_task(void * p1,void * p2,void * p3)147 static void futex_multiple_wait_wake_task(void *p1, void *p2, void *p3)
148 {
149 int32_t ret_value;
150 int time_val = *(int *)p1;
151 int idx = *(int *)p2;
152
153 zassert_true(time_val == (int)K_TICKS_FOREVER, "invalid timeout parameter");
154
155 ret_value = k_futex_wait(&multiple_futex[idx],
156 atomic_get(&(multiple_futex[idx].val)), K_TICKS(time_val));
157 zassert_true(ret_value == 0,
158 "k_futex_wait failed when it shouldn't have");
159
160 atomic_sub(&(multiple_futex[idx].val), 1);
161 }
162
163 /**
164 * @ingroup kernel_futex_tests
165 * @{
166 */
167
168 /**
169 * @brief Test k_futex_wait() forever
170 */
ZTEST(futex,test_futex_wait_forever)171 ZTEST(futex, test_futex_wait_forever)
172 {
173 timeout = K_TICKS_FOREVER;
174
175 atomic_set(&simple_futex.val, 1);
176
177 k_thread_create(&futex_tid, stack_1, STACK_SIZE,
178 futex_wait_task, &timeout, NULL, NULL,
179 PRIO_WAIT, K_USER | K_INHERIT_PERMS,
180 K_NO_WAIT);
181
182 /* giving time for the futex_wait_task to execute */
183 k_yield();
184
185 zassert_false(atomic_get(&simple_futex.val) == 0,
186 "wait forever shouldn't wake");
187
188 k_thread_abort(&futex_tid);
189 }
190
ZTEST(futex,test_futex_wait_timeout)191 ZTEST(futex, test_futex_wait_timeout)
192 {
193 timeout = k_ms_to_ticks_ceil32(50);
194
195 atomic_set(&simple_futex.val, 1);
196
197 k_thread_create(&futex_tid, stack_1, STACK_SIZE,
198 futex_wait_task, &timeout, NULL, NULL,
199 PRIO_WAIT, K_USER | K_INHERIT_PERMS,
200 K_NO_WAIT);
201
202 /* giving time for the futex_wait_task to execute */
203 k_sleep(K_MSEC(100));
204
205 zassert_true(atomic_get(&simple_futex.val) == 0,
206 "wait timeout doesn't timeout");
207
208 k_thread_abort(&futex_tid);
209 }
210
ZTEST(futex,test_futex_wait_nowait)211 ZTEST(futex, test_futex_wait_nowait)
212 {
213 timeout = 0;
214
215 atomic_set(&simple_futex.val, 1);
216
217 k_thread_create(&futex_tid, stack_1, STACK_SIZE,
218 futex_wait_task, &timeout, NULL, NULL,
219 PRIO_WAIT, K_USER | K_INHERIT_PERMS,
220 K_NO_WAIT);
221
222 /* giving time for the futex_wait_task to execute */
223 k_sleep(K_MSEC(100));
224
225 zassert_true(atomic_get(&simple_futex.val) == 0, "wait nowait fail");
226
227 k_thread_abort(&futex_tid);
228 }
229
230 /**
231 * @brief Test k_futex_wait() and k_futex_wake()
232 */
ZTEST(futex,test_futex_wait_forever_wake)233 ZTEST(futex, test_futex_wait_forever_wake)
234 {
235 woken = 1;
236 timeout = K_TICKS_FOREVER;
237
238 atomic_set(&simple_futex.val, 1);
239
240 k_thread_create(&futex_tid, stack_1, STACK_SIZE,
241 futex_wait_wake_task, &timeout, NULL, NULL,
242 PRIO_WAIT, K_USER | K_INHERIT_PERMS,
243 K_NO_WAIT);
244
245 /* giving time for the futex_wait_wake_task to execute */
246 k_yield();
247
248 k_thread_create(&futex_wake_tid, futex_wake_stack, STACK_SIZE,
249 futex_wake_task, &woken, NULL, NULL,
250 PRIO_WAKE, K_USER | K_INHERIT_PERMS,
251 K_NO_WAIT);
252
253 /* giving time for the futex_wake_task
254 * and futex_wait_wake_task to execute
255 */
256 k_yield();
257
258 zassert_true(atomic_get(&simple_futex.val) == 0,
259 "wait forever doesn't wake");
260
261 k_thread_abort(&futex_wake_tid);
262 k_thread_abort(&futex_tid);
263 }
264
ZTEST(futex,test_futex_wait_timeout_wake)265 ZTEST(futex, test_futex_wait_timeout_wake)
266 {
267 woken = 1;
268 timeout = k_ms_to_ticks_ceil32(100);
269
270 atomic_set(&simple_futex.val, 1);
271
272 k_thread_create(&futex_tid, stack_1, STACK_SIZE,
273 futex_wait_wake_task, &timeout, NULL, NULL,
274 PRIO_WAIT, K_USER | K_INHERIT_PERMS,
275 K_NO_WAIT);
276
277 /* giving time for the futex_wait_wake_task to execute */
278 k_yield();
279
280 k_thread_create(&futex_wake_tid, futex_wake_stack, STACK_SIZE,
281 futex_wake_task, &woken, NULL, NULL,
282 PRIO_WAKE, K_USER | K_INHERIT_PERMS,
283 K_NO_WAIT);
284
285 /*
286 * giving time for the futex_wake_task
287 * and futex_wait_wake_task to execute
288 */
289 k_yield();
290
291 zassert_true(atomic_get(&simple_futex.val) == 0,
292 "wait timeout doesn't wake");
293
294 k_thread_abort(&futex_wake_tid);
295 k_thread_abort(&futex_tid);
296 }
297
ZTEST(futex,test_futex_wait_nowait_wake)298 ZTEST(futex, test_futex_wait_nowait_wake)
299 {
300 woken = 0;
301 timeout = 0;
302
303 atomic_set(&simple_futex.val, 1);
304
305 k_thread_create(&futex_tid, stack_1, STACK_SIZE,
306 futex_wait_wake_task, &timeout, NULL, NULL,
307 PRIO_WAIT, K_USER | K_INHERIT_PERMS,
308 K_NO_WAIT);
309
310 /* giving time for the futex_wait_wake_task to execute */
311 k_sleep(K_MSEC(100));
312
313 k_thread_create(&futex_wake_tid, futex_wake_stack, STACK_SIZE,
314 futex_wake_task, &woken, NULL, NULL,
315 PRIO_WAKE, K_USER | K_INHERIT_PERMS,
316 K_NO_WAIT);
317
318 /* giving time for the futex_wake_task to execute */
319 k_yield();
320
321 k_thread_abort(&futex_wake_tid);
322 k_thread_abort(&futex_tid);
323 }
324
ZTEST(futex,test_futex_wait_forever_wake_from_isr)325 ZTEST(futex, test_futex_wait_forever_wake_from_isr)
326 {
327 timeout = K_TICKS_FOREVER;
328
329 atomic_set(&simple_futex.val, 1);
330
331 k_thread_create(&futex_tid, stack_1, STACK_SIZE,
332 futex_wait_wake_task, &timeout, NULL, NULL,
333 PRIO_WAIT, K_USER | K_INHERIT_PERMS,
334 K_NO_WAIT);
335
336 /* giving time for the futex_wait_wake_task to execute */
337 k_yield();
338
339 futex_wake_from_isr(&simple_futex);
340
341 /* giving time for the futex_wait_wake_task to execute */
342 k_yield();
343
344 zassert_true(atomic_get(&simple_futex.val) == 0,
345 "wait forever wake from isr doesn't wake");
346
347 k_thread_abort(&futex_tid);
348 }
349
ZTEST(futex,test_futex_multiple_threads_wait_wake)350 ZTEST(futex, test_futex_multiple_threads_wait_wake)
351 {
352 timeout = K_TICKS_FOREVER;
353 woken = TOTAL_THREADS_WAITING;
354
355 atomic_clear(&simple_futex.val);
356
357 for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
358 atomic_inc(&simple_futex.val);
359 k_thread_create(&multiple_tid[i], multiple_stack[i],
360 STACK_SIZE, futex_wait_wake_task,
361 &timeout, NULL, NULL, PRIO_WAIT,
362 K_USER | K_INHERIT_PERMS, K_NO_WAIT);
363 }
364
365 /* giving time for the other threads to execute */
366 k_yield();
367
368 k_thread_create(&futex_wake_tid, futex_wake_stack,
369 STACK_SIZE, futex_wake_task, &woken,
370 NULL, NULL, PRIO_WAKE,
371 K_USER | K_INHERIT_PERMS, K_NO_WAIT);
372
373 /* giving time for the other threads to execute */
374 k_yield();
375
376 zassert_true(atomic_get(&simple_futex.val) == 0,
377 "wait forever wake doesn't wake all threads");
378
379 k_thread_abort(&futex_wake_tid);
380 for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
381 k_thread_abort(&multiple_tid[i]);
382 }
383 }
384
ZTEST(futex,test_multiple_futex_wait_wake)385 ZTEST(futex, test_multiple_futex_wait_wake)
386 {
387 woken = 1;
388 timeout = K_TICKS_FOREVER;
389
390 for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
391 index[i] = i;
392 atomic_set(&multiple_futex[i].val, 1);
393 k_thread_create(&multiple_tid[i], multiple_stack[i],
394 STACK_SIZE, futex_multiple_wait_wake_task,
395 &timeout, &index[i], NULL, PRIO_WAIT,
396 K_USER | K_INHERIT_PERMS, K_NO_WAIT);
397 }
398
399 /* giving time for the other threads to execute */
400 k_yield();
401
402 for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
403 k_thread_create(&multiple_wake_tid[i], multiple_wake_stack[i],
404 STACK_SIZE, futex_multiple_wake_task,
405 &woken, &index[i], NULL, PRIO_WAKE,
406 K_USER | K_INHERIT_PERMS, K_NO_WAIT);
407 }
408
409 /* giving time for the other threads to execute */
410 k_yield();
411
412 for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
413 zassert_true(atomic_get(&multiple_futex[i].val) == 0,
414 "wait forever wake doesn't wake %d thread", i);
415 }
416
417 for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
418 k_thread_abort(&multiple_tid[i]);
419 k_thread_abort(&multiple_wake_tid[i]);
420 }
421 }
422
ZTEST_USER(futex,test_user_futex_bad)423 ZTEST_USER(futex, test_user_futex_bad)
424 {
425 int ret;
426
427 /* Is a futex, but no access to its memory */
428 ret = k_futex_wait(&no_access_futex, 0, K_NO_WAIT);
429 zassert_equal(ret, -EACCES, "shouldn't have been able to access");
430 ret = k_futex_wake(&no_access_futex, false);
431 zassert_equal(ret, -EACCES, "shouldn't have been able to access");
432
433 /* Access to memory, but not a kernel object */
434 ret = k_futex_wait((struct k_futex *)¬_a_futex, 0, K_NO_WAIT);
435 zassert_equal(ret, -EINVAL, "waited on non-futex");
436 ret = k_futex_wake((struct k_futex *)¬_a_futex, false);
437 zassert_equal(ret, -EINVAL, "woke non-futex");
438
439 /* Access to memory, but wrong object type */
440 ret = k_futex_wait((struct k_futex *)&also_not_a_futex, 0, K_NO_WAIT);
441 zassert_equal(ret, -EINVAL, "waited on non-futex");
442 ret = k_futex_wake((struct k_futex *)&also_not_a_futex, false);
443 zassert_equal(ret, -EINVAL, "woke non-futex");
444
445 /* Wait with unexpected value */
446 atomic_set(&simple_futex.val, 100);
447 ret = k_futex_wait(&simple_futex, 0, K_NO_WAIT);
448 zassert_equal(ret, -EAGAIN, "waited when values did not match");
449
450 /* Timeout case */
451 ret = k_futex_wait(&simple_futex, 100, K_NO_WAIT);
452 zassert_equal(ret, -ETIMEDOUT, "didn't time out");
453 }
454
futex_wait_wake(void * p1,void * p2,void * p3)455 static void futex_wait_wake(void *p1, void *p2, void *p3)
456 {
457 int32_t ret_value;
458
459 /* Test user thread can make wait without error
460 * Use assertion to verify k_futex_wait() returns 0
461 */
462 ret_value = k_futex_wait(&simple_futex, 13, K_FOREVER);
463 zassert_equal(ret_value, 0);
464
465 /* Test user thread can make wake without error
466 * Use assertion to verify k_futex_wake() returns 1,
467 * because only 1 thread wakes
468 */
469 ret_value = k_futex_wake(&simple_futex, false);
470 zassert_equal(ret_value, 1);
471 }
472
futex_wake(void * p1,void * p2,void * p3)473 static void futex_wake(void *p1, void *p2, void *p3)
474 {
475 int32_t atomic_ret_val;
476 int32_t ret_value;
477
478 k_futex_wake(&simple_futex, false);
479
480 ret_value = k_futex_wait(&simple_futex, 13, K_FOREVER);
481 zassert_equal(ret_value, 0);
482
483 /* Test user can write to the futex value
484 * Use assertion to verify subtraction correctness
485 * Initial value was 13, after atomic_sub() must be 12
486 */
487 atomic_sub(&simple_futex.val, 1);
488 atomic_ret_val = atomic_get(&simple_futex.val);
489 zassert_equal(atomic_ret_val, 12);
490 }
491
492 /**
493 * @brief Test kernel supports locating kernel objects without private kernel
494 * data anywhere in memory, control access with the memory domain configuration
495 *
496 * @details For that test kernel object which doesn't contain private kernel
497 * data will be used futex. Test performs handshaking between two user threads
498 * to test next requirements:
499 * - Place a futex simple_futex in user memory using ZTEST_BMEM
500 * - Show that user threads can write to futex value
501 * - Show that user threads can make wait/wake syscalls on it.
502 *
503 * @see atomic_set(), atomic_sub(), k_futex_wake(), k_futex_wait()
504 *
505 * @ingroup kernel_futex_tests
506 */
ZTEST_USER(futex,test_futex_locate_access)507 ZTEST_USER(futex, test_futex_locate_access)
508 {
509
510 atomic_set(&simple_futex.val, 13);
511
512 k_thread_create(&futex_tid, stack_1, STACK_SIZE,
513 futex_wait_wake, NULL, NULL, NULL,
514 PRIORITY, K_USER | K_INHERIT_PERMS, K_NO_WAIT);
515
516 /* giving time for the futex_wait_wake_task to execute */
517 k_yield();
518
519 k_thread_create(&futex_wake_tid, futex_wake_stack, STACK_SIZE,
520 futex_wake, NULL, NULL, NULL,
521 PRIORITY, K_USER | K_INHERIT_PERMS, K_NO_WAIT);
522
523 /*
524 * giving time for the futex_wake_task
525 * and futex_wait_wake_task to execute
526 */
527 k_yield();
528
529 k_thread_abort(&futex_tid);
530 k_thread_abort(&futex_wake_tid);
531 }
532
533 /* ztest main entry*/
futex_setup(void)534 void *futex_setup(void)
535 {
536 k_thread_access_grant(k_current_get(),
537 &futex_tid, &stack_1, &futex_wake_tid, &futex_wake_stack,
538 &simple_futex);
539 return NULL;
540 }
541
542 ZTEST_SUITE(futex, NULL, futex_setup, NULL, NULL, NULL);
543 /**
544 * @}
545 */
546