All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 0/1] ethdev: per-packet Tx timestamp slot management
@ 2026-08-17 19:24 Rajesh Kumar
  2026-08-17 19:24 ` [RFC 1/1] ethdev: add per-packet Tx timestamp slot APIs Rajesh Kumar
                   ` (6 more replies)
  0 siblings, 7 replies; 14+ messages in thread
From: Rajesh Kumar @ 2026-08-17 19:24 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andrew.rybchenko, Rajesh Kumar

The current DPDK ethdev time synchronization framework is architected
around a single, shared hardware latch. The existing API,
`rte_eth_timesync_read_tx_timestamp()`, assumes a serialization model
where only one TX timestamp is outstanding at any given time.

This model creates severe limitations for modern high-throughput network
interface cards (NICs). When multiple packets requiring precise
transmit timestamps are sent concurrently, the shared latch becomes a
race-condition bottleneck. It makes timestamp retrieval unreliable and
drops accuracy. Furthermore, Poll Mode Drivers (PMDs) backed by
hardware that supports independent, per-packet timestamping slots have
no way to expose this capability to the user.

To solve this, this RFC introduces a formal slot-based lifecycle API
for per-packet Tx timestamp management. The API decouples timestamp
tracking from the global latch model, enabling true asynchronous,
parallel hardware timestamping.

Key Components of the Proposal:
================================
1. **Slot Lifecycle Management APIs**:
   - `rte_eth_timesync_tx_timestamp_slot_alloc()`: Allocates and locks
     a unique hardware slot prior to frame transmission.
   - `rte_eth_timesync_tx_timestamp_stamp_mbuf()`: Embeds the allocated
     slot handle into an mbuf dynamic field, allowing the PMD to
     program the specific hardware descriptor during the Tx burst.
   - `rte_eth_timesync_read_tx_timestamp_slot()`: Asynchronously polls
     a specific slot for its captured value, safely returning `-EAGAIN`
     if the hardware has not yet written back the timestamp.
   - `rte_eth_timesync_tx_timestamp_slot_release()`: Recycles the
     hardware slot back to the PMD resource pool after successful
     retrieval or an application timeout.

2. **Dual-Domain Timing Mechanics**:
   - Introduces `struct rte_eth_timesync_dual_domain_timestamp` to
     simultaneously capture both the adjusted PTP Hardware Clock (PHC)
     domain (`adjusted_ns`) and the raw, free-running cycles domain
     (`cycles_ns`).
   - A dedicated `valid_mask` tracks the validity of each time domain
     independently, giving applications granular telemetry options.

Scope of this RFC & Open Questions for the Community:
======================================================
This RFC establishes the structural definition of the APIs and driver
interfaces. Before proceeding to a full `v1` patch with active PMD
implementations, we would highly appreciate the community's feedback
on the following design choices:

- **Mbuf Dynamic Field Layout**: Is the encapsulation of the slot
  handle into an mbuf dynamic field using standard dynamic registration
  the preferred mechanism for passing steering context to the Tx
  descriptor path?
- **Dual-Domain Structure**: Does tracking both the adjusted PHC and
  the cycle counter within `rte_eth_timesync_dual_domain_timestamp`
  cover the needs of other hardware vendors supporting concurrent
  domains?
- **Error States**: Is `-EAGAIN` an acceptable return code for
  non-blocking polling of incomplete timestamps, or should we consider
  an explicit bitmask status?

Please review the proposed design. We welcome your feedback, design
suggestions, and critique.

Rajesh Kumar (1):
  ethdev: add per-packet Tx timestamp slot APIs

 lib/ethdev/ethdev_driver.h |  19 ++++++
 lib/ethdev/rte_ethdev.c    | 121 +++++++++++++++++++++++++++++++++++++
 lib/ethdev/rte_ethdev.h    | 108 +++++++++++++++++++++++++++++++++
 3 files changed, 248 insertions(+)

-- 
2.54.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [RFC 1/1] ethdev: add per-packet Tx timestamp slot APIs
  2026-08-17 19:24 [RFC 0/1] ethdev: per-packet Tx timestamp slot management Rajesh Kumar
@ 2026-08-17 19:24 ` Rajesh Kumar
  2026-08-20  4:51   ` Naga Harish K, S V
  2026-08-18  2:23 ` [RFC 0/1] ethdev: per-packet Tx timestamp slot management Stephen Hemminger
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Rajesh Kumar @ 2026-08-17 19:24 UTC (permalink / raw)
  To: dev; +Cc: thomas, bruce.richardson, andrew.rybchenko, Rajesh Kumar

Add ethdev public and driver-facing APIs for per-packet Tx
hardware timestamp slot management.

The existing `rte_eth_timesync_read_tx_timestamp()` exposes a
single shared latch, making it unreliable when multiple Tx
timestamps are outstanding concurrently. PMDs with per-packet
slot hardware cannot be exploited through this interface.

Introduce a slot lifecycle API:
- `rte_eth_timesync_tx_timestamp_slot_alloc()`: reserve a
  hardware slot before transmit
- `rte_eth_timesync_tx_timestamp_stamp_mbuf()`: embed slot
  handle into mbuf dynfield for per-packet NIC steering
- `rte_eth_timesync_read_tx_timestamp_slot()`: poll slot for
  a captured timestamp; returns -EAGAIN if not ready
- `rte_eth_timesync_tx_timestamp_slot_release()`: return slot
  to PMD after readback or timeout

Introduce `rte_eth_timesync_dual_domain_timestamp` to carry
both the adjusted PHC time (`adjusted_ns`) and free-running
cycles-domain time (`cycles_ns`), each with an individual
validity bit in `valid_mask`.

Signed-off-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
 lib/ethdev/ethdev_driver.h |  19 ++++++
 lib/ethdev/rte_ethdev.c    | 121 +++++++++++++++++++++++++++++++++++++
 lib/ethdev/rte_ethdev.h    | 108 +++++++++++++++++++++++++++++++++
 3 files changed, 248 insertions(+)

diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 0f336f9567..61dadcea49 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -795,6 +795,19 @@ typedef int (*eth_timesync_read_rx_timestamp_t)(struct rte_eth_dev *dev,
 typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 						struct timespec *timestamp);
 
+/** @internal Allocate a per-packet TX timestamp slot handle. */
+typedef int (*eth_timesync_tx_timestamp_slot_alloc_t)(struct rte_eth_dev *dev,
+		uint16_t tx_queue_id, uint32_t *slot_id);
+
+/** @internal Read TX timestamp by slot handle. */
+typedef int (*eth_timesync_read_tx_timestamp_slot_t)(struct rte_eth_dev *dev,
+		uint32_t slot_id,
+		struct rte_eth_timesync_dual_domain_timestamp *timestamp);
+
+/** @internal Release a previously allocated TX timestamp slot handle. */
+typedef int (*eth_timesync_tx_timestamp_slot_release_t)(struct rte_eth_dev *dev,
+		uint32_t slot_id);
+
 /** @internal Function used to adjust the device clock. */
 typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
 
@@ -1561,6 +1574,12 @@ struct eth_dev_ops {
 	eth_timesync_read_rx_timestamp_t timesync_read_rx_timestamp;
 	/** Read the IEEE1588/802.1AS Tx timestamp */
 	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
+	/** Allocate a TX timestamp slot handle */
+	eth_timesync_tx_timestamp_slot_alloc_t timesync_tx_timestamp_slot_alloc;
+	/** Read a TX timestamp using a slot handle */
+	eth_timesync_read_tx_timestamp_slot_t timesync_read_tx_timestamp_slot;
+	/** Release a TX timestamp slot handle */
+	eth_timesync_tx_timestamp_slot_release_t timesync_tx_timestamp_slot_release;
 	/** Adjust the device clock */
 	eth_timesync_adjust_time   timesync_adjust_time;
 	/** Adjust the clock frequency */
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index 9efeaf77cb..b955784594 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -6699,6 +6699,127 @@ rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
 
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_slot_alloc, 26.11)
+int
+rte_eth_timesync_tx_timestamp_slot_alloc(uint16_t port_id,
+					 uint16_t tx_queue_id,
+					 uint32_t *slot_id)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (slot_id == NULL) {
+		RTE_ETHDEV_LOG_LINE(ERR,
+			"Cannot allocate ethdev port %u Tx timestamp slot to NULL",
+			port_id);
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->timesync_tx_timestamp_slot_alloc == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_tx_timestamp_slot_alloc(dev,
+				tx_queue_id, slot_id));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_read_tx_timestamp_slot, 26.11)
+int
+rte_eth_timesync_read_tx_timestamp_slot(uint16_t port_id,
+					uint32_t slot_id,
+					struct rte_eth_timesync_dual_domain_timestamp *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (timestamp == NULL) {
+		RTE_ETHDEV_LOG_LINE(ERR,
+			"Cannot read ethdev port %u Tx timestamp slot to NULL",
+			port_id);
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->timesync_read_tx_timestamp_slot == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_read_tx_timestamp_slot(dev,
+				slot_id, timestamp));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_slot_release, 26.11)
+int
+rte_eth_timesync_tx_timestamp_slot_release(uint16_t port_id, uint32_t slot_id)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (dev->dev_ops->timesync_tx_timestamp_slot_release == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_tx_timestamp_slot_release(dev,
+				slot_id));
+}
+
+static int rte_eth_timesync_tx_slot_dynfield_offset = -1;
+static uint64_t rte_eth_timesync_tx_slot_dynflag;
+
+static int
+rte_eth_timesync_tx_slot_dynfield_register(void)
+{
+	const struct rte_mbuf_dynfield slot_dynfield = {
+		.name  = RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME,
+		.size  = sizeof(uint32_t),
+		.align = alignof(uint32_t),
+	};
+
+	if (rte_eth_timesync_tx_slot_dynfield_offset >= 0)
+		return 0;
+
+	rte_eth_timesync_tx_slot_dynfield_offset =
+			rte_mbuf_dynfield_register(&slot_dynfield);
+	if (rte_eth_timesync_tx_slot_dynfield_offset < 0)
+		rte_eth_timesync_tx_slot_dynfield_offset =
+				rte_mbuf_dynfield_lookup(
+					RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME, NULL);
+	if (rte_eth_timesync_tx_slot_dynfield_offset < 0)
+		return -ENOTSUP;
+
+	{
+		int flag_bit = rte_mbuf_dynflag_register(
+			&(const struct rte_mbuf_dynflag){
+				.name = RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME "_flag"});
+		if (flag_bit < 0)
+			flag_bit = rte_mbuf_dynflag_lookup(
+				RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME "_flag", NULL);
+		if (flag_bit >= 0)
+			rte_eth_timesync_tx_slot_dynflag = RTE_BIT64(flag_bit);
+	}
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_stamp_mbuf, 26.11)
+int
+rte_eth_timesync_tx_timestamp_stamp_mbuf(uint16_t port_id __rte_unused,
+					 uint32_t slot_id, struct rte_mbuf *m)
+{
+	if (m == NULL)
+		return -EINVAL;
+	if (rte_eth_timesync_tx_slot_dynfield_register() != 0)
+		return -ENOTSUP;
+	*RTE_MBUF_DYNFIELD(m, rte_eth_timesync_tx_slot_dynfield_offset,
+			   uint32_t *) = slot_id;
+	m->ol_flags |= rte_eth_timesync_tx_slot_dynflag;
+	return 0;
+}
+
 RTE_EXPORT_SYMBOL(rte_eth_timesync_adjust_time)
 int
 rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta)
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index ee400b386f..339c39fcdd 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -5528,6 +5528,114 @@ int rte_eth_timesync_read_rx_timestamp(uint16_t port_id,
 int rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
 		struct timespec *timestamp);
 
+/** Valid bit for rte_eth_timesync_dual_domain_timestamp.adjusted_ns. */
+#define RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_ADJUSTED_VALID	RTE_BIT32(0)
+/** Valid bit for rte_eth_timesync_dual_domain_timestamp.cycles_ns. */
+#define RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_CYCLES_VALID	RTE_BIT32(1)
+
+/**
+ * Dual-domain TX timestamp payload.
+ */
+struct rte_eth_timesync_dual_domain_timestamp {
+	int64_t adjusted_ns;
+	int64_t cycles_ns;
+	uint32_t valid_mask;
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate a TX timestamp slot handle for per-packet correlation.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param tx_queue_id
+ *   TX queue used by the packet to be transmitted.
+ * @param slot_id
+ *   Output handle identifying the allocated slot.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_slot_alloc(uint16_t port_id,
+		uint16_t tx_queue_id, uint32_t *slot_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Read a per-packet TX timestamp using a previously allocated slot handle.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param slot_id
+ *   Slot handle returned by rte_eth_timesync_tx_timestamp_slot_alloc().
+ * @param timestamp
+ *   Output dual-domain timestamp payload.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -EAGAIN: Timestamp is not ready yet.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_read_tx_timestamp_slot(uint16_t port_id,
+		uint32_t slot_id,
+		struct rte_eth_timesync_dual_domain_timestamp *timestamp);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Release a previously allocated TX timestamp slot handle.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param slot_id
+ *   Slot handle to release.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_slot_release(uint16_t port_id,
+		uint32_t slot_id);
+
+/** Mbuf dynfield name for the TX timestamp slot handle. */
+#define RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME "rte_eth_timesync_tx_slot"
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Stamp an mbuf with a TX timestamp slot handle so the TX path steers the
+ * NIC to capture the timestamp in the correct per-packet slot.
+ *
+ * Must be called after rte_eth_timesync_tx_timestamp_slot_alloc() and before
+ * rte_eth_tx_burst(). Safe for concurrent callers — slot is stored per-mbuf.
+ *
+ * @param port_id  The port identifier (unused; reserved for future PMD use).
+ * @param slot_id  Slot handle from rte_eth_timesync_tx_timestamp_slot_alloc().
+ * @param m        Mbuf to stamp.
+ * @return 0 on success, -ENOTSUP if dynfield registration failed.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_stamp_mbuf(uint16_t port_id,
+		uint32_t slot_id, struct rte_mbuf *m);
+
 /**
  * Adjust the timesync clock on an Ethernet device.
  *
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [RFC 0/1] ethdev: per-packet Tx timestamp slot management
  2026-08-17 19:24 [RFC 0/1] ethdev: per-packet Tx timestamp slot management Rajesh Kumar
  2026-08-17 19:24 ` [RFC 1/1] ethdev: add per-packet Tx timestamp slot APIs Rajesh Kumar
@ 2026-08-18  2:23 ` Stephen Hemminger
  2026-08-20  4:41 ` Naga Harish K, S V
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 14+ messages in thread
From: Stephen Hemminger @ 2026-08-18  2:23 UTC (permalink / raw)
  To: Rajesh Kumar; +Cc: dev, thomas, bruce.richardson, andrew.rybchenko

On Tue, 18 Aug 2026 00:54:14 +0530
Rajesh Kumar <rajesh3.kumar@intel.com> wrote:

> The current DPDK ethdev time synchronization framework is architected
> around a single, shared hardware latch. The existing API,
> `rte_eth_timesync_read_tx_timestamp()`, assumes a serialization model
> where only one TX timestamp is outstanding at any given time.
> 
> This model creates severe limitations for modern high-throughput network
> interface cards (NICs). When multiple packets requiring precise
> transmit timestamps are sent concurrently, the shared latch becomes a
> race-condition bottleneck. It makes timestamp retrieval unreliable and
> drops accuracy. Furthermore, Poll Mode Drivers (PMDs) backed by
> hardware that supports independent, per-packet timestamping slots have
> no way to expose this capability to the user.
> 
> To solve this, this RFC introduces a formal slot-based lifecycle API
> for per-packet Tx timestamp management. The API decouples timestamp
> tracking from the global latch model, enabling true asynchronous,
> parallel hardware timestamping.


Lots of reasonable AI feedback to the design.

Review of the RFC. Design issues first since that's what they're asking for, then code defects.

Design

    No capability discovery or exhaustion semantics. Nothing reports how many slots exist, whether they're per-port or per-queue, and slot_alloc() doesn't document what it returns when slots run out (-ENOSPC? -EAGAIN?). That's the first thing an application hits. Needs a rte_eth_dev_info field or query, and a defined out-of-slots errno.
    Queue asymmetry: alloc() takes tx_queue_id but read() and release() don't. Either slot_id is port-global (then why does alloc need the queue?) or it's per-queue (then read/release are ambiguous). Pick one and document it. Also tx_queue_id is never validated against nb_tx_queues in the ethdev layer.
    Interaction with the existing mechanism is undefined. Does the app still set RTE_MBUF_F_TX_IEEE1588_TMST? Can the legacy latch API and the slot API coexist on one port? PMDs today key tx timestamping off that flag; the RFC needs to say what supersedes what.
    Fast-path cost contradicts the stated motivation. The cover letter argues high-throughput concurrent timestamping, but the lifecycle is three dev_ops indirect calls plus a dynfield write per packet, all through the slow path. Fine for PTP rates; if the claim is more than that, alloc/release want burst variants or the intended rate should be stated.
    cycles_ns is self-contradictory: is it raw counter cycles or nanoseconds from the free-running clock? If cycles, drop the _ns and expose the frequency; if ns, call it raw_ns or free_ns. Also this struct switches to int64 ns while every other timesync call uses struct timespec; probably the right move but justify it in the cover letter.
    PMDs can't consume the dynfield as written. The offset and flag are static in rte_ethdev.c and not exposed to drivers. A PMD has to re-lookup by name, and the dynflag name only exists as a string concat inside the .c file, so drivers would hardcode "..._flag". Define the flag name macro in the header and provide a lookup helper, following the RTE_MBUF_DYNFIELD_TIMESTAMP_NAME pattern.
    Naming: rte_eth_timesync_tx_timestamp_stamp_mbuf stutters. ..._tx_slot_set_mbuf or similar.

Defects

    Silent dynflag failure in rte_eth_timesync_tx_slot_dynfield_register(). If both rte_mbuf_dynflag_register() and the lookup fail, rte_eth_timesync_tx_slot_dynflag stays 0, the function returns 0, and stamp_mbuf() ORs 0 into ol_flags and reports success. The PMD never sees the request. Must return error. Worse, the early return on offset >= 0 means the flag is never retried on subsequent calls, so one transient failure is permanent.
    Likely doesn't compile as posted: the diff adds no includes, but uses struct rte_mbuf_dynfield, rte_mbuf_dynflag_register() (needs rte_mbuf_dyn.h) and alignof (needs stdalign.h pre-C23). Check whether rte_ethdev.c already pulls those in; I don't believe it does.
    stamp_mbuf() takes port_id and ignores it, documented as "reserved for future PMD use". Either validate it or drop it; a parameter whose semantics arrive later is an API trap. Dropping it also removes the false implication that the call is port-scoped.

