1 /*
2  * Copyright (c) 2016 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/sys/atomic.h>
9 
10 #define LOOP 10
11 #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
12 #define THREAD_NUM 4
13 #define SLAB_NUM 2
14 #define TIMEOUT K_MSEC(200)
15 #define BLK_NUM 3
16 #define BLK_ALIGN 8
17 #define BLK_SIZE1 16
18 #define BLK_SIZE2 8
19 
20 /* Blocks per slab.  Note this number carefully, because if it is
21  * smaller than this the test can deadlock.  There are 4 threads
22  * allocating 3 blocks each, so it's possible in exhaustion situations
23  * for all four threads to have already allocated 2 blocks, with three
24  * of them having gone to sleep waiting on a fourth.  There must
25  * remain at least one free block for that final allocation, otherwise
26  * all four threads will end up sleeping forever (or until the
27  * allocation timeout hits and the test fails).
28  */
29 #define SLAB_BLOCKS (THREAD_NUM * (BLK_NUM - 1) + 1)
30 
31 K_MEM_SLAB_DEFINE(mslab1, BLK_SIZE1, SLAB_BLOCKS, BLK_ALIGN);
32 static struct k_mem_slab mslab2, *slabs[SLAB_NUM] = { &mslab1, &mslab2 };
33 static K_THREAD_STACK_ARRAY_DEFINE(tstack, THREAD_NUM, STACK_SIZE);
34 static struct k_thread tdata[THREAD_NUM];
35 static char __aligned(BLK_ALIGN) tslab[BLK_SIZE2 * SLAB_BLOCKS];
36 static atomic_t slab_id;
37 static volatile bool success[THREAD_NUM];
38 
39 /* thread entry simply invoke the APIs*/
tmslab_api(void * p1,void * p2,void * p3)40 static void tmslab_api(void *p1, void *p2, void *p3)
41 {
42 	void *block[BLK_NUM];
43 	int id = atomic_inc(&slab_id);
44 	struct k_mem_slab *slab = slabs[id % SLAB_NUM];
45 	int j = LOOP, ret;
46 
47 	while (j--) {
48 		(void)memset(block, 0, sizeof(block));
49 
50 		for (int i = 0; i < BLK_NUM; i++) {
51 			ret = k_mem_slab_alloc(slab, &block[i], TIMEOUT);
52 			zassert_false(ret, "memory is not allocated");
53 		}
54 		for (int i = 0; i < BLK_NUM; i++) {
55 			if (block[i]) {
56 				k_mem_slab_free(slab, block[i]);
57 				block[i] = NULL;
58 			}
59 		}
60 	}
61 	success[id] = true;
62 }
63 
64 /* test cases*/
65 /**
66  * @brief Verify alloc and free from multiple equal priority threads
67  *
68  * @details Test creates 4 preemptive threads of equal priority. Then
69  * validates the synchronization of threads by allocating and
70  * freeing up the memory blocks in memory slab.
71  *
72  * @ingroup kernel_memory_slab_tests
73  */
ZTEST(mslab_threadsafe,test_mslab_threadsafe)74 ZTEST(mslab_threadsafe, test_mslab_threadsafe)
75 {
76 	k_tid_t tid[THREAD_NUM];
77 
78 	k_mem_slab_init(&mslab2, tslab, BLK_SIZE2, SLAB_BLOCKS);
79 
80 	/* create multiple threads to invoke same memory slab APIs*/
81 	for (int i = 0; i < THREAD_NUM; i++) {
82 		tid[i] = k_thread_create(&tdata[i], tstack[i], STACK_SIZE,
83 					 tmslab_api, NULL, NULL, NULL,
84 					 K_PRIO_PREEMPT(1), 0, K_NO_WAIT);
85 	}
86 
87 	/* wait for completion */
88 	for (int i = 0; i < THREAD_NUM; i++) {
89 		int ret = k_thread_join(tid[i], K_FOREVER);
90 
91 		zassert_false(ret, "k_thread_join() failed");
92 		zassert_true(success[i], "thread %d failed", i);
93 	}
94 }
95