Skip to contentSkip to conversation

eal: fail initialization if TSC frequency is zero

PATCH Superseded Stephen Hemminger 3 participants 9 messages Sep 27 – Oct 13

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 eal: fail initialization if TSC frequency is zero

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")
---
 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(-)
lib/eal/common/eal_common_timer.c +20 -2
index bbf8b8b11b..d502468053 100644
@@ -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)
lib/eal/common/eal_private.h +1 -1
index 6340bab8be..952cb5a03e 100644
@@ -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
lib/eal/freebsd/eal_timer.c +1 -2
index d21ffa2694..84127d876b 100644
@@ -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();
 }
lib/eal/include/generic/rte_cycles.h +6 -1
index 7cfd51f0eb..8e3d8aad91 100644
@@ -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);
lib/eal/linux/eal_timer.c +1 -2
index 39f975b6b9..bccff60ff8 100644
@@ -99,6 +99,5 @@ rte_eal_timer_init(void)
 
 	eal_timer_source = EAL_TIMER_TSC;
 
-	set_tsc_freq();
-	return 0;
+	return set_tsc_freq();
 }
lib/eal/windows/eal_timer.c +1 -2
index 33cbac6a03..aec8ea854d 100644
@@ -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();
 }
Signature · 1 line
2.53.0
#1 lore raw

lib/eal/common/eal_common_timer.c set_tsc_freq(void) · line 93

Quoted 12 lines · On Sun, 27 Sep 2026 12:54:40 -0700 Stephen Hemminger…
lib/eal/common/eal_common_timer.c +12 -0
+	/*
+	 * 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;
+	}
+

Sorry for the AI generated comment here. Will fix in next version.

#2 lore raw

PATCH v2 1/1 eal: fail initialization if TSC frequency is zero

In reply to Stephen Hemminger

Many 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")

---
v2 - cleanups: mostly squash useless comments

 lib/eal/common/eal_common_timer.c    | 19 ++++++++++++++++---
 lib/eal/common/eal_private.h         |  2 +-
 lib/eal/freebsd/eal_timer.c          |  3 +--
 lib/eal/include/generic/rte_cycles.h |  2 +-
 lib/eal/linux/eal_timer.c            |  3 +--
 lib/eal/windows/eal_timer.c          |  3 +--
 6 files changed, 21 insertions(+), 11 deletions(-)
lib/eal/common/eal_common_timer.c +16 -3
index bbf8b8b11b..e9f8b56b59 100644
@@ -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,18 +65,31 @@ 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();
 	freq = get_tsc_freq(freq);
-	if (!freq)
+	if (freq == 0) {
 		freq = estimate_tsc_freq();
 
+		/* Check if TSC is not moving */
+		if (freq == 0) {
+			EAL_LOG(ERR, "TSC frequency is not changing");
+			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)
lib/eal/common/eal_private.h +1 -1
index 6340bab8be..952cb5a03e 100644
@@ -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
lib/eal/freebsd/eal_timer.c +1 -2
index d21ffa2694..84127d876b 100644
@@ -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();
 }
lib/eal/include/generic/rte_cycles.h +1 -1
index 7cfd51f0eb..f8e1cde332 100644
@@ -34,7 +34,7 @@ extern enum timer_source eal_timer_source;
  * Get the measured frequency of the RDTSC counter
  *
  * @return
- *   The TSC frequency for this lcore
+ *   The TSC frequency for all lcores, always non-zero
  */
 uint64_t
 rte_get_tsc_hz(void);
lib/eal/linux/eal_timer.c +1 -2
index 39f975b6b9..bccff60ff8 100644
@@ -99,6 +99,5 @@ rte_eal_timer_init(void)
 
 	eal_timer_source = EAL_TIMER_TSC;
 
-	set_tsc_freq();
-	return 0;
+	return set_tsc_freq();
 }
lib/eal/windows/eal_timer.c +1 -2
index 33cbac6a03..aec8ea854d 100644
@@ -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();
 }
Signature · 1 line
2.53.0
#3 lore raw
Quoted 5 lines · Stephen Hemminger

Many 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.

Quoted 7 lines

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.

How was this discovered? Is there a coverity issue id, or was it just AI discovered?

Quoted 6 lines
Fixes 040cf8a41187 ("eal: deduplicate timer functions")

---
v2 - cleanups: mostly squash useless comments

Code looks generally ok to me.