Nits

    stamp_mbuf() doxygen deviates from the file's param style, omits the -EINVAL return the code actually produces, and contains an em-dash. The dual_domain_timestamp struct fields lack doxygen comments.
    v1 needs rel_notes and prog_guide (ptp section) updates; RFC is fine without.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC 0/1] ethdev: per-packet Tx timestamp slot management
  2026-08-17 19:24 [RFC 0/1] ethdev: per-packet Tx timestamp slot management Rajesh Kumar
  2026-08-17 19:24 ` [RFC 1/1] ethdev: add per-packet Tx timestamp slot APIs Rajesh Kumar
  2026-08-18  2:23 ` [RFC 0/1] ethdev: per-packet Tx timestamp slot management Stephen Hemminger
@ 2026-08-20  4:41 ` Naga Harish K, S V
  2026-08-27 11:09   ` Kumar, Rajesh
  2026-08-27 12:13 ` [RFC PATCH v2 0/1] ethdev: add Tx timestamp slot APIs Rajesh Kumar
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Naga Harish K, S V @ 2026-08-20  4:41 UTC (permalink / raw)
  To: Kumar, Rajesh3, dev@dpdk.org
  Cc: thomas@monjalon.net, Richardson, Bruce,
	andrew.rybchenko@oktetlabs.ru

[-- Attachment #1: Type: text/plain, Size: 4149 bytes --]




________________________________
From: Rajesh Kumar <rajesh3.kumar@intel.com>
Sent: Tuesday, August 18, 2026 12:54 AM
To: dev@dpdk.org <dev@dpdk.org>
Cc: thomas@monjalon.net <thomas@monjalon.net>; Richardson, Bruce <bruce.richardson@intel.com>; andrew.rybchenko@oktetlabs.ru <andrew.rybchenko@oktetlabs.ru>; Kumar, Rajesh3 <rajesh3.kumar@intel.com>
Subject: [RFC 0/1] ethdev: per-packet Tx timestamp slot management

The current DPDK ethdev time synchronization framework is architected
around a single, shared hardware latch. The existing API,
`rte_eth_timesync_read_tx_timestamp()`, assumes a serialization model
where only one TX timestamp is outstanding at any given time.

This model creates severe limitations for modern high-throughput network
interface cards (NICs). When multiple packets requiring precise
transmit timestamps are sent concurrently, the shared latch becomes a
race-condition bottleneck. It makes timestamp retrieval unreliable and
drops accuracy. Furthermore, Poll Mode Drivers (PMDs) backed by
hardware that supports independent, per-packet timestamping slots have
no way to expose this capability to the user.

To solve this, this RFC introduces a formal slot-based lifecycle API
for per-packet Tx timestamp management. The API decouples timestamp
tracking from the global latch model, enabling true asynchronous,
parallel hardware timestamping.

Key Components of the Proposal:
================================
1. **Slot Lifecycle Management APIs**:
   - `rte_eth_timesync_tx_timestamp_slot_alloc()`: Allocates and locks
     a unique hardware slot prior to frame transmission.
   - `rte_eth_timesync_tx_timestamp_stamp_mbuf()`: Embeds the allocated
     slot handle into an mbuf dynamic field, allowing the PMD to
     program the specific hardware descriptor during the Tx burst.
   - `rte_eth_timesync_read_tx_timestamp_slot()`: Asynchronously polls
     a specific slot for its captured value, safely returning `-EAGAIN`
     if the hardware has not yet written back the timestamp.
   - `rte_eth_timesync_tx_timestamp_slot_release()`: Recycles the
     hardware slot back to the PMD resource pool after successful
     retrieval or an application timeout.

2. **Dual-Domain Timing Mechanics**:
   - Introduces `struct rte_eth_timesync_dual_domain_timestamp` to
     simultaneously capture both the adjusted PTP Hardware Clock (PHC)
     domain (`adjusted_ns`) and the raw, free-running cycles domain
     (`cycles_ns`).
   - A dedicated `valid_mask` tracks the validity of each time domain
     independently, giving applications granular telemetry options.

Scope of this RFC & Open Questions for the Community:
======================================================
This RFC establishes the structural definition of the APIs and driver
interfaces. Before proceeding to a full `v1` patch with active PMD
implementations, we would highly appreciate the community's feedback
on the following design choices:

- **Mbuf Dynamic Field Layout**: Is the encapsulation of the slot
  handle into an mbuf dynamic field using standard dynamic registration
  the preferred mechanism for passing steering context to the Tx
  descriptor path?

 rte_mbuf structure has "timesync" field, which can be used for the slot.
 This way mbuf dynamic field use can be avoided.

- **Dual-Domain Structure**: Does tracking both the adjusted PHC and
  the cycle counter within `rte_eth_timesync_dual_domain_timestamp`
  cover the needs of other hardware vendors supporting concurrent
  domains?
- **Error States**: Is `-EAGAIN` an acceptable return code for
  non-blocking polling of incomplete timestamps, or should we consider
  an explicit bitmask status?

Please review the proposed design. We welcome your feedback, design
suggestions, and critique.

Rajesh Kumar (1):
  ethdev: add per-packet Tx timestamp slot APIs

 lib/ethdev/ethdev_driver.h |  19 ++++++
 lib/ethdev/rte_ethdev.c    | 121 +++++++++++++++++++++++++++++++++++++
 lib/ethdev/rte_ethdev.h    | 108 +++++++++++++++++++++++++++++++++
 3 files changed, 248 insertions(+)

--
2.54.0


[-- Attachment #2: Type: text/html, Size: 5977 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [RFC 1/1] ethdev: add per-packet Tx timestamp slot APIs
  2026-08-17 19:24 ` [RFC 1/1] ethdev: add per-packet Tx timestamp slot APIs Rajesh Kumar
@ 2026-08-20  4:51   ` Naga Harish K, S V
  0 siblings, 0 replies; 14+ messages in thread
From: Naga Harish K, S V @ 2026-08-20  4:51 UTC (permalink / raw)
  To: Kumar, Rajesh3, dev@dpdk.org
  Cc: thomas@monjalon.net, Richardson, Bruce,
	andrew.rybchenko@oktetlabs.ru

[-- Attachment #1: Type: text/plain, Size: 13063 bytes --]




________________________________
From: Rajesh Kumar <rajesh3.kumar@intel.com>
Sent: Tuesday, August 18, 2026 12:54 AM
To: dev@dpdk.org <dev@dpdk.org>
Cc: thomas@monjalon.net <thomas@monjalon.net>; Richardson, Bruce <bruce.richardson@intel.com>; andrew.rybchenko@oktetlabs.ru <andrew.rybchenko@oktetlabs.ru>; Kumar, Rajesh3 <rajesh3.kumar@intel.com>
Subject: [RFC 1/1] ethdev: add per-packet Tx timestamp slot APIs

Add ethdev public and driver-facing APIs for per-packet Tx
hardware timestamp slot management.

The existing `rte_eth_timesync_read_tx_timestamp()` exposes a
single shared latch, making it unreliable when multiple Tx
timestamps are outstanding concurrently. PMDs with per-packet
slot hardware cannot be exploited through this interface.

Introduce a slot lifecycle API:
- `rte_eth_timesync_tx_timestamp_slot_alloc()`: reserve a
  hardware slot before transmit
- `rte_eth_timesync_tx_timestamp_stamp_mbuf()`: embed slot
  handle into mbuf dynfield for per-packet NIC steering
- `rte_eth_timesync_read_tx_timestamp_slot()`: poll slot for
  a captured timestamp; returns -EAGAIN if not ready
- `rte_eth_timesync_tx_timestamp_slot_release()`: return slot
  to PMD after readback or timeout

Introduce `rte_eth_timesync_dual_domain_timestamp` to carry
both the adjusted PHC time (`adjusted_ns`) and free-running
cycles-domain time (`cycles_ns`), each with an individual
validity bit in `valid_mask`.

Signed-off-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
 lib/ethdev/ethdev_driver.h |  19 ++++++
 lib/ethdev/rte_ethdev.c    | 121 +++++++++++++++++++++++++++++++++++++
 lib/ethdev/rte_ethdev.h    | 108 +++++++++++++++++++++++++++++++++
 3 files changed, 248 insertions(+)

diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 0f336f9567..61dadcea49 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -795,6 +795,19 @@ typedef int (*eth_timesync_read_rx_timestamp_t)(struct rte_eth_dev *dev,
 typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
                                                 struct timespec *timestamp);

+/** @internal Allocate a per-packet TX timestamp slot handle. */
+typedef int (*eth_timesync_tx_timestamp_slot_alloc_t)(struct rte_eth_dev *dev,
+               uint16_t tx_queue_id, uint32_t *slot_id);
+
+/** @internal Read TX timestamp by slot handle. */
+typedef int (*eth_timesync_read_tx_timestamp_slot_t)(struct rte_eth_dev *dev,
+               uint32_t slot_id,
+               struct rte_eth_timesync_dual_domain_timestamp *timestamp);
+
+/** @internal Release a previously allocated TX timestamp slot handle. */
+typedef int (*eth_timesync_tx_timestamp_slot_release_t)(struct rte_eth_dev *dev,
+               uint32_t slot_id);
+
 /** @internal Function used to adjust the device clock. */
 typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);

@@ -1561,6 +1574,12 @@ struct eth_dev_ops {
         eth_timesync_read_rx_timestamp_t timesync_read_rx_timestamp;
         /** Read the IEEE1588/802.1AS Tx timestamp */
         eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
+       /** Allocate a TX timestamp slot handle */
+       eth_timesync_tx_timestamp_slot_alloc_t timesync_tx_timestamp_slot_alloc;
+       /** Read a TX timestamp using a slot handle */
+       eth_timesync_read_tx_timestamp_slot_t timesync_read_tx_timestamp_slot;
+       /** Release a TX timestamp slot handle */
+       eth_timesync_tx_timestamp_slot_release_t timesync_tx_timestamp_slot_release;
         /** Adjust the device clock */
         eth_timesync_adjust_time   timesync_adjust_time;
         /** Adjust the clock frequency */
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index 9efeaf77cb..b955784594 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -6699,6 +6699,127 @@ rte_eth_timesync_read_tx_timestamp(uint16_t port_id,

 }

+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_slot_alloc, 26.11)
+int
+rte_eth_timesync_tx_timestamp_slot_alloc(uint16_t port_id,
+                                        uint16_t tx_queue_id,
+                                        uint32_t *slot_id)
+{
+       struct rte_eth_dev *dev;
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+       dev = &rte_eth_devices[port_id];
+
+       if (slot_id == NULL) {
+               RTE_ETHDEV_LOG_LINE(ERR,
+                       "Cannot allocate ethdev port %u Tx timestamp slot to NULL",
+                       port_id);
+               return -EINVAL;
+       }
+
+       if (dev->dev_ops->timesync_tx_timestamp_slot_alloc == NULL)
+               return -ENOTSUP;
+
+       return eth_err(port_id,
+                       dev->dev_ops->timesync_tx_timestamp_slot_alloc(dev,
+                               tx_queue_id, slot_id));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_read_tx_timestamp_slot, 26.11)
+int
+rte_eth_timesync_read_tx_timestamp_slot(uint16_t port_id,
+                                       uint32_t slot_id,
+                                       struct rte_eth_timesync_dual_domain_timestamp *timestamp)
+{
+       struct rte_eth_dev *dev;
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+       dev = &rte_eth_devices[port_id];
+
+       if (timestamp == NULL) {
+               RTE_ETHDEV_LOG_LINE(ERR,
+                       "Cannot read ethdev port %u Tx timestamp slot to NULL",
+                       port_id);
+               return -EINVAL;
+       }
+
+       if (dev->dev_ops->timesync_read_tx_timestamp_slot == NULL)
+               return -ENOTSUP;
+
+       return eth_err(port_id,
+                       dev->dev_ops->timesync_read_tx_timestamp_slot(dev,
+                               slot_id, timestamp));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_slot_release, 26.11)
+int
+rte_eth_timesync_tx_timestamp_slot_release(uint16_t port_id, uint32_t slot_id)
+{
+       struct rte_eth_dev *dev;
+
+       RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+       dev = &rte_eth_devices[port_id];
+
+       if (dev->dev_ops->timesync_tx_timestamp_slot_release == NULL)
+               return -ENOTSUP;
+
+       return eth_err(port_id,
+                       dev->dev_ops->timesync_tx_timestamp_slot_release(dev,
+                               slot_id));
+}
+
+static int rte_eth_timesync_tx_slot_dynfield_offset = -1;
+static uint64_t rte_eth_timesync_tx_slot_dynflag;
+
+static int
+rte_eth_timesync_tx_slot_dynfield_register(void)
+{
+       const struct rte_mbuf_dynfield slot_dynfield = {
+               .name  = RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME,
+               .size  = sizeof(uint32_t),
+               .align = alignof(uint32_t),
+       };
+
+       if (rte_eth_timesync_tx_slot_dynfield_offset >= 0)
+               return 0;
+
+       rte_eth_timesync_tx_slot_dynfield_offset =
+                       rte_mbuf_dynfield_register(&slot_dynfield);
+       if (rte_eth_timesync_tx_slot_dynfield_offset < 0)
+               rte_eth_timesync_tx_slot_dynfield_offset =
+                               rte_mbuf_dynfield_lookup(
+                                       RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME, NULL);
+       if (rte_eth_timesync_tx_slot_dynfield_offset < 0)
+               return -ENOTSUP;
+
+       {
+               int flag_bit = rte_mbuf_dynflag_register(
+                       &(const struct rte_mbuf_dynflag){
+                               .name = RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME "_flag"});
+               if (flag_bit < 0)
+                       flag_bit = rte_mbuf_dynflag_lookup(
+                               RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME "_flag", NULL);
+               if (flag_bit >= 0)
+                       rte_eth_timesync_tx_slot_dynflag = RTE_BIT64(flag_bit);
+       }
+       return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_stamp_mbuf, 26.11)
+int
+rte_eth_timesync_tx_timestamp_stamp_mbuf(uint16_t port_id __rte_unused,
+                                        uint32_t slot_id, struct rte_mbuf *m)
+{
+       if (m == NULL)
+               return -EINVAL;
+       if (rte_eth_timesync_tx_slot_dynfield_register() != 0)
+               return -ENOTSUP;
+       *RTE_MBUF_DYNFIELD(m, rte_eth_timesync_tx_slot_dynfield_offset,
+                          uint32_t *) = slot_id;
+       m->ol_flags |= rte_eth_timesync_tx_slot_dynflag;
+       return 0;
+}
+
 RTE_EXPORT_SYMBOL(rte_eth_timesync_adjust_time)
 int
 rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta)
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index ee400b386f..339c39fcdd 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -5528,6 +5528,114 @@ int rte_eth_timesync_read_rx_timestamp(uint16_t port_id,
 int rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
                 struct timespec *timestamp);

+/** Valid bit for rte_eth_timesync_dual_domain_timestamp.adjusted_ns. */
+#define RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_ADJUSTED_VALID  RTE_BIT32(0)
+/** Valid bit for rte_eth_timesync_dual_domain_timestamp.cycles_ns. */
+#define RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_CYCLES_VALID    RTE_BIT32(1)
+
+/**
+ * Dual-domain TX timestamp payload.
+ */
+struct rte_eth_timesync_dual_domain_timestamp {
+       int64_t adjusted_ns;
+       int64_t cycles_ns;
+       uint32_t valid_mask;
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate a TX timestamp slot handle for per-packet correlation.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param tx_queue_id
+ *   TX queue used by the packet to be transmitted.
+ * @param slot_id
+ *   Output handle identifying the allocated slot.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_slot_alloc(uint16_t port_id,
+               uint16_t tx_queue_id, uint32_t *slot_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Read a per-packet TX timestamp using a previously allocated slot handle.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param slot_id
+ *   Slot handle returned by rte_eth_timesync_tx_timestamp_slot_alloc().
+ * @param timestamp
+ *   Output dual-domain timestamp payload.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -EAGAIN: Timestamp is not ready yet.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_read_tx_timestamp_slot(uint16_t port_id,
+               uint32_t slot_id,
+               struct rte_eth_timesync_dual_domain_timestamp *timestamp);

How is the egress port information conveyed?
The timestamp slot is a resource of the physical egress port.

+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Release a previously allocated TX timestamp slot handle.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param slot_id
+ *   Slot handle to release.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_slot_release(uint16_t port_id,
+               uint32_t slot_id);
+
+/** Mbuf dynfield name for the TX timestamp slot handle. */
+#define RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME "rte_eth_timesync_tx_slot"
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Stamp an mbuf with a TX timestamp slot handle so the TX path steers the
+ * NIC to capture the timestamp in the correct per-packet slot.
+ *
+ * Must be called after rte_eth_timesync_tx_timestamp_slot_alloc() and before
+ * rte_eth_tx_burst(). Safe for concurrent callers — slot is stored per-mbuf.
+ *
+ * @param port_id  The port identifier (unused; reserved for future PMD use).
+ * @param slot_id  Slot handle from rte_eth_timesync_tx_timestamp_slot_alloc().
+ * @param m        Mbuf to stamp.
+ * @return 0 on success, -ENOTSUP if dynfield registration failed.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_stamp_mbuf(uint16_t port_id,
+               uint32_t slot_id, struct rte_mbuf *m);
+
 /**
  * Adjust the timesync clock on an Ethernet device.
  *
--
2.54.0


[-- Attachment #2: Type: text/html, Size: 23332 bytes --]

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [RFC 0/1] ethdev: per-packet Tx timestamp slot management
  2026-08-20  4:41 ` Naga Harish K, S V
@ 2026-08-27 11:09   ` Kumar, Rajesh
  0 siblings, 0 replies; 14+ messages in thread
From: Kumar, Rajesh @ 2026-08-27 11:09 UTC (permalink / raw)
  To: Naga Harish K, S V, dev@dpdk.org
  Cc: thomas@monjalon.net, Richardson, Bruce,
	andrew.rybchenko@oktetlabs.ru


On 20-08-2026 10:11 am, Naga Harish K, S V wrote:
> rte_mbuf structure has "timesync" field, which can be used for the slot.
>  This way mbuf dynamic field use can be avoided.

"timesync" in rte_mbuf is already an established RX-side metadata field 
and not a generic TX slot carrier. As it is used as RX-side timestamp 
metadata, not as a generic TX slot ID. If the same field suddenly meant 
“TX slot handle” for one PMD and “RX queue/timestamp tag” for another, 
the ABI would become ambiguous and driver-specific, while the 
dynamic-field approach keeps the per-packet slot metadata explicit and 
portable. For that reason, we prefer the dynamic-field design for this 
feature.


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [RFC PATCH v2 0/1] ethdev: add Tx timestamp slot APIs
  2026-08-17 19:24 [RFC 0/1] ethdev: per-packet Tx timestamp slot management Rajesh Kumar
                   ` (2 preceding siblings ...)
  2026-08-20  4:41 ` Naga Harish K, S V
@ 2026-08-27 12:13 ` Rajesh Kumar
  2026-08-27 12:13   ` [RFC PATCH v3 1/1] ethdev: add Tx timestamp slot management APIs Rajesh Kumar
  2026-08-27 12:18 ` [RFC PATCH v3 0/1] ethdev: add Tx timestamp slot APIs Rajesh Kumar
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 14+ messages in thread
From: Rajesh Kumar @ 2026-08-27 12:13 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, andrew.rybchenko, stephen,
	aman.deep.singh, Rajesh Kumar

