1 /*
2  * Copyright (c) 2012-2015 Wind River Systems, Inc.
3  * Copyright (c) 2023 Intel Corporation.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /*
9  * @file measure time for sema lock and release
10  *
11  * This file contains the test that measures semaphore give and take time
12  * in the kernel. There is no contention on the semaphore being tested.
13  */
14 
15 #include <zephyr/kernel.h>
16 #include <zephyr/timing/timing.h>
17 #include "utils.h"
18 #include "timing_sc.h"
19 
20 static struct k_sem  sem;
21 
alt_thread_entry(void * p1,void * p2,void * p3)22 static void alt_thread_entry(void *p1, void *p2, void *p3)
23 {
24 	uint32_t   num_iterations = (uint32_t)(uintptr_t)p1;
25 	timing_t   mid;
26 
27 	ARG_UNUSED(p2);
28 	ARG_UNUSED(p3);
29 
30 	for (uint32_t i = 0; i < num_iterations; i++) {
31 
32 		/*
33 		 * 2. Give the semaphore, thereby forcing a context switch back
34 		 * to <start_thread>.
35 		 */
36 
37 		mid = timing_timestamp_get();
38 		k_sem_give(&sem);
39 
40 		/* 5. Share the <mid> timestamp. */
41 
42 		timestamp.sample = mid;
43 
44 		/* 6. Give <sem> so <start_thread> resumes execution */
45 
46 		k_sem_give(&sem);
47 	}
48 }
49 
start_thread_entry(void * p1,void * p2,void * p3)50 static void start_thread_entry(void *p1, void *p2, void *p3)
51 {
52 	uint32_t   num_iterations = (uint32_t)(uintptr_t)p1;
53 	timing_t   start;
54 	timing_t   mid;
55 	timing_t   finish;
56 	uint32_t   i;
57 	uint64_t   take_sum = 0ull;
58 	uint64_t   give_sum = 0ull;
59 
60 	ARG_UNUSED(p2);
61 	ARG_UNUSED(p3);
62 
63 	k_thread_start(&alt_thread);
64 
65 	for (i = 0; i < num_iterations; i++) {
66 
67 		/*
68 		 * 1. Block on taking the semaphore and force a context switch
69 		 * to <alt_thread>.
70 		 */
71 
72 		start = timing_timestamp_get();
73 		k_sem_take(&sem, K_FOREVER);
74 
75 		/* 3. Get the <finish> timestamp. */
76 
77 		finish = timing_timestamp_get();
78 
79 		/*
80 		 * 4. Let <alt_thread> run so it can share its <mid>
81 		 * timestamp.
82 		 */
83 
84 		k_sem_take(&sem, K_FOREVER);
85 
86 		/* 7. Retrieve the <mid> timestamp */
87 
88 		mid = timestamp.sample;
89 
90 		take_sum += timing_cycles_get(&start, &mid);
91 		give_sum += timing_cycles_get(&mid, &finish);
92 	}
93 
94 	k_thread_join(&alt_thread, K_FOREVER);
95 
96 	/* Share the totals with the main thread */
97 
98 	timestamp.cycles = take_sum;
99 
100 	k_sem_take(&sem, K_FOREVER);
101 
102 	timestamp.cycles = give_sum;
103 }
104 
sema_context_switch(uint32_t num_iterations,uint32_t start_options,uint32_t alt_options)105 void sema_context_switch(uint32_t num_iterations,
106 			 uint32_t start_options, uint32_t alt_options)
107 {
108 	uint64_t  cycles;
109 	char tag[50];
110 	char description[120];
111 	int  priority;
112 
113 	timing_start();
114 
115 	priority = k_thread_priority_get(k_current_get());
116 
117 	k_thread_create(&start_thread, start_stack,
118 			K_THREAD_STACK_SIZEOF(start_stack),
119 			start_thread_entry,
120 			(void *)(uintptr_t)num_iterations, NULL, NULL,
121 			priority - 2, start_options, K_FOREVER);
122 
123 	k_thread_create(&alt_thread, alt_stack,
124 			K_THREAD_STACK_SIZEOF(alt_stack),
125 			alt_thread_entry,
126 			(void *)(uintptr_t)num_iterations, NULL, NULL,
127 			priority - 1, alt_options, K_FOREVER);
128 
129 	k_thread_access_grant(&start_thread, &sem, &alt_thread);
130 
131 	k_thread_access_grant(&alt_thread, &sem);
132 
133 	/* Start the test threads */
134 
135 	k_thread_start(&start_thread);
136 
137 	/* Retrieve the number of cycles spent taking the semaphore */
138 
139 	cycles = timestamp.cycles;
140 	cycles -= timestamp_overhead_adjustment(start_options, alt_options);
141 
142 	snprintf(tag, sizeof(tag),
143 		 "semaphore.take.blocking.%c_to_%c",
144 		 ((start_options & K_USER) == K_USER) ? 'u' : 'k',
145 		 ((alt_options & K_USER) == K_USER) ? 'u' : 'k');
146 	snprintf(description, sizeof(description),
147 		 "%-40s - Take a semaphore (context switch)", tag);
148 	PRINT_STATS_AVG(description, (uint32_t)cycles,
149 			num_iterations, false, "");
150 
151 	/* Unblock <start_thread> */
152 
153 	k_sem_give(&sem);
154 
155 	/* Retrieve the number of cycles spent taking the semaphore */
156 
157 	cycles = timestamp.cycles;
158 	cycles -= timestamp_overhead_adjustment(start_options, alt_options);
159 
160 	snprintf(tag, sizeof(tag),
161 		 "semaphore.give.wake+ctx.%c_to_%c",
162 		 ((alt_options & K_USER) == K_USER) ? 'u' : 'k',
163 		 ((start_options & K_USER) == K_USER) ? 'u' : 'k');
164 	snprintf(description, sizeof(description),
165 		 "%-40s - Give a semaphore (context switch)", tag);
166 	PRINT_STATS_AVG(description, (uint32_t)cycles,
167 			num_iterations, false, "");
168 
169 	k_thread_join(&start_thread, K_FOREVER);
170 
171 	timing_stop();
172 
173 	return;
174 }
175 
176 /**
177  * This is the entry point for the test that performs uncontested operations
178  * on the semaphore. It gives the semaphore many times, takes the semaphore
179  * many times and then sends the results back to the main thread.
180  */
immediate_give_take(void * p1,void * p2,void * p3)181 static void immediate_give_take(void *p1, void *p2, void *p3)
182 {
183 	uint32_t   num_iterations = (uint32_t)(uintptr_t)p1;
184 	timing_t   start;
185 	timing_t   finish;
186 	uint64_t   give_cycles;
187 	uint64_t   take_cycles;
188 
189 	ARG_UNUSED(p2);
190 	ARG_UNUSED(p3);
191 
192 	/* 1. Give a semaphore. No threads are waiting on it */
193 
194 	start = timing_timestamp_get();
195 
196 	for (uint32_t i = 0; i < num_iterations; i++) {
197 		k_sem_give(&sem);
198 	}
199 
200 	finish = timing_timestamp_get();
201 	give_cycles = timing_cycles_get(&start, &finish);
202 
203 	/* 2. Take a semaphore--no contention */
204 
205 	start = timing_timestamp_get();
206 
207 	for (uint32_t i = 0; i < num_iterations; i++) {
208 		k_sem_take(&sem, K_NO_WAIT);
209 	}
210 
211 	finish = timing_timestamp_get();
212 	take_cycles = timing_cycles_get(&start, &finish);
213 
214 	/* 3. Post the number of cycles spent giving the semaphore */
215 
216 	timestamp.cycles = give_cycles;
217 
218 	/* 4. Wait for the main thread to retrieve the data */
219 
220 	k_sem_take(&sem, K_FOREVER);
221 
222 	/* 7. Post the number of cycles spent taking the semaphore */
223 
224 	timestamp.cycles = take_cycles;
225 }
226 
227 
228 /**
229  *
230  * @brief The function tests semaphore test/signal time
231  *
232  * The routine performs unlock the quite amount of semaphores and then
233  * acquires them in order to measure the necessary time.
234  *
235  * @return 0 on success
236  */
sema_test_signal(uint32_t num_iterations,uint32_t options)237 int sema_test_signal(uint32_t num_iterations, uint32_t options)
238 {
239 	uint64_t cycles;
240 	int priority;
241 	char tag[50];
242 	char description[120];
243 
244 	timing_start();
245 
246 	k_sem_init(&sem, 0, num_iterations);
247 
248 	priority = k_thread_priority_get(k_current_get());
249 
250 	k_thread_create(&start_thread, start_stack,
251 			K_THREAD_STACK_SIZEOF(start_stack),
252 			immediate_give_take,
253 			(void *)(uintptr_t)num_iterations, NULL, NULL,
254 			priority - 1, options, K_FOREVER);
255 
256 	k_thread_access_grant(&start_thread, &sem);
257 	k_thread_start(&start_thread);
258 
259 	/* 5. Retrieve the number of cycles spent giving the semaphore */
260 
261 	cycles = timestamp.cycles;
262 
263 	snprintf(tag, sizeof(tag),
264 		 "semaphore.give.immediate.%s",
265 		 (options & K_USER) == K_USER ? "user" : "kernel");
266 	snprintf(description, sizeof(description),
267 		 "%-40s - Give a semaphore (no waiters)", tag);
268 
269 	PRINT_STATS_AVG(description, (uint32_t)cycles,
270 			num_iterations, false, "");
271 
272 	/* 6. Unblock <start_thread> */
273 
274 	k_sem_give(&sem);
275 
276 	/* 8. Wait for <start_thread> to finish */
277 
278 	k_thread_join(&start_thread, K_FOREVER);
279 
280 	/* 9. Retrieve the number of cycles spent taking the semaphore */
281 
282 	cycles = timestamp.cycles;
283 
284 	snprintf(tag, sizeof(tag),
285 		 "semaphore.take.immediate.%s",
286 		 (options & K_USER) == K_USER ? "user" : "kernel");
287 	snprintf(description, sizeof(description),
288 		 "%-40s - Take a semaphore (no blocking)", tag);
289 
290 	PRINT_STATS_AVG(description, (uint32_t)cycles,
291 			num_iterations, false, "");
292 
293 	timing_stop();
294 
295 	return 0;
296 }
297