* [RFC 1/1] ethdev: add per-packet Tx timestamp slot APIs [not found] <20260817192117.3009211-1-rajesh3.kumar@intel.com> @ 2026-08-17 19:21 ` Rajesh Kumar 0 siblings, 0 replies; 2+ messages in thread From: Rajesh Kumar @ 2026-08-17 19:21 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] 2+ messages in thread
* [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
0 siblings, 1 reply; 2+ 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] 2+ 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 0 siblings, 0 replies; 2+ 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] 2+ messages in thread
end of thread, other threads:[~2026-08-17 19:24 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260817192117.3009211-1-rajesh3.kumar@intel.com>
2026-08-17 19:21 ` [RFC 1/1] ethdev: add per-packet Tx timestamp slot APIs Rajesh Kumar
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
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.