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 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 stream = fopen("/proc/cpuinfo", "r"); 202 if (!stream) { 203 EAL_LOG(WARNING, "Unable to open /proc/cpuinfo"); 204 return ret; 205 } 206 207 while (fgets(line, sizeof(line), stream)) { 208 if (strncmp(line, "flags", 5) != 0) 209 continue; 210 211 if (!strstr(line, "tsc_known_freq")) 212 ret = false; 213 214 break; 215 } 216 217 fclose(stream); 218 #endif 219 220 return ret; 221 } 222 223 uint64_t 224 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 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 if (arch_hz && is_tsc_known_freq()) 236 return arch_hz; 237 238 if (clock_gettime(CLOCK_MONOTONIC_RAW, &t_start) == 0) { 239 uint64_t ns, end, start = rte_rdtsc(); 240 nanosleep(&sleeptime,NULL); 241 clock_gettime(CLOCK_MONOTONIC_RAW, &t_end); 242 end = rte_rdtsc(); 243 ns = ((t_end.tv_sec - t_start.tv_sec) * NS_PER_SEC); 244 ns += (t_end.tv_nsec - t_start.tv_nsec); 245 246 double secs = (double)ns/NS_PER_SEC; 247 tsc_hz = (uint64_t)((end - start)/secs); 248 249 if (arch_hz) { 250 /* Make sure we're within 1% for sanity check */ 251 if (RTE_MAX(arch_hz, tsc_hz) - RTE_MIN(arch_hz, tsc_hz) > arch_hz / 100) 252 return arch_hz; 253 254 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 return RTE_ALIGN_MUL_NEAR(tsc_hz, CYC_PER_100KHZ); 261 } 262 #endif 263 return arch_hz; 264 } 265 266 int 267 rte_eal_timer_init(void) 268 { 269 270 eal_timer_source = EAL_TIMER_TSC; 271 272 set_tsc_freq(); 273 return 0; 274 } 275