eal: calibrate TSC only in primary process
[dpdk.git] / lib / librte_eal / common / eal_common_timer.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4
5 #include <string.h>
6 #include <stdio.h>
7 #include <unistd.h>
8 #include <inttypes.h>
9 #include <sys/types.h>
10 #include <time.h>
11 #include <errno.h>
12
13 #include <rte_common.h>
14 #include <rte_compat.h>
15 #include <rte_log.h>
16 #include <rte_cycles.h>
17 #include <rte_pause.h>
18 #include <rte_eal.h>
19
20 #include "eal_private.h"
21 #include "eal_memcfg.h"
22
23 /* The frequency of the RDTSC timer resolution */
24 static uint64_t eal_tsc_resolution_hz;
25
26 /* Pointer to user delay function */
27 void (*rte_delay_us)(unsigned int) = NULL;
28
29 void
30 rte_delay_us_block(unsigned int us)
31 {
32         const uint64_t start = rte_get_timer_cycles();
33         const uint64_t ticks = (uint64_t)us * rte_get_timer_hz() / 1E6;
34         while ((rte_get_timer_cycles() - start) < ticks)
35                 rte_pause();
36 }
37
38 void
39 rte_delay_us_sleep(unsigned int us)
40 {
41         struct timespec wait[2];
42         int ind = 0;
43
44         wait[0].tv_sec = 0;
45         if (us >= US_PER_S) {
46                 wait[0].tv_sec = us / US_PER_S;
47                 us -= wait[0].tv_sec * US_PER_S;
48         }
49         wait[0].tv_nsec = 1000 * us;
50
51         while (nanosleep(&wait[ind], &wait[1 - ind]) && errno == EINTR) {
52                 /*
53                  * Sleep was interrupted. Flip the index, so the 'remainder'
54                  * will become the 'request' for a next call.
55                  */
56                 ind = 1 - ind;
57         }
58 }
59
60 uint64_t
61 rte_get_tsc_hz(void)
62 {
63         return eal_tsc_resolution_hz;
64 }
65
66 static uint64_t
67 estimate_tsc_freq(void)
68 {
69 #define CYC_PER_10MHZ 1E7
70         RTE_LOG(WARNING, EAL, "WARNING: TSC frequency estimated roughly"
71                 " - clock timings may be less accurate.\n");
72         /* assume that the sleep(1) will sleep for 1 second */
73         uint64_t start = rte_rdtsc();
74         sleep(1);
75         /* Round up to 10Mhz. 1E7 ~ 10Mhz */
76         return RTE_ALIGN_MUL_NEAR(rte_rdtsc() - start, CYC_PER_10MHZ);
77 }
78
79 void
80 set_tsc_freq(void)
81 {
82         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
83         uint64_t freq;
84
85         if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
86                 /*
87                  * Just use the primary process calculated TSC rate in any
88                  * secondary process.  It avoids any unnecessary overhead on
89                  * systems where arch-specific frequency detection is not
90                  * available.
91                  */
92                 eal_tsc_resolution_hz = mcfg->tsc_hz;
93                 return;
94         }
95
96         freq = get_tsc_freq_arch();
97         if (!freq)
98                 freq = get_tsc_freq();
99         if (!freq)
100                 freq = estimate_tsc_freq();
101
102         RTE_LOG(DEBUG, EAL, "TSC frequency is ~%" PRIu64 " KHz\n", freq / 1000);
103         eal_tsc_resolution_hz = freq;
104         mcfg->tsc_hz = freq;
105 }
106
107 void rte_delay_us_callback_register(void (*userfunc)(unsigned int))
108 {
109         rte_delay_us = userfunc;
110 }
111
112 RTE_INIT(rte_timer_init)
113 {
114         /* set rte_delay_us_block as a delay function */
115         rte_delay_us_callback_register(rte_delay_us_block);
116 }