1 /*
2  * Copyright (c) 2024 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*
8  * @file measure time for various condition variable operations
9  * 1. Block waiting for a condition variable
10  * 2. Signal a condition variable (with context switch)
11  */
12 
13 #include <zephyr/kernel.h>
14 #include <zephyr/timing/timing.h>
15 #include "utils.h"
16 #include "timing_sc.h"
17 
18 static K_CONDVAR_DEFINE(condvar);
19 static K_MUTEX_DEFINE(mutex);
20 
start_thread_entry(void * p1,void * p2,void * p3)21 static void start_thread_entry(void *p1, void *p2, void *p3)
22 {
23 	uint32_t  num_iterations = (uint32_t)(uintptr_t)p1;
24 	uint32_t  i;
25 	timing_t  start;
26 	timing_t  finish;
27 	uint64_t  sum[2] = {0ull, 0ull};
28 
29 	k_mutex_lock(&mutex, K_FOREVER);
30 
31 	k_thread_start(&alt_thread);
32 
33 	for (i = 0; i < num_iterations; i++) {
34 		/* 1. Get the first timestamp and block on condvar */
35 
36 		start = timing_timestamp_get();
37 		k_condvar_wait(&condvar, &mutex, K_FOREVER);
38 
39 		/* 3. Get the final timstamp */
40 
41 		finish = timing_timestamp_get();
42 
43 		sum[0] += timing_cycles_get(&start, &timestamp.sample);
44 		sum[1] += timing_cycles_get(&timestamp.sample, &finish);
45 	}
46 
47 	/* Wait for alt_thread to finish */
48 
49 	k_thread_join(&alt_thread, K_FOREVER);
50 
51 	timestamp.cycles = sum[0];
52 	k_sem_take(&pause_sem, K_FOREVER);
53 
54 	timestamp.cycles = sum[1];
55 }
56 
alt_thread_entry(void * p1,void * p2,void * p3)57 static void alt_thread_entry(void *p1, void *p2, void *p3)
58 {
59 	uint32_t  num_iterations = (uint32_t)(uintptr_t)p1;
60 	uint32_t  i;
61 
62 	for (i = 0; i < num_iterations; i++) {
63 
64 		/* 2. Get midpoint timestamp and signal the condvar */
65 
66 		timestamp.sample = timing_timestamp_get();
67 		k_condvar_signal(&condvar);
68 	}
69 }
70 
condvar_blocking_ops(uint32_t num_iterations,uint32_t start_options,uint32_t alt_options)71 int condvar_blocking_ops(uint32_t num_iterations, uint32_t start_options,
72 			 uint32_t alt_options)
73 {
74 	int       priority;
75 	char      tag[50];
76 	char      description[120];
77 	uint64_t  cycles;
78 
79 	priority = k_thread_priority_get(k_current_get());
80 
81 	timing_start();
82 
83 	k_thread_create(&start_thread, start_stack,
84 			K_THREAD_STACK_SIZEOF(start_stack),
85 			start_thread_entry,
86 			(void *)(uintptr_t)num_iterations,
87 			NULL, NULL,
88 			priority - 2, start_options, K_FOREVER);
89 
90 	k_thread_create(&alt_thread, alt_stack,
91 			K_THREAD_STACK_SIZEOF(alt_stack),
92 			alt_thread_entry,
93 			(void *)(uintptr_t)num_iterations,
94 			NULL, NULL,
95 			priority - 1, alt_options, K_FOREVER);
96 
97 	k_thread_access_grant(&start_thread, &alt_thread,
98 			      &condvar, &mutex, &pause_sem);
99 	k_thread_access_grant(&alt_thread, &condvar);
100 
101 	/* Start test thread */
102 
103 	k_thread_start(&start_thread);
104 
105 	/* Stats gathered. Display them. */
106 
107 	snprintf(tag, sizeof(tag), "condvar.wait.blocking.%c_to_%c",
108 		 (start_options & K_USER) ? 'u' : 'k',
109 		 (alt_options & K_USER) ? 'u' : 'k');
110 	snprintf(description, sizeof(description),
111 		 "%-40s - Wait for a condvar (context switch)", tag);
112 
113 	cycles = timestamp.cycles;
114 	PRINT_STATS_AVG(description, (uint32_t)cycles,
115 			num_iterations, false, "");
116 
117 	k_sem_give(&pause_sem);
118 
119 	snprintf(tag, sizeof(tag), "condvar.signal.wake+ctx.%c_to_%c",
120 		 (alt_options & K_USER) ? 'u' : 'k',
121 		 (start_options & K_USER) ? 'u' : 'k');
122 	snprintf(description, sizeof(description),
123 		 "%-40s - Signal a condvar (context switch)", tag);
124 	cycles = timestamp.cycles;
125 	PRINT_STATS_AVG(description, (uint32_t)cycles,
126 			num_iterations, false, "");
127 
128 	k_thread_join(&start_thread, K_FOREVER);
129 
130 	timing_stop();
131 
132 	return 0;
133 }
134