From: Rajesh Kumar <rajesh3.kumar@intel.com>
To: dev@dpdk.org
Cc: thomas@monjalon.net, bruce.richardson@intel.com,
andrew.rybchenko@oktetlabs.ru, stephen@networkplumber.org,
aman.deep.singh@intel.com, Rajesh Kumar <rajesh3.kumar@intel.com>
Subject: [RFC PATCH v5 4/5] net/ice: support per-packet Tx timestamp slots
Date: Tue, 8 Sep 2026 13:02:05 +0530 [thread overview]
Message-ID: <20260908073206.1236372-5-rajesh3.kumar@intel.com> (raw)
In-Reply-To: <20260908073206.1236372-1-rajesh3.kumar@intel.com>
The ice PHY provides 64 Tx timestamp slots per port, but the driver only
ever used a single static slot (ptp_tx_index), which limits PTP Tx
timestamping to one in-flight packet at a time.
Implement the ethdev Tx timestamp slot management ops on top of an atomic
per-adapter slot bitmap:
- timesync_tx_slot_get_caps reports the per-packet slot type and
the number of slots,
- timesync_tx_slot_alloc reserves a free slot using a
compare-exchange loop; on E822 the 64 quad slots are split evenly
between the PFs sharing the quad so allocations never overlap,
- timesync_tx_slot_read polls the PHY ready bitmap for the
given slot and returns the adjusted timestamp,
- timesync_tx_slot_release clears the PHY timestamp (E810)
and frees the bitmap bit.
The Tx context descriptor now takes the timestamp index from the mbuf
dynamic field registered by the ethdev layer when the packet carries the
Tx timestamp slot dynamic flag, and falls back to the legacy static index
otherwise.
While at it, make the legacy single-slot read path use ptp_tx_index
instead of a hardcoded slot 0, and clear the PHY timestamp on E810 after
a read or a timeout so a stale entry cannot block later requests.
Signed-off-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
drivers/net/intel/ice/ice_ethdev.c | 211 ++++++++++++++++++++++++++++-
drivers/net/intel/ice/ice_ethdev.h | 2 +
drivers/net/intel/ice/ice_rxtx.c | 9 +-
3 files changed, 219 insertions(+), 3 deletions(-)
diff --git a/drivers/net/intel/ice/ice_ethdev.c b/drivers/net/intel/ice/ice_ethdev.c
index 76b8ff0a72..485842300d 100644
--- a/drivers/net/intel/ice/ice_ethdev.c
+++ b/drivers/net/intel/ice/ice_ethdev.c
@@ -196,6 +196,14 @@ static int ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
uint32_t flags);
static int ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
struct timespec *timestamp);
+static int ice_timesync_tx_slot_alloc(struct rte_eth_dev *dev,
+ uint32_t *slot_id);
+static int ice_timesync_tx_slot_get_caps(struct rte_eth_dev *dev,
+ struct rte_eth_timesync_tx_slot_caps *caps);
+static int ice_timesync_tx_slot_read(struct rte_eth_dev *dev, uint32_t slot_id,
+ struct rte_eth_timesync_dual_domain_timestamp *timestamp);
+static int ice_timesync_tx_slot_release(struct rte_eth_dev *dev,
+ uint32_t slot_id);
static int ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
static int ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm);
static int ice_timesync_read_time(struct rte_eth_dev *dev,
@@ -340,6 +348,10 @@ static const struct eth_dev_ops ice_eth_dev_ops = {
.timesync_enable = ice_timesync_enable,
.timesync_read_rx_timestamp = ice_timesync_read_rx_timestamp,
.timesync_read_tx_timestamp = ice_timesync_read_tx_timestamp,
+ .timesync_tx_slot_alloc = ice_timesync_tx_slot_alloc,
+ .timesync_tx_slot_get_caps = ice_timesync_tx_slot_get_caps,
+ .timesync_tx_slot_read = ice_timesync_tx_slot_read,
+ .timesync_tx_slot_release = ice_timesync_tx_slot_release,
.timesync_adjust_time = ice_timesync_adjust_time,
.timesync_adjust_freq = ice_timesync_adjust_freq,
.timesync_read_time = ice_timesync_read_time,
@@ -7157,6 +7169,189 @@ static int ice_ptp_write_init(struct ice_hw *hw)
return ice_ptp_init_time(hw, ns, true);
}
+/*
+ * Allocate one Tx timestamp slot from the per-port 64-slot bitmap using CAS.
+ * Returns slot index [0..max-1] or -ENOSPC if all slots are taken.
+ * For E822, max is capped so PFs sharing a quad use non-overlapping ranges.
+ */
+static int
+ice_ptp_alloc_tx_slot(struct ice_adapter *ad)
+{
+ uint64_t old, new_bm, range_mask, free_in_range;
+ uint8_t slot, max_slots, base_slot;
+ bool swapped;
+
+ /*
+ * E822: multiple PFs share one quad's 64 slots.
+ * Divide evenly: each PF occupies (64 / ports_per_quad) slots starting
+ * at (pf_offset_in_quad * slots_per_pf).
+ */
+ if (ad->hw.phy_model == ICE_PHY_E822) {
+ uint8_t ppq = ICE_PORTS_PER_QUAD;
+ uint8_t slots_per_pf = 64 / ppq;
+ uint8_t pf_offset = ad->hw.pf_id % ppq;
+
+ base_slot = pf_offset * slots_per_pf;
+ max_slots = slots_per_pf;
+ } else {
+ /* E810, E830, ETH56G: full 64 slots per PF BAR / per lport */
+ base_slot = 0;
+ max_slots = 64;
+ }
+
+ range_mask = (max_slots == 64) ? UINT64_MAX :
+ (((uint64_t)1 << max_slots) - 1) << base_slot;
+
+ do {
+ old = rte_atomic_load_explicit(&ad->ts_slot_bitmap,
+ rte_memory_order_relaxed);
+ free_in_range = ~old & range_mask;
+ if (free_in_range == 0)
+ return -ENOSPC;
+
+ slot = (uint8_t)rte_ctz64(free_in_range);
+ new_bm = old | RTE_BIT64(slot);
+ swapped = rte_atomic_compare_exchange_weak_explicit(&ad->ts_slot_bitmap,
+ &old, new_bm, rte_memory_order_acquire,
+ rte_memory_order_relaxed);
+ } while (!swapped);
+
+ return slot;
+}
+
+/* Release a Tx timestamp slot back to the bitmap. */
+static void
+ice_ptp_release_tx_slot(struct ice_adapter *ad, uint8_t slot)
+{
+ rte_atomic_fetch_and_explicit(&ad->ts_slot_bitmap, ~RTE_BIT64(slot),
+ rte_memory_order_release);
+}
+
+/* Allocate a Tx timestamp slot from the per-port bitmap for the per-packet slot API. */
+static int
+ice_get_next_tx_desc_idx(struct rte_eth_dev *dev)
+{
+ struct ice_adapter *ad;
+ int slot;
+
+ if (dev == NULL)
+ return -EINVAL;
+
+ ad = ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+ slot = ice_ptp_alloc_tx_slot(ad);
+ if (slot < 0) {
+ PMD_DRV_LOG(DEBUG, "PTP Tx: all 64 timestamp slots busy");
+ return slot;
+ }
+
+ /*
+ * Do NOT update ptp_tx_index: that field belongs to the legacy
+ * single-inflight path and is set statically in ice_ptp_init_info().
+ */
+ return slot;
+}
+
+static int
+ice_ptp_read_tx_dual_timestamp(struct rte_eth_dev *dev, uint8_t slot,
+ struct rte_eth_timesync_dual_domain_timestamp *dual)
+{
+ struct ice_adapter *ad;
+ struct ice_hw *hw;
+ uint64_t tstamp_ready, tstamp, adjusted_ns;
+ int ret;
+
+ if (dev == NULL || dual == NULL)
+ return -EINVAL;
+
+ ad = ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+ hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+ ret = ice_get_phy_tx_tstamp_ready(hw, ad->ptp_tx_block, &tstamp_ready);
+ if (ret)
+ return -EAGAIN;
+
+ if (!(tstamp_ready & RTE_BIT64(slot)))
+ return -EAGAIN;
+
+ ret = ice_read_phy_tstamp(hw, ad->ptp_tx_block, slot, &tstamp);
+ if (ret || tstamp == 0)
+ return -EAGAIN;
+
+ adjusted_ns = ice_tstamp_convert_32b_64b(hw, ad, 1,
+ (tstamp >> 8) & 0xFFFFFFFF);
+
+ dual->adjusted_ns = (int64_t)adjusted_ns;
+ dual->raw_ns = 0;
+ dual->valid_mask = RTE_ETH_TIMESYNC_DUAL_DOMAIN_TIMESTAMP_ADJUSTED_VALID;
+
+ return 0;
+}
+
+static void
+ice_ptp_free_tx_slot(struct rte_eth_dev *dev, uint8_t slot)
+{
+ struct ice_adapter *ad;
+ struct ice_hw *hw;
+
+ if (dev == NULL)
+ return;
+
+ ad = ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+ hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+ if (hw->phy_model == ICE_PHY_E810)
+ (void)ice_clear_phy_tstamp(hw, ad->ptp_tx_block, slot);
+ ice_ptp_release_tx_slot(ad, slot);
+}
+
+static int
+ice_timesync_tx_slot_get_caps(struct rte_eth_dev *dev __rte_unused,
+ struct rte_eth_timesync_tx_slot_caps *caps)
+{
+ caps->type = RTE_ETH_TIMESYNC_TX_SLOT_PER_PACKET;
+ caps->max_slots = 64;
+ return 0;
+}
+
+static int
+ice_timesync_tx_slot_alloc(struct rte_eth_dev *dev,
+ uint32_t *slot_id)
+{
+ int idx;
+
+ if (dev == NULL || slot_id == NULL)
+ return -EINVAL;
+
+ idx = ice_get_next_tx_desc_idx(dev);
+ if (idx < 0)
+ return idx;
+
+ *slot_id = (uint32_t)(uint16_t)idx;
+ return 0;
+}
+
+static int
+ice_timesync_tx_slot_read(struct rte_eth_dev *dev, uint32_t slot_id,
+ struct rte_eth_timesync_dual_domain_timestamp *timestamp)
+{
+ if (dev == NULL || timestamp == NULL || slot_id > 63)
+ return -EINVAL;
+
+ memset(timestamp, 0, sizeof(*timestamp));
+
+ return ice_ptp_read_tx_dual_timestamp(dev, (uint8_t)slot_id, timestamp);
+}
+
+static int
+ice_timesync_tx_slot_release(struct rte_eth_dev *dev, uint32_t slot_id)
+{
+ if (dev == NULL || slot_id > 63)
+ return -EINVAL;
+
+ ice_ptp_free_tx_slot(dev, (uint8_t)slot_id);
+ return 0;
+}
+
static int
ice_timesync_enable(struct rte_eth_dev *dev)
{
@@ -7239,6 +7434,11 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
/* Set the end time with a delay of 10 microseconds */
end_time = rte_get_timer_cycles() + (rte_get_timer_hz() / 100000);
+ /*
+ * ptp_tx_index is a static slot set in ice_ptp_init_info(); it is NOT
+ * allocated via ice_ptp_alloc_tx_slot() so the bitmap must not be
+ * touched.
+ */
do {
ret = ice_get_phy_tx_tstamp_ready(hw, ad->ptp_tx_block, &tstamp_ready);
if (ret) {
@@ -7246,11 +7446,15 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
return -1;
}
- if ((tstamp_ready & BIT_ULL(0)) == 0 && rte_get_timer_cycles() > end_time) {
+ if (!(tstamp_ready & BIT_ULL(ad->ptp_tx_index)) &&
+ rte_get_timer_cycles() > end_time) {
PMD_DRV_LOG(ERR, "Timeout to get phy ready for timestamp");
+ if (hw->phy_model == ICE_PHY_E810)
+ (void)ice_clear_phy_tstamp(hw, ad->ptp_tx_block,
+ ad->ptp_tx_index);
return -1;
}
- } while ((tstamp_ready & BIT_ULL(0)) == 0);
+ } while (!(tstamp_ready & BIT_ULL(ad->ptp_tx_index)));
ret = ice_read_phy_tstamp(hw, ad->ptp_tx_block, ad->ptp_tx_index, &tstamp);
if (ret || tstamp == 0) {
@@ -7261,6 +7465,9 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
ts_ns = ice_tstamp_convert_32b_64b(hw, ad, 1, (tstamp >> 8) & mask);
*timestamp = rte_ns_to_timespec(ts_ns);
+ if (hw->phy_model == ICE_PHY_E810)
+ (void)ice_clear_phy_tstamp(hw, ad->ptp_tx_block, ad->ptp_tx_index);
+
return 0;
}
diff --git a/drivers/net/intel/ice/ice_ethdev.h b/drivers/net/intel/ice/ice_ethdev.h
index 7ee3ea8a70..a5ad9bd504 100644
--- a/drivers/net/intel/ice/ice_ethdev.h
+++ b/drivers/net/intel/ice/ice_ethdev.h
@@ -684,6 +684,8 @@ struct ice_adapter {
/* For PTP */
uint8_t ptp_tx_block;
uint8_t ptp_tx_index;
+ /* Atomic bitmask of in-use Tx timestamp slots (bit N = slot N occupied). */
+ RTE_ATOMIC(uint64_t)ts_slot_bitmap;
bool ptp_ena;
bool txpp_ena; /* For TxPP */
uint64_t time_hw;
diff --git a/drivers/net/intel/ice/ice_rxtx.c b/drivers/net/intel/ice/ice_rxtx.c
index c4b5454c53..c2ce34226a 100644
--- a/drivers/net/intel/ice/ice_rxtx.c
+++ b/drivers/net/intel/ice/ice_rxtx.c
@@ -3041,7 +3041,14 @@ get_context_desc(uint64_t ol_flags, const struct rte_mbuf *tx_pkt,
uint16_t cd_l2tag2 = 0;
uint64_t cd_type_cmd_tso_mss = ICE_TX_DESC_DTYPE_CTX;
uint32_t cd_tunneling_params = 0;
- uint64_t ptp_tx_index = txq->ice_vsi->adapter->ptp_tx_index;
+ uint64_t ptp_tx_index;
+
+ if (ol_flags & rte_eth_timesync_tx_slot_dynflag)
+ ptp_tx_index = *RTE_MBUF_DYNFIELD(tx_pkt,
+ rte_eth_timesync_tx_slot_dynfield_offset,
+ uint32_t *);
+ else
+ ptp_tx_index = txq->ice_vsi->adapter->ptp_tx_index;
if (ice_calc_context_desc(ol_flags) == 0)
return 0;
--
2.55.0
next prev parent reply other threads:[~2026-09-08 7:32 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
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-09-02 5:51 ` [RFC PATCH v4 0/3] ethdev: add Tx timestamp slot APIs Rajesh Kumar
2026-09-02 5:51 ` [RFC PATCH v4 1/3] ethdev: add Tx timestamp slot management APIs Rajesh Kumar
2026-09-02 14:13 ` Stephen Hemminger
2026-09-08 7:25 ` Kumar, Rajesh
2026-09-02 5:51 ` [RFC PATCH v4 2/3] net/ice: support per-packet Tx timestamp slots Rajesh Kumar
2026-09-02 5:51 ` [RFC PATCH v4 3/3] app/testpmd: add Tx timestamp capabilities command Rajesh Kumar
2026-09-08 7:32 ` [RFC PATCH v5 0/5] ethdev: add Tx timestamp slot APIs Rajesh Kumar
2026-09-08 7:32 ` [RFC PATCH v5 1/5] ethdev: add Tx timestamp slot management APIs Rajesh Kumar
2026-09-08 7:32 ` [RFC PATCH v5 2/5] doc: describe ethdev timesync clock and Rx timestamp API Rajesh Kumar
2026-09-08 7:32 ` [RFC PATCH v5 3/5] doc: describe ethdev Tx timestamp slot API Rajesh Kumar
2026-09-08 7:32 ` Rajesh Kumar [this message]
2026-09-08 7:32 ` [RFC PATCH v5 5/5] app/testpmd: add Tx timestamp capabilities command Rajesh Kumar
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
2026-09-02 14:16 ` [RFC PATCH v2 0/1] ethdev: add Tx timestamp slot APIs Stephen Hemminger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260908073206.1236372-5-rajesh3.kumar@intel.com \
--to=rajesh3.kumar@intel.com \
--cc=aman.deep.singh@intel.com \
--cc=andrew.rybchenko@oktetlabs.ru \
--cc=bruce.richardson@intel.com \
--cc=dev@dpdk.org \
--cc=stephen@networkplumber.org \
--cc=thomas@monjalon.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox