1 /*
2 * Copyright (c) 2018 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/__assert.h>
10 #include <zephyr/sys/util.h>
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_REGISTER(test);
13
14 /*
15 * @file
16 * @brief Test fifo APIs timeout
17 *
18 * This module tests following fifo timeout scenarios
19 *
20 * First, the thread waits with a timeout and times out. Then it wait with a
21 * timeout, but gets the data in time.
22 *
23 * Then, multiple timeout tests are done for the threads, to test the ordering
24 * of queueing/dequeueing when timeout occurs, first on one fifo, then on
25 * multiple fifos.
26 *
27 * Finally, multiple threads pend on one fifo, and they all get the
28 * data in time, except the last one: this tests that the timeout is
29 * recomputed correctly when timeouts are aborted.
30 */
31
32 struct scratch_fifo_packet {
33 void *link_in_fifo;
34 void *data_if_needed;
35 };
36
37 struct reply_packet {
38 void *link_in_fifo;
39 int32_t reply;
40 };
41
42 struct timeout_order_data {
43 void *link_in_fifo;
44 struct k_fifo *fifo;
45 uint32_t timeout;
46 int32_t timeout_order;
47 int32_t q_order;
48 };
49
50
51 #define NUM_SCRATCH_FIFO_PACKETS 20
52 struct scratch_fifo_packet scratch_fifo_packets[NUM_SCRATCH_FIFO_PACKETS];
53
54 struct k_fifo scratch_fifo_packets_fifo;
55
56 static struct k_fifo fifo_timeout[2];
57 struct k_fifo timeout_order_fifo;
58
59 struct timeout_order_data timeout_order_data[] = {
60 {0, &fifo_timeout[0], 20, 2, 0},
61 {0, &fifo_timeout[0], 40, 4, 1},
62 {0, &fifo_timeout[0], 0, 0, 2},
63 {0, &fifo_timeout[0], 10, 1, 3},
64 {0, &fifo_timeout[0], 30, 3, 4},
65 };
66
67 struct timeout_order_data timeout_order_data_mult_fifo[] = {
68 {0, &fifo_timeout[1], 0, 0, 0},
69 {0, &fifo_timeout[0], 30, 3, 1},
70 {0, &fifo_timeout[0], 50, 5, 2},
71 {0, &fifo_timeout[1], 80, 8, 3},
72 {0, &fifo_timeout[1], 70, 7, 4},
73 {0, &fifo_timeout[0], 10, 1, 5},
74 {0, &fifo_timeout[0], 60, 6, 6},
75 {0, &fifo_timeout[0], 20, 2, 7},
76 {0, &fifo_timeout[1], 40, 4, 8},
77 };
78
79 #define TIMEOUT_ORDER_NUM_THREADS ARRAY_SIZE(timeout_order_data_mult_fifo)
80 #define TSTACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
81 #define FIFO_THREAD_PRIO -5
82
83 static K_THREAD_STACK_ARRAY_DEFINE(ttstack,
84 TIMEOUT_ORDER_NUM_THREADS, TSTACK_SIZE);
85 static struct k_thread ttdata[TIMEOUT_ORDER_NUM_THREADS];
86 static k_tid_t tid[TIMEOUT_ORDER_NUM_THREADS];
87
get_scratch_packet(void)88 static void *get_scratch_packet(void)
89 {
90 void *packet = k_fifo_get(&scratch_fifo_packets_fifo, K_NO_WAIT);
91
92 zassert_true(packet != NULL);
93 return packet;
94 }
95
put_scratch_packet(void * packet)96 static void put_scratch_packet(void *packet)
97 {
98 k_fifo_put(&scratch_fifo_packets_fifo, packet);
99 }
100
is_timeout_in_range(uint32_t start_time,uint32_t timeout)101 static bool is_timeout_in_range(uint32_t start_time, uint32_t timeout)
102 {
103 uint32_t stop_time, diff;
104
105 stop_time = k_cycle_get_32();
106 diff = (uint32_t)k_cyc_to_ns_floor64(stop_time -
107 start_time) / NSEC_PER_USEC;
108 diff = diff / USEC_PER_MSEC;
109 return timeout <= diff;
110 }
111
112 /* a thread sleeps then puts data on the fifo */
test_thread_put_timeout(void * p1,void * p2,void * p3)113 static void test_thread_put_timeout(void *p1, void *p2, void *p3)
114 {
115 uint32_t timeout = *((uint32_t *)p2);
116
117 k_msleep(timeout);
118 k_fifo_put((struct k_fifo *)p1, get_scratch_packet());
119 }
120
121 /* a thread pends on a fifo then times out */
test_thread_pend_and_timeout(void * p1,void * p2,void * p3)122 static void test_thread_pend_and_timeout(void *p1, void *p2, void *p3)
123 {
124 struct timeout_order_data *d = (struct timeout_order_data *)p1;
125 uint32_t start_time;
126 void *packet;
127
128 k_msleep(1); /* Align to ticks */
129
130 start_time = k_cycle_get_32();
131 packet = k_fifo_get(d->fifo, K_MSEC(d->timeout));
132 zassert_true(packet == NULL);
133 zassert_true(is_timeout_in_range(start_time, d->timeout));
134
135 k_fifo_put(&timeout_order_fifo, d);
136 }
137 /* Spins several threads that pend and timeout on fifos */
test_multiple_threads_pending(struct timeout_order_data * test_data,int test_data_size)138 static int test_multiple_threads_pending(struct timeout_order_data *test_data,
139 int test_data_size)
140 {
141 int ii, j;
142 uint32_t diff_ms;
143
144 for (ii = 0; ii < test_data_size; ii++) {
145 tid[ii] = k_thread_create(&ttdata[ii], ttstack[ii], TSTACK_SIZE,
146 test_thread_pend_and_timeout,
147 &test_data[ii], NULL, NULL,
148 FIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
149 }
150
151 /* In general, there is no guarantee of wakeup order when multiple
152 * threads are woken up on the same tick. This can especially happen
153 * when the system is loaded. However, in this particular test, we
154 * are controlling the system state and hence we can make a reasonable
155 * estimation of a timeout occurring with the max deviation of an
156 * additional tick. Hence the timeout order may slightly be different
157 * from what we normally expect.
158 */
159 for (ii = 0; ii < test_data_size; ii++) {
160 struct timeout_order_data *data =
161 k_fifo_get(&timeout_order_fifo, K_FOREVER);
162
163 zassert_not_null(data, NULL);
164 if (data->timeout_order == ii) {
165 LOG_DBG(" thread (q order: %d, t/o: %d, fifo %p)",
166 data->q_order, data->timeout, data->fifo);
167 } else {
168 /* Get the index of the thread which should have
169 * actually timed out.
170 */
171 for (j = 0; j < test_data_size; j++) {
172 if (test_data[j].timeout_order == ii) {
173 break;
174 }
175 }
176
177 if (data->timeout > test_data[j].timeout) {
178 diff_ms = data->timeout - test_data[j].timeout;
179 } else {
180 diff_ms = test_data[j].timeout - data->timeout;
181 }
182
183 if (k_ms_to_ticks_ceil32(diff_ms) == 1) {
184 LOG_DBG(
185 " thread (q order: %d, t/o: %d, fifo %p)",
186 data->q_order, data->timeout, data->fifo);
187 } else {
188 TC_ERROR(
189 " *** thread %d woke up, expected %d\n",
190 data->q_order, j);
191 return TC_FAIL;
192 }
193 }
194 }
195
196 return TC_PASS;
197 }
198
199 /* a thread pends on a fifo with a timeout and gets the data in time */
test_thread_pend_and_get_data(void * p1,void * p2,void * p3)200 static void test_thread_pend_and_get_data(void *p1, void *p2, void *p3)
201 {
202 struct timeout_order_data *d = (struct timeout_order_data *)p1;
203 void *packet;
204
205 packet = k_fifo_get(d->fifo, K_MSEC(d->timeout));
206 zassert_true(packet != NULL);
207
208 put_scratch_packet(packet);
209 k_fifo_put(&timeout_order_fifo, d);
210 }
211
212 /* Spins child threads that get fifo data in time, except the last one */
test_multiple_threads_get_data(struct timeout_order_data * test_data,int test_data_size)213 static int test_multiple_threads_get_data(struct timeout_order_data *test_data,
214 int test_data_size)
215 {
216 struct timeout_order_data *data;
217 int ii;
218
219 for (ii = 0; ii < test_data_size-1; ii++) {
220 tid[ii] = k_thread_create(&ttdata[ii], ttstack[ii], TSTACK_SIZE,
221 test_thread_pend_and_get_data,
222 &test_data[ii], NULL, NULL,
223 K_PRIO_PREEMPT(0), K_INHERIT_PERMS, K_NO_WAIT);
224 }
225
226 tid[ii] = k_thread_create(&ttdata[ii], ttstack[ii], TSTACK_SIZE,
227 test_thread_pend_and_timeout,
228 &test_data[ii], NULL, NULL,
229 K_PRIO_PREEMPT(0), K_INHERIT_PERMS, K_NO_WAIT);
230
231 for (ii = 0; ii < test_data_size-1; ii++) {
232 k_fifo_put(test_data[ii].fifo, get_scratch_packet());
233
234 data = k_fifo_get(&timeout_order_fifo, K_FOREVER);
235 if (!data) {
236 TC_ERROR("thread %d got NULL value from fifo\n", ii);
237 return TC_FAIL;
238 }
239
240 if (data->q_order != ii) {
241 TC_ERROR(" *** thread %d woke up, expected %d\n",
242 data->q_order, ii);
243 return TC_FAIL;
244 }
245
246 if (data->q_order == ii) {
247 LOG_DBG(" thread (q order: %d, t/o: %d, fifo %p)",
248 data->q_order, data->timeout, data->fifo);
249 }
250 }
251
252 data = k_fifo_get(&timeout_order_fifo, K_FOREVER);
253 if (!data) {
254 TC_ERROR("thread %d got NULL value from fifo\n", ii);
255 return TC_FAIL;
256 }
257
258 if (data->q_order != ii) {
259 TC_ERROR(" *** thread %d woke up, expected %d\n",
260 data->q_order, ii);
261 return TC_FAIL;
262 }
263
264 LOG_DBG(" thread (q order: %d, t/o: %d, fifo %p)",
265 data->q_order, data->timeout, data->fifo);
266
267 return TC_PASS;
268 }
269
270 /* try getting data on fifo with special timeout value, return result in fifo */
test_thread_timeout_reply_values(void * p1,void * p2,void * p3)271 static void test_thread_timeout_reply_values(void *p1, void *p2, void *p3)
272 {
273 struct reply_packet *reply_packet = (struct reply_packet *)p1;
274
275 reply_packet->reply =
276 !!k_fifo_get(&fifo_timeout[0], K_NO_WAIT);
277
278 k_fifo_put(&timeout_order_fifo, reply_packet);
279 }
280
test_thread_timeout_reply_values_wfe(void * p1,void * p2,void * p3)281 static void test_thread_timeout_reply_values_wfe(void *p1, void *p2, void *p3)
282 {
283 struct reply_packet *reply_packet = (struct reply_packet *)p1;
284
285 reply_packet->reply =
286 !!k_fifo_get(&fifo_timeout[0], K_FOREVER);
287
288 k_fifo_put(&timeout_order_fifo, reply_packet);
289 }
290
291 /**
292 * @addtogroup kernel_fifo_tests
293 * @{
294 */
295
296 /**
297 * @brief Test empty fifo with timeout and K_NO_WAIT
298 * @see k_fifo_get()
299 */
ZTEST(fifo_timeout_1cpu,test_timeout_empty_fifo)300 ZTEST(fifo_timeout_1cpu, test_timeout_empty_fifo)
301 {
302 void *packet;
303 uint32_t start_time, timeout;
304
305 k_msleep(1); /* Align to ticks */
306
307 /* Test empty fifo with timeout */
308 timeout = 10U;
309 start_time = k_cycle_get_32();
310 packet = k_fifo_get(&fifo_timeout[0], K_MSEC(timeout));
311 zassert_true(packet == NULL);
312 zassert_true(is_timeout_in_range(start_time, timeout));
313
314 /* Test empty fifo with timeout of K_NO_WAIT */
315 packet = k_fifo_get(&fifo_timeout[0], K_NO_WAIT);
316 zassert_true(packet == NULL);
317 }
318
319 /**
320 * @brief Test non empty fifo with timeout and K_NO_WAIT
321 * @see k_fifo_get(), k_fifo_put()
322 */
ZTEST(fifo_timeout,test_timeout_non_empty_fifo)323 ZTEST(fifo_timeout, test_timeout_non_empty_fifo)
324 {
325 void *packet, *scratch_packet;
326
327 /* Test k_fifo_get with K_NO_WAIT */
328 scratch_packet = get_scratch_packet();
329 k_fifo_put(&fifo_timeout[0], scratch_packet);
330 packet = k_fifo_get(&fifo_timeout[0], K_NO_WAIT);
331 zassert_true(packet != NULL);
332 put_scratch_packet(scratch_packet);
333
334 /* Test k_fifo_get with K_FOREVER */
335 scratch_packet = get_scratch_packet();
336 k_fifo_put(&fifo_timeout[0], scratch_packet);
337 packet = k_fifo_get(&fifo_timeout[0], K_FOREVER);
338 zassert_true(packet != NULL);
339 put_scratch_packet(scratch_packet);
340 }
341
342 /**
343 * @brief Test fifo with timeout and K_NO_WAIT
344 * @details In first scenario test fifo with some timeout where child thread
345 * puts data on the fifo on time. In second scenario test k_fifo_get with
346 * timeout of K_NO_WAIT and the fifo should be filled by the child thread
347 * based on the data availability on another fifo. In third scenario test
348 * k_fifo_get with timeout of K_FOREVER and the fifo should be filled by
349 * the child thread based on the data availability on another fifo.
350 * @see k_fifo_get(), k_fifo_put()
351 */
ZTEST(fifo_timeout_1cpu,test_timeout_fifo_thread)352 ZTEST(fifo_timeout_1cpu, test_timeout_fifo_thread)
353 {
354 void *packet, *scratch_packet;
355 struct reply_packet reply_packet;
356 uint32_t start_time, timeout;
357
358 k_msleep(1); /* Align to ticks */
359
360 /*
361 * Test fifo with some timeout and child thread that puts
362 * data on the fifo on time
363 */
364 timeout = 10U;
365 start_time = k_cycle_get_32();
366
367 tid[0] = k_thread_create(&ttdata[0], ttstack[0], TSTACK_SIZE,
368 test_thread_put_timeout, &fifo_timeout[0],
369 &timeout, NULL,
370 FIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
371
372 packet = k_fifo_get(&fifo_timeout[0], K_MSEC(timeout + 10));
373 zassert_true(packet != NULL);
374 zassert_true(is_timeout_in_range(start_time, timeout));
375 put_scratch_packet(packet);
376
377 /*
378 * Test k_fifo_get with timeout of K_NO_WAIT and the fifo
379 * should be filled be filled by the child thread based on
380 * the data availability on another fifo. In this test child
381 * thread does not find data on fifo.
382 */
383 tid[0] = k_thread_create(&ttdata[0], ttstack[0], TSTACK_SIZE,
384 test_thread_timeout_reply_values,
385 &reply_packet, NULL, NULL,
386 FIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
387
388 k_yield();
389 packet = k_fifo_get(&timeout_order_fifo, K_NO_WAIT);
390 zassert_true(packet != NULL);
391 zassert_false(reply_packet.reply);
392
393 /*
394 * Test k_fifo_get with timeout of K_NO_WAIT and the fifo
395 * should be filled be filled by the child thread based on
396 * the data availability on another fifo. In this test child
397 * thread does find data on fifo.
398 */
399 scratch_packet = get_scratch_packet();
400 k_fifo_put(&fifo_timeout[0], scratch_packet);
401
402 tid[0] = k_thread_create(&ttdata[0], ttstack[0], TSTACK_SIZE,
403 test_thread_timeout_reply_values,
404 &reply_packet, NULL, NULL,
405 FIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
406
407 k_yield();
408 packet = k_fifo_get(&timeout_order_fifo, K_NO_WAIT);
409 zassert_true(packet != NULL);
410 zassert_true(reply_packet.reply);
411 put_scratch_packet(scratch_packet);
412
413 /*
414 * Test k_fifo_get with timeout of K_FOREVER and the fifo
415 * should be filled be filled by the child thread based on
416 * the data availability on another fifo. In this test child
417 * thread does find data on fifo.
418 */
419 scratch_packet = get_scratch_packet();
420 k_fifo_put(&fifo_timeout[0], scratch_packet);
421
422 tid[0] = k_thread_create(&ttdata[0], ttstack[0], TSTACK_SIZE,
423 test_thread_timeout_reply_values_wfe,
424 &reply_packet, NULL, NULL,
425 FIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
426
427 packet = k_fifo_get(&timeout_order_fifo, K_FOREVER);
428 zassert_true(packet != NULL);
429 zassert_true(reply_packet.reply);
430 put_scratch_packet(scratch_packet);
431 }
432
433 /**
434 * @brief Test fifo with different timeouts
435 * @details test multiple threads pending on the same fifo with
436 * different timeouts
437 * @see k_fifo_get(), k_fifo_put()
438 */
ZTEST(fifo_timeout_1cpu,test_timeout_threads_pend_on_fifo)439 ZTEST(fifo_timeout_1cpu, test_timeout_threads_pend_on_fifo)
440 {
441 int32_t rv, test_data_size;
442
443 /*
444 * Test multiple threads pending on the same
445 * fifo with different timeouts
446 */
447 test_data_size = ARRAY_SIZE(timeout_order_data);
448 rv = test_multiple_threads_pending(timeout_order_data, test_data_size);
449 zassert_equal(rv, TC_PASS);
450 }
451
452 /**
453 * @brief Test multiple fifos with different timeouts
454 * @details test multiple threads pending on different fifos
455 * with different timeouts
456 * @see k_fifo_get(), k_fifo_put()
457 */
ZTEST(fifo_timeout_1cpu,test_timeout_threads_pend_on_dual_fifos)458 ZTEST(fifo_timeout_1cpu, test_timeout_threads_pend_on_dual_fifos)
459 {
460 int32_t rv, test_data_size;
461
462 /*
463 * Test multiple threads pending on different
464 * fifos with different timeouts
465 */
466 test_data_size = ARRAY_SIZE(timeout_order_data_mult_fifo);
467 rv = test_multiple_threads_pending(timeout_order_data_mult_fifo,
468 test_data_size);
469 zassert_equal(rv, TC_PASS);
470
471 }
472
473 /**
474 * @brief Test same fifo with different timeouts
475 * @details test multiple threads pending on the same fifo with
476 * different timeouts but getting the data in time
477 * @see k_fifo_get(), k_fifo_put()
478 */
ZTEST(fifo_timeout_1cpu,test_timeout_threads_pend_fail_on_fifo)479 ZTEST(fifo_timeout_1cpu, test_timeout_threads_pend_fail_on_fifo)
480 {
481 int32_t rv, test_data_size;
482
483 /*
484 * Test multiple threads pending on same
485 * fifo with different timeouts, but getting
486 * the data in time, except the last one.
487 */
488 test_data_size = ARRAY_SIZE(timeout_order_data);
489 rv = test_multiple_threads_get_data(timeout_order_data, test_data_size);
490 zassert_equal(rv, TC_PASS);
491 }
492
493 /**
494 * @brief Test fifo init
495 * @see k_fifo_init(), k_fifo_put()
496 */
test_timeout_setup(void)497 static void *test_timeout_setup(void)
498 {
499 intptr_t ii;
500
501 /* Init kernel objects */
502 k_fifo_init(&fifo_timeout[0]);
503 k_fifo_init(&fifo_timeout[1]);
504 k_fifo_init(&timeout_order_fifo);
505 k_fifo_init(&scratch_fifo_packets_fifo);
506
507 /* Fill scratch fifo */
508 for (ii = 0; ii < NUM_SCRATCH_FIFO_PACKETS; ii++) {
509 scratch_fifo_packets[ii].data_if_needed = (void *)ii;
510 k_fifo_put(&scratch_fifo_packets_fifo,
511 (void *)&scratch_fifo_packets[ii]);
512 }
513
514 return NULL;
515 }
516 /**
517 * @}
518 */
519
520 ZTEST_SUITE(fifo_timeout, NULL, test_timeout_setup, NULL, NULL, NULL);
521
522 ZTEST_SUITE(fifo_timeout_1cpu, NULL, test_timeout_setup,
523 ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL);
524