The ethdev timesync API currently exposes Tx timestamps through a shared
hardware register. This requires applications to serialize timestamped
packets and does not allow correlation when multiple packets are in
flight.

This RFC proposes an ethdev interface for hardware with independent Tx
timestamp slots. The interface reports the supported timestamping
mechanism, provides a port-global slot lifecycle, and lets applications
poll each slot asynchronously after transmission.

The proposal includes the following components:

* Capability reporting for shared-register and per-packet timestamping.
* Slot allocation, asynchronous timestamp retrieval, and slot release.
* A dual-domain timestamp structure for adjusted PHC and raw hardware
  time.
* Mbuf dynamic field and dynflag support for passing slot handles to Tx.
* Registration and process-local disabling of slot metadata.
* A compatibility alias for the mbuf stamping helper.
* Programmer-guide and NIC feature documentation.

The legacy rte_eth_timesync_read_tx_timestamp() API remains available on
devices using a shared timestamp register. This RFC adds the ethdev and
PMD interfaces but does not add a hardware-specific PMD implementation.

The following areas would benefit from review:

* Is the capability model sufficient for devices with different slot
  allocation or completion mechanisms?
* Is an mbuf dynamic field and dynflag the appropriate way to pass the
  slot handle into the Tx datapath?
* Is -EAGAIN the appropriate result while a slot timestamp is pending?
* Should the adjusted and raw timestamp domains use nanoseconds in the
  public structure, or should one domain expose hardware cycles instead?

Rajesh Kumar (1):
  ethdev: add Tx timestamp slot management APIs

 doc/guides/nics/features.rst              |  16 +-
 doc/guides/prog_guide/ethdev/index.rst    |   1 +
 doc/guides/prog_guide/ethdev/timesync.rst | 217 +++++++++++++++++++
 doc/guides/rel_notes/release_26_11.rst    |   7 +
 lib/ethdev/ethdev_driver.h                |  25 +++
 lib/ethdev/rte_ethdev.c                   | 158 ++++++++++++++
 lib/ethdev/rte_ethdev.h                   | 242 ++++++++++++++++++++++
 7 files changed, 662 insertions(+), 4 deletions(-)
 create mode 100644 doc/guides/prog_guide/ethdev/timesync.rst

-- 
2.55.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 1/1] ethdev: add Tx timestamp slot management APIs
  2026-08-27 12:13 ` [RFC PATCH v2 0/1] ethdev: add Tx timestamp slot APIs Rajesh Kumar
@ 2026-08-27 12:13   ` Rajesh Kumar
  0 siblings, 0 replies; 14+ messages in thread
From: Rajesh Kumar @ 2026-08-27 12:13 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, andrew.rybchenko, stephen,
	aman.deep.singh, Rajesh Kumar

Extend ethdev timesync with a capability model for selecting between
shared-register and per-packet Tx timestamping.

Add public and PMD interfaces to query timestamp capabilities, allocate
timestamp slots, retrieve timestamps asynchronously, and release slots.
Slots have port-global scope and can be used across Tx queues.

Add a dual-domain timestamp structure for reporting adjusted PHC time
and raw hardware time independently through validity flags.

Add APIs to register and unregister the mbuf dynamic field and dynflag
used to pass slot handles to the Tx datapath. Add helpers to associate
a slot handle with an mbuf before transmission.

Keep the legacy Tx timestamp API for shared-register hardware and provide
a compatibility alias for the mbuf stamping helper.

Document the timestamp capability model, slot lifecycle, and application
workflow.

Signed-off-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
 doc/guides/nics/features.rst              |  16 +-
 doc/guides/prog_guide/ethdev/index.rst    |   1 +
 doc/guides/prog_guide/ethdev/timesync.rst | 217 +++++++++++++++++++
 doc/guides/rel_notes/release_26_11.rst    |   7 +
 lib/ethdev/ethdev_driver.h                |  25 +++
 lib/ethdev/rte_ethdev.c                   | 158 ++++++++++++++
 lib/ethdev/rte_ethdev.h                   | 242 ++++++++++++++++++++++
 7 files changed, 662 insertions(+), 4 deletions(-)
 create mode 100644 doc/guides/prog_guide/ethdev/timesync.rst

diff --git a/doc/guides/nics/features.rst b/doc/guides/nics/features.rst
index 0b0c69e7cd..040a996156 100644
--- a/doc/guides/nics/features.rst
+++ b/doc/guides/nics/features.rst
@@ -692,14 +692,22 @@ Timesync
 
 Supports IEEE1588/802.1AS timestamping.
 
-* **[implements] eth_dev_ops**: ``timesync_enable``, ``timesync_disable``
+* **[implements] eth_dev_ops**: ``timesync_enable``, ``timesync_disable``,
   ``timesync_read_rx_timestamp``, ``timesync_read_tx_timestamp``,
+  ``timesync_tx_ts_get_capabilities``, ``timesync_tx_timestamp_slot_alloc``,
+  ``timesync_read_tx_timestamp_slot``, ``timesync_tx_timestamp_slot_release``,
   ``timesync_adjust_time``, ``timesync_adjust_freq``,
   ``timesync_read_time``, ``timesync_write_time``.
 * **[related]    API**: ``rte_eth_timesync_enable()``, ``rte_eth_timesync_disable()``,
-  ``rte_eth_timesync_read_rx_timestamp()``,
-  ``rte_eth_timesync_read_tx_timestamp``, ``rte_eth_timesync_adjust_time()``,
-  ``rte_eth_timesync_adjust_freq()``,
+  ``rte_eth_timesync_read_rx_timestamp()``, ``rte_eth_timesync_read_tx_timestamp()``,
+  ``rte_eth_timesync_tx_timestamp_slot_get_capabilities()``,
+  ``rte_eth_timesync_tx_timestamp_slot_alloc()``,
+  ``rte_eth_timesync_read_tx_timestamp_slot()``,
+  ``rte_eth_timesync_tx_timestamp_slot_release()``,
+  ``rte_eth_timesync_tx_slot_dynfield_register()``,
+  ``rte_eth_timesync_tx_slot_dynfield_unregister()``,
+  ``rte_eth_timesync_tx_timestamp_stamp_mbuf()``,
+  ``rte_eth_timesync_adjust_time()``, ``rte_eth_timesync_adjust_freq()``,
   ``rte_eth_timesync_read_time()``, ``rte_eth_timesync_write_time()``.
 
 
diff --git a/doc/guides/prog_guide/ethdev/index.rst b/doc/guides/prog_guide/ethdev/index.rst
index 392ced0a2e..bc21f28091 100644
--- a/doc/guides/prog_guide/ethdev/index.rst
+++ b/doc/guides/prog_guide/ethdev/index.rst
@@ -13,3 +13,4 @@ Ethernet Device Library
     traffic_metering_and_policing
     traffic_management
     qos_framework
+    timesync
diff --git a/doc/guides/prog_guide/ethdev/timesync.rst b/doc/guides/prog_guide/ethdev/timesync.rst
new file mode 100644
index 0000000000..73ca771248
--- /dev/null
+++ b/doc/guides/prog_guide/ethdev/timesync.rst
@@ -0,0 +1,217 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2026 Intel Corporation.
+
+IEEE 1588 / PTP Timesync API
+============================
+
+Overview
+--------
+
+The DPDK IEEE 1588 / Precision Time Protocol (PTP) Timesync API provides
+a standardized framework for managing PTP Hardware Clocks (PHCs) and retrieving
+precise hardware transmit (Tx) and receive (Rx) timestamps.
+
+The Timesync framework encompasses three core capabilities:
+
+1. **Clock Control & Adjustment**: Enabling/disabling hardware timestamping, reading/setting clock time, and adjusting phase/frequency.
+2. **Receive Timestamping**: Hardware capture of incoming PTP packet arrival timestamps.
+3. **Transmit Timestamping**: Hardware capture of outbound PTP packet departure timestamps.
+
+
+Clock Management & Control
+--------------------------
+
+To initialize and discipline a port's PTP Hardware Clock (PHC), the API provides:
+
+* **Enable / Disable**:
+  ``rte_eth_timesync_enable(port_id)`` enables hardware timestamping on the specified port.
+  ``rte_eth_timesync_disable(port_id)`` disables timesync offloads.
+
+* **Clock Time Read / Write**:
+  ``rte_eth_timesync_read_time(port_id, &ts)`` reads the current PHC wall-clock time as a ``struct timespec``.
+  ``rte_eth_timesync_write_time(port_id, &ts)`` sets the PHC wall-clock time.
+
+* **Clock Adjustments**:
+  ``rte_eth_timesync_adjust_time(port_id, delta_ns)`` adjusts the clock phase by a delta offset in nanoseconds.
+  ``rte_eth_timesync_adjust_freq(port_id, scaled_ppm)`` adjusts the clock frequency in scaled parts-per-million (1 ppm = 1 << 16).
+
+
+Receive (Rx) Timestamping
+-------------------------
+
+When receive timestamping is enabled, the hardware identifies incoming PTP packets (e.g. IEEE 1588 EtherType ``0x88F7`` or UDP destination ports 319/320) and latches their arrival time.
+
+Rx Timestamp Extraction Workflow
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. On packet reception via ``rte_eth_rx_burst()``, the PMD checks if the received mbuf represents a PTP packet.
+2. The PMD sets the ``RTE_MBUF_F_RX_IEEE1588_PTP`` flag in ``mbuf->ol_flags``.
+3. Depending on the PMD and hardware capability, the Rx timestamp is either:
+   * **Extracted via API**: Application calls ``rte_eth_timesync_read_rx_timestamp(port_id, &ts, flags)``.
+   * **Inlined in Mbuf**: Stored in a registered mbuf dynamic field (e.g. ``rte_mbuf_dyn_rx_timestamp_register()``).
+
+
+Transmit (Tx) Timestamping Architectures
+----------------------------------------
+
+The framework supports two hardware transmit timestamping architectures:
+
+* **Single Shared Register** (``RTE_ETH_TIMESYNC_TX_TS_SINGLE_REG``):
+  The hardware contains a single shared transmit timestamp latch register.
+  Only one outbound packet can be timestamped at a time across the entire port.
+  The application calls ``rte_eth_timesync_read_tx_timestamp(port_id, &ts)`` to retrieve the departure time.
+
+* **Per-Packet Slot Bank** (``RTE_ETH_TIMESYNC_TX_TS_PER_PACKET``):
+  The hardware provides a bank of independent transmit timestamp slots or
+  descriptors. Multiple outbound PTP packets can be timestamped concurrently and
+  correlated asynchronously on a per-packet basis using slot handles.
+
+
+Dual-Domain Timestamps
+~~~~~~~~~~~~~~~~~~~~~~
+
+When retrieving transmit timestamps using slot handles, the API returns
+a dual-domain timestamp structure:
+
+.. code-block:: c
+
+    struct rte_eth_timesync_dual_domain_timestamp {
+        int64_t adjusted_ns; /**< PHC adjusted time (wall-clock nanoseconds) */
+        int64_t raw_ns;      /**< Free-running hardware cycle counter or raw nanoseconds */
+        uint32_t valid_mask; /**< Validity bits for the adjusted/raw domains */
+    };
+
+* **Adjusted Domain** (``RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_ADJUSTED_VALID``):
+  Represents the wall-clock time after frequency adjustments (``rte_eth_timesync_adjust_freq``)
+  or phase steps (``rte_eth_timesync_adjust_time``) have been applied.
+
+* **Raw Domain** (``RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_RAW_VALID``):
+  Represents the unadjusted free-running hardware cycle counter or raw timestamp.
+  This domain is required when correlating adjusted wall-clock time with the
+  underlying hardware timebase or when performing cross-timestamp analysis.
+
+
+Per-Packet Tx Timestamp Workflow
+--------------------------------
+
+To use per-packet transmit timestamping, applications follow this sequence:
+
+1. **Query Port Capabilities**
+   Determine whether the PMD supports slot-based per-packet timestamping:
+
+   .. code-block:: c
+
+       struct rte_eth_timesync_tx_ts_caps caps;
+
+       ret = rte_eth_timesync_tx_timestamp_slot_get_capabilities(port_id, &caps);
+       if (ret == 0 && caps.type == RTE_ETH_TIMESYNC_TX_TS_PER_PACKET) {
+           printf("Port %u supports per-packet timestamping with %u max slots\n",
+                  port_id, caps.max_slots);
+       }
+
+2. **Register Mbuf Dynamic Fields**
+   Register the dynamic field and dynamic flag used to pass slot handles to the Tx datapath:
+
+   .. code-block:: c
+
+       ret = rte_eth_timesync_tx_slot_dynfield_register();
+       if (ret < 0) {
+           /* Dynamic field space exhausted or registration failed */
+       }
+
+   .. note::
+
+      ``rte_eth_timesync_enable()`` registers the dynamic field automatically.
+      Call ``rte_eth_timesync_tx_slot_dynfield_register()`` explicitly only if creating
+      mempools before enabling timesync on the port.
+
+3. **Allocate a Timestamp Slot**
+   Before transmitting a PTP packet requiring a transmit timestamp, allocate a slot handle:
+
+   .. code-block:: c
+
+       uint32_t slot_id;
+
+       ret = rte_eth_timesync_tx_timestamp_slot_alloc(port_id, &slot_id);
+       if (ret != 0) {
+           /* Handle allocation error (e.g. -ENOSPC if all slots are in flight) */
+       }
+
+4. **Stamp the Mbuf**
+   Attach the allocated slot handle to the mbuf:
+
+   .. code-block:: c
+
+       rte_eth_timesync_tx_timestamp_stamp_mbuf(port_id, slot_id, mbuf);
+       mbuf->ol_flags |= RTE_MBUF_F_TX_IEEE1588_TMST;
+
+5. **Transmit the Packet**
+   Send the packet via ``rte_eth_tx_burst()`` as usual.
+
+6. **Poll for Timestamp Completion**
+   Read the captured timestamp using the allocated slot handle:
+
+   .. code-block:: c
+
+       struct rte_eth_timesync_dual_domain_timestamp ts;
+
+       ret = rte_eth_timesync_read_tx_timestamp_slot(port_id, slot_id, &ts);
+       if (ret == 0) {
+           /* Timestamp is ready */
+           if (ts.valid_mask & RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_ADJUSTED_VALID) {
+               /* Process ts.adjusted_ns */
+           }
+       } else if (ret == -EAGAIN) {
+           /* Timestamp hardware processing is still pending; retry later */
+       }
+
+7. **Release the Slot**
+   After successfully reading the timestamp or timing out, release the slot handle:
+
+   .. code-block:: c
+
+       rte_eth_timesync_tx_timestamp_slot_release(port_id, slot_id);
+
+8. **Unregister Dynfield State on Shutdown (Optional)**
+   When shutting down timesync offloads, the application can unregister the cached dynfield state:
+
+   .. code-block:: c
+
+       rte_eth_timesync_tx_slot_dynfield_unregister();
+
+   .. note::
+
+      This resets process-local dynfield state so subsequent
+      ``rte_eth_timesync_tx_timestamp_stamp_mbuf()`` calls return ``-ENOTSUP``
+      and PMD Tx datapaths fall back to port-level legacy mode.
+      Note that underlying mbuf dynfield bytes remain allocated in DPDK layout as DPDK does not
+      support dynamic field deallocation.
+
+
+PMD Implementation Requirements
+-------------------------------
+
+To support full timesync capabilities, a Poll Mode Driver (PMD) implements the following driver contract:
+
+1. **Clock Operations** (``timesync_enable``, ``timesync_disable``, ``timesync_read_time``, ``timesync_write_time``, ``timesync_adjust_time``, ``timesync_adjust_freq``)
+   * Controls hardware timestamp generation and disciplines the hardware clock registers.
+
+2. **Rx Timestamping** (``timesync_read_rx_timestamp``)
+   * Configures Rx filters to latch incoming PTP arrival times and flags received mbufs with ``RTE_MBUF_F_RX_IEEE1588_PTP``.
+
+3. **Tx Slot Capability Reporting** (``timesync_tx_ts_get_capabilities``)
+   * Reports ``RTE_ETH_TIMESYNC_TX_TS_SINGLE_REG`` or ``RTE_ETH_TIMESYNC_TX_TS_PER_PACKET`` in `caps->type` and sets `caps->max_slots`.
+
+4. **Slot Allocation & Release** (``timesync_tx_timestamp_slot_alloc`` / ``timesync_tx_timestamp_slot_release``)
+   * Maintains a port-global pool or bitmap of hardware timestamp slots.
+   * `timesync_tx_timestamp_slot_alloc` returns a port-unique slot identifier and returns ``-ENOSPC`` when no slots are free.
+   * `timesync_tx_timestamp_slot_release` clears hardware slot state and returns the slot handle to the free pool.
+
+5. **Tx Datapath Integration**
+   * Checks if ``RTE_MBUF_F_TX_IEEE1588_TMST`` is set on `mbuf->ol_flags`.
+   * For per-packet slot mode, retrieves `slot_id` from mbuf dynamic field via ``*RTE_MBUF_DYNFIELD(m, dynfield_offset, uint32_t *)``.
+   * Configures hardware Tx descriptors to capture departure timestamps into the specified slot.
+
+6. **Tx Slot Timestamp Retrieval** (``timesync_read_tx_timestamp_slot``)
+   * Queries hardware slot or descriptor completion ring corresponding to `slot_id`.
+   * Populates ``struct rte_eth_timesync_dual_domain_timestamp`` and returns ``0`` when ready, or ``-EAGAIN`` if pending.
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index c8cc86295d..122d44afeb 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -55,6 +55,13 @@ New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* **ethdev: Added experimental per-packet Tx timestamp slot APIs.**
+
+  Added slot-based TX timestamp allocation, mbuf stamping, and per-packet
+  timestamp reads for timesync-capable Ethernet devices. The new APIs support
+  both shared-register and slot-bank usage models through the
+  ``rte_eth_timesync_tx_timestamp_slot_*`` interface family.
+
 
 Removed Items
 -------------
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 0f336f9567..9d981995ea 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -795,6 +795,23 @@ typedef int (*eth_timesync_read_rx_timestamp_t)(struct rte_eth_dev *dev,
 typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 						struct timespec *timestamp);
 
+/** @internal Query TX timestamp hardware capability (single-register vs per-packet slot bank). */
+typedef int (*eth_timesync_tx_ts_get_caps_t)(struct rte_eth_dev *dev,
+		struct rte_eth_timesync_tx_ts_caps *caps);
+
+/** @internal Allocate a per-packet TX timestamp slot handle. */
+typedef int (*eth_timesync_tx_timestamp_slot_alloc_t)(struct rte_eth_dev *dev,
+		uint32_t *slot_id);
+
+/** @internal Read a dual-domain TX timestamp by slot handle. */
+typedef int (*eth_timesync_read_tx_timestamp_slot_t)(struct rte_eth_dev *dev,
+		uint32_t slot_id,
+		struct rte_eth_timesync_dual_domain_timestamp *timestamp);
+
+/** @internal Release a previously allocated TX timestamp slot handle. */
+typedef int (*eth_timesync_tx_timestamp_slot_release_t)(struct rte_eth_dev *dev,
+		uint32_t slot_id);
+
 /** @internal Function used to adjust the device clock. */
 typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
 