Quoted 60 lines
 lib/eal/common/eal_common_timer.c    | 19 ++++++++++++++++---
 lib/eal/common/eal_private.h         |  2 +-
 lib/eal/freebsd/eal_timer.c          |  3 +--
 lib/eal/include/generic/rte_cycles.h |  2 +-
 lib/eal/linux/eal_timer.c            |  3 +--
 lib/eal/windows/eal_timer.c          |  3 +--
 6 files changed, 21 insertions(+), 11 deletions(-)
lib/eal/common/eal_common_timer.c +16 -3
index bbf8b8b11b..e9f8b56b59 100644
@@ -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,18 +65,31 @@ 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();
 	freq = get_tsc_freq(freq);
-	if (!freq)
+	if (freq == 0) {
 		freq = estimate_tsc_freq();
 
+		/* Check if TSC is not moving */
+		if (freq == 0) {
+			EAL_LOG(ERR, "TSC frequency is not changing");
+			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)
lib/eal/common/eal_private.h +0 -0
index 6340bab8be..952cb5a03e 100644

lib/eal/common/eal_private.h int eal_cpu_detected(unsigned lcore_id); · line 413

Quoted 6 lines
lib/eal/common/eal_private.h +1 -1
@@ -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);
 

One minor suggestion: set_tsc_freq name implies that the user passes in a value to be set. I wonder if "init_tsc_freq" might be a better name here, since you are updating all calls anyway to handle an error return.

Quoted 54 lines
lib/eal/common/eal_private.h +0 -0
 /**
  * Get precise TSC frequency from system
lib/eal/freebsd/eal_timer.c +1 -2
index d21ffa2694..84127d876b 100644
@@ -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();
 }
lib/eal/include/generic/rte_cycles.h +1 -1
index 7cfd51f0eb..f8e1cde332 100644
@@ -34,7 +34,7 @@ extern enum timer_source eal_timer_source;
  * Get the measured frequency of the RDTSC counter
  *
  * @return
- *   The TSC frequency for this lcore
+ *   The TSC frequency for all lcores, always non-zero
  */
 uint64_t
 rte_get_tsc_hz(void);
lib/eal/linux/eal_timer.c +1 -2
index 39f975b6b9..bccff60ff8 100644
@@ -99,6 +99,5 @@ rte_eal_timer_init(void)
 
 	eal_timer_source = EAL_TIMER_TSC;
 
-	set_tsc_freq();
-	return 0;
+	return set_tsc_freq();
 }
lib/eal/windows/eal_timer.c +1 -2
index 33cbac6a03..aec8ea854d 100644
@@ -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();
 }
Signature · 1 line
2.53.0
#4 lore raw
Quoted 9 lines · On Tue, 29 Sep 2026 09:39:36 +0100 Bruce Richardson…
Quoted 8 lines · Stephen Hemminger

Many 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().

Quoted 7 lines
Quoted 4 lines

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.

How was this discovered? Is there a coverity issue id, or was it just AI discovered?

It was part of review of pcapng timestamp calculation.
Kind of funny, AI spotted overflow (which I verified) in the code which it had previously helped write. Then it was asking it self, "what if TSC was 0". I didn't see how this could happen unless QEMU had some broken CPU emulation.

To me it seemed best to fail fast and die in a safer manner.

#5 lore raw
Quoted 20 lines · Stephen Hemminger
Quoted 9 lines · On Tue, 29 Sep 2026 09:39:36 +0100 Bruce Richardson…
Quoted 8 lines · Stephen Hemminger

Many 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().

Quoted 7 lines
Quoted 4 lines

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.

How was this discovered? Is there a coverity issue id, or was it just AI discovered?

Quoted 8 lines

It was part of review of pcapng timestamp calculation.
Kind of funny, AI spotted overflow (which I verified) in the code which it had previously helped write. Then it was asking it self, "what if TSC was 0". I didn't see how this could happen unless QEMU had some broken CPU emulation.

To me it seemed best to fail fast and die in a safer manner.

Agreed. You can add my ack, either for this version or a new version with the "set" function renamed as I suggest.

#6 lore raw
Quoted 27 lines · On Tue, 29 Sep 2026 14:57:54 +0100 Bruce Richardson…
Quoted 26 lines · Stephen Hemminger
Quoted 9 lines · On Tue, 29 Sep 2026 09:39:36 +0100 Bruce Richardson…
Quoted 8 lines · Stephen Hemminger

Many 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().

