1 /*
2 * Copyright (c) 2012-2014 Wind River Systems, Inc.
3 * Copyright (c) 2017, 2023 Intel Corporation.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /**
9 * @file
10 *
11 * @brief Measure time from ISR back to interrupted thread
12 *
13 * This file covers three interrupt to threads scenarios:
14 * 1. ISR returning to the interrupted kernel thread
15 * 2. ISR returning to a different (kernel) thread
16 * 3. ISR returning to a different (user) thread
17 *
18 * In all three scenarios, the source of the ISR is a software generated
19 * interrupt originating from a kernel thread. Ideally, these tests would
20 * also cover the scenarios where the interrupted thread is a user thread.
21 * However, some implementations of the irq_offload() routine lock interrupts,
22 * which is not allowed in userspace.
23 */
24
25 #include <zephyr/kernel.h>
26 #include "utils.h"
27 #include "timing_sc.h"
28
29 #include <zephyr/irq_offload.h>
30
31 static K_SEM_DEFINE(isr_sem, 0, 1);
32
33 /**
34 * @brief Test ISR used to measure time to return to thread
35 *
36 * The interrupt handler gets the first timestamp used in the test.
37 * It then copies the timetsamp into a message queue and returns.
38 */
test_isr(const void * arg)39 static void test_isr(const void *arg)
40 {
41 struct k_sem *sem = (struct k_sem *)arg;
42
43 if (arg != NULL) {
44 k_sem_give(sem);
45 }
46
47 timestamp.sample = timing_timestamp_get();
48 }
49
50 /**
51 * @brief Measure time to return from interrupt
52 *
53 * This function is used to measure the time it takes to return from an
54 * interrupt.
55 */
int_to_interrupted_thread(uint32_t num_iterations,uint64_t * sum)56 static void int_to_interrupted_thread(uint32_t num_iterations, uint64_t *sum)
57 {
58 timing_t start;
59 timing_t finish;
60
61 *sum = 0ull;
62
63 for (uint32_t i = 0; i < num_iterations; i++) {
64 irq_offload(test_isr, NULL);
65 finish = timing_timestamp_get();
66 start = timestamp.sample;
67
68 *sum += timing_cycles_get(&start, &finish);
69 }
70 }
71
start_thread_entry(void * p1,void * p2,void * p3)72 static void start_thread_entry(void *p1, void *p2, void *p3)
73 {
74 uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
75 struct k_sem *sem = p2;
76
77 ARG_UNUSED(p3);
78
79 uint64_t sum = 0ull;
80 timing_t start;
81 timing_t finish;
82
83 /* Ensure that <isr_sem> is unavailable */
84
85 (void) k_sem_take(sem, K_NO_WAIT);
86 k_thread_start(&alt_thread);
87
88 for (uint32_t i = 0; i < num_iterations; i++) {
89
90 /* 1. Wait on an unavailable semaphore */
91
92 k_sem_take(sem, K_FOREVER);
93
94 /* 3. Obtain the start and finish timestamps */
95
96 finish = timing_timestamp_get();
97 start = timestamp.sample;
98
99 sum += timing_cycles_get(&start, &finish);
100 }
101
102 timestamp.cycles = sum;
103 }
104
alt_thread_entry(void * p1,void * p2,void * p3)105 static void alt_thread_entry(void *p1, void *p2, void *p3)
106 {
107 uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
108 struct k_sem *sem = p2;
109
110 ARG_UNUSED(p3);
111
112 for (uint32_t i = 0; i < num_iterations; i++) {
113
114 /* 2. Trigger the test_isr() to execute */
115
116 irq_offload(test_isr, sem);
117
118 /*
119 * ISR expected to have awakened higher priority start_thread
120 * thereby preempting alt_thread.
121 */
122 }
123
124 k_thread_join(&start_thread, K_FOREVER);
125 }
126
int_to_another_thread(uint32_t num_iterations,uint64_t * sum,uint32_t options)127 static void int_to_another_thread(uint32_t num_iterations, uint64_t *sum,
128 uint32_t options)
129 {
130 int priority;
131 *sum = 0ull;
132
133 priority = k_thread_priority_get(k_current_get());
134
135 k_thread_create(&start_thread, start_stack,
136 K_THREAD_STACK_SIZEOF(start_stack),
137 start_thread_entry,
138 (void *)(uintptr_t)num_iterations, &isr_sem, NULL,
139 priority - 2, options, K_FOREVER);
140
141 k_thread_create(&alt_thread, alt_stack,
142 K_THREAD_STACK_SIZEOF(alt_stack),
143 alt_thread_entry,
144 (void *)(uintptr_t)num_iterations, &isr_sem, NULL,
145 priority - 1, 0, K_FOREVER);
146
147 #if CONFIG_USERSPACE
148 if (options != 0) {
149 k_thread_access_grant(&start_thread, &isr_sem, &alt_thread);
150 }
151 #endif
152
153 k_thread_start(&start_thread);
154
155 k_thread_join(&alt_thread, K_FOREVER);
156
157 *sum = timestamp.cycles;
158 }
159
160 /**
161 *
162 * @brief The test main function
163 *
164 * @return 0 on success
165 */
int_to_thread(uint32_t num_iterations)166 int int_to_thread(uint32_t num_iterations)
167 {
168 uint64_t sum;
169 char description[120];
170
171 timing_start();
172 TICK_SYNCH();
173
174 int_to_interrupted_thread(num_iterations, &sum);
175
176 sum -= timestamp_overhead_adjustment(0, 0);
177
178 snprintf(description, sizeof(description),
179 "%-40s - Return from ISR to interrupted thread",
180 "isr.resume.interrupted.thread.kernel");
181 PRINT_STATS_AVG(description, (uint32_t)sum, num_iterations, false, "");
182
183 /* ************** */
184
185 int_to_another_thread(num_iterations, &sum, 0);
186
187 sum -= timestamp_overhead_adjustment(0, 0);
188
189 snprintf(description, sizeof(description),
190 "%-40s - Return from ISR to another thread",
191 "isr.resume.different.thread.kernel");
192 PRINT_STATS_AVG(description, (uint32_t)sum, num_iterations, false, "");
193
194 /* ************** */
195
196 #if CONFIG_USERSPACE
197 int_to_another_thread(num_iterations, &sum, K_USER);
198
199 sum -= timestamp_overhead_adjustment(0, K_USER);
200
201 snprintf(description, sizeof(description),
202 "%-40s - Return from ISR to another thread",
203 "isr.resume.different.thread.user");
204 PRINT_STATS_AVG(description, (uint32_t)sum, num_iterations, false, "");
205 #endif
206
207 timing_stop();
208 return 0;
209 }
210