[v2,2/2] timer/linux/x86: override TSC freq if no tsc_known_freq
Checks
Commit Message
If the tsc_known_freq cpu flag is missing, it means the kernel doesn't
trust it and calculates its own. We should do the same to avoid drift.
Signed-off-by: Isaac Boukris <iboukris@gmail.com>
---
lib/eal/common/eal_common_timer.c | 3 +-
lib/eal/common/eal_private.h | 2 +-
lib/eal/freebsd/eal_timer.c | 5 +++-
lib/eal/linux/eal_timer.c | 50 +++++++++++++++++++++++++++++--
lib/eal/windows/eal_timer.c | 5 +++-
5 files changed, 58 insertions(+), 7 deletions(-)
Comments
On Tue, 1 Oct 2024 01:08:32 +0300
Isaac Boukris <iboukris@gmail.com> wrote:
> +
> + EAL_LOG(DEBUG, "Refined architecture frequency %"PRIu64
> + " to measured frequency %"PRIu64".",
> + arch_hz, tsc_hz);
> + }
Best not to break messages, it make's it harder for users to search.
Don't need period either.
EAL_LOG(DEBUG,
"Refined architecture frequency %"PRIu64" to measured frequency %"PRIu64,
@@ -66,8 +66,7 @@ set_tsc_freq(void)
}
freq = get_tsc_freq_arch();
- if (!freq)
- freq = get_tsc_freq();
+ freq = get_tsc_freq(freq);
if (!freq)
freq = estimate_tsc_freq();
@@ -374,7 +374,7 @@ void set_tsc_freq(void);
*
* This function is private to the EAL.
*/
-uint64_t get_tsc_freq(void);
+uint64_t get_tsc_freq(uint64_t arch_hz);
/**
* Get TSC frequency if the architecture supports.
@@ -26,12 +26,15 @@
enum timer_source eal_timer_source = EAL_TIMER_TSC;
uint64_t
-get_tsc_freq(void)
+get_tsc_freq(uint64_t arch_hz)
{
size_t sz;
int tmp;
uint64_t tsc_hz;
+ if (arch_hz)
+ return arch_hz;
+
sz = sizeof(tmp);
tmp = 0;
@@ -5,9 +5,9 @@
#include <stdio.h>
#include <stdint.h>
+#include <inttypes.h>
#ifdef RTE_LIBEAL_USE_HPET
#include <fcntl.h>
-#include <inttypes.h>
#include <sys/mman.h>
#include <unistd.h>
#endif
@@ -187,8 +187,40 @@ rte_eal_hpet_init(int make_default)
}
#endif
+/* Check if the kernel deems the arch provided TSC frequency trustworthy. */
+
+static bool
+is_tsc_known_freq(void)
+{
+ char line[2048];
+ FILE *stream;
+ bool ret = true; /* Assume tsc_known_freq */
+
+#if defined(RTE_ARCH_X86)
+ stream = fopen("/proc/cpuinfo", "r");
+ if (!stream) {
+ EAL_LOG(WARNING, "Unable to open /proc/cpuinfo");
+ return ret;
+ }
+
+ while (fgets(line, sizeof(line), stream)) {
+ if (strncmp(line, "flags", 5) != 0)
+ continue;
+
+ if (!strstr(line, "tsc_known_freq"))
+ ret = false;
+
+ break;
+ }
+
+ fclose(stream);
+#endif
+
+ return ret;
+}
+
uint64_t
-get_tsc_freq(void)
+get_tsc_freq(uint64_t arch_hz)
{
#ifdef CLOCK_MONOTONIC_RAW
#define NS_PER_SEC 1E9
@@ -199,6 +231,9 @@ get_tsc_freq(void)
struct timespec t_start, t_end;
uint64_t tsc_hz;
+ if (arch_hz && is_tsc_known_freq())
+ return arch_hz;
+
if (clock_gettime(CLOCK_MONOTONIC_RAW, &t_start) == 0) {
uint64_t ns, end, start = rte_rdtsc();
nanosleep(&sleeptime,NULL);
@@ -209,6 +244,17 @@ get_tsc_freq(void)
double secs = (double)ns/NS_PER_SEC;
tsc_hz = (uint64_t)((end - start)/secs);
+
+ if (arch_hz) {
+ /* Make sure we're within 1% for sanity check */
+ if (arch_hz - tsc_hz > arch_hz / 100)
+ return arch_hz;
+
+ EAL_LOG(DEBUG, "Refined architecture frequency %"PRIu64
+ " to measured frequency %"PRIu64".",
+ arch_hz, tsc_hz);
+ }
+
/* Round up to 100Khz. 1E5 ~ 100Khz */
return RTE_ALIGN_MUL_NEAR(tsc_hz, CYC_PER_100KHZ);
}
@@ -49,13 +49,16 @@ rte_delay_us_sleep(unsigned int us)
}
uint64_t
-get_tsc_freq(void)
+get_tsc_freq(uint64_t arch_hz)
{
LARGE_INTEGER t_start, t_end, elapsed_us;
LARGE_INTEGER frequency;
uint64_t tsc_hz;
uint64_t end, start;
+ if (arch_hz)
+ return arch_hz;
+
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&t_start);