Quoted 7 lines
Quoted 4 lines

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.

How was this discovered? Is there a coverity issue id, or was it just AI discovered?

It was part of review of pcapng timestamp calculation.
Kind of funny, AI spotted overflow (which I verified) in the code which it had previously helped write. Then it was asking it self, "what if TSC was 0". I didn't see how this could happen unless QEMU had some broken CPU emulation.

Quoted 7 lines
Quoted 2 lines

To me it seemed best to fail fast and die in a safer manner.

Agreed. You can add my ack, either for this version or a new version with the "set" function renamed as I suggest.

I also updated comment to rte_get_tsc_hz() since existing comment implies the value could be different on each lcore.

#7 lore raw

PATCH v3 1/1 eal: fail initialization if TSC frequency is zero

In reply to Stephen Hemminger

Many 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.

Also, clarify that DPDK TSC is stable and the same for all lcore's. The comment in rte_cycles.h implied that it could be different per lcore.

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

---
v3 - rename set_tsc_freq to init_tsc_freq

 lib/eal/common/eal_common_timer.c    | 19 +++++++++++++++----
 lib/eal/common/eal_private.h         |  2 +-
 lib/eal/freebsd/eal_timer.c          |  3 +--
 lib/eal/include/generic/rte_cycles.h |  2 +-
 lib/eal/linux/eal_timer.c            |  3 +--
 lib/eal/windows/eal_timer.c          |  3 +--
 6 files changed, 20 insertions(+), 12 deletions(-)
lib/eal/common/eal_common_timer.c +15 -4
index bbf8b8b11b..67ede70c62 100644
@@ -52,8 +52,8 @@ estimate_tsc_freq(void)
 	return RTE_ALIGN_MUL_NEAR(rte_rdtsc() - start, CYC_PER_10MHZ);
 }
 
-void
-set_tsc_freq(void)
+int
+init_tsc_freq(void)
 {
 	struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
 	uint64_t freq;
@@ -65,18 +65,29 @@ 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();
 	freq = get_tsc_freq(freq);
-	if (!freq)
+	if (freq == 0) {
 		freq = estimate_tsc_freq();
+		if (freq == 0) {
+			EAL_LOG(ERR, "TSC frequency is too slow");
+			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)
lib/eal/common/eal_private.h +1 -1
index 6340bab8be..7da3783c9e 100644
@@ -409,7 +409,7 @@ int eal_cpu_detected(unsigned lcore_id);
  *
  * This function is private to the EAL.
  */
-void set_tsc_freq(void);
+int init_tsc_freq(void);
 
 /**
  * Get precise TSC frequency from system
lib/eal/freebsd/eal_timer.c +1 -2
index d21ffa2694..b966c30258 100644
@@ -65,6 +65,5 @@ get_tsc_freq(uint64_t arch_hz)
 int
 rte_eal_timer_init(void)
 {
-	set_tsc_freq();
-	return 0;
+	return init_tsc_freq();
 }
lib/eal/include/generic/rte_cycles.h +1 -1
index 7cfd51f0eb..f8e1cde332 100644
@@ -34,7 +34,7 @@ extern enum timer_source eal_timer_source;
  * Get the measured frequency of the RDTSC counter
  *
  * @return
- *   The TSC frequency for this lcore
+ *   The TSC frequency for all lcores, always non-zero
  */
 uint64_t
 rte_get_tsc_hz(void);
lib/eal/linux/eal_timer.c +1 -2
index 39f975b6b9..c425ea4ed8 100644
@@ -99,6 +99,5 @@ rte_eal_timer_init(void)
 
 	eal_timer_source = EAL_TIMER_TSC;
 
-	set_tsc_freq();
-	return 0;
+	return init_tsc_freq();
 }
lib/eal/windows/eal_timer.c +1 -2
index 33cbac6a03..1e381ad673 100644
@@ -94,6 +94,5 @@ get_tsc_freq(uint64_t arch_hz)
 int
 rte_eal_timer_init(void)
 {
-	set_tsc_freq();
-	return 0;
+	return init_tsc_freq();
 }
Signature · 1 line
2.53.0
#8 lore raw

29/09/2026 16:13, Stephen Hemminger:

Quoted 12 lines

Many 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.

Quoted 7 lines

Also, clarify that DPDK TSC is stable and the same for all lcore's. The comment in rte_cycles.h implied that it could be different per lcore.

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

Applied, thanks.