[PATCH] eal: fail initialization if TSC frequency is zero

Stephen Hemminger stephen at networkplumber.org
Sun Sep 27 21:54:40 CEST 2026


Lots of parts of DPDK will fail with divide by zero and
other errors if the initialization logic ever TSC hz was ever
determined to be zero. This might happen on a broken get_tsc_freq_arch()
or bad emulation in QEMU.

If TSC hz is zero, log the error and propagate back to
fail rte_eal_init().

This fix doesn't need to go to stable since it is a purely
theoretical problem; we aren't getting divide by zero reports
from users.

Fixes: 040cf8a41187 ("eal: deduplicate timer functions")

Signed-off-by: Stephen Hemminger <stephen at networkplumber.org>
---
 lib/eal/common/eal_common_timer.c    | 22 ++++++++++++++++++++--
 lib/eal/common/eal_private.h         |  2 +-
 lib/eal/freebsd/eal_timer.c          |  3 +--
 lib/eal/include/generic/rte_cycles.h |  7 ++++++-
 lib/eal/linux/eal_timer.c            |  3 +--
 lib/eal/windows/eal_timer.c          |  3 +--
 6 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/lib/eal/common/eal_common_timer.c b/lib/eal/common/eal_common_timer.c
index bbf8b8b11b..d502468053 100644
--- a/lib/eal/common/eal_common_timer.c
+++ b/lib/eal/common/eal_common_timer.c
@@ -52,7 +52,7 @@ estimate_tsc_freq(void)
 	return RTE_ALIGN_MUL_NEAR(rte_rdtsc() - start, CYC_PER_10MHZ);
 }
 
-void
+int
 set_tsc_freq(void)
 {
 	struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
@@ -65,8 +65,13 @@ set_tsc_freq(void)
 		 * systems where arch-specific frequency detection is not
 		 * available.
 		 */
+		if (mcfg->tsc_hz == 0) {
+			EAL_LOG(ERR, "Primary process TSC frequency is zero");
+			return -1;
+		}
+
 		eal_tsc_resolution_hz = mcfg->tsc_hz;
-		return;
+		return 0;
 	}
 
 	freq = get_tsc_freq_arch();
@@ -74,9 +79,22 @@ set_tsc_freq(void)
 	if (!freq)
 		freq = estimate_tsc_freq();
 
+	/*
+	 * Several get_tsc_freq_arch() implementations return zero when the
+	 * frequency cannot be read, and a TSC that does not advance at least
+	 * once per second measures as zero.  Such a counter cannot be scaled
+	 * to a time at all, so fail rather than let callers of
+	 * rte_get_tsc_hz() divide by zero.
+	 */
+	if (freq == 0) {
+		EAL_LOG(ERR, "TSC frequency could not be determined");
+		return -1;
+	}
+
 	EAL_LOG(DEBUG, "TSC frequency is ~%" PRIu64 " KHz", freq / 1000);
 	eal_tsc_resolution_hz = freq;
 	mcfg->tsc_hz = freq;
+	return 0;
 }
 
 RTE_EXPORT_SYMBOL(rte_delay_us_callback_register)
diff --git a/lib/eal/common/eal_private.h b/lib/eal/common/eal_private.h
index 6340bab8be..952cb5a03e 100644
--- a/lib/eal/common/eal_private.h
+++ b/lib/eal/common/eal_private.h
@@ -409,7 +409,7 @@ int eal_cpu_detected(unsigned lcore_id);
  *
  * This function is private to the EAL.
  */
-void set_tsc_freq(void);
+int set_tsc_freq(void);
 
 /**
  * Get precise TSC frequency from system
diff --git a/lib/eal/freebsd/eal_timer.c b/lib/eal/freebsd/eal_timer.c
index d21ffa2694..84127d876b 100644
--- a/lib/eal/freebsd/eal_timer.c
+++ b/lib/eal/freebsd/eal_timer.c
@@ -65,6 +65,5 @@ get_tsc_freq(uint64_t arch_hz)
 int
 rte_eal_timer_init(void)
 {
-	set_tsc_freq();
-	return 0;
+	return set_tsc_freq();
 }
diff --git a/lib/eal/include/generic/rte_cycles.h b/lib/eal/include/generic/rte_cycles.h
index 7cfd51f0eb..8e3d8aad91 100644
--- a/lib/eal/include/generic/rte_cycles.h
+++ b/lib/eal/include/generic/rte_cycles.h
@@ -33,8 +33,13 @@ extern enum timer_source eal_timer_source;
 /**
  * Get the measured frequency of the RDTSC counter
  *
+ * The frequency is determined once during rte_eal_init(), which fails if
+ * it cannot be established, so this never returns zero once the EAL is
+ * initialized and the value does not change afterwards.  It is therefore
+ * safe to divide by without checking.
+ *
  * @return
- *   The TSC frequency for this lcore
+ *   The TSC frequency for this lcore, always non-zero.
  */
 uint64_t
 rte_get_tsc_hz(void);
diff --git a/lib/eal/linux/eal_timer.c b/lib/eal/linux/eal_timer.c
index 39f975b6b9..bccff60ff8 100644
--- a/lib/eal/linux/eal_timer.c
+++ b/lib/eal/linux/eal_timer.c
@@ -99,6 +99,5 @@ rte_eal_timer_init(void)
 
 	eal_timer_source = EAL_TIMER_TSC;
 
-	set_tsc_freq();
-	return 0;
+	return set_tsc_freq();
 }
diff --git a/lib/eal/windows/eal_timer.c b/lib/eal/windows/eal_timer.c
index 33cbac6a03..aec8ea854d 100644
--- a/lib/eal/windows/eal_timer.c
+++ b/lib/eal/windows/eal_timer.c
@@ -94,6 +94,5 @@ get_tsc_freq(uint64_t arch_hz)
 int
 rte_eal_timer_init(void)
 {
-	set_tsc_freq();
-	return 0;
+	return set_tsc_freq();
 }
-- 
2.53.0



More information about the dev mailing list