Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2010-2014 Intel Corporation.
3 : : * Copyright(c) 2012-2013 6WIND S.A.
4 : : */
5 : :
6 : : #include <stdio.h>
7 : : #include <stdint.h>
8 : : #include <inttypes.h>
9 : : #ifdef RTE_LIBEAL_USE_HPET
10 : : #include <fcntl.h>
11 : : #include <sys/mman.h>
12 : : #include <unistd.h>
13 : : #endif
14 : :
15 : : #include <rte_common.h>
16 : : #include <rte_cycles.h>
17 : : #include <rte_thread.h>
18 : :
19 : : #include "eal_private.h"
20 : :
21 : : enum timer_source eal_timer_source = EAL_TIMER_HPET;
22 : :
23 : : #ifdef RTE_LIBEAL_USE_HPET
24 : :
25 : : #define DEV_HPET "/dev/hpet"
26 : :
27 : : /* Maximum number of counters. */
28 : : #define HPET_TIMER_NUM 3
29 : :
30 : : /* General capabilities register */
31 : : #define CLK_PERIOD_SHIFT 32 /* Clock period shift. */
32 : : #define CLK_PERIOD_MASK 0xffffffff00000000ULL /* Clock period mask. */
33 : :
34 : : /**
35 : : * HPET timer registers. From the Intel IA-PC HPET (High Precision Event
36 : : * Timers) Specification.
37 : : */
38 : : struct eal_hpet_regs {
39 : : /* Memory-mapped, software visible registers */
40 : : uint64_t capabilities; /**< RO General Capabilities Register. */
41 : : uint64_t reserved0; /**< Reserved for future use. */
42 : : uint64_t config; /**< RW General Configuration Register. */
43 : : uint64_t reserved1; /**< Reserved for future use. */
44 : : uint64_t isr; /**< RW Clear General Interrupt Status. */
45 : : uint64_t reserved2[25]; /**< Reserved for future use. */
46 : : union {
47 : : uint64_t counter; /**< RW Main Counter Value Register. */
48 : : struct {
49 : : uint32_t counter_l; /**< RW Main Counter Low. */
50 : : uint32_t counter_h; /**< RW Main Counter High. */
51 : : };
52 : : };
53 : : uint64_t reserved3; /**< Reserved for future use. */
54 : : struct {
55 : : uint64_t config; /**< RW Timer Config and Capability Reg. */
56 : : uint64_t comp; /**< RW Timer Comparator Value Register. */
57 : : uint64_t fsb; /**< RW FSB Interrupt Route Register. */
58 : : uint64_t reserved4; /**< Reserved for future use. */
59 : : } timers[HPET_TIMER_NUM]; /**< Set of HPET timers. */
60 : : };
61 : :
62 : : /* Mmap'd hpet registers */
63 : : static volatile struct eal_hpet_regs *eal_hpet = NULL;
64 : :
65 : : /* Period at which the HPET counter increments in
66 : : * femtoseconds (10^-15 seconds). */
67 : : static uint32_t eal_hpet_resolution_fs = 0;
68 : :
69 : : /* Frequency of the HPET counter in Hz */
70 : : static uint64_t eal_hpet_resolution_hz = 0;
71 : :
72 : : /* Incremented 4 times during one 32bits hpet full count */
73 : : static uint32_t eal_hpet_msb;
74 : :
75 : : static rte_thread_t msb_inc_thread_id;
76 : :
77 : : /*
78 : : * This function runs on a specific thread to update a global variable
79 : : * containing used to process MSB of the HPET (unfortunately, we need
80 : : * this because hpet is 32 bits by default under linux).
81 : : */
82 : : static uint32_t
83 : : hpet_msb_inc(__rte_unused void *arg)
84 : : {
85 : : uint32_t t;
86 : :
87 : : while (1) {
88 : : t = (eal_hpet->counter_l >> 30);
89 : : if (t != (eal_hpet_msb & 3))
90 : : eal_hpet_msb ++;
91 : : sleep(10);
92 : : }
93 : : return 0;
94 : : }
95 : :
96 : : uint64_t
97 : : rte_get_hpet_hz(void)
98 : : {
99 : : const struct internal_config *internal_conf =
100 : : eal_get_internal_configuration();
101 : :
102 : : if (internal_conf->no_hpet)
103 : : rte_panic("Error, HPET called, but no HPET present\n");
104 : :
105 : : return eal_hpet_resolution_hz;
106 : : }
107 : :
108 : : uint64_t
109 : : rte_get_hpet_cycles(void)
110 : : {
111 : : uint32_t t, msb;
112 : : uint64_t ret;
113 : : const struct internal_config *internal_conf =
114 : : eal_get_internal_configuration();
115 : :
116 : : if (internal_conf->no_hpet)
117 : : rte_panic("Error, HPET called, but no HPET present\n");
118 : :
119 : : t = eal_hpet->counter_l;
120 : : msb = eal_hpet_msb;
121 : : ret = (msb + 2 - (t >> 30)) / 4;
122 : : ret <<= 32;
123 : : ret += t;
124 : : return ret;
125 : : }
126 : :
127 : : #endif
128 : :
129 : : #ifdef RTE_LIBEAL_USE_HPET
130 : : /*
131 : : * Open and mmap /dev/hpet (high precision event timer) that will
132 : : * provide our time reference.
133 : : */
134 : : int
135 : : rte_eal_hpet_init(int make_default)
136 : : {
137 : : int fd, ret;
138 : : struct internal_config *internal_conf =
139 : : eal_get_internal_configuration();
140 : :
141 : : if (internal_conf->no_hpet) {
142 : : EAL_LOG(NOTICE, "HPET is disabled");
143 : : return -1;
144 : : }
145 : :
146 : : fd = open(DEV_HPET, O_RDONLY);
147 : : if (fd < 0) {
148 : : EAL_LOG(ERR, "ERROR: Cannot open "DEV_HPET": %s!",
149 : : strerror(errno));
150 : : internal_conf->no_hpet = 1;
151 : : return -1;
152 : : }
153 : : eal_hpet = mmap(NULL, 1024, PROT_READ, MAP_SHARED, fd, 0);
154 : : if (eal_hpet == MAP_FAILED) {
155 : : EAL_LOG(ERR, "ERROR: Cannot mmap "DEV_HPET"!");
156 : : close(fd);
157 : : internal_conf->no_hpet = 1;
158 : : return -1;
159 : : }
160 : : close(fd);
161 : :
162 : : eal_hpet_resolution_fs = (uint32_t)((eal_hpet->capabilities &
163 : : CLK_PERIOD_MASK) >>
164 : : CLK_PERIOD_SHIFT);
165 : :
166 : : eal_hpet_resolution_hz = (1000ULL*1000ULL*1000ULL*1000ULL*1000ULL) /
167 : : (uint64_t)eal_hpet_resolution_fs;
168 : :
169 : : EAL_LOG(INFO, "HPET frequency is ~%"PRIu64" kHz",
170 : : eal_hpet_resolution_hz/1000);
171 : :
172 : : eal_hpet_msb = (eal_hpet->counter_l >> 30);
173 : :
174 : : /* create a thread that will increment a global variable for
175 : : * msb (hpet is 32 bits by default under linux) */
176 : : ret = rte_thread_create_internal_control(&msb_inc_thread_id, "hpet-msb",
177 : : hpet_msb_inc, NULL);
178 : : if (ret != 0) {
179 : : EAL_LOG(ERR, "ERROR: Cannot create HPET timer thread!");
180 : : internal_conf->no_hpet = 1;
181 : : return -1;
182 : : }
183 : :
184 : : if (make_default)
185 : : eal_timer_source = EAL_TIMER_HPET;
186 : : return 0;
187 : : }
188 : : #endif
189 : :
190 : : /* Check if the kernel deems the arch provided TSC frequency trustworthy. */
191 : :
192 : : static bool
193 : 0 : is_tsc_known_freq(void)
194 : : {
195 : : bool ret = true; /* Assume tsc_known_freq */
196 : :
197 : : #if defined(RTE_ARCH_X86)
198 : : char line[2048];
199 : : FILE *stream;
200 : :
201 : 0 : stream = fopen("/proc/cpuinfo", "r");
202 [ # # ]: 0 : if (!stream) {
203 : 0 : EAL_LOG(WARNING, "Unable to open /proc/cpuinfo");
204 : 0 : return ret;
205 : : }
206 : :
207 [ # # ]: 0 : while (fgets(line, sizeof(line), stream)) {
208 [ # # ]: 0 : if (strncmp(line, "flags", 5) != 0)
209 : 0 : continue;
210 : :
211 [ # # ]: 0 : if (!strstr(line, "tsc_known_freq"))
212 : : ret = false;
213 : :
214 : : break;
215 : : }
216 : :
217 : 0 : fclose(stream);
218 : : #endif
219 : :
220 : 0 : return ret;
221 : : }
222 : :
223 : : uint64_t
224 : 154 : get_tsc_freq(uint64_t arch_hz)
225 : : {
226 : : #ifdef CLOCK_MONOTONIC_RAW
227 : : #define NS_PER_SEC 1E9
228 : : #define CYC_PER_100KHZ 1E5
229 : :
230 : 154 : struct timespec sleeptime = {.tv_nsec = NS_PER_SEC / 10 }; /* 1/10 second */
231 : :
232 : : struct timespec t_start, t_end;
233 : : uint64_t tsc_hz;
234 : :
235 [ - + - - ]: 154 : if (arch_hz && is_tsc_known_freq())
236 : : return arch_hz;
237 : :
238 [ + - ]: 154 : if (clock_gettime(CLOCK_MONOTONIC_RAW, &t_start) == 0) {
239 : : uint64_t ns, end, start = rte_rdtsc();
240 : 154 : nanosleep(&sleeptime,NULL);
241 : 154 : clock_gettime(CLOCK_MONOTONIC_RAW, &t_end);
242 : : end = rte_rdtsc();
243 : 154 : ns = ((t_end.tv_sec - t_start.tv_sec) * NS_PER_SEC);
244 : 154 : ns += (t_end.tv_nsec - t_start.tv_nsec);
245 : :
246 : 154 : double secs = (double)ns/NS_PER_SEC;
247 : 154 : tsc_hz = (uint64_t)((end - start)/secs);
248 : :
249 [ - + ]: 154 : if (arch_hz) {
250 : : /* Make sure we're within 1% for sanity check */
251 [ # # ]: 0 : if (RTE_MAX(arch_hz, tsc_hz) - RTE_MIN(arch_hz, tsc_hz) > arch_hz / 100)
252 : : return arch_hz;
253 : :
254 : 0 : EAL_LOG(DEBUG,
255 : : "Refined arch frequency %"PRIu64" to measured frequency %"PRIu64,
256 : : arch_hz, tsc_hz);
257 : : }
258 : :
259 : : /* Round up to 100Khz. 1E5 ~ 100Khz */
260 [ + + ]: 287 : return RTE_ALIGN_MUL_NEAR(tsc_hz, CYC_PER_100KHZ);
261 : : }
262 : : #endif
263 : : return arch_hz;
264 : : }
265 : :
266 : : int
267 : 179 : rte_eal_timer_init(void)
268 : : {
269 : :
270 : 179 : eal_timer_source = EAL_TIMER_TSC;
271 : :
272 : 179 : set_tsc_freq();
273 : 179 : return 0;
274 : : }
|