1 /*
2 * Copyright (c) 2016 Wind River Systems, Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/tc_util.h>
8 #include <zephyr/ztest.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/kernel_structs.h>
11 #include <stdbool.h>
12
13 #define NUM_SECONDS(x) ((x) * 1000)
14 #define HALF_SECOND (500)
15 #define THIRD_SECOND (333)
16 #define FOURTH_SECOND (250)
17
18 #define COOP_STACKSIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
19 #define PREEM_STACKSIZE (1024 + CONFIG_TEST_EXTRA_STACK_SIZE)
20
21 #define FIFO_TEST_START 10
22 #define FIFO_TEST_END 20
23
24 #define SEM_TEST_START 30
25 #define SEM_TEST_END 40
26
27 #define LIFO_TEST_START 50
28 #define LIFO_TEST_END 60
29
30 #define NON_NULL_PTR ((void *)0x12345678)
31
32 #ifdef CONFIG_COVERAGE_GCOV
33 #define OFFLOAD_WORKQUEUE_STACK_SIZE 4096
34 #else
35 #define OFFLOAD_WORKQUEUE_STACK_SIZE 1024
36 #endif
37
38 #define OFFLOAD_WORKQUEUE_PRIORITY (-1)
39 static struct k_work_q offload_work_q;
40 static K_THREAD_STACK_DEFINE(offload_work_q_stack,
41 OFFLOAD_WORKQUEUE_STACK_SIZE);
42
43 struct fifo_data {
44 intptr_t reserved;
45 uint32_t data;
46 };
47
48 struct lifo_data {
49 intptr_t reserved;
50 uint32_t data;
51 };
52
53 struct offload_work {
54 struct k_work work_item;
55 struct k_sem *sem;
56 };
57
58 static K_THREAD_STACK_ARRAY_DEFINE(coop_stack, 2, COOP_STACKSIZE);
59 static struct k_thread coop_thread[2];
60
61 static struct k_fifo fifo;
62 static struct k_lifo lifo;
63 static struct k_timer timer;
64
65 static struct k_sem start_test_sem;
66 static struct k_sem sync_test_sem;
67 static struct k_sem end_test_sem;
68
69 struct fifo_data fifo_test_data[4] = {
70 { 0, FIFO_TEST_END + 1 }, { 0, FIFO_TEST_END + 2 },
71 { 0, FIFO_TEST_END + 3 }, { 0, FIFO_TEST_END + 4 }
72 };
73
74 struct lifo_data lifo_test_data[4] = {
75 { 0, LIFO_TEST_END + 1 }, { 0, LIFO_TEST_END + 2 },
76 { 0, LIFO_TEST_END + 3 }, { 0, LIFO_TEST_END + 4 }
77 };
78
79 static uint32_t timer_start_tick;
80 static uint32_t timer_end_tick;
81 static void *timer_data;
82
83 static int __noinit coop_high_state;
84 static int __noinit coop_low_state;
85 static int __noinit task_high_state;
86 static int __noinit task_low_state;
87
88 static int __noinit counter;
89
my_fifo_get(struct k_fifo * my_fifo,int32_t timeout)90 static inline void *my_fifo_get(struct k_fifo *my_fifo, int32_t timeout)
91 {
92 return k_fifo_get(my_fifo, K_MSEC(timeout));
93 }
94
my_lifo_get(struct k_lifo * my_lifo,int32_t timeout)95 static inline void *my_lifo_get(struct k_lifo *my_lifo, int32_t timeout)
96 {
97 return k_lifo_get(my_lifo, K_MSEC(timeout));
98 }
99
increment_counter(void)100 static int increment_counter(void)
101 {
102 int tmp;
103 unsigned int key = irq_lock();
104
105 tmp = ++counter;
106 irq_unlock(key);
107
108 return tmp;
109 }
110
sync_threads(struct k_work * work)111 static void sync_threads(struct k_work *work)
112 {
113 struct offload_work *offload =
114 CONTAINER_OF(work, struct offload_work, work_item);
115
116 k_sem_give(offload->sem);
117 k_sem_give(offload->sem);
118 k_sem_give(offload->sem);
119 k_sem_give(offload->sem);
120
121 }
122
fifo_tests(int32_t timeout,volatile int * state,void * (* get)(struct k_fifo *,int32_t),int (* sem_take)(struct k_sem *,k_timeout_t))123 static void fifo_tests(int32_t timeout, volatile int *state,
124 void *(*get)(struct k_fifo *, int32_t),
125 int (*sem_take)(struct k_sem *, k_timeout_t))
126 {
127 struct fifo_data *data;
128
129 sem_take(&start_test_sem, K_FOREVER);
130
131 *state = FIFO_TEST_START;
132 /* Expect this to time out */
133 data = get(&fifo, timeout);
134 if (data != NULL) {
135 TC_ERROR("**** Unexpected data on FIFO get\n");
136 return;
137 }
138 *state = increment_counter();
139
140 /* Sync up fifo test threads */
141 sem_take(&sync_test_sem, K_FOREVER);
142
143 /* Expect this to receive data from the fifo */
144 *state = FIFO_TEST_END;
145 data = get(&fifo, timeout);
146 if (data == NULL) {
147 TC_ERROR("**** No data on FIFO get\n");
148 return;
149 }
150 *state = increment_counter();
151
152 if (data->data != *state) {
153 TC_ERROR("**** Got FIFO data %d, not %d (%d)\n",
154 data->data, *state, timeout);
155 return;
156 }
157
158 sem_take(&end_test_sem, K_FOREVER);
159 }
160
lifo_tests(int32_t timeout,volatile int * state,void * (* get)(struct k_lifo *,int32_t),int (* sem_take)(struct k_sem *,k_timeout_t))161 static void lifo_tests(int32_t timeout, volatile int *state,
162 void *(*get)(struct k_lifo *, int32_t),
163 int (*sem_take)(struct k_sem *, k_timeout_t))
164 {
165 struct lifo_data *data;
166
167 sem_take(&start_test_sem, K_FOREVER);
168
169 *state = LIFO_TEST_START;
170 /* Expect this to time out */
171 data = get(&lifo, timeout);
172 if (data != NULL) {
173 TC_ERROR("**** Unexpected data on LIFO get\n");
174 return;
175 }
176 *state = increment_counter();
177
178 /* Sync up all threads */
179 sem_take(&sync_test_sem, K_FOREVER);
180
181 /* Expect this to receive data from the lifo */
182 *state = LIFO_TEST_END;
183 data = get(&lifo, timeout);
184 if (data == NULL) {
185 TC_ERROR("**** No data on LIFO get\n");
186 return;
187 }
188 *state = increment_counter();
189
190 if (data->data != *state) {
191 TC_ERROR("**** Got LIFO data %d, not %d (%d)\n",
192 data->data, *state, timeout);
193 return;
194 }
195
196 sem_take(&end_test_sem, K_FOREVER);
197 }
198
timer_tests(void)199 static void timer_tests(void)
200 {
201 k_sem_take(&start_test_sem, K_FOREVER);
202
203 timer_start_tick = k_uptime_get_32();
204
205 k_timer_start(&timer, K_SECONDS(1), K_NO_WAIT);
206
207 if (k_timer_status_sync(&timer)) {
208 timer_data = timer.user_data;
209 }
210
211 timer_end_tick = k_uptime_get_32();
212
213 k_sem_take(&end_test_sem, K_FOREVER);
214 }
215
coop_high(void * arg1,void * arg2,void * arg3)216 static void coop_high(void *arg1, void *arg2, void *arg3)
217 {
218 ARG_UNUSED(arg1);
219 ARG_UNUSED(arg2);
220 ARG_UNUSED(arg3);
221
222 fifo_tests(NUM_SECONDS(1), &coop_high_state, my_fifo_get, k_sem_take);
223
224 lifo_tests(NUM_SECONDS(1), &coop_high_state, my_lifo_get, k_sem_take);
225 }
226
coop_low(void * arg1,void * arg2,void * arg3)227 static void coop_low(void *arg1, void *arg2, void *arg3)
228 {
229 ARG_UNUSED(arg1);
230 ARG_UNUSED(arg2);
231 ARG_UNUSED(arg3);
232
233 fifo_tests(HALF_SECOND, &coop_low_state, my_fifo_get, k_sem_take);
234
235 lifo_tests(HALF_SECOND, &coop_low_state, my_lifo_get, k_sem_take);
236 }
237
task_high(void)238 void task_high(void)
239 {
240 k_fifo_init(&fifo);
241 k_lifo_init(&lifo);
242
243 k_timer_init(&timer, NULL, NULL);
244 timer.user_data = NON_NULL_PTR;
245
246 k_sem_init(&start_test_sem, 0, UINT_MAX);
247 k_sem_init(&sync_test_sem, 0, UINT_MAX);
248 k_sem_init(&end_test_sem, 0, UINT_MAX);
249
250 k_work_queue_start(&offload_work_q,
251 offload_work_q_stack,
252 K_THREAD_STACK_SIZEOF(offload_work_q_stack),
253 OFFLOAD_WORKQUEUE_PRIORITY, NULL);
254
255 counter = SEM_TEST_START;
256
257 k_thread_create(&coop_thread[0], coop_stack[0], COOP_STACKSIZE,
258 coop_high, NULL, NULL, NULL, K_PRIO_COOP(3), 0,
259 K_NO_WAIT);
260
261 k_thread_create(&coop_thread[1], coop_stack[1], COOP_STACKSIZE,
262 coop_low, NULL, NULL, NULL, K_PRIO_COOP(7), 0,
263 K_NO_WAIT);
264
265 counter = FIFO_TEST_START;
266 fifo_tests(THIRD_SECOND, &task_high_state, my_fifo_get, k_sem_take);
267
268 counter = LIFO_TEST_START;
269 lifo_tests(THIRD_SECOND, &task_high_state, my_lifo_get, k_sem_take);
270
271 timer_tests();
272 }
273
task_low(void)274 void task_low(void)
275 {
276 fifo_tests(FOURTH_SECOND, &task_low_state, my_fifo_get, k_sem_take);
277
278 lifo_tests(FOURTH_SECOND, &task_low_state, my_lifo_get, k_sem_take);
279 }
280
281 /**
282 * @brief Test pending
283 *
284 * @defgroup kernel_pending_tests Pending tests
285 *
286 * @ingroup all_tests
287 *
288 * @{
289 */
290
291 /**
292 * @brief Test pending of workq, fifo and lifo
293 *
294 * @see k_sleep(), K_THREAD_DEFINE()
295 */
ZTEST(pending,test_pending_fifo)296 ZTEST(pending, test_pending_fifo)
297 {
298 /*
299 * Main thread(test_main) priority was 9 but ztest thread runs at
300 * priority -1. To run the test smoothly make both main and ztest
301 * threads run at same priority level.
302 */
303 k_thread_priority_set(k_current_get(), 9);
304
305 struct offload_work offload1 = {0};
306
307 k_work_init(&offload1.work_item, sync_threads);
308 offload1.sem = &start_test_sem;
309 k_work_submit_to_queue(&offload_work_q, &offload1.work_item);
310
311 /*
312 * Verify that preemptible threads 'task_high' and 'task_low' do not
313 * busy-wait. If they are not busy-waiting, then they must be pending.
314 */
315
316 TC_PRINT("Testing preemptible threads block on fifos ...\n");
317 zassert_false((coop_high_state != FIFO_TEST_START) ||
318 (coop_low_state != FIFO_TEST_START) ||
319 (task_high_state != FIFO_TEST_START) ||
320 (task_low_state != FIFO_TEST_START), NULL);
321
322 /* Give waiting threads time to time-out */
323 k_sleep(K_SECONDS(2));
324
325 /*
326 * Verify that the cooperative and preemptible threads timed-out in
327 * the correct order.
328 */
329
330 TC_PRINT("Testing fifos time-out in correct order ...\n");
331 zassert_false((task_low_state != FIFO_TEST_START + 1) ||
332 (task_high_state != FIFO_TEST_START + 2) ||
333 (coop_low_state != FIFO_TEST_START + 3) ||
334 (coop_high_state != FIFO_TEST_START + 4),
335 "**** Threads timed-out in unexpected order");
336
337 counter = FIFO_TEST_END;
338
339 k_work_init(&offload1.work_item, sync_threads);
340 offload1.sem = &sync_test_sem;
341 k_work_submit_to_queue(&offload_work_q, &offload1.work_item);
342
343 /*
344 * Two cooperative and two preemptible threads should be waiting on
345 * the FIFO
346 */
347
348 /* Add data to the FIFO */
349 TC_PRINT("Testing fifos delivered data correctly ...\n");
350 k_fifo_put(&fifo, &fifo_test_data[0]);
351 k_fifo_put(&fifo, &fifo_test_data[1]);
352 k_fifo_put(&fifo, &fifo_test_data[2]);
353 k_fifo_put(&fifo, &fifo_test_data[3]);
354
355 zassert_false((coop_high_state != FIFO_TEST_END + 1) ||
356 (coop_low_state != FIFO_TEST_END + 2) ||
357 (task_high_state != FIFO_TEST_END + 3) ||
358 (task_low_state != FIFO_TEST_END + 4),
359 "**** Unexpected delivery order");
360 }
361
362
ZTEST(pending,test_pending_lifo)363 ZTEST(pending, test_pending_lifo)
364 {
365 /*
366 * Main thread(test_main) priority was 9 but ztest thread runs at
367 * priority -1. To run the test smoothly make both main and ztest
368 * threads run at same priority level.
369 */
370 k_thread_priority_set(k_current_get(), 9);
371
372 struct offload_work offload1 = {0};
373 struct offload_work offload2 = {0};
374
375 k_work_init(&offload1.work_item, sync_threads);
376 offload1.sem = &end_test_sem;
377 k_work_submit_to_queue(&offload_work_q, &offload1.work_item);
378
379 k_work_init(&offload2.work_item, sync_threads);
380 offload2.sem = &start_test_sem;
381 k_work_submit_to_queue(&offload_work_q, &offload2.work_item);
382
383 /*
384 * Verify that cooperative threads 'task_high' and 'task_low' do not
385 * busy-wait. If they are not busy-waiting, then they must be pending.
386 */
387
388 TC_PRINT("Testing preemptible threads block on lifos ...\n");
389 zassert_false((coop_high_state != LIFO_TEST_START) ||
390 (coop_low_state != LIFO_TEST_START) ||
391 (task_high_state != LIFO_TEST_START) ||
392 (task_low_state != LIFO_TEST_START), NULL);
393
394 /* Give waiting threads time to time-out */
395 k_sleep(K_SECONDS(2));
396
397 TC_PRINT("Testing lifos time-out in correct order ...\n");
398 zassert_false((task_low_state != LIFO_TEST_START + 1) ||
399 (task_high_state != LIFO_TEST_START + 2) ||
400 (coop_low_state != LIFO_TEST_START + 3) ||
401 (coop_high_state != LIFO_TEST_START + 4),
402 "**** Threads timed-out in unexpected order");
403
404 counter = LIFO_TEST_END;
405
406 k_work_init(&offload1.work_item, sync_threads);
407 offload1.sem = &sync_test_sem;
408 k_work_submit_to_queue(&offload_work_q, &offload1.work_item);
409
410 /*
411 * Two cooperative threads and two preemptive threads should
412 * be waiting on the LIFO
413 */
414
415 /* Add data to the LIFO */
416 k_lifo_put(&lifo, &lifo_test_data[0]);
417 k_lifo_put(&lifo, &lifo_test_data[1]);
418 k_lifo_put(&lifo, &lifo_test_data[2]);
419 k_lifo_put(&lifo, &lifo_test_data[3]);
420
421 TC_PRINT("Testing lifos delivered data correctly ...\n");
422 zassert_false((coop_high_state != LIFO_TEST_END + 1) ||
423 (coop_low_state != LIFO_TEST_END + 2) ||
424 (task_high_state != LIFO_TEST_END + 3) ||
425 (task_low_state != LIFO_TEST_END + 4),
426 "**** Unexpected timeout order");
427
428 }
429
ZTEST(pending,test_pending_timer)430 ZTEST(pending, test_pending_timer)
431 {
432 /*
433 * Main thread(test_main) priority was 9 but ztest thread runs at
434 * priority -1. To run the test smoothly make both main and ztest
435 * threads run at same priority level.
436 */
437 k_thread_priority_set(k_current_get(), 9);
438
439 struct offload_work offload2 = {0};
440
441 k_work_init(&offload2.work_item, sync_threads);
442 offload2.sem = &end_test_sem;
443 k_work_submit_to_queue(&offload_work_q, &offload2.work_item);
444
445 timer_end_tick = 0U;
446 k_sem_give(&start_test_sem); /* start timer tests */
447
448 /*
449 * NOTE: The timer test is running in the context of high_task().
450 * Scheduling is expected to yield to high_task(). If high_task()
451 * does not pend as expected, then timer_end_tick will be non-zero.
452 */
453
454 TC_PRINT("Testing preemptible thread waiting on timer ...\n");
455 zassert_equal(timer_end_tick, 0, "Task did not pend on timer");
456
457 /* Let the timer expire */
458 k_sleep(K_SECONDS(2));
459
460 zassert_false((timer_end_tick < timer_start_tick + NUM_SECONDS(1)),
461 "Task waiting on timer error");
462
463 zassert_equal(timer_data, NON_NULL_PTR,
464 "Incorrect data from timer");
465
466 k_sem_give(&end_test_sem);
467 }
468
469 /**
470 * @}
471 */
472
473 K_THREAD_DEFINE(TASK_LOW, PREEM_STACKSIZE, task_low, NULL, NULL, NULL,
474 7, 0, 0);
475
476 K_THREAD_DEFINE(TASK_HIGH, PREEM_STACKSIZE, task_high, NULL, NULL, NULL,
477 5, 0, 0);
478
479 ZTEST_SUITE(pending, NULL, NULL,
480 ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL);
481