1 /*
2  * Copyright (c) 2023 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*
8  * @file
9  * This file contains routines for implementing a timestamp system call
10  * as well as routines for determining the overhead associated with it.
11  */
12 
13 #include <zephyr/kernel.h>
14 #include "utils.h"
15 #include "timing_sc.h"
16 
17 BENCH_BMEM uint64_t timestamp_overhead;
18 #ifdef CONFIG_USERSPACE
19 BENCH_BMEM uint64_t user_timestamp_overhead;
20 #endif
21 
22 #define OVERHEAD_CALC_ITER 10
23 
z_impl_timing_timestamp_get(void)24 timing_t z_impl_timing_timestamp_get(void)
25 {
26 	return timing_counter_get();
27 }
28 
29 #ifdef CONFIG_USERSPACE
z_vrfy_timing_timestamp_get(void)30 timing_t z_vrfy_timing_timestamp_get(void)
31 {
32 	return z_impl_timing_timestamp_get();
33 }
34 #include <zephyr/syscalls/timing_timestamp_get_mrsh.c>
35 #endif
36 
start_thread_entry(void * p1,void * p2,void * p3)37 static void start_thread_entry(void *p1, void *p2, void *p3)
38 {
39 	uint32_t  num_iterations = (uint32_t)(uintptr_t)p1;
40 	timing_t  start;
41 	timing_t  finish;
42 	uint64_t min_cycles = UINT64_MAX;
43 
44 	ARG_UNUSED(p2);
45 	ARG_UNUSED(p3);
46 
47 	/* Repeat the overhead measurements for a few times to obtain the minimum overhead */
48 	for (int n = 0; n < OVERHEAD_CALC_ITER; n++) {
49 		start = timing_timestamp_get();
50 		for (uint32_t i = 0; i < num_iterations; i++) {
51 			timing_timestamp_get();
52 		}
53 		finish = timing_timestamp_get();
54 
55 		min_cycles = MIN(min_cycles, timing_cycles_get(&start, &finish));
56 	}
57 
58 	timestamp.cycles = min_cycles;
59 }
60 
timestamp_overhead_init(uint32_t num_iterations)61 void timestamp_overhead_init(uint32_t num_iterations)
62 {
63 	int  priority;
64 
65 	priority = k_thread_priority_get(k_current_get());
66 
67 	k_thread_create(&start_thread, start_stack,
68 			K_THREAD_STACK_SIZEOF(start_stack),
69 			start_thread_entry,
70 			(void *)(uintptr_t)num_iterations, NULL, NULL,
71 			priority - 1, 0, K_FOREVER);
72 
73 	k_thread_start(&start_thread);
74 
75 	k_thread_join(&start_thread, K_FOREVER);
76 
77 	timestamp_overhead = timestamp.cycles;
78 
79 #ifdef CONFIG_USERSPACE
80 	k_thread_create(&start_thread, start_stack,
81 			K_THREAD_STACK_SIZEOF(start_stack),
82 			start_thread_entry,
83 			(void *)(uintptr_t)num_iterations, NULL, NULL,
84 			priority - 1, K_USER, K_FOREVER);
85 
86 	k_thread_start(&start_thread);
87 
88 	k_thread_join(&start_thread, K_FOREVER);
89 
90 	user_timestamp_overhead = timestamp.cycles;
91 #endif
92 }
93 
timestamp_overhead_adjustment(uint32_t options1,uint32_t options2)94 uint64_t timestamp_overhead_adjustment(uint32_t options1, uint32_t options2)
95 {
96 #ifdef CONFIG_USERSPACE
97 	if (((options1 | options2) & K_USER) == K_USER) {
98 		if (((options1 & options2) & K_USER) == K_USER) {
99 			/*
100 			 * Both start and finish timestamps were obtained
101 			 * from userspace.
102 			 */
103 			return user_timestamp_overhead;
104 		}
105 		/*
106 		 * One timestamp came from userspace, and the other came
107 		 * from kernel space. Estimate the overhead as the mean
108 		 * between the two.
109 		 */
110 		return (timestamp_overhead + user_timestamp_overhead) / 2;
111 	}
112 #endif
113 
114 	/*
115 	 * Both start and finish timestamps were obtained
116 	 * from kernel space.
117 	 */
118 	return timestamp_overhead;
119 }
120