1 /*
2  * Copyright (c) 2021 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/ztest_error_hook.h>
9 
10 /* Macro declarations */
11 #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
12 #define SEM_INIT_VAL (0U)
13 #define SEM_MAX_VAL  (3U)
14 #define TOTAL_MAX (4U)
15 #define STACK_NUMS 5
16 #define PRIO 5
17 #define LOW_PRIO 8
18 #define HIGH_PRIO 2
19 
20 static K_THREAD_STACK_ARRAY_DEFINE(multi_stack_give, STACK_NUMS, STACK_SIZE);
21 static K_THREAD_STACK_ARRAY_DEFINE(multi_stack_take, STACK_NUMS, STACK_SIZE);
22 
23 static struct k_thread multi_tid_give[STACK_NUMS];
24 static struct k_thread multi_tid_take[STACK_NUMS];
25 static struct k_sem usage_sem, sync_sem, limit_sem, uninit_sem;
26 static ZTEST_DMEM int flag;
27 static ZTEST_DMEM atomic_t atomic_count;
28 
29 /**
30  * @defgroup kernel_sys_sem_tests Semaphore
31  * @ingroup all_tests
32  * @{
33  * @}
34  */
35 
sem_thread_give_uninit(void * p1,void * p2,void * p3)36 static void sem_thread_give_uninit(void *p1, void *p2, void *p3)
37 {
38 	ztest_set_fault_valid(true);
39 
40 	/* use sem without initialise */
41 	k_sem_give(&uninit_sem);
42 
43 	ztest_test_fail();
44 }
45 
sem_thread_give(void * p1,void * p2,void * p3)46 static void sem_thread_give(void *p1, void *p2, void *p3)
47 {
48 	flag = 1;
49 	k_sem_give(&usage_sem);
50 }
51 
thread_low_prio_sem_take(void * p1,void * p2,void * p3)52 static void thread_low_prio_sem_take(void *p1, void *p2, void *p3)
53 {
54 	k_sem_take(&usage_sem, K_FOREVER);
55 
56 	flag = LOW_PRIO;
57 	k_sem_give(&sync_sem);
58 }
59 
thread_high_prio_sem_take(void * p1,void * p2,void * p3)60 static void thread_high_prio_sem_take(void *p1, void *p2, void *p3)
61 {
62 	k_sem_take(&usage_sem, K_FOREVER);
63 
64 	flag = HIGH_PRIO;
65 	k_sem_give(&sync_sem);
66 }
67 
68 /**
69  * @brief Test semaphore usage with multiple thread
70  *
71  * @details Using semaphore with some situations
72  * - Use a uninitialized semaphore
73  * - Use semaphore normally
74  * - Use semaphore with different priority threads
75  *
76  * @ingroup kernel_sys_sem_tests
77  */
ZTEST_USER(kernel_sys_sem,test_multiple_thread_sem_usage)78 ZTEST_USER(kernel_sys_sem, test_multiple_thread_sem_usage)
79 {
80 	k_sem_init(&usage_sem, SEM_INIT_VAL, SEM_MAX_VAL);
81 	k_sem_init(&sync_sem, SEM_INIT_VAL, SEM_MAX_VAL);
82 	/* Use a semaphore to synchronize processing between threads */
83 	k_sem_reset(&usage_sem);
84 	k_thread_create(&multi_tid_give[0], multi_stack_give[0], STACK_SIZE,
85 			sem_thread_give, NULL, NULL,
86 			NULL, PRIO, K_USER | K_INHERIT_PERMS,
87 			K_NO_WAIT);
88 
89 	k_sem_take(&usage_sem, K_FOREVER);
90 	zassert_equal(flag, 1, "value != 1");
91 	zassert_equal(k_sem_count_get(&usage_sem), 0, "sem not be took");
92 
93 	k_sem_reset(&usage_sem);
94 	/* Use sem with different priority thread */
95 	k_thread_create(&multi_tid_take[0], multi_stack_take[0], STACK_SIZE,
96 			thread_low_prio_sem_take, NULL, NULL,
97 			NULL, LOW_PRIO, K_USER | K_INHERIT_PERMS,
98 			K_NO_WAIT);
99 
100 	k_thread_create(&multi_tid_take[1], multi_stack_take[1], STACK_SIZE,
101 			thread_high_prio_sem_take, NULL, NULL,
102 			NULL, HIGH_PRIO, K_USER | K_INHERIT_PERMS,
103 			K_NO_WAIT);
104 
105 	k_sleep(K_MSEC(50));
106 
107 	/* Verify if the high prio thread take sem first */
108 	k_sem_give(&usage_sem);
109 	k_sem_take(&sync_sem, K_FOREVER);
110 	zassert_equal(flag, HIGH_PRIO, "high prio value error");
111 
112 	k_sem_give(&usage_sem);
113 	k_sem_take(&sync_sem, K_FOREVER);
114 	zassert_equal(flag, LOW_PRIO, "low prio value error");
115 
116 	k_thread_join(&multi_tid_give[0], K_FOREVER);
117 	k_thread_join(&multi_tid_take[0], K_FOREVER);
118 	k_thread_join(&multi_tid_take[1], K_FOREVER);
119 
120 	k_thread_create(&multi_tid_give[1], multi_stack_give[1], STACK_SIZE,
121 			sem_thread_give_uninit, NULL, NULL,
122 			NULL, PRIO, K_USER | K_INHERIT_PERMS,
123 			K_NO_WAIT);
124 	k_sleep(K_MSEC(20));
125 	k_thread_join(&multi_tid_give[1], K_FOREVER);
126 }
127 
multi_thread_sem_give(void * p1,void * p2,void * p3)128 static void multi_thread_sem_give(void *p1, void *p2, void *p3)
129 {
130 	int count;
131 
132 	(void)atomic_inc(&atomic_count);
133 	count = atomic_get(&atomic_count);
134 	k_sem_give(&limit_sem);
135 
136 	if (count < TOTAL_MAX) {
137 		zassert_equal(k_sem_count_get(&limit_sem), count, "multi get sem error");
138 	} else {
139 		zassert_equal(k_sem_count_get(&limit_sem), SEM_MAX_VAL, "count > SEM_MAX_VAL");
140 	}
141 
142 	k_sem_take(&sync_sem, K_FOREVER);
143 }
144 
multi_thread_sem_take(void * p1,void * p2,void * p3)145 static void multi_thread_sem_take(void *p1, void *p2, void *p3)
146 {
147 	int count;
148 
149 	k_sem_take(&limit_sem, K_FOREVER);
150 	(void)atomic_dec(&atomic_count);
151 	count = atomic_get(&atomic_count);
152 
153 	if (count >= 0) {
154 		zassert_equal(k_sem_count_get(&limit_sem), count, "multi take sem error");
155 	} else {
156 		zassert_equal(k_sem_count_get(&limit_sem), 0, "count < SEM_INIT_VAL");
157 	}
158 
159 	k_sem_give(&sync_sem);
160 }
161 
162 /**
163  * @brief Test max semaphore can be give and take with multiple thread
164  *
165  * @details
166  * - Define and initialize semaphore and thread.
167  * - Give sem by multiple threads.
168  * - Verify more than max count about semaphore can reach.
169  * - Take sem by multiple threads and verify if sem count is correct.
170  *
171  * @ingroup kernel_sys_sem_tests
172  */
ZTEST_USER(kernel_sys_sem,test_multi_thread_sem_limit)173 ZTEST_USER(kernel_sys_sem, test_multi_thread_sem_limit)
174 {
175 	k_sem_init(&limit_sem, SEM_INIT_VAL, SEM_MAX_VAL);
176 	k_sem_init(&sync_sem, SEM_INIT_VAL, SEM_MAX_VAL);
177 
178 	(void)atomic_set(&atomic_count, 0);
179 	for (int i = 1; i <= TOTAL_MAX; i++) {
180 		k_thread_create(&multi_tid_give[i], multi_stack_give[i], STACK_SIZE,
181 				multi_thread_sem_give, NULL, NULL, NULL,
182 				i, K_USER | K_INHERIT_PERMS, K_NO_WAIT);
183 	}
184 
185 	k_sleep(K_MSEC(50));
186 
187 	(void)atomic_set(&atomic_count, SEM_MAX_VAL);
188 	for (int i = 1; i <= TOTAL_MAX; i++) {
189 		k_thread_create(&multi_tid_take[i], multi_stack_take[i], STACK_SIZE,
190 				multi_thread_sem_take, NULL, NULL, NULL,
191 				PRIO, K_USER | K_INHERIT_PERMS, K_NO_WAIT);
192 	}
193 
194 	/* cleanup all threads for the following test cases */
195 	k_sleep(K_MSEC(50));
196 	for (int i = 1; i <= TOTAL_MAX; i++) {
197 		k_thread_abort(&multi_tid_give[i]);
198 		k_thread_abort(&multi_tid_take[i]);
199 	}
200 }
201 
test_init(void)202 void *test_init(void)
203 {
204 	k_thread_access_grant(k_current_get(), &usage_sem, &sync_sem, &limit_sem,
205 			      &multi_tid_give[0], &multi_tid_give[1],
206 			      &multi_tid_give[2], &multi_tid_give[3],
207 			      &multi_tid_give[4], &multi_tid_take[4],
208 			      &multi_tid_take[2], &multi_tid_take[3],
209 			      &multi_tid_take[0], &multi_tid_take[1],
210 			      &multi_tid_give[5], &multi_tid_take[5],
211 			      &multi_stack_take[0], &multi_stack_take[1],
212 			      &multi_stack_take[3], &multi_stack_take[4],
213 			      &multi_stack_take[2], &multi_stack_give[0],
214 			      &multi_stack_give[1], &multi_stack_give[2],
215 			      &multi_stack_give[3], &multi_stack_give[4]);
216 	return NULL;
217 }
218 ZTEST_SUITE(kernel_sys_sem, NULL, test_init,
219 	    ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL);
220