@@ -1561,6 +1578,14 @@ struct eth_dev_ops {
 	eth_timesync_read_rx_timestamp_t timesync_read_rx_timestamp;
 	/** Read the IEEE1588/802.1AS Tx timestamp */
 	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
+	/** Allocate a TX timestamp slot handle */
+	eth_timesync_tx_timestamp_slot_alloc_t timesync_tx_timestamp_slot_alloc;
+	/** Query TX timestamp hardware capability (single-reg vs per-packet) */
+	eth_timesync_tx_ts_get_caps_t timesync_tx_ts_get_capabilities;
+	/** Read a TX timestamp using a slot handle */
+	eth_timesync_read_tx_timestamp_slot_t timesync_read_tx_timestamp_slot;
+	/** Release a TX timestamp slot handle */
+	eth_timesync_tx_timestamp_slot_release_t timesync_tx_timestamp_slot_release;
 	/** Adjust the device clock */
 	eth_timesync_adjust_time   timesync_adjust_time;
 	/** Adjust the clock frequency */
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index 9efeaf77cb..22bebb6e3d 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -21,6 +21,7 @@
 #include <rte_mempool.h>
 #include <rte_malloc.h>
 #include <rte_mbuf.h>
+#include <rte_mbuf_dyn.h>
 #include <rte_errno.h>
 #include <rte_spinlock.h>
 #include <rte_string_fns.h>
@@ -6699,6 +6700,163 @@ rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
 
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_slot_alloc, 26.11)
+int
+rte_eth_timesync_tx_timestamp_slot_alloc(uint16_t port_id,
+					 uint32_t *slot_id)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (slot_id == NULL) {
+		RTE_ETHDEV_LOG_LINE(ERR,
+			"Cannot allocate ethdev port %u Tx timestamp slot to NULL",
+			port_id);
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->timesync_tx_timestamp_slot_alloc == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_tx_timestamp_slot_alloc(dev, slot_id));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_slot_get_capabilities, 26.11)
+int
+rte_eth_timesync_tx_timestamp_slot_get_capabilities(uint16_t port_id,
+		struct rte_eth_timesync_tx_ts_caps *caps)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (caps == NULL) {
+		RTE_ETHDEV_LOG_LINE(ERR,
+			"Cannot get ethdev port %u Tx timestamp capabilities to NULL",
+			port_id);
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->timesync_tx_ts_get_capabilities == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_tx_ts_get_capabilities(dev, caps));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_read_tx_timestamp_slot, 26.11)
+int
+rte_eth_timesync_read_tx_timestamp_slot(uint16_t port_id,
+					uint32_t slot_id,
+					struct rte_eth_timesync_dual_domain_timestamp *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (timestamp == NULL) {
+		RTE_ETHDEV_LOG_LINE(ERR,
+			"Cannot read ethdev port %u Tx timestamp slot to NULL",
+			port_id);
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->timesync_read_tx_timestamp_slot == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_read_tx_timestamp_slot(dev,
+				slot_id, timestamp));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_slot_release, 26.11)
+int
+rte_eth_timesync_tx_timestamp_slot_release(uint16_t port_id, uint32_t slot_id)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (dev->dev_ops->timesync_tx_timestamp_slot_release == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_tx_timestamp_slot_release(dev,
+				slot_id));
+}
+/* Internal process-local cache for Tx timestamp slot mbuf metadata. */
+static int rte_eth_timesync_tx_slot_dynfield_offset = -1;
+static uint64_t rte_eth_timesync_tx_slot_dynflag;
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_slot_dynfield_register, 26.11)
+int
+rte_eth_timesync_tx_slot_dynfield_register(void)
+{
+	const struct rte_mbuf_dynfield slot_dynfield = {
+		.name  = RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME,
+		.size  = sizeof(uint32_t),
+		.align = alignof(uint32_t),
+	};
+
+	if (rte_eth_timesync_tx_slot_dynfield_offset >= 0)
+		return 0;
+
+	rte_eth_timesync_tx_slot_dynfield_offset =
+			rte_mbuf_dynfield_register(&slot_dynfield);
+	if (rte_eth_timesync_tx_slot_dynfield_offset < 0)
+		rte_eth_timesync_tx_slot_dynfield_offset =
+				rte_mbuf_dynfield_lookup(
+					RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME, NULL);
+	if (rte_eth_timesync_tx_slot_dynfield_offset < 0)
+		return -ENOTSUP;
+
+	{
+		int flag_bit = rte_mbuf_dynflag_register(
+			&(const struct rte_mbuf_dynflag){
+				.name = RTE_ETH_TIMESYNC_TX_SLOT_DYNFLAG_NAME});
+		if (flag_bit < 0)
+			flag_bit = rte_mbuf_dynflag_lookup(
+				RTE_ETH_TIMESYNC_TX_SLOT_DYNFLAG_NAME, NULL);
+		if (flag_bit < 0)
+			return -ENOTSUP;
+		rte_eth_timesync_tx_slot_dynflag = RTE_BIT64(flag_bit);
+	}
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_slot_dynfield_unregister, 26.11)
+int
+rte_eth_timesync_tx_slot_dynfield_unregister(void)
+{
+	/* Reset cached state without freeing dynamic-field bytes. */
+	rte_eth_timesync_tx_slot_dynfield_offset = -1;
+	rte_eth_timesync_tx_slot_dynflag = 0;
+	return 0;
+}
+
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_stamp_mbuf, 26.11)
+int
+rte_eth_timesync_tx_timestamp_stamp_mbuf(uint16_t port_id,
+						 uint32_t slot_id, struct rte_mbuf *m)
+{
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	if (m == NULL)
+		return -EINVAL;
+	if (rte_eth_timesync_tx_slot_dynfield_register() != 0)
+		return -ENOTSUP;
+	*RTE_MBUF_DYNFIELD(m, rte_eth_timesync_tx_slot_dynfield_offset,
+			   uint32_t *) = slot_id;
+	m->ol_flags |= rte_eth_timesync_tx_slot_dynflag;
+	return 0;
+}
+
 RTE_EXPORT_SYMBOL(rte_eth_timesync_adjust_time)
 int
 rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta)
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index ee400b386f..c7af668718 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -5513,6 +5513,19 @@ int rte_eth_timesync_read_rx_timestamp(uint16_t port_id,
 /**
  * Read an IEEE1588/802.1AS Tx timestamp from an Ethernet device.
  *
+ * This is the legacy Tx timestamp API and is intended for register-based
+ * timestamp reads. It does not provide per-packet correlation.
+ *
+ * Applications requiring per-packet Tx timestamp correlation should use the
+ * slot-based APIs:
+ * - Setup: rte_eth_timesync_tx_slot_dynfield_register()
+ * - Runtime per-packet loop:
+ *   - rte_eth_timesync_tx_timestamp_slot_alloc()
+ *   - rte_eth_timesync_tx_timestamp_stamp_mbuf()
+ *   - rte_eth_timesync_read_tx_timestamp_slot()
+ *   - rte_eth_timesync_tx_timestamp_slot_release()
+ * - Teardown: rte_eth_timesync_tx_slot_dynfield_unregister()
+ *
  * @param port_id
  *   The port identifier of the Ethernet device.
  * @param timestamp
@@ -5528,6 +5541,235 @@ int rte_eth_timesync_read_rx_timestamp(uint16_t port_id,
 int rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
 		struct timespec *timestamp);
 
+/** Valid bit for rte_eth_timesync_dual_domain_timestamp.adjusted_ns. */
+#define RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_ADJUSTED_VALID	RTE_BIT32(0)
+/** Valid bit for rte_eth_timesync_dual_domain_timestamp.raw_ns. */
+#define RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_RAW_VALID	RTE_BIT32(1)
+
+/**
+ * Dual-domain TX timestamp payload in nanoseconds.
+ *
+ * `adjusted_ns` is the synchronized/adjusted domain.
+ * `raw_ns` is the free-running raw hardware clock domain.
+ *
+ * Scalar `int64_t` nanoseconds are used (instead of `struct timespec`) to
+ * keep both domains compact in one payload and to avoid extra split/merge
+ * conversions when processing per-packet timestamp correlation data.
+ */
+struct rte_eth_timesync_dual_domain_timestamp {
+	int64_t adjusted_ns;
+	int64_t raw_ns;
+	uint32_t valid_mask;
+};
+
+/** Valid bit for rte_eth_timesync_tx_timestamp_slot_info.max_slots. */
+#define RTE_ETH_TIMESYNC_TX_TIMESTAMP_SLOT_INFO_MAX_VALID	RTE_BIT32(0)
+/** Valid bit for rte_eth_timesync_tx_timestamp_slot_info.free_slots. */
+#define RTE_ETH_TIMESYNC_TX_TIMESTAMP_SLOT_INFO_FREE_VALID	RTE_BIT32(1)
+
+/** TX timestamp retrieval mechanism supported by a port. */
+enum rte_eth_timesync_tx_ts_type {
+	RTE_ETH_TIMESYNC_TX_TS_NONE       = 0, /**< not supported */
+	/** One hardware latch register shared across all packets. */
+	RTE_ETH_TIMESYNC_TX_TS_SINGLE_REG = 1,
+	/** Per-packet slot bank supports concurrent in-flight correlation. */
+	RTE_ETH_TIMESYNC_TX_TS_PER_PACKET = 2,
+};
+
+/**
+ * TX timestamp capabilities returned by
+ * rte_eth_timesync_tx_timestamp_slot_get_capabilities().
+ */
+struct rte_eth_timesync_tx_ts_caps {
+	enum rte_eth_timesync_tx_ts_type type; /**< mechanism supported by this port */
+	uint32_t max_slots; /**< concurrent slots available; valid only for PER_PACKET */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query the TX timestamp capability of a port.
+ *
+ * Reports whether the hardware uses a single shared latch register
+ * (RTE_ETH_TIMESYNC_TX_TS_SINGLE_REG) or a per-packet slot bank
+ * (RTE_ETH_TIMESYNC_TX_TS_PER_PACKET), and how many concurrent slots exist.
+ *
+ * Use this to choose between:
+ *   - Slot-based: rte_eth_timesync_tx_timestamp_slot_alloc() +
+ *     rte_eth_timesync_read_tx_timestamp_slot()
+ *   - Legacy:     rte_eth_timesync_read_tx_timestamp()
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param caps
+ *   Output TX timestamp capability structure.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_slot_get_capabilities(uint16_t port_id,
+		struct rte_eth_timesync_tx_ts_caps *caps);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate a TX timestamp slot handle for per-packet timestamp correlation.
+ *
+ * Intended for PTP/event timestamping rates.
+ *
+ * Slots are allocated from a port-global pool and can be used across any
+ * TX queue on the port. The application stamps an mbuf with the slot handle
+ * using rte_eth_timesync_tx_timestamp_stamp_mbuf() before transmission.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param slot_id
+ *   Output handle identifying the allocated slot (port-global scope).
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENOSPC: No free slots are available.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_slot_alloc(uint16_t port_id,
+		uint32_t *slot_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Read a per-packet TX timestamp using a previously allocated slot handle.
+ *
+ * Intended for PTP/event timestamping rates.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param slot_id
+ *   Slot handle returned by rte_eth_timesync_tx_timestamp_slot_alloc().
+ * @param timestamp
+ *   Output dual-domain timestamp payload.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -EAGAIN: Timestamp is not ready yet.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_read_tx_timestamp_slot(uint16_t port_id,
+		uint32_t slot_id,
+		struct rte_eth_timesync_dual_domain_timestamp *timestamp);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Release a previously allocated TX timestamp slot handle.
+ *
+ * Intended for PTP/event timestamping rates.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param slot_id
+ *   Slot handle to release.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_slot_release(uint16_t port_id,
+		uint32_t slot_id);
+
+/** Mbuf dynfield name for the TX timestamp slot handle. */
+#define RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME "rte_eth_timesync_tx_slot"
+/** Mbuf dynflag name indicating TX timestamp slot handle is present. */
+#define RTE_ETH_TIMESYNC_TX_SLOT_DYNFLAG_NAME "rte_eth_timesync_tx_slot_flag"
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Register the per-packet TX timestamp slot dynfield and dynflag in the mbuf
+ * layout.
+ *
+ * Must be called before the first rte_pktmbuf_pool_create() when the
+ * application intends to use rte_eth_timesync_tx_timestamp_stamp_mbuf()
+ * for per-packet TX timestamp correlation. Calling it after pool creation
+ * may still succeed if the default dynfield area has not been exhausted.
+ *
+ * rte_eth_timesync_enable() calls this automatically, so explicit calls are
+ * only needed when the application creates pools before enabling timesync.
+ *
+ * Note: dynfields and dynflags cannot be unregistered in DPDK. Once
+ * registered they remain allocated for the lifetime of the process, whether
+ * or not the application ultimately uses per-packet slot correlation.
+ *
+ * @return
+ *   - 0: Success (or already registered).
+ *   - -ENOTSUP: Registration and lookup both failed (no dynfield space).
+ */
+__rte_experimental
+int rte_eth_timesync_tx_slot_dynfield_register(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Disable per-packet TX timestamp slot correlation for this process.
+ *
+ * Resets the cached dynfield offset and dynflag to their unregistered state.
+ * After this call rte_eth_timesync_tx_timestamp_stamp_mbuf() returns
+ * -ENOTSUP and the PMD TX path falls back to the port-level ptp_tx_index
+ * (legacy mode).
+ *
+ * The underlying DPDK dynfield bytes are NOT freed — DPDK provides no dynfield
+ * deallocation. The 4 bytes per mbuf remain allocated but dormant.
+ *
+ * @return   Always 0.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_slot_dynfield_unregister(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Set TX timestamp slot metadata in an mbuf so the TX path steers the
+ * NIC to capture the timestamp in the correct per-packet slot.
+ *
+ * Must be called after rte_eth_timesync_tx_timestamp_slot_alloc() and before
+ * rte_eth_tx_burst(). Safe for concurrent callers — slot is stored per-mbuf.
+ *
+ * @param port_id  The port identifier of the Ethernet device.
+ * @param slot_id  Slot handle from rte_eth_timesync_tx_timestamp_slot_alloc().
+ * @param m        Mbuf to stamp.
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EINVAL: Invalid parameters.
+ *   - -ENOTSUP: Registration/lookup failed.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_stamp_mbuf(uint16_t port_id,
+		uint32_t slot_id, struct rte_mbuf *m);
+
 /**
  * Adjust the timesync clock on an Ethernet device.
  *
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 0/1] ethdev: add Tx timestamp slot APIs
  2026-08-17 19:24 [RFC 0/1] ethdev: per-packet Tx timestamp slot management Rajesh Kumar
                   ` (3 preceding siblings ...)
  2026-08-27 12:13 ` [RFC PATCH v2 0/1] ethdev: add Tx timestamp slot APIs Rajesh Kumar
@ 2026-08-27 12:18 ` Rajesh Kumar
  2026-08-27 12:21 ` Rajesh Kumar
  2026-08-27 12:34 ` [RFC PATCH v2 0/1] ethdev: add Tx timestamp slot APIs Rajesh Kumar
  6 siblings, 0 replies; 14+ messages in thread
From: Rajesh Kumar @ 2026-08-27 12:18 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, andrew.rybchenko, stephen,
	aman.deep.singh, Rajesh Kumar

The ethdev timesync API currently exposes Tx timestamps through a shared
hardware register. This requires applications to serialize timestamped
packets and does not allow correlation when multiple packets are in
flight.

This RFC proposes an ethdev interface for hardware with independent Tx
timestamp slots. The interface reports the supported timestamping
mechanism, provides a port-global slot lifecycle, and lets applications
poll each slot asynchronously after transmission.

The proposal includes the following components:

* Capability reporting for shared-register and per-packet timestamping.
* Slot allocation, asynchronous timestamp retrieval, and slot release.
* A dual-domain timestamp structure for adjusted PHC and raw hardware
  time.
* Mbuf dynamic field and dynflag support for passing slot handles to Tx.
* Registration and process-local disabling of slot metadata.
* A compatibility alias for the mbuf stamping helper.
* Programmer-guide and NIC feature documentation.

The legacy rte_eth_timesync_read_tx_timestamp() API remains available on
devices using a shared timestamp register. This RFC adds the ethdev and
PMD interfaces but does not add a hardware-specific PMD implementation.

The following areas would benefit from review:

* Is the capability model sufficient for devices with different slot
  allocation or completion mechanisms?
* Is an mbuf dynamic field and dynflag the appropriate way to pass the
  slot handle into the Tx datapath?
* Is -EAGAIN the appropriate result while a slot timestamp is pending?
* Should the adjusted and raw timestamp domains use nanoseconds in the
  public structure, or should one domain expose hardware cycles instead?

Rajesh Kumar (1):
  ethdev: add Tx timestamp slot management APIs

 doc/guides/nics/features.rst              |  16 +-
 doc/guides/prog_guide/ethdev/index.rst    |   1 +
 doc/guides/prog_guide/ethdev/timesync.rst | 217 +++++++++++++++++++
 doc/guides/rel_notes/release_26_11.rst    |   7 +
 lib/ethdev/ethdev_driver.h                |  25 +++
 lib/ethdev/rte_ethdev.c                   | 158 ++++++++++++++
 lib/ethdev/rte_ethdev.h                   | 242 ++++++++++++++++++++++
 7 files changed, 662 insertions(+), 4 deletions(-)
 create mode 100644 doc/guides/prog_guide/ethdev/timesync.rst

-- 
2.55.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 0/1] ethdev: add Tx timestamp slot APIs
  2026-08-17 19:24 [RFC 0/1] ethdev: per-packet Tx timestamp slot management Rajesh Kumar
                   ` (4 preceding siblings ...)
  2026-08-27 12:18 ` [RFC PATCH v3 0/1] ethdev: add Tx timestamp slot APIs Rajesh Kumar
@ 2026-08-27 12:21 ` Rajesh Kumar
  2026-08-27 12:21   ` [RFC PATCH v3 1/1] ethdev: add Tx timestamp slot management APIs Rajesh Kumar
  2026-08-27 12:34 ` [RFC PATCH v2 0/1] ethdev: add Tx timestamp slot APIs Rajesh Kumar
  6 siblings, 1 reply; 14+ messages in thread
From: Rajesh Kumar @ 2026-08-27 12:21 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, andrew.rybchenko, stephen,
	aman.deep.singh, Rajesh Kumar

The ethdev timesync API currently exposes Tx timestamps through a shared
hardware register. This requires applications to serialize timestamped
packets and does not allow correlation when multiple packets are in
flight.

This RFC proposes an ethdev interface for hardware with independent Tx
timestamp slots. The interface reports the supported timestamping
mechanism, provides a port-global slot lifecycle, and lets applications
poll each slot asynchronously after transmission.

The proposal includes the following components:

* Capability reporting for shared-register and per-packet timestamping.
* Slot allocation, asynchronous timestamp retrieval, and slot release.
* A dual-domain timestamp structure for adjusted PHC and raw hardware
  time.
* Mbuf dynamic field and dynflag support for passing slot handles to Tx.
* Registration and process-local disabling of slot metadata.
* A compatibility alias for the mbuf stamping helper.
* Programmer-guide and NIC feature documentation.

The legacy rte_eth_timesync_read_tx_timestamp() API remains available on
devices using a shared timestamp register. This RFC adds the ethdev and
PMD interfaces but does not add a hardware-specific PMD implementation.

The following areas would benefit from review:

* Is the capability model sufficient for devices with different slot
  allocation or completion mechanisms?
* Is an mbuf dynamic field and dynflag the appropriate way to pass the
  slot handle into the Tx datapath?
* Is -EAGAIN the appropriate result while a slot timestamp is pending?
* Should the adjusted and raw timestamp domains use nanoseconds in the
  public structure, or should one domain expose hardware cycles instead?

Rajesh Kumar (1):
  ethdev: add Tx timestamp slot management APIs

 doc/guides/nics/features.rst              |  16 +-
 doc/guides/prog_guide/ethdev/index.rst    |   1 +
 doc/guides/prog_guide/ethdev/timesync.rst | 217 +++++++++++++++++++
 doc/guides/rel_notes/release_26_11.rst    |   7 +
 lib/ethdev/ethdev_driver.h                |  25 +++
 lib/ethdev/rte_ethdev.c                   | 158 ++++++++++++++
 lib/ethdev/rte_ethdev.h                   | 242 ++++++++++++++++++++++
 7 files changed, 662 insertions(+), 4 deletions(-)
 create mode 100644 doc/guides/prog_guide/ethdev/timesync.rst

-- 
2.55.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 1/1] ethdev: add Tx timestamp slot management APIs
  2026-08-27 12:21 ` Rajesh Kumar
@ 2026-08-27 12:21   ` Rajesh Kumar
  2026-08-27 21:45     ` Stephen Hemminger
  0 siblings, 1 reply; 14+ messages in thread
From: Rajesh Kumar @ 2026-08-27 12:21 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, andrew.rybchenko, stephen,
	aman.deep.singh, Rajesh Kumar

Extend ethdev timesync with a capability model for selecting between
shared-register and per-packet Tx timestamping.

Add public and PMD interfaces to query timestamp capabilities, allocate
timestamp slots, retrieve timestamps asynchronously, and release slots.
Slots have port-global scope and can be used across Tx queues.

Add a dual-domain timestamp structure for reporting adjusted PHC time
and raw hardware time independently through validity flags.

Add APIs to register and unregister the mbuf dynamic field and dynflag
used to pass slot handles to the Tx datapath. Add helpers to associate
a slot handle with an mbuf before transmission.

Keep the legacy Tx timestamp API for shared-register hardware and provide
a compatibility alias for the mbuf stamping helper.

Document the timestamp capability model, slot lifecycle, and application
workflow.

Signed-off-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
 doc/guides/nics/features.rst              |  16 +-
 doc/guides/prog_guide/ethdev/index.rst    |   1 +
 doc/guides/prog_guide/ethdev/timesync.rst | 217 +++++++++++++++++++
 doc/guides/rel_notes/release_26_11.rst    |   7 +
 lib/ethdev/ethdev_driver.h                |  25 +++
 lib/ethdev/rte_ethdev.c                   | 158 ++++++++++++++
 lib/ethdev/rte_ethdev.h                   | 242 ++++++++++++++++++++++
 7 files changed, 662 insertions(+), 4 deletions(-)
 create mode 100644 doc/guides/prog_guide/ethdev/timesync.rst

diff --git a/doc/guides/nics/features.rst b/doc/guides/nics/features.rst
index 0b0c69e7cd..040a996156 100644
--- a/doc/guides/nics/features.rst
+++ b/doc/guides/nics/features.rst
@@ -692,14 +692,22 @@ Timesync
 
 Supports IEEE1588/802.1AS timestamping.
 
-* **[implements] eth_dev_ops**: ``timesync_enable``, ``timesync_disable``
+* **[implements] eth_dev_ops**: ``timesync_enable``, ``timesync_disable``,
   ``timesync_read_rx_timestamp``, ``timesync_read_tx_timestamp``,
+  ``timesync_tx_ts_get_capabilities``, ``timesync_tx_timestamp_slot_alloc``,
+  ``timesync_read_tx_timestamp_slot``, ``timesync_tx_timestamp_slot_release``,
   ``timesync_adjust_time``, ``timesync_adjust_freq``,
   ``timesync_read_time``, ``timesync_write_time``.
 * **[related]    API**: ``rte_eth_timesync_enable()``, ``rte_eth_timesync_disable()``,
-  ``rte_eth_timesync_read_rx_timestamp()``,
-  ``rte_eth_timesync_read_tx_timestamp``, ``rte_eth_timesync_adjust_time()``,
-  ``rte_eth_timesync_adjust_freq()``,
+  ``rte_eth_timesync_read_rx_timestamp()``, ``rte_eth_timesync_read_tx_timestamp()``,
+  ``rte_eth_timesync_tx_timestamp_slot_get_capabilities()``,
+  ``rte_eth_timesync_tx_timestamp_slot_alloc()``,
+  ``rte_eth_timesync_read_tx_timestamp_slot()``,
+  ``rte_eth_timesync_tx_timestamp_slot_release()``,
+  ``rte_eth_timesync_tx_slot_dynfield_register()``,
+  ``rte_eth_timesync_tx_slot_dynfield_unregister()``,
+  ``rte_eth_timesync_tx_timestamp_stamp_mbuf()``,
+  ``rte_eth_timesync_adjust_time()``, ``rte_eth_timesync_adjust_freq()``,
   ``rte_eth_timesync_read_time()``, ``rte_eth_timesync_write_time()``.
 
 
diff --git a/doc/guides/prog_guide/ethdev/index.rst b/doc/guides/prog_guide/ethdev/index.rst
index 392ced0a2e..bc21f28091 100644
--- a/doc/guides/prog_guide/ethdev/index.rst
+++ b/doc/guides/prog_guide/ethdev/index.rst
@@ -13,3 +13,4 @@ Ethernet Device Library
     traffic_metering_and_policing
     traffic_management
     qos_framework
+    timesync
diff --git a/doc/guides/prog_guide/ethdev/timesync.rst b/doc/guides/prog_guide/ethdev/timesync.rst
new file mode 100644
index 0000000000..73ca771248
--- /dev/null
+++ b/doc/guides/prog_guide/ethdev/timesync.rst
@@ -0,0 +1,217 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2026 Intel Corporation.
+
+IEEE 1588 / PTP Timesync API
+============================
+
+Overview
+--------
+
+The DPDK IEEE 1588 / Precision Time Protocol (PTP) Timesync API provides
+a standardized framework for managing PTP Hardware Clocks (PHCs) and retrieving
+precise hardware transmit (Tx) and receive (Rx) timestamps.
+
+The Timesync framework encompasses three core capabilities:
+
+1. **Clock Control & Adjustment**: Enabling/disabling hardware timestamping, reading/setting clock time, and adjusting phase/frequency.
+2. **Receive Timestamping**: Hardware capture of incoming PTP packet arrival timestamps.
+3. **Transmit Timestamping**: Hardware capture of outbound PTP packet departure timestamps.
+
+
+Clock Management & Control
+--------------------------
+
+To initialize and discipline a port's PTP Hardware Clock (PHC), the API provides:
+
+* **Enable / Disable**:
+  ``rte_eth_timesync_enable(port_id)`` enables hardware timestamping on the specified port.
+  ``rte_eth_timesync_disable(port_id)`` disables timesync offloads.
+
+* **Clock Time Read / Write**:
+  ``rte_eth_timesync_read_time(port_id, &ts)`` reads the current PHC wall-clock time as a ``struct timespec``.
+  ``rte_eth_timesync_write_time(port_id, &ts)`` sets the PHC wall-clock time.
+
+* **Clock Adjustments**:
+  ``rte_eth_timesync_adjust_time(port_id, delta_ns)`` adjusts the clock phase by a delta offset in nanoseconds.
+  ``rte_eth_timesync_adjust_freq(port_id, scaled_ppm)`` adjusts the clock frequency in scaled parts-per-million (1 ppm = 1 << 16).
+
+
+Receive (Rx) Timestamping
+-------------------------
+
+When receive timestamping is enabled, the hardware identifies incoming PTP packets (e.g. IEEE 1588 EtherType ``0x88F7`` or UDP destination ports 319/320) and latches their arrival time.
+
+Rx Timestamp Extraction Workflow
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. On packet reception via ``rte_eth_rx_burst()``, the PMD checks if the received mbuf represents a PTP packet.
+2. The PMD sets the ``RTE_MBUF_F_RX_IEEE1588_PTP`` flag in ``mbuf->ol_flags``.
+3. Depending on the PMD and hardware capability, the Rx timestamp is either:
+   * **Extracted via API**: Application calls ``rte_eth_timesync_read_rx_timestamp(port_id, &ts, flags)``.
+   * **Inlined in Mbuf**: Stored in a registered mbuf dynamic field (e.g. ``rte_mbuf_dyn_rx_timestamp_register()``).
+
+
+Transmit (Tx) Timestamping Architectures
+----------------------------------------
+
+The framework supports two hardware transmit timestamping architectures:
+
+* **Single Shared Register** (``RTE_ETH_TIMESYNC_TX_TS_SINGLE_REG``):
+  The hardware contains a single shared transmit timestamp latch register.
+  Only one outbound packet can be timestamped at a time across the entire port.
+  The application calls ``rte_eth_timesync_read_tx_timestamp(port_id, &ts)`` to retrieve the departure time.
+
+* **Per-Packet Slot Bank** (``RTE_ETH_TIMESYNC_TX_TS_PER_PACKET``):
+  The hardware provides a bank of independent transmit timestamp slots or
+  descriptors. Multiple outbound PTP packets can be timestamped concurrently and
+  correlated asynchronously on a per-packet basis using slot handles.
+
+
+Dual-Domain Timestamps
+~~~~~~~~~~~~~~~~~~~~~~
+
+When retrieving transmit timestamps using slot handles, the API returns
+a dual-domain timestamp structure:
+
+.. code-block:: c
+
+    struct rte_eth_timesync_dual_domain_timestamp {
+        int64_t adjusted_ns; /**< PHC adjusted time (wall-clock nanoseconds) */
+        int64_t raw_ns;      /**< Free-running hardware cycle counter or raw nanoseconds */
+        uint32_t valid_mask; /**< Validity bits for the adjusted/raw domains */
+    };
+
+* **Adjusted Domain** (``RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_ADJUSTED_VALID``):
+  Represents the wall-clock time after frequency adjustments (``rte_eth_timesync_adjust_freq``)
+  or phase steps (``rte_eth_timesync_adjust_time``) have been applied.
+
+* **Raw Domain** (``RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_RAW_VALID``):
+  Represents the unadjusted free-running hardware cycle counter or raw timestamp.
+  This domain is required when correlating adjusted wall-clock time with the
+  underlying hardware timebase or when performing cross-timestamp analysis.
+
+
+Per-Packet Tx Timestamp Workflow
+--------------------------------
+
+To use per-packet transmit timestamping, applications follow this sequence:
+
+1. **Query Port Capabilities**
+   Determine whether the PMD supports slot-based per-packet timestamping:
+
+   .. code-block:: c
+
+       struct rte_eth_timesync_tx_ts_caps caps;
+
+       ret = rte_eth_timesync_tx_timestamp_slot_get_capabilities(port_id, &caps);
+       if (ret == 0 && caps.type == RTE_ETH_TIMESYNC_TX_TS_PER_PACKET) {
+           printf("Port %u supports per-packet timestamping with %u max slots\n",
+                  port_id, caps.max_slots);
+       }
+
+2. **Register Mbuf Dynamic Fields**
+   Register the dynamic field and dynamic flag used to pass slot handles to the Tx datapath:
+
+   .. code-block:: c
+
+       ret = rte_eth_timesync_tx_slot_dynfield_register();
+       if (ret < 0) {
+           /* Dynamic field space exhausted or registration failed */
+       }
+
+   .. note::
+
+      ``rte_eth_timesync_enable()`` registers the dynamic field automatically.
+      Call ``rte_eth_timesync_tx_slot_dynfield_register()`` explicitly only if creating
+      mempools before enabling timesync on the port.
+
+3. **Allocate a Timestamp Slot**
+   Before transmitting a PTP packet requiring a transmit timestamp, allocate a slot handle:
+
+   .. code-block:: c
+
+       uint32_t slot_id;
+
+       ret = rte_eth_timesync_tx_timestamp_slot_alloc(port_id, &slot_id);
+       if (ret != 0) {
+           /* Handle allocation error (e.g. -ENOSPC if all slots are in flight) */
+       }
+
+4. **Stamp the Mbuf**
+   Attach the allocated slot handle to the mbuf:
+
+   .. code-block:: c
+
+       rte_eth_timesync_tx_timestamp_stamp_mbuf(port_id, slot_id, mbuf);
+       mbuf->ol_flags |= RTE_MBUF_F_TX_IEEE1588_TMST;
+
+5. **Transmit the Packet**
+   Send the packet via ``rte_eth_tx_burst()`` as usual.
+
+6. **Poll for Timestamp Completion**
+   Read the captured timestamp using the allocated slot handle:
+
+   .. code-block:: c
+
+       struct rte_eth_timesync_dual_domain_timestamp ts;
+
+       ret = rte_eth_timesync_read_tx_timestamp_slot(port_id, slot_id, &ts);
+       if (ret == 0) {
+           /* Timestamp is ready */
+           if (ts.valid_mask & RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_ADJUSTED_VALID) {
+               /* Process ts.adjusted_ns */
+           }
+       } else if (ret == -EAGAIN) {
+           /* Timestamp hardware processing is still pending; retry later */
+       }
+
+7. **Release the Slot**
+   After successfully reading the timestamp or timing out, release the slot handle:
+
+   .. code-block:: c
+
+       rte_eth_timesync_tx_timestamp_slot_release(port_id, slot_id);
+
+8. **Unregister Dynfield State on Shutdown (Optional)**
+   When shutting down timesync offloads, the application can unregister the cached dynfield state:
+
+   .. code-block:: c
+
+       rte_eth_timesync_tx_slot_dynfield_unregister();
+
+   .. note::
+
+      This resets process-local dynfield state so subsequent
+      ``rte_eth_timesync_tx_timestamp_stamp_mbuf()`` calls return ``-ENOTSUP``
+      and PMD Tx datapaths fall back to port-level legacy mode.
+      Note that underlying mbuf dynfield bytes remain allocated in DPDK layout as DPDK does not
+      support dynamic field deallocation.
+
+
+PMD Implementation Requirements
+-------------------------------
+
+To support full timesync capabilities, a Poll Mode Driver (PMD) implements the following driver contract:
+
+1. **Clock Operations** (``timesync_enable``, ``timesync_disable``, ``timesync_read_time``, ``timesync_write_time``, ``timesync_adjust_time``, ``timesync_adjust_freq``)
+   * Controls hardware timestamp generation and disciplines the hardware clock registers.
+
+2. **Rx Timestamping** (``timesync_read_rx_timestamp``)
+   * Configures Rx filters to latch incoming PTP arrival times and flags received mbufs with ``RTE_MBUF_F_RX_IEEE1588_PTP``.
+
+3. **Tx Slot Capability Reporting** (``timesync_tx_ts_get_capabilities``)
+   * Reports ``RTE_ETH_TIMESYNC_TX_TS_SINGLE_REG`` or ``RTE_ETH_TIMESYNC_TX_TS_PER_PACKET`` in `caps->type` and sets `caps->max_slots`.
+
+4. **Slot Allocation & Release** (``timesync_tx_timestamp_slot_alloc`` / ``timesync_tx_timestamp_slot_release``)
+   * Maintains a port-global pool or bitmap of hardware timestamp slots.
+   * `timesync_tx_timestamp_slot_alloc` returns a port-unique slot identifier and returns ``-ENOSPC`` when no slots are free.
+   * `timesync_tx_timestamp_slot_release` clears hardware slot state and returns the slot handle to the free pool.
+
+5. **Tx Datapath Integration**
+   * Checks if ``RTE_MBUF_F_TX_IEEE1588_TMST`` is set on `mbuf->ol_flags`.
+   * For per-packet slot mode, retrieves `slot_id` from mbuf dynamic field via ``*RTE_MBUF_DYNFIELD(m, dynfield_offset, uint32_t *)``.
+   * Configures hardware Tx descriptors to capture departure timestamps into the specified slot.
+
+6. **Tx Slot Timestamp Retrieval** (``timesync_read_tx_timestamp_slot``)
+   * Queries hardware slot or descriptor completion ring corresponding to `slot_id`.
+   * Populates ``struct rte_eth_timesync_dual_domain_timestamp`` and returns ``0`` when ready, or ``-EAGAIN`` if pending.
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index c8cc86295d..122d44afeb 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -55,6 +55,13 @@ New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* **ethdev: Added experimental per-packet Tx timestamp slot APIs.**
+
+  Added slot-based TX timestamp allocation, mbuf stamping, and per-packet
+  timestamp reads for timesync-capable Ethernet devices. The new APIs support
+  both shared-register and slot-bank usage models through the
+  ``rte_eth_timesync_tx_timestamp_slot_*`` interface family.
+
 
 Removed Items
 -------------
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 0f336f9567..9d981995ea 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -795,6 +795,23 @@ typedef int (*eth_timesync_read_rx_timestamp_t)(struct rte_eth_dev *dev,
 typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 						struct timespec *timestamp);
 
+/** @internal Query TX timestamp hardware capability (single-register vs per-packet slot bank). */
+typedef int (*eth_timesync_tx_ts_get_caps_t)(struct rte_eth_dev *dev,
+		struct rte_eth_timesync_tx_ts_caps *caps);
+
+/** @internal Allocate a per-packet TX timestamp slot handle. */
+typedef int (*eth_timesync_tx_timestamp_slot_alloc_t)(struct rte_eth_dev *dev,
+		uint32_t *slot_id);
+
+/** @internal Read a dual-domain TX timestamp by slot handle. */
+typedef int (*eth_timesync_read_tx_timestamp_slot_t)(struct rte_eth_dev *dev,
+		uint32_t slot_id,
+		struct rte_eth_timesync_dual_domain_timestamp *timestamp);
+
+/** @internal Release a previously allocated TX timestamp slot handle. */
+typedef int (*eth_timesync_tx_timestamp_slot_release_t)(struct rte_eth_dev *dev,
+		uint32_t slot_id);
+
 /** @internal Function used to adjust the device clock. */
 typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
 
@@ -1561,6 +1578,14 @@ struct eth_dev_ops {
 	eth_timesync_read_rx_timestamp_t timesync_read_rx_timestamp;
 	/** Read the IEEE1588/802.1AS Tx timestamp */
 	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
+	/** Allocate a TX timestamp slot handle */
+	eth_timesync_tx_timestamp_slot_alloc_t timesync_tx_timestamp_slot_alloc;
+	/** Query TX timestamp hardware capability (single-reg vs per-packet) */
+	eth_timesync_tx_ts_get_caps_t timesync_tx_ts_get_capabilities;
+	/** Read a TX timestamp using a slot handle */
+	eth_timesync_read_tx_timestamp_slot_t timesync_read_tx_timestamp_slot;
+	/** Release a TX timestamp slot handle */
+	eth_timesync_tx_timestamp_slot_release_t timesync_tx_timestamp_slot_release;
 	/** Adjust the device clock */
 	eth_timesync_adjust_time   timesync_adjust_time;
 	/** Adjust the clock frequency */
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index 9efeaf77cb..22bebb6e3d 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -21,6 +21,7 @@
 #include <rte_mempool.h>
 #include <rte_malloc.h>
 #include <rte_mbuf.h>
+#include <rte_mbuf_dyn.h>
 #include <rte_errno.h>
 #include <rte_spinlock.h>
 #include <rte_string_fns.h>
@@ -6699,6 +6700,163 @@ rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
 
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_slot_alloc, 26.11)
+int
+rte_eth_timesync_tx_timestamp_slot_alloc(uint16_t port_id,
+					 uint32_t *slot_id)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (slot_id == NULL) {
+		RTE_ETHDEV_LOG_LINE(ERR,
+			"Cannot allocate ethdev port %u Tx timestamp slot to NULL",
+			port_id);
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->timesync_tx_timestamp_slot_alloc == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_tx_timestamp_slot_alloc(dev, slot_id));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_slot_get_capabilities, 26.11)
+int
+rte_eth_timesync_tx_timestamp_slot_get_capabilities(uint16_t port_id,
+		struct rte_eth_timesync_tx_ts_caps *caps)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (caps == NULL) {
+		RTE_ETHDEV_LOG_LINE(ERR,
+			"Cannot get ethdev port %u Tx timestamp capabilities to NULL",
+			port_id);
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->timesync_tx_ts_get_capabilities == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_tx_ts_get_capabilities(dev, caps));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_read_tx_timestamp_slot, 26.11)
+int
+rte_eth_timesync_read_tx_timestamp_slot(uint16_t port_id,
+					uint32_t slot_id,
+					struct rte_eth_timesync_dual_domain_timestamp *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (timestamp == NULL) {
+		RTE_ETHDEV_LOG_LINE(ERR,
+			"Cannot read ethdev port %u Tx timestamp slot to NULL",
+			port_id);
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->timesync_read_tx_timestamp_slot == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_read_tx_timestamp_slot(dev,
+				slot_id, timestamp));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_slot_release, 26.11)
+int
+rte_eth_timesync_tx_timestamp_slot_release(uint16_t port_id, uint32_t slot_id)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (dev->dev_ops->timesync_tx_timestamp_slot_release == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_tx_timestamp_slot_release(dev,
+				slot_id));
+}
+/* Internal process-local cache for Tx timestamp slot mbuf metadata. */
+static int rte_eth_timesync_tx_slot_dynfield_offset = -1;
+static uint64_t rte_eth_timesync_tx_slot_dynflag;
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_slot_dynfield_register, 26.11)
+int
+rte_eth_timesync_tx_slot_dynfield_register(void)
+{
+	const struct rte_mbuf_dynfield slot_dynfield = {
+		.name  = RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME,
+		.size  = sizeof(uint32_t),
+		.align = alignof(uint32_t),
+	};
+
+	if (rte_eth_timesync_tx_slot_dynfield_offset >= 0)
+		return 0;
+
+	rte_eth_timesync_tx_slot_dynfield_offset =
+			rte_mbuf_dynfield_register(&slot_dynfield);
+	if (rte_eth_timesync_tx_slot_dynfield_offset < 0)
+		rte_eth_timesync_tx_slot_dynfield_offset =
+				rte_mbuf_dynfield_lookup(
+					RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME, NULL);
+	if (rte_eth_timesync_tx_slot_dynfield_offset < 0)
+		return -ENOTSUP;
+
+	{
+		int flag_bit = rte_mbuf_dynflag_register(
+			&(const struct rte_mbuf_dynflag){
+				.name = RTE_ETH_TIMESYNC_TX_SLOT_DYNFLAG_NAME});
+		if (flag_bit < 0)
+			flag_bit = rte_mbuf_dynflag_lookup(
+				RTE_ETH_TIMESYNC_TX_SLOT_DYNFLAG_NAME, NULL);
+		if (flag_bit < 0)
+			return -ENOTSUP;
+		rte_eth_timesync_tx_slot_dynflag = RTE_BIT64(flag_bit);
+	}
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_slot_dynfield_unregister, 26.11)
+int
+rte_eth_timesync_tx_slot_dynfield_unregister(void)
+{
+	/* Reset cached state without freeing dynamic-field bytes. */
+	rte_eth_timesync_tx_slot_dynfield_offset = -1;
+	rte_eth_timesync_tx_slot_dynflag = 0;
+	return 0;
+}
+
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_stamp_mbuf, 26.11)
+int
+rte_eth_timesync_tx_timestamp_stamp_mbuf(uint16_t port_id,
+						 uint32_t slot_id, struct rte_mbuf *m)
+{
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	if (m == NULL)
+		return -EINVAL;
+	if (rte_eth_timesync_tx_slot_dynfield_register() != 0)
+		return -ENOTSUP;
+	*RTE_MBUF_DYNFIELD(m, rte_eth_timesync_tx_slot_dynfield_offset,
+			   uint32_t *) = slot_id;
+	m->ol_flags |= rte_eth_timesync_tx_slot_dynflag;
+	return 0;
+}
+
 RTE_EXPORT_SYMBOL(rte_eth_timesync_adjust_time)
 int
 rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta)
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index ee400b386f..c7af668718 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -5513,6 +5513,19 @@ int rte_eth_timesync_read_rx_timestamp(uint16_t port_id,
 /**
  * Read an IEEE1588/802.1AS Tx timestamp from an Ethernet device.
  *
+ * This is the legacy Tx timestamp API and is intended for register-based
+ * timestamp reads. It does not provide per-packet correlation.
+ *
+ * Applications requiring per-packet Tx timestamp correlation should use the
+ * slot-based APIs:
+ * - Setup: rte_eth_timesync_tx_slot_dynfield_register()
+ * - Runtime per-packet loop:
+ *   - rte_eth_timesync_tx_timestamp_slot_alloc()
+ *   - rte_eth_timesync_tx_timestamp_stamp_mbuf()
+ *   - rte_eth_timesync_read_tx_timestamp_slot()
+ *   - rte_eth_timesync_tx_timestamp_slot_release()
+ * - Teardown: rte_eth_timesync_tx_slot_dynfield_unregister()
+ *
  * @param port_id
  *   The port identifier of the Ethernet device.
  * @param timestamp
@@ -5528,6 +5541,235 @@ int rte_eth_timesync_read_rx_timestamp(uint16_t port_id,
 int rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
 		struct timespec *timestamp);
 
+/** Valid bit for rte_eth_timesync_dual_domain_timestamp.adjusted_ns. */
+#define RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_ADJUSTED_VALID	RTE_BIT32(0)
+/** Valid bit for rte_eth_timesync_dual_domain_timestamp.raw_ns. */
+#define RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_RAW_VALID	RTE_BIT32(1)
+
+/**
+ * Dual-domain TX timestamp payload in nanoseconds.
+ *
+ * `adjusted_ns` is the synchronized/adjusted domain.
+ * `raw_ns` is the free-running raw hardware clock domain.
+ *
+ * Scalar `int64_t` nanoseconds are used (instead of `struct timespec`) to
+ * keep both domains compact in one payload and to avoid extra split/merge
+ * conversions when processing per-packet timestamp correlation data.
+ */
+struct rte_eth_timesync_dual_domain_timestamp {
+	int64_t adjusted_ns;
+	int64_t raw_ns;
+	uint32_t valid_mask;
+};
+
+/** Valid bit for rte_eth_timesync_tx_timestamp_slot_info.max_slots. */
+#define RTE_ETH_TIMESYNC_TX_TIMESTAMP_SLOT_INFO_MAX_VALID	RTE_BIT32(0)
+/** Valid bit for rte_eth_timesync_tx_timestamp_slot_info.free_slots. */
+#define RTE_ETH_TIMESYNC_TX_TIMESTAMP_SLOT_INFO_FREE_VALID	RTE_BIT32(1)
+
+/** TX timestamp retrieval mechanism supported by a port. */
+enum rte_eth_timesync_tx_ts_type {
+	RTE_ETH_TIMESYNC_TX_TS_NONE       = 0, /**< not supported */
+	/** One hardware latch register shared across all packets. */
+	RTE_ETH_TIMESYNC_TX_TS_SINGLE_REG = 1,
+	/** Per-packet slot bank supports concurrent in-flight correlation. */
+	RTE_ETH_TIMESYNC_TX_TS_PER_PACKET = 2,
+};
+
+/**
+ * TX timestamp capabilities returned by
+ * rte_eth_timesync_tx_timestamp_slot_get_capabilities().
+ */
+struct rte_eth_timesync_tx_ts_caps {
+	enum rte_eth_timesync_tx_ts_type type; /**< mechanism supported by this port */
+	uint32_t max_slots; /**< concurrent slots available; valid only for PER_PACKET */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query the TX timestamp capability of a port.
+ *
+ * Reports whether the hardware uses a single shared latch register
+ * (RTE_ETH_TIMESYNC_TX_TS_SINGLE_REG) or a per-packet slot bank
+ * (RTE_ETH_TIMESYNC_TX_TS_PER_PACKET), and how many concurrent slots exist.
+ *
+ * Use this to choose between:
+ *   - Slot-based: rte_eth_timesync_tx_timestamp_slot_alloc() +
+ *     rte_eth_timesync_read_tx_timestamp_slot()
+ *   - Legacy:     rte_eth_timesync_read_tx_timestamp()
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param caps
+ *   Output TX timestamp capability structure.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_slot_get_capabilities(uint16_t port_id,
+		struct rte_eth_timesync_tx_ts_caps *caps);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate a TX timestamp slot handle for per-packet timestamp correlation.
+ *
+ * Intended for PTP/event timestamping rates.
+ *
+ * Slots are allocated from a port-global pool and can be used across any
+ * TX queue on the port. The application stamps an mbuf with the slot handle
+ * using rte_eth_timesync_tx_timestamp_stamp_mbuf() before transmission.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param slot_id
+ *   Output handle identifying the allocated slot (port-global scope).
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENOSPC: No free slots are available.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_slot_alloc(uint16_t port_id,
+		uint32_t *slot_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Read a per-packet TX timestamp using a previously allocated slot handle.
+ *
+ * Intended for PTP/event timestamping rates.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param slot_id
+ *   Slot handle returned by rte_eth_timesync_tx_timestamp_slot_alloc().
+ * @param timestamp
+ *   Output dual-domain timestamp payload.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -EAGAIN: Timestamp is not ready yet.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_read_tx_timestamp_slot(uint16_t port_id,
+		uint32_t slot_id,
+		struct rte_eth_timesync_dual_domain_timestamp *timestamp);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Release a previously allocated TX timestamp slot handle.
+ *
+ * Intended for PTP/event timestamping rates.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param slot_id
+ *   Slot handle to release.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_slot_release(uint16_t port_id,
+		uint32_t slot_id);
+
+/** Mbuf dynfield name for the TX timestamp slot handle. */
+#define RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME "rte_eth_timesync_tx_slot"
+/** Mbuf dynflag name indicating TX timestamp slot handle is present. */
+#define RTE_ETH_TIMESYNC_TX_SLOT_DYNFLAG_NAME "rte_eth_timesync_tx_slot_flag"
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Register the per-packet TX timestamp slot dynfield and dynflag in the mbuf
+ * layout.
+ *
+ * Must be called before the first rte_pktmbuf_pool_create() when the
+ * application intends to use rte_eth_timesync_tx_timestamp_stamp_mbuf()
+ * for per-packet TX timestamp correlation. Calling it after pool creation
+ * may still succeed if the default dynfield area has not been exhausted.
+ *
+ * rte_eth_timesync_enable() calls this automatically, so explicit calls are
+ * only needed when the application creates pools before enabling timesync.
+ *
+ * Note: dynfields and dynflags cannot be unregistered in DPDK. Once
+ * registered they remain allocated for the lifetime of the process, whether
+ * or not the application ultimately uses per-packet slot correlation.
+ *
+ * @return
+ *   - 0: Success (or already registered).
+ *   - -ENOTSUP: Registration and lookup both failed (no dynfield space).
+ */
+__rte_experimental
+int rte_eth_timesync_tx_slot_dynfield_register(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Disable per-packet TX timestamp slot correlation for this process.
+ *
+ * Resets the cached dynfield offset and dynflag to their unregistered state.
+ * After this call rte_eth_timesync_tx_timestamp_stamp_mbuf() returns
+ * -ENOTSUP and the PMD TX path falls back to the port-level ptp_tx_index
+ * (legacy mode).
+ *
+ * The underlying DPDK dynfield bytes are NOT freed — DPDK provides no dynfield
+ * deallocation. The 4 bytes per mbuf remain allocated but dormant.
+ *
+ * @return   Always 0.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_slot_dynfield_unregister(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Set TX timestamp slot metadata in an mbuf so the TX path steers the
+ * NIC to capture the timestamp in the correct per-packet slot.
+ *
+ * Must be called after rte_eth_timesync_tx_timestamp_slot_alloc() and before
+ * rte_eth_tx_burst(). Safe for concurrent callers — slot is stored per-mbuf.
+ *
+ * @param port_id  The port identifier of the Ethernet device.
+ * @param slot_id  Slot handle from rte_eth_timesync_tx_timestamp_slot_alloc().
+ * @param m        Mbuf to stamp.
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EINVAL: Invalid parameters.
+ *   - -ENOTSUP: Registration/lookup failed.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_stamp_mbuf(uint16_t port_id,
+		uint32_t slot_id, struct rte_mbuf *m);
+
 /**
  * Adjust the timesync clock on an Ethernet device.
  *
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v2 0/1] ethdev: add Tx timestamp slot APIs
  2026-08-17 19:24 [RFC 0/1] ethdev: per-packet Tx timestamp slot management Rajesh Kumar
                   ` (5 preceding siblings ...)
  2026-08-27 12:21 ` Rajesh Kumar
@ 2026-08-27 12:34 ` Rajesh Kumar
  2026-08-27 12:34   ` [RFC PATCH v2 1/1] ethdev: add Tx timestamp slot management APIs Rajesh Kumar
  6 siblings, 1 reply; 14+ messages in thread
From: Rajesh Kumar @ 2026-08-27 12:34 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, andrew.rybchenko, stephen,
	aman.deep.singh, Rajesh Kumar

The ethdev timesync API currently exposes Tx timestamps through a shared
hardware register. This requires applications to serialize timestamped
packets and does not allow correlation when multiple packets are in
flight.

This RFC proposes an ethdev interface for hardware with independent Tx
timestamp slots. The interface reports the supported timestamping
mechanism, provides a port-global slot lifecycle, and lets applications
poll each slot asynchronously after transmission.

The proposal includes the following components:

* Capability reporting for shared-register and per-packet timestamping.
* Slot allocation, asynchronous timestamp retrieval, and slot release.
* A dual-domain timestamp structure for adjusted PHC and raw hardware
  time.
* Mbuf dynamic field and dynflag support for passing slot handles to Tx.
* Registration and process-local disabling of slot metadata.
* A compatibility alias for the mbuf stamping helper.
* Programmer-guide and NIC feature documentation.

The legacy rte_eth_timesync_read_tx_timestamp() API remains available on
devices using a shared timestamp register. This RFC adds the ethdev and
PMD interfaces but does not add a hardware-specific PMD implementation.

The following areas would benefit from review:

* Is the capability model sufficient for devices with different slot
  allocation or completion mechanisms?
* Is an mbuf dynamic field and dynflag the appropriate way to pass the
  slot handle into the Tx datapath?
* Is -EAGAIN the appropriate result while a slot timestamp is pending?
* Should the adjusted and raw timestamp domains use nanoseconds in the
  public structure, or should one domain expose hardware cycles instead?

Rajesh Kumar (1):
  ethdev: add Tx timestamp slot management APIs

 doc/guides/nics/features.rst              |  16 +-
 doc/guides/prog_guide/ethdev/index.rst    |   1 +
 doc/guides/prog_guide/ethdev/timesync.rst | 216 +++++++++++++++++++
 lib/ethdev/ethdev_driver.h                |  25 +++
 lib/ethdev/rte_ethdev.c                   | 152 +++++++++++++
 lib/ethdev/rte_ethdev.h                   | 251 ++++++++++++++++++++++
 6 files changed, 657 insertions(+), 4 deletions(-)
 create mode 100644 doc/guides/prog_guide/ethdev/timesync.rst

-- 
2.55.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [RFC PATCH v2 1/1] ethdev: add Tx timestamp slot management APIs
  2026-08-27 12:34 ` [RFC PATCH v2 0/1] ethdev: add Tx timestamp slot APIs Rajesh Kumar
@ 2026-08-27 12:34   ` Rajesh Kumar
  0 siblings, 0 replies; 14+ messages in thread
From: Rajesh Kumar @ 2026-08-27 12:34 UTC (permalink / raw)
  To: dev
  Cc: thomas, bruce.richardson, andrew.rybchenko, stephen,
	aman.deep.singh, Rajesh Kumar

Extend ethdev timesync with a capability model for selecting between
shared-register and per-packet Tx timestamping.

Add public and PMD interfaces to query timestamp capabilities, allocate
timestamp slots, retrieve timestamps asynchronously, and release slots.
Slots have port-global scope and can be used across Tx queues.

Add a dual-domain timestamp structure for reporting adjusted PHC time
and raw hardware time independently through validity flags.

Add APIs to register and unregister the mbuf dynamic field and dynflag
used to pass slot handles to the Tx datapath. Add helpers to associate
a slot handle with an mbuf before transmission.

Keep the legacy Tx timestamp API for shared-register hardware and provide
a compatibility alias for the mbuf stamping helper.

Document the timestamp capability model, slot lifecycle, and application
workflow.

Signed-off-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
 doc/guides/nics/features.rst              |  16 +-
 doc/guides/prog_guide/ethdev/index.rst    |   1 +
 doc/guides/prog_guide/ethdev/timesync.rst | 216 +++++++++++++++++++
 lib/ethdev/ethdev_driver.h                |  25 +++
 lib/ethdev/rte_ethdev.c                   | 152 +++++++++++++
 lib/ethdev/rte_ethdev.h                   | 251 ++++++++++++++++++++++
 6 files changed, 657 insertions(+), 4 deletions(-)
 create mode 100644 doc/guides/prog_guide/ethdev/timesync.rst

diff --git a/doc/guides/nics/features.rst b/doc/guides/nics/features.rst
index 0b0c69e7cd..171e2aabba 100644
--- a/doc/guides/nics/features.rst
+++ b/doc/guides/nics/features.rst
@@ -692,14 +692,22 @@ Timesync
 
 Supports IEEE1588/802.1AS timestamping.
 
-* **[implements] eth_dev_ops**: ``timesync_enable``, ``timesync_disable``
+* **[implements] eth_dev_ops**: ``timesync_enable``, ``timesync_disable``,
   ``timesync_read_rx_timestamp``, ``timesync_read_tx_timestamp``,
+  ``timesync_tx_ts_get_capabilities``, ``timesync_tx_timestamp_slot_alloc``,
+  ``timesync_read_tx_timestamp_slot``, ``timesync_tx_timestamp_slot_release``,
   ``timesync_adjust_time``, ``timesync_adjust_freq``,
   ``timesync_read_time``, ``timesync_write_time``.
 * **[related]    API**: ``rte_eth_timesync_enable()``, ``rte_eth_timesync_disable()``,
-  ``rte_eth_timesync_read_rx_timestamp()``,
-  ``rte_eth_timesync_read_tx_timestamp``, ``rte_eth_timesync_adjust_time()``,
-  ``rte_eth_timesync_adjust_freq()``,
+  ``rte_eth_timesync_read_rx_timestamp()``, ``rte_eth_timesync_read_tx_timestamp()``,
+  ``rte_eth_timesync_tx_timestamp_slot_get_capabilities()``,
+  ``rte_eth_timesync_tx_timestamp_slot_alloc()``,
+  ``rte_eth_timesync_read_tx_timestamp_slot()``,
+  ``rte_eth_timesync_tx_timestamp_slot_release()``,
+  ``rte_eth_timesync_tx_slot_dynfield_register()``,
+  ``rte_eth_timesync_tx_slot_dynfield_unregister()``,
+  ``rte_eth_timesync_tx_slot_set_mbuf()``,
+  ``rte_eth_timesync_adjust_time()``, ``rte_eth_timesync_adjust_freq()``,
   ``rte_eth_timesync_read_time()``, ``rte_eth_timesync_write_time()``.
 
 
diff --git a/doc/guides/prog_guide/ethdev/index.rst b/doc/guides/prog_guide/ethdev/index.rst
index 392ced0a2e..bc21f28091 100644
--- a/doc/guides/prog_guide/ethdev/index.rst
+++ b/doc/guides/prog_guide/ethdev/index.rst
@@ -13,3 +13,4 @@ Ethernet Device Library
     traffic_metering_and_policing
     traffic_management
     qos_framework
+    timesync
diff --git a/doc/guides/prog_guide/ethdev/timesync.rst b/doc/guides/prog_guide/ethdev/timesync.rst
new file mode 100644
index 0000000000..74405732ae
--- /dev/null
+++ b/doc/guides/prog_guide/ethdev/timesync.rst
@@ -0,0 +1,216 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2026 Intel Corporation.
+
+IEEE 1588 / PTP Timesync API
+============================
+
+Overview
+--------
+
+The DPDK IEEE 1588 / Precision Time Protocol (PTP) Timesync API provides
+a standardized framework for managing PTP Hardware Clocks (PHCs) and retrieving
+precise hardware transmit (Tx) and receive (Rx) timestamps.
+
+The Timesync framework encompasses three core capabilities:
+
+1. **Clock Control & Adjustment**: Enabling/disabling hardware timestamping, reading/setting clock time, and adjusting phase/frequency.
+2. **Receive Timestamping**: Hardware capture of incoming PTP packet arrival timestamps.
+3. **Transmit Timestamping**: Hardware capture of outbound PTP packet departure timestamps.
+
+
+Clock Management & Control
+--------------------------
+
+To initialize and discipline a port's PTP Hardware Clock (PHC), the API provides:
+
+* **Enable / Disable**:
+  ``rte_eth_timesync_enable(port_id)`` enables hardware timestamping on the specified port.
+  ``rte_eth_timesync_disable(port_id)`` disables timesync offloads.
+
+* **Clock Time Read / Write**:
+  ``rte_eth_timesync_read_time(port_id, &ts)`` reads the current PHC wall-clock time as a ``struct timespec``.
+  ``rte_eth_timesync_write_time(port_id, &ts)`` sets the PHC wall-clock time.
+
+* **Clock Adjustments**:
+  ``rte_eth_timesync_adjust_time(port_id, delta_ns)`` adjusts the clock phase by a delta offset in nanoseconds.
+  ``rte_eth_timesync_adjust_freq(port_id, scaled_ppm)`` adjusts the clock frequency in scaled parts-per-million (1 ppm = 1 << 16).
+
+
+Receive (Rx) Timestamping
+-------------------------
+
+When receive timestamping is enabled, the hardware identifies incoming PTP packets (e.g. IEEE 1588 EtherType ``0x88F7`` or UDP destination ports 319/320) and latches their arrival time.
+
+Rx Timestamp Extraction Workflow
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. On packet reception via ``rte_eth_rx_burst()``, the PMD checks if the received mbuf represents a PTP packet.
+2. The PMD sets the ``RTE_MBUF_F_RX_IEEE1588_PTP`` flag in ``mbuf->ol_flags``.
+3. Depending on the PMD and hardware capability, the Rx timestamp is either:
+   * **Extracted via API**: Application calls ``rte_eth_timesync_read_rx_timestamp(port_id, &ts, flags)``.
+   * **Inlined in Mbuf**: Stored in a registered mbuf dynamic field (e.g. ``rte_mbuf_dyn_rx_timestamp_register()``).
+
+
+Transmit (Tx) Timestamping Architectures
+----------------------------------------
+
+The framework supports two hardware transmit timestamping architectures:
+
+* **Single Shared Register** (``RTE_ETH_TIMESYNC_TX_TS_SINGLE_REG``):
+  The hardware contains a single shared transmit timestamp latch register.
+  Only one outbound packet can be timestamped at a time across the entire port.
+  The application calls ``rte_eth_timesync_read_tx_timestamp(port_id, &ts)`` to retrieve the departure time.
+
+* **Per-Packet Slot Bank** (``RTE_ETH_TIMESYNC_TX_TS_PER_PACKET``):
+  The hardware provides a bank of independent transmit timestamp slots or
+  descriptors. Multiple outbound PTP packets can be timestamped concurrently and
+  correlated asynchronously on a per-packet basis using slot handles.
+
+
+Dual-Domain Timestamps
+~~~~~~~~~~~~~~~~~~~~~~
+
+When retrieving transmit timestamps using slot handles, the API returns
+a dual-domain timestamp structure:
+
+.. code-block:: c
+
+    struct rte_eth_timesync_dual_domain_timestamp {
+        int64_t adjusted_ns; /**< PHC adjusted time (wall-clock nanoseconds) */
+        int64_t raw_ns;      /**< Free-running hardware cycle counter or raw nanoseconds */
+        uint32_t valid_mask; /**< Validity bits for the adjusted/raw domains */
+    };
+
+* **Adjusted Domain** (``RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_ADJUSTED_VALID``):
+  Represents the wall-clock time after frequency adjustments (``rte_eth_timesync_adjust_freq``)
+  or phase steps (``rte_eth_timesync_adjust_time``) have been applied.
+
+* **Raw Domain** (``RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_RAW_VALID``):
+  Represents the unadjusted free-running hardware cycle counter or raw timestamp.
+  This domain is required when correlating adjusted wall-clock time with the
+  underlying hardware timebase or when performing cross-timestamp analysis.
+
+
+Per-Packet Tx Timestamp Workflow
+--------------------------------
+
+To use per-packet transmit timestamping, applications follow this sequence:
+
+1. **Query Port Capabilities**
+   Determine whether the PMD supports slot-based per-packet timestamping:
+
+   .. code-block:: c
+
+       struct rte_eth_timesync_tx_ts_caps caps;
+
+       ret = rte_eth_timesync_tx_timestamp_slot_get_capabilities(port_id, &caps);
+       if (ret == 0 && caps.type == RTE_ETH_TIMESYNC_TX_TS_PER_PACKET) {
+           printf("Port %u supports per-packet timestamping with %u max slots\n",
+                  port_id, caps.max_slots);
+       }
+
+2. **Register Mbuf Dynamic Fields**
+   Register the dynamic field and dynamic flag used to pass slot handles to the Tx datapath:
+
+   .. code-block:: c
+
+       ret = rte_eth_timesync_tx_slot_dynfield_register();
+       if (ret < 0) {
+           /* Dynamic field space exhausted or registration failed */
+       }
+
+   .. note::
+
+      ``rte_eth_timesync_enable()`` registers the dynamic field automatically.
+      Call ``rte_eth_timesync_tx_slot_dynfield_register()`` explicitly only if creating
+      mempools before enabling timesync on the port.
+
+3. **Allocate a Timestamp Slot**
+   Before transmitting a PTP packet requiring a transmit timestamp, allocate a slot handle:
+
+   .. code-block:: c
+
+       uint32_t slot_id;
+
+       ret = rte_eth_timesync_tx_timestamp_slot_alloc(port_id, &slot_id);
+       if (ret != 0) {
+           /* Handle allocation error (e.g. -ENOSPC if all slots are in flight) */
+       }
+
+4. **Stamp the Mbuf**
+   Attach the allocated slot handle to the mbuf:
+
+   .. code-block:: c
+
+       rte_eth_timesync_tx_slot_set_mbuf(port_id, slot_id, mbuf);
+       mbuf->ol_flags |= RTE_MBUF_F_TX_IEEE1588_TMST;
+
+5. **Transmit the Packet**
+   Send the packet via ``rte_eth_tx_burst()`` as usual.
+
+6. **Poll for Timestamp Completion**
+   Read the captured timestamp using the allocated slot handle:
+
+   .. code-block:: c
+
+       struct rte_eth_timesync_dual_domain_timestamp ts;
+
+       ret = rte_eth_timesync_read_tx_timestamp_slot(port_id, slot_id, &ts);
+       if (ret == 0) {
+           /* Timestamp is ready */
+           if (ts.valid_mask & RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_ADJUSTED_VALID) {
+               /* Process ts.adjusted_ns */
+           }
+       } else if (ret == -EAGAIN) {
+           /* Timestamp hardware processing is still pending; retry later */
+       }
+
+7. **Release the Slot**
+   After successfully reading the timestamp or timing out, release the slot handle:
+
+   .. code-block:: c
+
+       rte_eth_timesync_tx_timestamp_slot_release(port_id, slot_id);
+
+8. **Unregister Dynfield State on Shutdown (Optional)**
+   When shutting down timesync offloads, the application can unregister the cached dynfield state:
+
+   .. code-block:: c
+
+       rte_eth_timesync_tx_slot_dynfield_unregister();
+
+   .. note::
+
+      This resets process-local dynfield state so subsequent ``rte_eth_timesync_tx_slot_set_mbuf()``
+      calls return ``-ENOTSUP`` and PMD Tx datapaths fall back to port-level legacy mode.
+      Note that underlying mbuf dynfield bytes remain allocated in DPDK layout as DPDK does not
+      support dynamic field deallocation.
+
+
+PMD Implementation Requirements
+-------------------------------
+
+To support full timesync capabilities, a Poll Mode Driver (PMD) implements the following driver contract:
+
+1. **Clock Operations** (``timesync_enable``, ``timesync_disable``, ``timesync_read_time``, ``timesync_write_time``, ``timesync_adjust_time``, ``timesync_adjust_freq``)
+   * Controls hardware timestamp generation and disciplines the hardware clock registers.
+
+2. **Rx Timestamping** (``timesync_read_rx_timestamp``)
+   * Configures Rx filters to latch incoming PTP arrival times and flags received mbufs with ``RTE_MBUF_F_RX_IEEE1588_PTP``.
+
+3. **Tx Slot Capability Reporting** (``timesync_tx_ts_get_capabilities``)
+   * Reports ``RTE_ETH_TIMESYNC_TX_TS_SINGLE_REG`` or ``RTE_ETH_TIMESYNC_TX_TS_PER_PACKET`` in `caps->type` and sets `caps->max_slots`.
+
+4. **Slot Allocation & Release** (``timesync_tx_timestamp_slot_alloc`` / ``timesync_tx_timestamp_slot_release``)
+   * Maintains a port-global pool or bitmap of hardware timestamp slots.
+   * `timesync_tx_timestamp_slot_alloc` returns a port-unique slot identifier and returns ``-ENOSPC`` when no slots are free.
+   * `timesync_tx_timestamp_slot_release` clears hardware slot state and returns the slot handle to the free pool.
+
+5. **Tx Datapath Integration**
+   * Checks if ``RTE_MBUF_F_TX_IEEE1588_TMST`` is set on `mbuf->ol_flags`.
+   * For per-packet slot mode, retrieves `slot_id` from mbuf dynamic field via ``*RTE_MBUF_DYNFIELD(m, dynfield_offset, uint32_t *)``.
+   * Configures hardware Tx descriptors to capture departure timestamps into the specified slot.
+
+6. **Tx Slot Timestamp Retrieval** (``timesync_read_tx_timestamp_slot``)
+   * Queries hardware slot or descriptor completion ring corresponding to `slot_id`.
+   * Populates ``struct rte_eth_timesync_dual_domain_timestamp`` and returns ``0`` when ready, or ``-EAGAIN`` if pending.
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 0f336f9567..9d981995ea 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -795,6 +795,23 @@ typedef int (*eth_timesync_read_rx_timestamp_t)(struct rte_eth_dev *dev,
 typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 						struct timespec *timestamp);
 
+/** @internal Query TX timestamp hardware capability (single-register vs per-packet slot bank). */
+typedef int (*eth_timesync_tx_ts_get_caps_t)(struct rte_eth_dev *dev,
+		struct rte_eth_timesync_tx_ts_caps *caps);
+
+/** @internal Allocate a per-packet TX timestamp slot handle. */
+typedef int (*eth_timesync_tx_timestamp_slot_alloc_t)(struct rte_eth_dev *dev,
+		uint32_t *slot_id);
+
+/** @internal Read a dual-domain TX timestamp by slot handle. */
+typedef int (*eth_timesync_read_tx_timestamp_slot_t)(struct rte_eth_dev *dev,
+		uint32_t slot_id,
+		struct rte_eth_timesync_dual_domain_timestamp *timestamp);
+
+/** @internal Release a previously allocated TX timestamp slot handle. */
+typedef int (*eth_timesync_tx_timestamp_slot_release_t)(struct rte_eth_dev *dev,
+		uint32_t slot_id);
+
 /** @internal Function used to adjust the device clock. */
 typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
 
@@ -1561,6 +1578,14 @@ struct eth_dev_ops {
 	eth_timesync_read_rx_timestamp_t timesync_read_rx_timestamp;
 	/** Read the IEEE1588/802.1AS Tx timestamp */
 	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
+	/** Allocate a TX timestamp slot handle */
+	eth_timesync_tx_timestamp_slot_alloc_t timesync_tx_timestamp_slot_alloc;
+	/** Query TX timestamp hardware capability (single-reg vs per-packet) */
+	eth_timesync_tx_ts_get_caps_t timesync_tx_ts_get_capabilities;
+	/** Read a TX timestamp using a slot handle */
+	eth_timesync_read_tx_timestamp_slot_t timesync_read_tx_timestamp_slot;
+	/** Release a TX timestamp slot handle */
+	eth_timesync_tx_timestamp_slot_release_t timesync_tx_timestamp_slot_release;
 	/** Adjust the device clock */
 	eth_timesync_adjust_time   timesync_adjust_time;
 	/** Adjust the clock frequency */
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index 9efeaf77cb..204e1db2b7 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -21,6 +21,7 @@
 #include <rte_mempool.h>
 #include <rte_malloc.h>
 #include <rte_mbuf.h>
+#include <rte_mbuf_dyn.h>
 #include <rte_errno.h>
 #include <rte_spinlock.h>
 #include <rte_string_fns.h>
@@ -6699,6 +6700,157 @@ rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
 
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_slot_alloc, 26.11)
+int
+rte_eth_timesync_tx_timestamp_slot_alloc(uint16_t port_id,
+					 uint32_t *slot_id)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (slot_id == NULL) {
+		RTE_ETHDEV_LOG_LINE(ERR,
+			"Cannot allocate ethdev port %u Tx timestamp slot to NULL",
+			port_id);
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->timesync_tx_timestamp_slot_alloc == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_tx_timestamp_slot_alloc(dev, slot_id));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_slot_get_capabilities, 26.11)
+int
+rte_eth_timesync_tx_timestamp_slot_get_capabilities(uint16_t port_id,
+		struct rte_eth_timesync_tx_ts_caps *caps)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (caps == NULL)
+		return -EINVAL;
+
+	if (dev->dev_ops->timesync_tx_ts_get_capabilities == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_tx_ts_get_capabilities(dev, caps));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_read_tx_timestamp_slot, 26.11)
+int
+rte_eth_timesync_read_tx_timestamp_slot(uint16_t port_id,
+					uint32_t slot_id,
+					struct rte_eth_timesync_dual_domain_timestamp *timestamp)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (timestamp == NULL) {
+		RTE_ETHDEV_LOG_LINE(ERR,
+			"Cannot read ethdev port %u Tx timestamp slot to NULL",
+			port_id);
+		return -EINVAL;
+	}
+
+	if (dev->dev_ops->timesync_read_tx_timestamp_slot == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_read_tx_timestamp_slot(dev,
+				slot_id, timestamp));
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_slot_release, 26.11)
+int
+rte_eth_timesync_tx_timestamp_slot_release(uint16_t port_id, uint32_t slot_id)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (dev->dev_ops->timesync_tx_timestamp_slot_release == NULL)
+		return -ENOTSUP;
+
+	return eth_err(port_id,
+			dev->dev_ops->timesync_tx_timestamp_slot_release(dev,
+				slot_id));
+}
+
+static int rte_eth_timesync_tx_slot_dynfield_offset = -1;
+static uint64_t rte_eth_timesync_tx_slot_dynflag;
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_slot_dynfield_register, 26.11)
+int
+rte_eth_timesync_tx_slot_dynfield_register(void)
+{
+	const struct rte_mbuf_dynfield slot_dynfield = {
+		.name  = RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME,
+		.size  = sizeof(uint32_t),
+		.align = alignof(uint32_t),
+	};
+
+	if (rte_eth_timesync_tx_slot_dynfield_offset >= 0)
+		return 0;
+
+	rte_eth_timesync_tx_slot_dynfield_offset =
+			rte_mbuf_dynfield_register(&slot_dynfield);
+	if (rte_eth_timesync_tx_slot_dynfield_offset < 0)
+		rte_eth_timesync_tx_slot_dynfield_offset =
+				rte_mbuf_dynfield_lookup(
+					RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME, NULL);
+	if (rte_eth_timesync_tx_slot_dynfield_offset < 0)
+		return -ENOTSUP;
+
+	{
+		int flag_bit = rte_mbuf_dynflag_register(
+			&(const struct rte_mbuf_dynflag){
+				.name = RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME "_flag"});
+		if (flag_bit < 0)
+			flag_bit = rte_mbuf_dynflag_lookup(
+				RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME "_flag", NULL);
+		if (flag_bit >= 0)
+			rte_eth_timesync_tx_slot_dynflag = RTE_BIT64(flag_bit);
+	}
+	return 0;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_slot_dynfield_unregister, 26.11)
+int
+rte_eth_timesync_tx_slot_dynfield_unregister(void)
+{
+	/* Reset cached state without freeing dynamic-field bytes. */
+	rte_eth_timesync_tx_slot_dynfield_offset = -1;
+	rte_eth_timesync_tx_slot_dynflag = 0;
+	return 0;
+}
+
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_eth_timesync_tx_timestamp_stamp_mbuf, 26.11)
+int
+rte_eth_timesync_tx_timestamp_stamp_mbuf(uint16_t port_id __rte_unused,
+					 uint32_t slot_id, struct rte_mbuf *m)
+{
+	if (m == NULL)
+		return -EINVAL;
+	if (rte_eth_timesync_tx_slot_dynfield_register() != 0)
+		return -ENOTSUP;
+	*RTE_MBUF_DYNFIELD(m, rte_eth_timesync_tx_slot_dynfield_offset,
+			   uint32_t *) = slot_id;
+	m->ol_flags |= rte_eth_timesync_tx_slot_dynflag;
+	return 0;
+}
+
 RTE_EXPORT_SYMBOL(rte_eth_timesync_adjust_time)
 int
 rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta)
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index ee400b386f..a4e8fc2c24 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -5513,6 +5513,19 @@ int rte_eth_timesync_read_rx_timestamp(uint16_t port_id,
 /**
  * Read an IEEE1588/802.1AS Tx timestamp from an Ethernet device.
  *
+ * This is the legacy Tx timestamp API and is intended for register-based
+ * timestamp reads. It does not provide per-packet correlation.
+ *
+ * Applications requiring per-packet Tx timestamp correlation should use the
+ * slot-based APIs:
+ * - Setup: rte_eth_timesync_tx_slot_dynfield_register()
+ * - Runtime per-packet loop:
+ *   - rte_eth_timesync_tx_timestamp_slot_alloc()
+ *   - rte_eth_timesync_tx_slot_set_mbuf()
+ *   - rte_eth_timesync_read_tx_timestamp_slot()
+ *   - rte_eth_timesync_tx_timestamp_slot_release()
+ * - Teardown: rte_eth_timesync_tx_slot_dynfield_unregister()
+ *
  * @param port_id
  *   The port identifier of the Ethernet device.
  * @param timestamp
@@ -5528,6 +5541,244 @@ int rte_eth_timesync_read_rx_timestamp(uint16_t port_id,
 int rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
 		struct timespec *timestamp);
 
+/** Valid bit for rte_eth_timesync_dual_domain_timestamp.adjusted_ns. */
+#define RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_ADJUSTED_VALID	RTE_BIT32(0)
+/** Valid bit for rte_eth_timesync_dual_domain_timestamp.raw_ns. */
+#define RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_RAW_VALID	RTE_BIT32(1)
+
+/**
+ * Dual-domain TX timestamp payload in nanoseconds.
+ *
+ * `adjusted_ns` is the synchronized/adjusted domain.
+ * `raw_ns` is the free-running raw hardware clock domain.
+ *
+ * Scalar `int64_t` nanoseconds are used (instead of `struct timespec`) to
+ * keep both domains compact in one payload and to avoid extra split/merge
+ * conversions when processing per-packet timestamp correlation data.
+ */
+struct rte_eth_timesync_dual_domain_timestamp {
+	int64_t adjusted_ns;
+	int64_t raw_ns;
+	uint32_t valid_mask;
+};
+
+/** Valid bit for rte_eth_timesync_tx_timestamp_slot_info.max_slots. */
+#define RTE_ETH_TIMESYNC_TX_TIMESTAMP_SLOT_INFO_MAX_VALID	RTE_BIT32(0)
+/** Valid bit for rte_eth_timesync_tx_timestamp_slot_info.free_slots. */
+#define RTE_ETH_TIMESYNC_TX_TIMESTAMP_SLOT_INFO_FREE_VALID	RTE_BIT32(1)
+
+/** TX timestamp retrieval mechanism supported by a port. */
+enum rte_eth_timesync_tx_ts_type {
+	RTE_ETH_TIMESYNC_TX_TS_NONE       = 0, /**< not supported */
+	/** One hardware latch register shared across all packets. */
+	RTE_ETH_TIMESYNC_TX_TS_SINGLE_REG = 1,
+	/** Per-packet slot bank supports concurrent in-flight correlation. */
+	RTE_ETH_TIMESYNC_TX_TS_PER_PACKET = 2,
+};
+
+/**
+ * TX timestamp capabilities returned by
+ * rte_eth_timesync_tx_timestamp_slot_get_capabilities().
+ */
+struct rte_eth_timesync_tx_ts_caps {
+	enum rte_eth_timesync_tx_ts_type type; /**< mechanism supported by this port */
+	uint32_t max_slots; /**< concurrent slots available; valid only for PER_PACKET */
+};
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Query the TX timestamp capability of a port.
+ *
+ * Reports whether the hardware uses a single shared latch register
+ * (RTE_ETH_TIMESYNC_TX_TS_SINGLE_REG) or a per-packet slot bank
+ * (RTE_ETH_TIMESYNC_TX_TS_PER_PACKET), and how many concurrent slots exist.
+ *
+ * Use this to choose between:
+ *   - Slot-based: rte_eth_timesync_tx_timestamp_slot_alloc() +
+ *     rte_eth_timesync_read_tx_timestamp_slot()
+ *   - Legacy:     rte_eth_timesync_read_tx_timestamp()
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param caps
+ *   Output TX timestamp capability structure.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_slot_get_capabilities(uint16_t port_id,
+		struct rte_eth_timesync_tx_ts_caps *caps);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Allocate a TX timestamp slot handle for per-packet timestamp correlation.
+ *
+ * Intended for PTP/event timestamping rates.
+ *
+ * Slots are allocated from a port-global pool and can be used across any
+ * TX queue on the port. The application stamps an mbuf with the slot handle
+ * using rte_eth_timesync_tx_slot_set_mbuf() before transmission.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param slot_id
+ *   Output handle identifying the allocated slot (port-global scope).
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENOSPC: No free slots are available.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_slot_alloc(uint16_t port_id,
+		uint32_t *slot_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Read a per-packet TX timestamp using a previously allocated slot handle.
+ *
+ * Intended for PTP/event timestamping rates.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param slot_id
+ *   Slot handle returned by rte_eth_timesync_tx_timestamp_slot_alloc().
+ * @param timestamp
+ *   Output dual-domain timestamp payload.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -EAGAIN: Timestamp is not ready yet.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_read_tx_timestamp_slot(uint16_t port_id,
+		uint32_t slot_id,
+		struct rte_eth_timesync_dual_domain_timestamp *timestamp);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Release a previously allocated TX timestamp slot handle.
+ *
+ * Intended for PTP/event timestamping rates.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param slot_id
+ *   Slot handle to release.
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ *   - -EINVAL: Invalid parameters.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_slot_release(uint16_t port_id,
+		uint32_t slot_id);
+
+/** Mbuf dynfield name for the TX timestamp slot handle. */
+#define RTE_ETH_TIMESYNC_TX_SLOT_DYNFIELD_NAME "rte_eth_timesync_tx_slot"
+/** Mbuf dynflag name indicating TX timestamp slot handle is present. */
+#define RTE_ETH_TIMESYNC_TX_SLOT_DYNFLAG_NAME "rte_eth_timesync_tx_slot_flag"
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Register the per-packet TX timestamp slot dynfield and dynflag in the mbuf
+ * layout.
+ *
+ * Must be called before the first rte_pktmbuf_pool_create() when the
+ * application intends to use rte_eth_timesync_tx_slot_set_mbuf() for
+ * per-packet TX timestamp correlation. Calling it after pool creation may
+ * still succeed if the default dynfield area has not been exhausted.
+ *
+ * rte_eth_timesync_enable() calls this automatically, so explicit calls are
+ * only needed when the application creates pools before enabling timesync.
+ *
+ * Note: dynfields and dynflags cannot be unregistered in DPDK. Once
+ * registered they remain allocated for the lifetime of the process, whether
+ * or not the application ultimately uses per-packet slot correlation.
+ *
+ * @return
+ *   - 0: Success (or already registered).
+ *   - -ENOTSUP: Registration and lookup both failed (no dynfield space).
+ */
+__rte_experimental
+int rte_eth_timesync_tx_slot_dynfield_register(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Disable per-packet TX timestamp slot correlation for this process.
+ *
+ * Resets the cached dynfield offset and dynflag to their unregistered state.
+ * After this call rte_eth_timesync_tx_slot_set_mbuf() returns -ENOTSUP and
+ * the PMD TX path falls back to the port-level ptp_tx_index (legacy mode).
+ *
+ * The underlying DPDK dynfield bytes are NOT freed — DPDK provides no dynfield
+ * deallocation. The 4 bytes per mbuf remain allocated but dormant.
+ *
+ * @return   Always 0.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_slot_dynfield_unregister(void);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Set TX timestamp slot metadata in an mbuf so the TX path steers the
+ * NIC to capture the timestamp in the correct per-packet slot.
+ *
+ * Must be called after rte_eth_timesync_tx_timestamp_slot_alloc() and before
+ * rte_eth_tx_burst(). Safe for concurrent callers — slot is stored per-mbuf.
+ *
+ * @param port_id  The port identifier of the Ethernet device.
+ * @param slot_id  Slot handle from rte_eth_timesync_tx_timestamp_slot_alloc().
+ * @param m        Mbuf to stamp.
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EINVAL: Invalid parameters.
+ *   - -ENOTSUP: Registration/lookup failed.
+ */
+__rte_experimental
+int rte_eth_timesync_tx_slot_set_mbuf(uint16_t port_id,
+		uint32_t slot_id, struct rte_mbuf *m);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * Compatibility alias for rte_eth_timesync_tx_slot_set_mbuf().
+ */
+__rte_experimental
+int rte_eth_timesync_tx_timestamp_stamp_mbuf(uint16_t port_id,
+		uint32_t slot_id, struct rte_mbuf *m);
+
 /**
  * Adjust the timesync clock on an Ethernet device.
  *
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [RFC PATCH v3 1/1] ethdev: add Tx timestamp slot management APIs
  2026-08-27 12:21   ` [RFC PATCH v3 1/1] ethdev: add Tx timestamp slot management APIs Rajesh Kumar
@ 2026-08-27 21:45     ` Stephen Hemminger
  0 siblings, 0 replies; 14+ messages in thread
From: Stephen Hemminger @ 2026-08-27 21:45 UTC (permalink / raw)
  To: Rajesh Kumar
  Cc: dev, thomas, bruce.richardson, andrew.rybchenko, aman.deep.singh

On Thu, 27 Aug 2026 17:51:59 +0530
Rajesh Kumar <rajesh3.kumar@intel.com> wrote:

> Extend ethdev timesync with a capability model for selecting between
> shared-register and per-packet Tx timestamping.
> 
> Add public and PMD interfaces to query timestamp capabilities, allocate
> timestamp slots, retrieve timestamps asynchronously, and release slots.
> Slots have port-global scope and can be used across Tx queues.
> 
> Add a dual-domain timestamp structure for reporting adjusted PHC time
> and raw hardware time independently through validity flags.
> 
> Add APIs to register and unregister the mbuf dynamic field and dynflag
> used to pass slot handles to the Tx datapath. Add helpers to associate
> a slot handle with an mbuf before transmission.
> 
> Keep the legacy Tx timestamp API for shared-register hardware and provide
> a compatibility alias for the mbuf stamping helper.
> 
> Document the timestamp capability model, slot lifecycle, and application
> workflow.
> 
> Signed-off-by: Rajesh Kumar <rajesh3.kumar@intel.com>
> ---

Lots of feedback for AI review that needs addressing (Claude Fable).
Also a feature like this needs some form of test coverage. Perhaps
mocking up something in null PMD or related.

Review of [RFC PATCH v3 1/1] ethdev: add Tx timestamp slot management APIs

Applied cleanly to current main and read post-apply. No build
performed (meson not available here); nothing in the diff looks like
it would fail to compile.

Errors:

1. Documentation and Doxygen claim rte_eth_timesync_enable() registers
   the dynfield/dynflag automatically. It does not. The patch does not
   touch rte_eth_timesync_enable(); post-apply it still just calls the
   PMD op. So the .. note:: in timesync.rst ("registers the dynamic
   field automatically ... Call ... explicitly only if creating
   mempools before enabling timesync") and the same statement in the
   header Doxygen for rte_eth_timesync_tx_slot_dynfield_register() are
   false. An application that follows the doc and relies on
   timesync_enable will get -ENOTSUP from stamp_mbuf (or worse, a late
   registration that fails after pools are created). Either add the
   call in rte_eth_timesync_enable() or drop the claim; given the
   "must register before pool create" constraint, dropping the claim
   and making the explicit call mandatory is the safer contract.

2. Commit message says "provide a compatibility alias for the mbuf
   stamping helper". No such alias exists in the diff. Either the
   alias was dropped between v2 and v3 and the message is stale, or
   it is missing. Fix one or the other.

3. Dead macros referencing a nonexistent structure:
     RTE_ETH_TIMESYNC_TX_TIMESTAMP_SLOT_INFO_MAX_VALID
     RTE_ETH_TIMESYNC_TX_TIMESTAMP_SLOT_INFO_FREE_VALID
   Doxygen says they are valid bits for
   rte_eth_timesync_tx_timestamp_slot_info.max_slots / .free_slots,
   but that struct is not defined anywhere. Leftover from an earlier
   revision; remove them.

4. The PMD contract is incomplete: the datapath needs the dynfield
   offset and dynflag bit, but they are file-static in rte_ethdev.c
   with no accessor in ethdev_driver.h. A PMD is forced to do its own
   rte_mbuf_dynfield_lookup()/dynflag_lookup() by name, which then
   cannot observe rte_eth_timesync_tx_slot_dynfield_unregister()
   resetting the library-side cache. So the documented behaviour that
   after unregister "PMD Tx datapaths fall back to port-level legacy
   mode" cannot actually happen: the PMD keeps testing the dynflag it
   looked up, and mbufs stamped before unregister still carry it.
   Either export an internal accessor (offset + flag) in
   ethdev_driver.h that PMDs must use, or drop the unregister API and
   its fallback claim. As written, the unregister function only
   changes library-local state and cannot deliver what its Doxygen
   promises.

Warnings:

5. rte_ethdev.h Doxygen for rte_eth_timesync_tx_slot_dynfield_unregister
   refers to "the port-level ptp_tx_index". That is an Intel driver
   internal, not an ethdev concept; a generic header should not
   reference it.

6. rte_eth_timesync_tx_timestamp_stamp_mbuf() calls
   rte_eth_timesync_tx_slot_dynfield_register() on every invocation.
   Post-registration this is just an int compare, but it also means
   the first stamp_mbuf call can silently register the dynfield after
   pools exist, which the register() Doxygen says may fail. It is also
   inconsistent with the "-ENOTSUP after unregister" contract: after
   unregister, stamp_mbuf will simply re-register (lookup succeeds)
   and go on working. Do the offset check inline and return -ENOTSUP
   if the offset is < 0 rather than re-registering.

7. Slot handles are uint32_t, but rte_eth_timesync_tx_ts_caps has no
   way to express the free-slot count, and slot_release() has no
   documented behaviour for double-release or release of a slot whose
   timestamp was never read. For an RFC that is acceptable, but the
   PMD contract section in timesync.rst should say what a PMD must do
   for an invalid or already-free slot_id (-EINVAL is the obvious
   answer, and the ethdev wrapper could enforce slot_id < max_slots
   if caps are cached).

8. Release notes and header call these experimental, but the new
   eth_dev_ops members are inserted in the middle of struct
   eth_dev_ops rather than at the end. eth_dev_ops is internal so
   this is not an ABI issue, but the ordering in the struct
   (alloc, get_capabilities, read_slot, release) does not match the
   typedef order or the order in features.rst
   (get_capabilities, alloc, read_slot, release). Make them consistent.

9. features.rst adds the ops and API names, but there is no PMD
   implementing them in this series and no testpmd hook or unit test
   exercising the new API. Per contributing guidelines a new ethdev
   API needs at least one driver implementation and a testpmd hook
   before it can be merged out of RFC.

Info:

10. timesync.rst: the "Clock Management & Control", "Rx Timestamp
    Extraction Workflow" and "PMD Implementation Requirements"
    sections use bullet lists with bold term + description; RST
    definition lists would render better. Several lines in the .rst
    are well over 100 columns; wrap at sentence boundaries.

11. In rte_eth_timesync_tx_slot_dynfield_register(), the inner
    braced block for flag_bit is unusual style in DPDK; declare
    flag_bit at function top or at point of use without the block.

12. Blank line separating the slot_release function from the
    "Internal process-local cache" comment is missing; there is a
    double blank line before stamp_mbuf.

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-08-27 21:45 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-17 19:24 [RFC 0/1] ethdev: per-packet Tx timestamp slot management Rajesh Kumar
2026-08-17 19:24 ` [RFC 1/1] ethdev: add per-packet Tx timestamp slot APIs Rajesh Kumar
2026-08-20  4:51   ` Naga Harish K, S V
2026-08-18  2:23 ` [RFC 0/1] ethdev: per-packet Tx timestamp slot management Stephen Hemminger
2026-08-20  4:41 ` Naga Harish K, S V
2026-08-27 11:09   ` Kumar, Rajesh
2026-08-27 12:13 ` [RFC PATCH v2 0/1] ethdev: add Tx timestamp slot APIs Rajesh Kumar
2026-08-27 12:13   ` [RFC PATCH v3 1/1] ethdev: add Tx timestamp slot management APIs Rajesh Kumar
2026-08-27 12:18 ` [RFC PATCH v3 0/1] ethdev: add Tx timestamp slot APIs Rajesh Kumar
2026-08-27 12:21 ` Rajesh Kumar
2026-08-27 12:21   ` [RFC PATCH v3 1/1] ethdev: add Tx timestamp slot management APIs Rajesh Kumar
2026-08-27 21:45     ` Stephen Hemminger
2026-08-27 12:34 ` [RFC PATCH v2 0/1] ethdev: add Tx timestamp slot APIs Rajesh Kumar
2026-08-27 12:34   ` [RFC PATCH v2 1/1] ethdev: add Tx timestamp slot management APIs Rajesh Kumar

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.