From: Stephen Hemminger <stephen@networkplumber.org>
To: dev@dpdk.org
Cc: Stephen Hemminger <stephen@networkplumber.org>
Subject: [PATCH 1/3] eal: add function to convert TSC to epoch time
Date: Tue, 20 Oct 2026 10:55:42 -0700 [thread overview]
Message-ID: <20261020175732.1552406-2-stephen@networkplumber.org> (raw)
In-Reply-To: <20261020175732.1552406-1-stephen@networkplumber.org>
The pcapng library converts the TSC into nanoseconds since 1/1/1970,
because that is what the pcapng file format records. Nothing about
that conversion is specific to packet capture: it needs the TSC
frequency and a reference point relating the TSC to the wall clock,
both of which EAL already has or can take at init.
Record the TSC alongside a CLOCK_REALTIME reading while the TSC
frequency is being determined, and add rte_tsc_to_ns() to convert any
TSC value using that reference point. The seconds are computed before
the remainder is scaled, so the multiply by NS_PER_S cannot overflow.
The reference point is taken per process rather than shared through the
memory config, so a secondary process may differ from the primary by
the sampling error.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
doc/api/doxy-api-index.md | 1 +
doc/guides/rel_notes/release_26_11.rst | 6 +++
lib/eal/common/eal_common_timer.c | 59 ++++++++++++++++++++++++++
lib/eal/include/rte_time.h | 34 +++++++++++++++
4 files changed, 100 insertions(+)
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 62390f7be3..97b908667c 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -73,6 +73,7 @@ The public API headers are grouped by topics:
- **timers**:
[cycles](@ref rte_cycles.h),
+ [time](@ref rte_time.h),
[timer](@ref rte_timer.h),
[alarm](@ref rte_alarm.h)
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index be758a0c02..5f960692e2 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -73,6 +73,12 @@ New Features
Added the experimental ``rte_cpu_socket_id()`` function
to map an OS logical CPU ID to the NUMA socket containing that CPU.
+* **Added API to convert TSC to epoch time.**
+
+ Added the experimental ``rte_tsc_to_ns()`` function in ``rte_time.h``
+ to convert a TSC value into nanoseconds since 1/1/1970,
+ using a reference point recorded during EAL initialization.
+
* **Added pile stack and mempool driver.**
Introduced pile, a lock-free, stack-like implementation,
diff --git a/lib/eal/common/eal_common_timer.c b/lib/eal/common/eal_common_timer.c
index 67ede70c62..4dfb7099b3 100644
--- a/lib/eal/common/eal_common_timer.c
+++ b/lib/eal/common/eal_common_timer.c
@@ -8,8 +8,10 @@
#include <rte_common.h>
#include <rte_log.h>
#include <rte_cycles.h>
+#include <rte_os_shim.h>
#include <rte_pause.h>
#include <rte_eal.h>
+#include <rte_time.h>
#include <eal_export.h>
#include "eal_private.h"
@@ -18,6 +20,10 @@
/* The frequency of the RDTSC timer resolution */
static uint64_t eal_tsc_resolution_hz;
+/* Reference point relating the TSC to the wall clock, taken at init */
+static uint64_t eal_tsc_base;
+static uint64_t eal_epoch_ns;
+
/* Pointer to user delay function */
RTE_EXPORT_SYMBOL(rte_delay_us)
void (*rte_delay_us)(unsigned int) = NULL;
@@ -39,6 +45,36 @@ rte_get_tsc_hz(void)
return eal_tsc_resolution_hz;
}
+/*
+ * Convert a count of cycles to nanoseconds.
+ *
+ * Compute the whole seconds first, so that the remainder is always
+ * less than the frequency and scaling it by NS_PER_S cannot overflow.
+ */
+static uint64_t
+cycles_to_ns(uint64_t cycles)
+{
+ uint64_t secs = cycles / eal_tsc_resolution_hz;
+ uint64_t rem = cycles % eal_tsc_resolution_hz;
+
+ return secs * NS_PER_S + (rem * NS_PER_S) / eal_tsc_resolution_hz;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_tsc_to_ns, 26.11)
+uint64_t
+rte_tsc_to_ns(uint64_t tsc)
+{
+ /*
+ * A caller may convert a TSC value sampled before the reference
+ * point was taken, so handle both directions on an unsigned
+ * magnitude.
+ */
+ if (unlikely(tsc < eal_tsc_base))
+ return eal_epoch_ns - cycles_to_ns(eal_tsc_base - tsc);
+
+ return eal_epoch_ns + cycles_to_ns(tsc - eal_tsc_base);
+}
+
static uint64_t
estimate_tsc_freq(void)
{
@@ -52,6 +88,27 @@ estimate_tsc_freq(void)
return RTE_ALIGN_MUL_NEAR(rte_rdtsc() - start, CYC_PER_10MHZ);
}
+/*
+ * Record the TSC value matching a wall clock reading, so that any TSC
+ * value can later be expressed as nanoseconds since the Unix epoch.
+ * Read the TSC either side of the clock to halve the sampling error.
+ */
+static void
+init_tsc_base(void)
+{
+ struct timespec ts;
+ uint64_t cycles;
+
+ cycles = rte_get_tsc_cycles();
+ if (clock_gettime(CLOCK_REALTIME, &ts) < 0) {
+ EAL_LOG(WARNING, "Could not read wall clock,"
+ " TSC to epoch conversion unavailable");
+ return;
+ }
+ eal_tsc_base = (cycles + rte_get_tsc_cycles()) / 2;
+ eal_epoch_ns = rte_timespec_to_ns(&ts);
+}
+
int
init_tsc_freq(void)
{
@@ -71,6 +128,7 @@ init_tsc_freq(void)
}
eal_tsc_resolution_hz = mcfg->tsc_hz;
+ init_tsc_base();
return 0;
}
@@ -87,6 +145,7 @@ init_tsc_freq(void)
EAL_LOG(DEBUG, "TSC frequency is ~%" PRIu64 " KHz", freq / 1000);
eal_tsc_resolution_hz = freq;
mcfg->tsc_hz = freq;
+ init_tsc_base();
return 0;
}
diff --git a/lib/eal/include/rte_time.h b/lib/eal/include/rte_time.h
index c5c3a233e4..3122e062ab 100644
--- a/lib/eal/include/rte_time.h
+++ b/lib/eal/include/rte_time.h
@@ -5,9 +5,17 @@
#ifndef _RTE_TIME_H_
#define _RTE_TIME_H_
+/**
+ * @file
+ *
+ * Time and cycle counter conversion.
+ */
+
#include <stdint.h>
#include <time.h>
+#include <rte_compat.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -102,6 +110,32 @@ rte_ns_to_timespec(uint64_t nsec)
return ts;
}
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Convert a TSC value into nanoseconds since the Unix epoch.
+ *
+ * EAL records the TSC alongside a CLOCK_REALTIME reading during
+ * initialization, and this converts using that reference point and the
+ * TSC frequency. Values sampled before initialization are also handled,
+ * and the result tracks the wall clock as it was at init: later changes
+ * to the system time are not reflected.
+ *
+ * Each process takes its own reference point, so values converted in a
+ * secondary process may differ from the primary by the sampling error.
+ *
+ * Must be called after rte_eal_init(), which fails if the TSC frequency
+ * cannot be determined.
+ *
+ * @param tsc
+ * A value read from rte_get_tsc_cycles() or rte_rdtsc().
+ * @return
+ * Nanoseconds since 1/1/1970.
+ */
+__rte_experimental
+uint64_t rte_tsc_to_ns(uint64_t tsc);
+
#ifdef __cplusplus
}
#endif
--
2.53.0
next prev parent reply other threads:[~2026-10-20 17:57 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-18 14:38 [PATCH v3 1/1] pcapng: add user-supplied timestamp support Dawid Wesierski
2026-06-18 15:22 ` Stephen Hemminger
2026-06-18 16:45 ` Morten Brørup
2026-06-18 18:51 ` Stephen Hemminger
2026-06-23 14:10 ` [PATCH v4] " Dawid Wesierski
2026-06-23 13:53 ` Stephen Hemminger
2026-06-24 21:57 ` [PATCH v5] " Dawid Wesierski
2026-06-24 22:07 ` Stephen Hemminger
2026-06-24 22:17 ` Stephen Hemminger
2026-06-26 10:22 ` [v5] " Dawid Wesierski
2026-06-26 15:04 ` Stephen Hemminger
2026-06-29 9:37 ` [PATCH v6 1/1] " Dawid Wesierski
2026-06-29 16:50 ` Stephen Hemminger
2026-10-14 17:21 ` [PATCH v7] " Dawid Wesierski
2026-10-14 17:38 ` Stephen Hemminger
2026-10-20 15:22 ` Dawid Wesierski
2026-10-20 15:29 ` [PATCH v8] " Dawid Wesierski
2026-10-20 17:55 ` [PATCH 0/3] pcapng timestamp conversion and user-supplied timestamps Stephen Hemminger
2026-10-20 17:55 ` Stephen Hemminger [this message]
2026-10-20 17:55 ` [PATCH 2/3] pcapng: use EAL TSC to epoch conversion Stephen Hemminger
2026-10-20 17:55 ` [PATCH 3/3] pcapng: add user-supplied timestamp support Stephen Hemminger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261020175732.1552406-2-stephen@networkplumber.org \
--to=stephen@networkplumber.org \
--cc=dev@dpdk.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.