* [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support
@ 2026-06-23 21:53 Mark Blasko
2026-06-23 22:06 ` Stephen Hemminger
` (2 more replies)
0 siblings, 3 replies; 15+ messages in thread
From: Mark Blasko @ 2026-06-23 21:53 UTC (permalink / raw)
To: dev, Ciara Loftus, Maryam Tahhan
Cc: Mark Blasko, Joshua Washington, Jasper Tran O'Leary
Enable dynamic RX timestamping in the AF_XDP Poll Mode Driver.
This extracts the ingress timestamp prepended to the packet
headroom by the XDP program and populates it in the mbuf.
Signed-off-by: Mark Blasko <blasko@google.com>
Reviewed-by: Joshua Washington <joshwash@google.com>
Reviewed-by: Jasper Tran O'Leary <jtranoleary@google.com>
---
doc/guides/rel_notes/release_26_07.rst | 5 +++
drivers/net/af_xdp/rte_eth_af_xdp.c | 56 +++++++++++++++++++++++++-
2 files changed, 60 insertions(+), 1 deletion(-)
diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index 6eba91a5e9..727442258f 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -63,6 +63,11 @@ New Features
``rte_eal_init`` and the application is responsible for probing each device,
* ``--auto-probing`` enables the initial bus probing, which is the current default behavior.
+* **Updated AF_XDP ethernet driver.**
+
+ * Added support for dynamic RX metadata and timestamping offload
+ (``RTE_ETH_RX_OFFLOAD_TIMESTAMP``).
+
* **Added LinkData sxe2 ethernet driver.**
Added network driver for the LinkData network adapters.
diff --git a/drivers/net/af_xdp/rte_eth_af_xdp.c b/drivers/net/af_xdp/rte_eth_af_xdp.c
index 2cdb533276..c90e232d57 100644
--- a/drivers/net/af_xdp/rte_eth_af_xdp.c
+++ b/drivers/net/af_xdp/rte_eth_af_xdp.c
@@ -62,6 +62,13 @@
#define PF_XDP AF_XDP
#endif
+struct af_xdp_rx_metadata {
+ uint64_t rx_timestamp;
+};
+
+static int timestamp_dynfield_offset = -1;
+static uint64_t timestamp_dynflag;
+
RTE_LOG_REGISTER_DEFAULT(af_xdp_logtype, NOTICE);
#define RTE_LOGTYPE_NET_AF_XDP af_xdp_logtype
@@ -144,6 +151,7 @@ struct pkt_rx_queue {
struct pollfd fds[1];
int xsk_queue_idx;
int busy_budget;
+ bool rx_timestamp_enabled;
};
struct tx_stats {
@@ -398,6 +406,20 @@ af_xdp_rx_zc(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
rte_pktmbuf_pkt_len(bufs[i]) = len;
rte_pktmbuf_data_len(bufs[i]) = len;
+
+ if (rxq->rx_timestamp_enabled &&
+ timestamp_dynfield_offset >= 0) {
+ struct af_xdp_rx_metadata *meta;
+
+ meta = (struct af_xdp_rx_metadata *)
+ ((char *)rte_pktmbuf_mtod(bufs[i], void *) -
+ sizeof(struct af_xdp_rx_metadata));
+ *RTE_MBUF_DYNFIELD(bufs[i],
+ timestamp_dynfield_offset,
+ uint64_t *) = meta->rx_timestamp;
+ bufs[i]->ol_flags |= timestamp_dynflag;
+ }
+
rx_bytes += len;
}
@@ -457,6 +479,18 @@ af_xdp_rx_cp(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
len = desc->len;
pkt = xsk_umem__get_data(rxq->umem->mz->addr, addr);
+ if (rxq->rx_timestamp_enabled &&
+ timestamp_dynfield_offset >= 0) {
+ struct af_xdp_rx_metadata *meta;
+
+ meta = (struct af_xdp_rx_metadata *)((char *)pkt -
+ sizeof(struct af_xdp_rx_metadata));
+ *RTE_MBUF_DYNFIELD(mbufs[i],
+ timestamp_dynfield_offset,
+ uint64_t *) = meta->rx_timestamp;
+ mbufs[i]->ol_flags |= timestamp_dynflag;
+ }
+
rte_memcpy(rte_pktmbuf_mtod(mbufs[i], void *), pkt, len);
rte_ring_enqueue(umem->buf_ring, (void *)addr);
rte_pktmbuf_pkt_len(mbufs[i]) = len;
@@ -743,6 +777,23 @@ eth_dev_start(struct rte_eth_dev *dev)
{
uint16_t i;
+ if (dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP) {
+ int rc;
+
+ rc = rte_mbuf_dyn_rx_timestamp_register(
+ ×tamp_dynfield_offset,
+ ×tamp_dynflag);
+ if (rc) {
+ AF_XDP_LOG_LINE(ERR,
+ "Failed to register mbuf timestamp field");
+ return rc;
+ }
+ AF_XDP_LOG_LINE(INFO,
+ "Registered mbuf timestamp field, offset: %d",
+ timestamp_dynfield_offset);
+ }
+
dev->data->dev_link.link_status = RTE_ETH_LINK_UP;
for (i = 0; i < dev->data->nb_rx_queues; i++) {
dev->data->rx_queue_state[i] = RTE_ETH_QUEUE_STATE_STARTED;
@@ -870,6 +921,8 @@ eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
dev_info->max_rx_queues = internals->queue_cnt;
dev_info->max_tx_queues = internals->queue_cnt;
+ dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_TIMESTAMP;
+
dev_info->min_mtu = RTE_ETHER_MIN_MTU;
#if defined(XDP_UMEM_UNALIGNED_CHUNK_FLAG)
dev_info->max_rx_pktlen = getpagesize() -
@@ -1873,7 +1926,8 @@ eth_rx_queue_setup(struct rte_eth_dev *dev,
process_private->rxq_xsk_fds[rx_queue_id] = rxq->fds[0].fd;
rxq->port = dev->data->port_id;
-
+ rxq->rx_timestamp_enabled = !!(dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP);
dev->data->rx_queues[rx_queue_id] = rxq;
return 0;
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support
2026-06-23 21:53 [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support Mark Blasko
@ 2026-06-23 22:06 ` Stephen Hemminger
2026-06-29 0:50 ` Mark Blasko
2026-07-10 22:10 ` [PATCH v2 0/2] net/af_xdp: add Rx timestamping and read_clock support Mark Blasko
2026-07-21 12:11 ` [PATCH v3 0/2] net/af_xdp: add Rx timestamping and read_clock support Mark Blasko
2 siblings, 1 reply; 15+ messages in thread
From: Stephen Hemminger @ 2026-06-23 22:06 UTC (permalink / raw)
To: Mark Blasko
Cc: dev, Ciara Loftus, Maryam Tahhan, Joshua Washington,
Jasper Tran O'Leary
On Tue, 23 Jun 2026 21:53:24 +0000
Mark Blasko <blasko@google.com> wrote:
> + if (rxq->rx_timestamp_enabled &&
> + timestamp_dynfield_offset >= 0) {
> + struct af_xdp_rx_metadata *meta;
> +
> + meta = (struct af_xdp_rx_metadata *)
> + ((char *)rte_pktmbuf_mtod(bufs[i], void *) -
> + sizeof(struct af_xdp_rx_metadata));
> + *RTE_MBUF_DYNFIELD(bufs[i],
> + timestamp_dynfield_offset,
> + uint64_t *) = meta->rx_timestamp;
> + bufs[i]->ol_flags |= timestamp_dynflag;
> + }
> +
Why does XDP time stamp need to be different than how other drivers
already do timestamps. See AF_PACKET and TAP device?
Should not be driver specific here.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support
2026-06-23 22:06 ` Stephen Hemminger
@ 2026-06-29 0:50 ` Mark Blasko
2026-06-29 17:38 ` Stephen Hemminger
0 siblings, 1 reply; 15+ messages in thread
From: Mark Blasko @ 2026-06-29 0:50 UTC (permalink / raw)
To: Stephen Hemminger
Cc: dev, Ciara Loftus, Maryam Tahhan, Joshua Washington,
Jasper Tran O'Leary
In AF_PACKET, the PMD reads the timestamp from socket ring headers
because the kernel stack processes the packet and allocates a socket
buffer. Since the kernel stack and socket buffer allocation are bypassed
in AF_XDP, UMEM frames are given directly to the PMD, which
means the kernel never generates these socket headers. Also, it
looks like the TAP PMD doesn't support RX timestamps.
Since the location of metadata in the UMEM headroom is determined
by the XDP program, the current driver implementation is coupled to
a specific XDP program layout. As an alternative, we could just
plumb the entire metadata headroom to the DPDK application and let
the application parse it based on whatever XDP program is in use.
That would couple the application and the XDP program, but would
at least decouple the driver and the XDP program.
On Tue, Jun 23, 2026 at 3:06 PM Stephen Hemminger
<stephen@networkplumber.org> wrote:
>
> On Tue, 23 Jun 2026 21:53:24 +0000
> Mark Blasko <blasko@google.com> wrote:
>
> > + if (rxq->rx_timestamp_enabled &&
> > + timestamp_dynfield_offset >= 0) {
> > + struct af_xdp_rx_metadata *meta;
> > +
> > + meta = (struct af_xdp_rx_metadata *)
> > + ((char *)rte_pktmbuf_mtod(bufs[i], void *) -
> > + sizeof(struct af_xdp_rx_metadata));
> > + *RTE_MBUF_DYNFIELD(bufs[i],
> > + timestamp_dynfield_offset,
> > + uint64_t *) = meta->rx_timestamp;
> > + bufs[i]->ol_flags |= timestamp_dynflag;
> > + }
> > +
>
> Why does XDP time stamp need to be different than how other drivers
> already do timestamps. See AF_PACKET and TAP device?
>
> Should not be driver specific here.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support
2026-06-29 0:50 ` Mark Blasko
@ 2026-06-29 17:38 ` Stephen Hemminger
2026-06-29 19:10 ` Joshua Washington
0 siblings, 1 reply; 15+ messages in thread
From: Stephen Hemminger @ 2026-06-29 17:38 UTC (permalink / raw)
To: Mark Blasko
Cc: dev, Ciara Loftus, Maryam Tahhan, Joshua Washington,
Jasper Tran O'Leary
On Sun, 28 Jun 2026 17:50:03 -0700
Mark Blasko <blasko@google.com> wrote:
> In AF_PACKET, the PMD reads the timestamp from socket ring headers
> because the kernel stack processes the packet and allocates a socket
> buffer. Since the kernel stack and socket buffer allocation are bypassed
> in AF_XDP, UMEM frames are given directly to the PMD, which
> means the kernel never generates these socket headers. Also, it
> looks like the TAP PMD doesn't support RX timestamps.
>
> Since the location of metadata in the UMEM headroom is determined
> by the XDP program, the current driver implementation is coupled to
> a specific XDP program layout. As an alternative, we could just
> plumb the entire metadata headroom to the DPDK application and let
> the application parse it based on whatever XDP program is in use.
> That would couple the application and the XDP program, but would
> at least decouple the driver and the XDP program.
Sorry if I was not clear enough before.
The DPDK PMD provides an abstraction to applications to avoid exposing
as many details as possible. Whenever possible a PMD should follow
precedent and implement functions in a manner similar to other drivers.
The method of expressing received timestamps was never well described
in DPDK documentation. The convention is:
- A dynamic field in mbuf is used for the timestamp.
- All drivers using timestamp should register the same field
using rte_mbuf_dyn_rx_timestamp_register.
- The mbuf field is filled inside the rx_burst processing.
- The timestamp dynamic field is a 64 bit unsigned number rolling
clock value.
- A PMD providing timestamp, must also define a readclock ethdev dev ops
so that application can compute the number of ticks in timestamp per
time interval.
- A PMD providing timestamp must advertise that in offload flags.
- Timestamp should only be inserted if the Rx timestamp offload flag
is set during configuration.
When I read this was a little confused about meta data in mbuf.
Would have been clearer with a simple helper:
static inline void
af_xdp_rx_timestamp(struct rte_mbuf *m)
{
const struct af_xdp_rx_metadata *meta
= rte_pktmbuf_mtod_offset(m, struct af_xdp_rx_metadata, -(int)sizeof(*meta));
*RTE_MBUF_DYNFIELD(m, timestamp_dynfield_offset, uint64_t *) = meta->rx_timestamp;
m->ol_flags |= timestamp_dynflag;
}
If you are going to do Rx timestamp then a simple readclock ethdev op
is also needed to tell the application what the units are.
But there are bigger issues with this patch:
1. The patch assumes metadata is always present in XDP receive but device
used by XDP may not implement it. There is no capability checking.
The DPDK PMD ends up advertising RTE_ETH_RX_OFFLOAD_TIMESTAMP
unconditionally even if underling kernel device doesn't do it.
2. The format of metadata is not a documented contract between kernel
device implementing XDP and the DPDK.
3. The XDP documentation says you need to check for metadata
in each frame.
4. There are no head bounds checks; must check that there is
packet headroom is configured with enough space for metadata.
I am sure AI will find several more things but need fixing
before ready to merge.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support
2026-06-29 17:38 ` Stephen Hemminger
@ 2026-06-29 19:10 ` Joshua Washington
2026-06-29 20:02 ` Stephen Hemminger
2026-06-29 20:03 ` Stephen Hemminger
0 siblings, 2 replies; 15+ messages in thread
From: Joshua Washington @ 2026-06-29 19:10 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Mark Blasko, dev, Ciara Loftus, Maryam Tahhan,
Jasper Tran O'Leary
On Mon, Jun 29, 2026 at 10:38 AM Stephen Hemminger
<stephen@networkplumber.org> wrote:
>
> On Sun, 28 Jun 2026 17:50:03 -0700
> Mark Blasko <blasko@google.com> wrote:
>
> > In AF_PACKET, the PMD reads the timestamp from socket ring headers
> > because the kernel stack processes the packet and allocates a socket
> > buffer. Since the kernel stack and socket buffer allocation are bypassed
> > in AF_XDP, UMEM frames are given directly to the PMD, which
> > means the kernel never generates these socket headers. Also, it
> > looks like the TAP PMD doesn't support RX timestamps.
> >
> > Since the location of metadata in the UMEM headroom is determined
> > by the XDP program, the current driver implementation is coupled to
> > a specific XDP program layout. As an alternative, we could just
> > plumb the entire metadata headroom to the DPDK application and let
> > the application parse it based on whatever XDP program is in use.
> > That would couple the application and the XDP program, but would
> > at least decouple the driver and the XDP program.
>
>
> Sorry if I was not clear enough before.
> The DPDK PMD provides an abstraction to applications to avoid exposing
> as many details as possible. Whenever possible a PMD should follow
> precedent and implement functions in a manner similar to other drivers.
>
> The method of expressing received timestamps was never well described
> in DPDK documentation. The convention is:
> - A dynamic field in mbuf is used for the timestamp.
> - All drivers using timestamp should register the same field
> using rte_mbuf_dyn_rx_timestamp_register.
> - The mbuf field is filled inside the rx_burst processing.
> - The timestamp dynamic field is a 64 bit unsigned number rolling
> clock value.
> - A PMD providing timestamp, must also define a readclock ethdev dev ops
> so that application can compute the number of ticks in timestamp per
> time interval.
> - A PMD providing timestamp must advertise that in offload flags.
> - Timestamp should only be inserted if the Rx timestamp offload flag
> is set during configuration.
These are all very good points for ensuring an airtight abstraction.
However, I'd like to note that neither of the kernel-bound PMD
interfaces (pcap, AF_PACKET) have support for the `read_clock` op. I
am not certain of the reason for this, but I suspect it's because
ethtool IOCTLs already cover that functionality. This patch's
implementation follows the other implementations pretty closely,
differing only in how the timestamp is extracted.
>
> When I read this was a little confused about meta data in mbuf.
> Would have been clearer with a simple helper:
>
> static inline void
> af_xdp_rx_timestamp(struct rte_mbuf *m)
> {
> const struct af_xdp_rx_metadata *meta
> = rte_pktmbuf_mtod_offset(m, struct af_xdp_rx_metadata, -(int)sizeof(*meta));
>
> *RTE_MBUF_DYNFIELD(m, timestamp_dynfield_offset, uint64_t *) = meta->rx_timestamp;
> m->ol_flags |= timestamp_dynflag;
> }
>
> If you are going to do Rx timestamp then a simple readclock ethdev op
> is also needed to tell the application what the units are.
>
> But there are bigger issues with this patch:
> 1. The patch assumes metadata is always present in XDP receive but device
> used by XDP may not implement it. There is no capability checking.
> The DPDK PMD ends up advertising RTE_ETH_RX_OFFLOAD_TIMESTAMP
> unconditionally even if underling kernel device doesn't do it.
The patch as submitted is incorrect; my apologies for that. I missed
it in review. The layout of XDP metadata is something which is
determined by the XDP program and consumed by an AF_XDP application
tailor-made for the XDP program. This means that the PMD itself would
not be able to process the RX timestamp from an XDP program on its
own; that functionality would have to be handled by the DPDK
application.
>
> 2. The format of metadata is not a documented contract between kernel
> device implementing XDP and the DPDK.
This is true. The contract lies between the XDP program and the AF_XDP
application, which can both be controlled by the DPDK application and
its invocation.
>
> 3. The XDP documentation says you need to check for metadata
> in each frame.
>
> 4. There are no head bounds checks; must check that there is
> packet headroom is configured with enough space for metadata.
>
> I am sure AI will find several more things but need fixing
> before ready to merge.
>
Unfortunately, I think that some amount of abstraction leakage is
unavoidable by the very nature of AF_XDP. What I propose is as
follows:
Introduce a new PMD capability for processing XDP metadata. The mbuf
dyn_fields can store a pointer to the metadata and the size of the
metadata. The DPDK application processes the metadata as it sees fit.
As mentioned before, both the reader and the writer of the XDP
metadata are both controllable, and this would add the flexiblity for
one to use pre-existing DPDK mbuf infrastructure with minor
modifications to support this functionality, rather than introducing a
full rewrite using an AF_XDP application.
Would this be acceptable?
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support
2026-06-29 19:10 ` Joshua Washington
@ 2026-06-29 20:02 ` Stephen Hemminger
2026-06-29 20:03 ` Stephen Hemminger
1 sibling, 0 replies; 15+ messages in thread
From: Stephen Hemminger @ 2026-06-29 20:02 UTC (permalink / raw)
To: Joshua Washington
Cc: Mark Blasko, dev, Ciara Loftus, Maryam Tahhan,
Jasper Tran O'Leary
On Mon, 29 Jun 2026 12:10:27 -0700
Joshua Washington <joshwash@google.com> wrote:
> These are all very good points for ensuring an airtight abstraction.
> However, I'd like to note that neither of the kernel-bound PMD
> interfaces (pcap, AF_PACKET) have support for the `read_clock` op. I
> am not certain of the reason for this, but I suspect it's because
> ethtool IOCTLs already cover that functionality. This patch's
> implementation follows the other implementations pretty closely,
> differing only in how the timestamp is extracted.
Yes, that needs to be fixed :-)
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support
2026-06-29 19:10 ` Joshua Washington
2026-06-29 20:02 ` Stephen Hemminger
@ 2026-06-29 20:03 ` Stephen Hemminger
2026-06-30 0:41 ` Joshua Washington
1 sibling, 1 reply; 15+ messages in thread
From: Stephen Hemminger @ 2026-06-29 20:03 UTC (permalink / raw)
To: Joshua Washington
Cc: Mark Blasko, dev, Ciara Loftus, Maryam Tahhan,
Jasper Tran O'Leary
On Mon, 29 Jun 2026 12:10:27 -0700
Joshua Washington <joshwash@google.com> wrote:
> On Mon, Jun 29, 2026 at 10:38 AM Stephen Hemminger
> <stephen@networkplumber.org> wrote:
> >
> > On Sun, 28 Jun 2026 17:50:03 -0700
> > Mark Blasko <blasko@google.com> wrote:
> >
> > > In AF_PACKET, the PMD reads the timestamp from socket ring headers
> > > because the kernel stack processes the packet and allocates a socket
> > > buffer. Since the kernel stack and socket buffer allocation are bypassed
> > > in AF_XDP, UMEM frames are given directly to the PMD, which
> > > means the kernel never generates these socket headers. Also, it
> > > looks like the TAP PMD doesn't support RX timestamps.
> > >
> > > Since the location of metadata in the UMEM headroom is determined
> > > by the XDP program, the current driver implementation is coupled to
> > > a specific XDP program layout. As an alternative, we could just
> > > plumb the entire metadata headroom to the DPDK application and let
> > > the application parse it based on whatever XDP program is in use.
> > > That would couple the application and the XDP program, but would
> > > at least decouple the driver and the XDP program.
> >
> >
> > Sorry if I was not clear enough before.
> > The DPDK PMD provides an abstraction to applications to avoid exposing
> > as many details as possible. Whenever possible a PMD should follow
> > precedent and implement functions in a manner similar to other drivers.
> >
> > The method of expressing received timestamps was never well described
> > in DPDK documentation. The convention is:
> > - A dynamic field in mbuf is used for the timestamp.
> > - All drivers using timestamp should register the same field
> > using rte_mbuf_dyn_rx_timestamp_register.
> > - The mbuf field is filled inside the rx_burst processing.
> > - The timestamp dynamic field is a 64 bit unsigned number rolling
> > clock value.
> > - A PMD providing timestamp, must also define a readclock ethdev dev ops
> > so that application can compute the number of ticks in timestamp per
> > time interval.
> > - A PMD providing timestamp must advertise that in offload flags.
> > - Timestamp should only be inserted if the Rx timestamp offload flag
> > is set during configuration.
>
> These are all very good points for ensuring an airtight abstraction.
> However, I'd like to note that neither of the kernel-bound PMD
> interfaces (pcap, AF_PACKET) have support for the `read_clock` op. I
> am not certain of the reason for this, but I suspect it's because
> ethtool IOCTLs already cover that functionality. This patch's
> implementation follows the other implementations pretty closely,
> differing only in how the timestamp is extracted.
>
> >
> > When I read this was a little confused about meta data in mbuf.
> > Would have been clearer with a simple helper:
> >
> > static inline void
> > af_xdp_rx_timestamp(struct rte_mbuf *m)
> > {
> > const struct af_xdp_rx_metadata *meta
> > = rte_pktmbuf_mtod_offset(m, struct af_xdp_rx_metadata, -(int)sizeof(*meta));
> >
> > *RTE_MBUF_DYNFIELD(m, timestamp_dynfield_offset, uint64_t *) = meta->rx_timestamp;
> > m->ol_flags |= timestamp_dynflag;
> > }
> >
> > If you are going to do Rx timestamp then a simple readclock ethdev op
> > is also needed to tell the application what the units are.
> >
> > But there are bigger issues with this patch:
> > 1. The patch assumes metadata is always present in XDP receive but device
> > used by XDP may not implement it. There is no capability checking.
> > The DPDK PMD ends up advertising RTE_ETH_RX_OFFLOAD_TIMESTAMP
> > unconditionally even if underling kernel device doesn't do it.
>
> The patch as submitted is incorrect; my apologies for that. I missed
> it in review. The layout of XDP metadata is something which is
> determined by the XDP program and consumed by an AF_XDP application
> tailor-made for the XDP program. This means that the PMD itself would
> not be able to process the RX timestamp from an XDP program on its
> own; that functionality would have to be handled by the DPDK
> application.
>
> >
> > 2. The format of metadata is not a documented contract between kernel
> > device implementing XDP and the DPDK.
>
> This is true. The contract lies between the XDP program and the AF_XDP
> application, which can both be controlled by the DPDK application and
> its invocation.
>
> >
> > 3. The XDP documentation says you need to check for metadata
> > in each frame.
> >
> > 4. There are no head bounds checks; must check that there is
> > packet headroom is configured with enough space for metadata.
> >
> > I am sure AI will find several more things but need fixing
> > before ready to merge.
> >
>
> Unfortunately, I think that some amount of abstraction leakage is
> unavoidable by the very nature of AF_XDP. What I propose is as
> follows:
>
> Introduce a new PMD capability for processing XDP metadata. The mbuf
> dyn_fields can store a pointer to the metadata and the size of the
> metadata. The DPDK application processes the metadata as it sees fit.
> As mentioned before, both the reader and the writer of the XDP
> metadata are both controllable, and this would add the flexiblity for
> one to use pre-existing DPDK mbuf infrastructure with minor
> modifications to support this functionality, rather than introducing a
> full rewrite using an AF_XDP application.
>
> Would this be acceptable?
There needs to be an API that XDP PMD can call to check if underlying
driver will add metadata; and another API to check that buffer has
valid timestamp
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support
2026-06-29 20:03 ` Stephen Hemminger
@ 2026-06-30 0:41 ` Joshua Washington
0 siblings, 0 replies; 15+ messages in thread
From: Joshua Washington @ 2026-06-30 0:41 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Mark Blasko, dev, Ciara Loftus, Maryam Tahhan,
Jasper Tran O'Leary
On Mon, Jun 29, 2026 at 1:03 PM Stephen Hemminger
<stephen@networkplumber.org> wrote:
>
> On Mon, 29 Jun 2026 12:10:27 -0700
> Joshua Washington <joshwash@google.com> wrote:
>
> > On Mon, Jun 29, 2026 at 10:38 AM Stephen Hemminger
> > <stephen@networkplumber.org> wrote:
> > >
> > > On Sun, 28 Jun 2026 17:50:03 -0700
> > > Mark Blasko <blasko@google.com> wrote:
> > >
> > > > In AF_PACKET, the PMD reads the timestamp from socket ring headers
> > > > because the kernel stack processes the packet and allocates a socket
> > > > buffer. Since the kernel stack and socket buffer allocation are bypassed
> > > > in AF_XDP, UMEM frames are given directly to the PMD, which
> > > > means the kernel never generates these socket headers. Also, it
> > > > looks like the TAP PMD doesn't support RX timestamps.
> > > >
> > > > Since the location of metadata in the UMEM headroom is determined
> > > > by the XDP program, the current driver implementation is coupled to
> > > > a specific XDP program layout. As an alternative, we could just
> > > > plumb the entire metadata headroom to the DPDK application and let
> > > > the application parse it based on whatever XDP program is in use.
> > > > That would couple the application and the XDP program, but would
> > > > at least decouple the driver and the XDP program.
> > >
> > >
> > > Sorry if I was not clear enough before.
> > > The DPDK PMD provides an abstraction to applications to avoid exposing
> > > as many details as possible. Whenever possible a PMD should follow
> > > precedent and implement functions in a manner similar to other drivers.
> > >
> > > The method of expressing received timestamps was never well described
> > > in DPDK documentation. The convention is:
> > > - A dynamic field in mbuf is used for the timestamp.
> > > - All drivers using timestamp should register the same field
> > > using rte_mbuf_dyn_rx_timestamp_register.
> > > - The mbuf field is filled inside the rx_burst processing.
> > > - The timestamp dynamic field is a 64 bit unsigned number rolling
> > > clock value.
> > > - A PMD providing timestamp, must also define a readclock ethdev dev ops
> > > so that application can compute the number of ticks in timestamp per
> > > time interval.
> > > - A PMD providing timestamp must advertise that in offload flags.
> > > - Timestamp should only be inserted if the Rx timestamp offload flag
> > > is set during configuration.
> >
> > These are all very good points for ensuring an airtight abstraction.
> > However, I'd like to note that neither of the kernel-bound PMD
> > interfaces (pcap, AF_PACKET) have support for the `read_clock` op. I
> > am not certain of the reason for this, but I suspect it's because
> > ethtool IOCTLs already cover that functionality. This patch's
> > implementation follows the other implementations pretty closely,
> > differing only in how the timestamp is extracted.
> >
> > >
> > > When I read this was a little confused about meta data in mbuf.
> > > Would have been clearer with a simple helper:
> > >
> > > static inline void
> > > af_xdp_rx_timestamp(struct rte_mbuf *m)
> > > {
> > > const struct af_xdp_rx_metadata *meta
> > > = rte_pktmbuf_mtod_offset(m, struct af_xdp_rx_metadata, -(int)sizeof(*meta));
> > >
> > > *RTE_MBUF_DYNFIELD(m, timestamp_dynfield_offset, uint64_t *) = meta->rx_timestamp;
> > > m->ol_flags |= timestamp_dynflag;
> > > }
> > >
> > > If you are going to do Rx timestamp then a simple readclock ethdev op
> > > is also needed to tell the application what the units are.
> > >
> > > But there are bigger issues with this patch:
> > > 1. The patch assumes metadata is always present in XDP receive but device
> > > used by XDP may not implement it. There is no capability checking.
> > > The DPDK PMD ends up advertising RTE_ETH_RX_OFFLOAD_TIMESTAMP
> > > unconditionally even if underling kernel device doesn't do it.
> >
> > The patch as submitted is incorrect; my apologies for that. I missed
> > it in review. The layout of XDP metadata is something which is
> > determined by the XDP program and consumed by an AF_XDP application
> > tailor-made for the XDP program. This means that the PMD itself would
> > not be able to process the RX timestamp from an XDP program on its
> > own; that functionality would have to be handled by the DPDK
> > application.
> >
> > >
> > > 2. The format of metadata is not a documented contract between kernel
> > > device implementing XDP and the DPDK.
> >
> > This is true. The contract lies between the XDP program and the AF_XDP
> > application, which can both be controlled by the DPDK application and
> > its invocation.
> >
> > >
> > > 3. The XDP documentation says you need to check for metadata
> > > in each frame.
> > >
> > > 4. There are no head bounds checks; must check that there is
> > > packet headroom is configured with enough space for metadata.
> > >
> > > I am sure AI will find several more things but need fixing
> > > before ready to merge.
> > >
> >
> > Unfortunately, I think that some amount of abstraction leakage is
> > unavoidable by the very nature of AF_XDP. What I propose is as
> > follows:
> >
> > Introduce a new PMD capability for processing XDP metadata. The mbuf
> > dyn_fields can store a pointer to the metadata and the size of the
> > metadata. The DPDK application processes the metadata as it sees fit.
> > As mentioned before, both the reader and the writer of the XDP
> > metadata are both controllable, and this would add the flexiblity for
> > one to use pre-existing DPDK mbuf infrastructure with minor
> > modifications to support this functionality, rather than introducing a
> > full rewrite using an AF_XDP application.
> >
> > Would this be acceptable?
>
> There needs to be an API that XDP PMD can call to check if underlying
> driver will add metadata; and another API to check that buffer has
> valid timestamp
I don't think the API to check if the driver has metadata support
exists, unfortunately. bpf_xdp_adjust_meta() is used to by XDP
programs to adjust the metadata pointer on a packet.
If xdp_md->data_meta > xdp_md->data then metadata isn't supported, and
the adjust call will fail. Otherwise, drivers have up to 256B of
metadata. What this means for the PMD is that it can read data_meta
and data pointers to decide if the metadata is valid on a per-packet
basis, and pass the metadata to the application when appropriate.
We were thinking to implement this via the vdev parameters passed in
as part of invoking the application. This solution would introduce
three new parameters:
1. `xdp_meta_valid_hint_offset` (byte offset of RX validity flag(s))
2. `xdp_meta_rx_ts_valid_mask` (mask to read if the RX timesamp is
valid. Mask covers a single byte.)
3. `xdp_meta_rx_ts_offset` (offset of RX timestamp in XDP metadata. RX
timestamp is assumed to be 8 bytes by kernel convention.)
modeled after xdp_metadata.h in the kernel. This solution would be
quite verbose, so I'm open to suggested improvements. If the valid
mask/hint offset are not passed in the invocation parameters, the
timestamp would be assumed valid if the metadata is valid and has
enough bytes available to feasibly read the timestamp value at the
intended offset.
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 0/2] net/af_xdp: add Rx timestamping and read_clock support
2026-06-23 21:53 [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support Mark Blasko
2026-06-23 22:06 ` Stephen Hemminger
@ 2026-07-10 22:10 ` Mark Blasko
2026-07-10 22:10 ` [PATCH v2 1/2] net/af_xdp: add af_xdp rx metadata and dynamic timestamping support Mark Blasko
2026-07-10 22:10 ` [PATCH v2 2/2] net/af_xdp: add read_clock support to AF_XDP PMD Mark Blasko
2026-07-21 12:11 ` [PATCH v3 0/2] net/af_xdp: add Rx timestamping and read_clock support Mark Blasko
2 siblings, 2 replies; 15+ messages in thread
From: Mark Blasko @ 2026-07-10 22:10 UTC (permalink / raw)
To: dev; +Cc: joshwash, jtranoleary, blasko
This patch series introduces support for dynamic RX timestamping and
clock querying in the AF_XDP Poll Mode Driver.
The first patch introduces three new vdev devargs to specify
layout-agnostic metadata offsets and bitmasks for extracting hardware
RX timestamps from XDP metadata into the mbuf dynamic timestamp field.
The second patch implements the read_clock ethdev operation, querying
ethtool for the interface's PTP Hardware Clock index at start and using
clock_gettime to query the NIC hardware clock time.
---
v2:
- Patch 1:
- Replace static metadata struct assumption with configurable vdev devargs
for layout-agnostic timestamp offset extraction and validity verification.
- Patch 2:
- New patch introduced in v2 to support read_clock ethdev operation.
---
Mark Blasko (2):
net/af_xdp: add af_xdp rx metadata and dynamic timestamping support
net/af_xdp: add read_clock support to AF_XDP PMD
drivers/net/af_xdp/rte_eth_af_xdp.c | 266 +++++++++++++++++++++++++++-
1 file changed, 261 insertions(+), 5 deletions(-)
--
2.55.0.141.g00534a21ce-goog
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 1/2] net/af_xdp: add af_xdp rx metadata and dynamic timestamping support
2026-07-10 22:10 ` [PATCH v2 0/2] net/af_xdp: add Rx timestamping and read_clock support Mark Blasko
@ 2026-07-10 22:10 ` Mark Blasko
2026-07-10 22:10 ` [PATCH v2 2/2] net/af_xdp: add read_clock support to AF_XDP PMD Mark Blasko
1 sibling, 0 replies; 15+ messages in thread
From: Mark Blasko @ 2026-07-10 22:10 UTC (permalink / raw)
To: dev, Ciara Loftus, Maryam Tahhan; +Cc: joshwash, jtranoleary, blasko
Enable dynamic RX timestamping in the AF_XDP Poll Mode Driver using
layout-agnostic metadata offsets and validity verification.
This introduces three new vdev devargs to describe the metadata layout:
1. xdp_meta_rx_ts_offset: byte offset of the RX timestamp in metadata.
2. xdp_meta_valid_hint_offset: byte offset of the validity flag byte.
3. xdp_meta_rx_ts_valid_mask: bitmask to verify timestamp validity.
If validation parameters are configured, the PMD verifies the validity
bit at the target offset on a per-packet basis before copying the
timestamp to the standard EAL dynamic field. Otherwise, the timestamp
is assumed valid if metadata space is available.
Signed-off-by: Mark Blasko <blasko@google.com>
Reviewed-by: Joshua Washington <joshwash@google.com>
---
v2:
- Replace static metadata struct assumption with layout-agnostic devargs
(xdp_meta_rx_ts_offset, xdp_meta_valid_hint_offset, and
xdp_meta_rx_ts_valid_mask).
- Support per-packet timestamp validity verification via metadata hint flag.
- Reject PMD probe if validity mask is zero when validity offset is set.
---
drivers/net/af_xdp/rte_eth_af_xdp.c | 139 +++++++++++++++++++++++++++-
1 file changed, 134 insertions(+), 5 deletions(-)
diff --git a/drivers/net/af_xdp/rte_eth_af_xdp.c b/drivers/net/af_xdp/rte_eth_af_xdp.c
index 2cdb533276..2f9ad7d180 100644
--- a/drivers/net/af_xdp/rte_eth_af_xdp.c
+++ b/drivers/net/af_xdp/rte_eth_af_xdp.c
@@ -62,6 +62,10 @@
#define PF_XDP AF_XDP
#endif
+
+static int timestamp_dynfield_offset = -1;
+static uint64_t timestamp_dynflag;
+
RTE_LOG_REGISTER_DEFAULT(af_xdp_logtype, NOTICE);
#define RTE_LOGTYPE_NET_AF_XDP af_xdp_logtype
@@ -144,6 +148,10 @@ struct pkt_rx_queue {
struct pollfd fds[1];
int xsk_queue_idx;
int busy_budget;
+ bool rx_timestamp_enabled;
+ int rx_timestamp_offset;
+ int rx_timestamp_valid_offset;
+ uint8_t rx_timestamp_valid_mask;
};
struct tx_stats {
@@ -183,6 +191,9 @@ struct pmd_internals {
struct pkt_rx_queue *rx_queues;
struct pkt_tx_queue *tx_queues;
+ int rx_timestamp_offset;
+ int rx_timestamp_valid_offset;
+ uint8_t rx_timestamp_valid_mask;
};
struct pmd_process_private {
@@ -200,6 +211,9 @@ struct pmd_process_private {
#define ETH_AF_XDP_USE_PINNED_MAP_ARG "use_pinned_map"
#define ETH_AF_XDP_DP_PATH_ARG "dp_path"
#define ETH_AF_XDP_MODE_ARG "mode"
+#define ETH_AF_XDP_RX_TIMESTAMP_OFFSET_ARG "xdp_meta_rx_ts_offset"
+#define ETH_AF_XDP_RX_TIMESTAMP_VALID_OFFSET_ARG "xdp_meta_valid_hint_offset"
+#define ETH_AF_XDP_RX_TIMESTAMP_VALID_MASK_ARG "xdp_meta_rx_ts_valid_mask"
/* Define different modes for af_xdp prog to attach */
#define ETH_AF_XDP_DRV_MODE_ARG "drv"
@@ -232,6 +246,9 @@ static const char * const valid_arguments[] = {
ETH_AF_XDP_USE_PINNED_MAP_ARG,
ETH_AF_XDP_DP_PATH_ARG,
ETH_AF_XDP_MODE_ARG,
+ ETH_AF_XDP_RX_TIMESTAMP_OFFSET_ARG,
+ ETH_AF_XDP_RX_TIMESTAMP_VALID_OFFSET_ARG,
+ ETH_AF_XDP_RX_TIMESTAMP_VALID_MASK_ARG,
NULL
};
@@ -398,6 +415,27 @@ af_xdp_rx_zc(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
rte_pktmbuf_pkt_len(bufs[i]) = len;
rte_pktmbuf_data_len(bufs[i]) = len;
+
+ if (rxq->rx_timestamp_enabled &&
+ timestamp_dynfield_offset >= 0) {
+ /*
+ * Copy timestamp if validity offset is not defined or
+ * flag is valid.
+ */
+ if (rxq->rx_timestamp_valid_offset < 0 ||
+ (*(uint8_t *)((char *)rte_pktmbuf_mtod(bufs[i], void *) -
+ rxq->rx_timestamp_valid_offset) &
+ rxq->rx_timestamp_valid_mask)) {
+ uint64_t *ts_ptr = (uint64_t *)
+ ((char *)rte_pktmbuf_mtod(bufs[i], void *) -
+ rxq->rx_timestamp_offset);
+ *RTE_MBUF_DYNFIELD(bufs[i],
+ timestamp_dynfield_offset,
+ uint64_t *) = *ts_ptr;
+ bufs[i]->ol_flags |= timestamp_dynflag;
+ }
+ }
+
rx_bytes += len;
}
@@ -457,6 +495,25 @@ af_xdp_rx_cp(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
len = desc->len;
pkt = xsk_umem__get_data(rxq->umem->mz->addr, addr);
+ if (rxq->rx_timestamp_enabled &&
+ timestamp_dynfield_offset >= 0) {
+ /*
+ * Copy timestamp if validity offset is not defined or
+ * flag is valid.
+ */
+ if (rxq->rx_timestamp_valid_offset < 0 ||
+ (*(uint8_t *)((char *)pkt -
+ rxq->rx_timestamp_valid_offset) &
+ rxq->rx_timestamp_valid_mask)) {
+ uint64_t *ts_ptr = (uint64_t *)((char *)pkt -
+ rxq->rx_timestamp_offset);
+ *RTE_MBUF_DYNFIELD(mbufs[i],
+ timestamp_dynfield_offset,
+ uint64_t *) = *ts_ptr;
+ mbufs[i]->ol_flags |= timestamp_dynflag;
+ }
+ }
+
rte_memcpy(rte_pktmbuf_mtod(mbufs[i], void *), pkt, len);
rte_ring_enqueue(umem->buf_ring, (void *)addr);
rte_pktmbuf_pkt_len(mbufs[i]) = len;
@@ -743,6 +800,22 @@ eth_dev_start(struct rte_eth_dev *dev)
{
uint16_t i;
+ if (dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP) {
+ int rc;
+
+ rc = rte_mbuf_dyn_rx_timestamp_register(×tamp_dynfield_offset,
+ ×tamp_dynflag);
+ if (rc) {
+ AF_XDP_LOG_LINE(ERR,
+ "Failed to register mbuf timestamp field");
+ return rc;
+ }
+ AF_XDP_LOG_LINE(INFO,
+ "Registered mbuf timestamp field, offset: %d",
+ timestamp_dynfield_offset);
+ }
+
dev->data->dev_link.link_status = RTE_ETH_LINK_UP;
for (i = 0; i < dev->data->nb_rx_queues; i++) {
dev->data->rx_queue_state[i] = RTE_ETH_QUEUE_STATE_STARTED;
@@ -870,6 +943,8 @@ eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
dev_info->max_rx_queues = internals->queue_cnt;
dev_info->max_tx_queues = internals->queue_cnt;
+ dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_TIMESTAMP;
+
dev_info->min_mtu = RTE_ETHER_MIN_MTU;
#if defined(XDP_UMEM_UNALIGNED_CHUNK_FLAG)
dev_info->max_rx_pktlen = getpagesize() -
@@ -1873,7 +1948,12 @@ eth_rx_queue_setup(struct rte_eth_dev *dev,
process_private->rxq_xsk_fds[rx_queue_id] = rxq->fds[0].fd;
rxq->port = dev->data->port_id;
-
+ rxq->rx_timestamp_offset = internals->rx_timestamp_offset;
+ rxq->rx_timestamp_valid_offset = internals->rx_timestamp_valid_offset;
+ rxq->rx_timestamp_valid_mask = internals->rx_timestamp_valid_mask;
+ rxq->rx_timestamp_enabled = (rxq->rx_timestamp_offset >= 0) &&
+ !!(dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP);
dev->data->rx_queues[rx_queue_id] = rxq;
return 0;
@@ -2032,6 +2112,22 @@ parse_integer_arg(const char *key __rte_unused,
return 0;
}
+static int
+parse_mask_arg(const char *key __rte_unused,
+ const char *value, void *extra_args)
+{
+ int *i = (int *)extra_args;
+ char *end;
+
+ *i = strtol(value, &end, 0);
+ if (*i < 0) {
+ AF_XDP_LOG_LINE(ERR, "Argument has to be positive.");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/** parse name argument */
static int
parse_name_arg(const char *key __rte_unused,
@@ -2146,7 +2242,9 @@ static int
parse_parameters(struct rte_kvargs *kvlist, char *if_name, int *start_queue,
int *queue_cnt, int *shared_umem, char *prog_path,
int *busy_budget, int *force_copy, int *use_cni,
- int *use_pinned_map, char *dp_path, uint32_t *xdp_mode)
+ int *use_pinned_map, char *dp_path, uint32_t *xdp_mode,
+ int *rx_timestamp_offset, int *rx_timestamp_valid_offset,
+ int *rx_timestamp_valid_mask)
{
int ret;
@@ -2209,6 +2307,21 @@ parse_parameters(struct rte_kvargs *kvlist, char *if_name, int *start_queue,
if (ret < 0)
goto free_kvlist;
+ ret = rte_kvargs_process(kvlist, ETH_AF_XDP_RX_TIMESTAMP_OFFSET_ARG,
+ &parse_integer_arg, rx_timestamp_offset);
+ if (ret < 0)
+ goto free_kvlist;
+
+ ret = rte_kvargs_process(kvlist, ETH_AF_XDP_RX_TIMESTAMP_VALID_OFFSET_ARG,
+ &parse_integer_arg, rx_timestamp_valid_offset);
+ if (ret < 0)
+ goto free_kvlist;
+
+ ret = rte_kvargs_process(kvlist, ETH_AF_XDP_RX_TIMESTAMP_VALID_MASK_ARG,
+ &parse_mask_arg, rx_timestamp_valid_mask);
+ if (ret < 0)
+ goto free_kvlist;
+
free_kvlist:
rte_kvargs_free(kvlist);
return ret;
@@ -2248,7 +2361,9 @@ static struct rte_eth_dev *
init_internals(struct rte_vdev_device *dev, const char *if_name,
int start_queue_idx, int queue_cnt, int shared_umem,
const char *prog_path, int busy_budget, int force_copy,
- int use_cni, int use_pinned_map, const char *dp_path, uint32_t xdp_mode)
+ int use_cni, int use_pinned_map, const char *dp_path, uint32_t xdp_mode,
+ int rx_timestamp_offset, int rx_timestamp_valid_offset,
+ int rx_timestamp_valid_mask)
{
const char *name = rte_vdev_device_name(dev);
const unsigned int numa_node = dev->device.numa_node;
@@ -2281,6 +2396,9 @@ init_internals(struct rte_vdev_device *dev, const char *if_name,
internals->use_pinned_map = use_pinned_map;
internals->mode_flag = XDP_FLAGS_UPDATE_IF_NOEXIST | xdp_mode;
strlcpy(internals->dp_path, dp_path, PATH_MAX);
+ internals->rx_timestamp_offset = rx_timestamp_offset;
+ internals->rx_timestamp_valid_offset = rx_timestamp_valid_offset;
+ internals->rx_timestamp_valid_mask = (uint8_t)rx_timestamp_valid_mask;
if (xdp_get_channels_info(if_name, &internals->max_queue_cnt,
&internals->configured_queue_cnt)) {
@@ -2474,6 +2592,9 @@ rte_pmd_af_xdp_probe(struct rte_vdev_device *dev)
int use_pinned_map = 0;
uint32_t xdp_mode = 0;
char dp_path[PATH_MAX] = {'\0'};
+ int rx_timestamp_offset = -1;
+ int rx_timestamp_valid_offset = -1;
+ int rx_timestamp_valid_mask = 0;
struct rte_eth_dev *eth_dev = NULL;
const char *name = rte_vdev_device_name(dev);
@@ -2517,11 +2638,18 @@ rte_pmd_af_xdp_probe(struct rte_vdev_device *dev)
if (parse_parameters(kvlist, if_name, &xsk_start_queue_idx,
&xsk_queue_cnt, &shared_umem, prog_path,
&busy_budget, &force_copy, &use_cni, &use_pinned_map,
- dp_path, &xdp_mode) < 0) {
+ dp_path, &xdp_mode, &rx_timestamp_offset,
+ &rx_timestamp_valid_offset, &rx_timestamp_valid_mask) < 0) {
AF_XDP_LOG_LINE(ERR, "Invalid kvargs value");
return -EINVAL;
}
+ if (rx_timestamp_valid_offset >= 0 && rx_timestamp_valid_mask == 0) {
+ AF_XDP_LOG_LINE(ERR,
+ "Validity mask cannot be zero when validity offset is configured");
+ return -EINVAL;
+ }
+
if (use_cni && use_pinned_map) {
AF_XDP_LOG_LINE(ERR, "When '%s' parameter is used, '%s' parameter is not valid",
ETH_AF_XDP_USE_CNI_ARG, ETH_AF_XDP_USE_PINNED_MAP_ARG);
@@ -2585,7 +2713,8 @@ rte_pmd_af_xdp_probe(struct rte_vdev_device *dev)
eth_dev = init_internals(dev, if_name, xsk_start_queue_idx,
xsk_queue_cnt, shared_umem, prog_path,
busy_budget, force_copy, use_cni, use_pinned_map,
- dp_path, xdp_mode);
+ dp_path, xdp_mode, rx_timestamp_offset,
+ rx_timestamp_valid_offset, rx_timestamp_valid_mask);
if (eth_dev == NULL) {
AF_XDP_LOG_LINE(ERR, "Failed to init internals");
return -1;
--
2.55.0.141.g00534a21ce-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 2/2] net/af_xdp: add read_clock support to AF_XDP PMD
2026-07-10 22:10 ` [PATCH v2 0/2] net/af_xdp: add Rx timestamping and read_clock support Mark Blasko
2026-07-10 22:10 ` [PATCH v2 1/2] net/af_xdp: add af_xdp rx metadata and dynamic timestamping support Mark Blasko
@ 2026-07-10 22:10 ` Mark Blasko
1 sibling, 0 replies; 15+ messages in thread
From: Mark Blasko @ 2026-07-10 22:10 UTC (permalink / raw)
To: dev, Ciara Loftus, Maryam Tahhan; +Cc: joshwash, jtranoleary, blasko
Implement the ethdev read_clock operation in the AF_XDP Poll Mode
Driver. This allows DPDK applications to query the current time of the
NIC hardware clock.
At device start, the PMD queries ethtool for the interface's PTP
Hardware Clock index and opens the corresponding PTP device node. The
read_clock operation queries the current time via clock_gettime.
Signed-off-by: Mark Blasko <blasko@google.com>
Reviewed-by: Joshua Washington <joshwash@google.com>
---
v2:
- New patch introduced in v2 to support read_clock ethdev operation.
---
drivers/net/af_xdp/rte_eth_af_xdp.c | 127 ++++++++++++++++++++++++++++
1 file changed, 127 insertions(+)
diff --git a/drivers/net/af_xdp/rte_eth_af_xdp.c b/drivers/net/af_xdp/rte_eth_af_xdp.c
index 2f9ad7d180..410be570a0 100644
--- a/drivers/net/af_xdp/rte_eth_af_xdp.c
+++ b/drivers/net/af_xdp/rte_eth_af_xdp.c
@@ -15,6 +15,7 @@
#include <linux/if_link.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>
+#include <linux/net_tstamp.h>
#include "af_xdp_deps.h"
#include <rte_ethdev.h>
@@ -28,6 +29,7 @@
#include <dev_driver.h>
#include <rte_eal.h>
#include <rte_ether.h>
+#include <rte_time.h>
#include <rte_lcore.h>
#include <rte_log.h>
#include <rte_memory.h>
@@ -62,6 +64,9 @@
#define PF_XDP AF_XDP
#endif
+#include <fcntl.h>
+#include <time.h>
+
static int timestamp_dynfield_offset = -1;
static uint64_t timestamp_dynflag;
@@ -194,6 +199,7 @@ struct pmd_internals {
int rx_timestamp_offset;
int rx_timestamp_valid_offset;
uint8_t rx_timestamp_valid_mask;
+ int ptp_fd;
};
struct pmd_process_private {
@@ -795,9 +801,72 @@ eth_af_xdp_tx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
#endif
}
+static int
+eth_af_xdp_enable_hw_timestamping(const char *if_name)
+{
+ struct hwtstamp_config config = {0};
+ struct ifreq ifr = {0};
+ int fd, ret;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return -1;
+
+ ifr.ifr_data = (caddr_t)&config;
+ strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+ ret = ioctl(fd, SIOCGHWTSTAMP, &ifr);
+ if (ret == 0) {
+ if (config.rx_filter != HWTSTAMP_FILTER_NONE) {
+ close(fd);
+ return 0;
+ }
+ }
+
+ config.flags = 0;
+ config.tx_type = HWTSTAMP_TX_OFF;
+ config.rx_filter = HWTSTAMP_FILTER_ALL;
+
+ ret = ioctl(fd, SIOCSHWTSTAMP, &ifr);
+ close(fd);
+
+ if (ret < 0)
+ return -errno;
+
+ if (config.rx_filter == HWTSTAMP_FILTER_NONE)
+ return -ENOTSUP;
+
+ return 0;
+}
+
+static int
+eth_af_xdp_get_ptp_index(const char *if_name)
+{
+ struct ethtool_ts_info info = {0};
+ struct ifreq ifr = {0};
+ int fd, ret;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return -1;
+
+ ifr.ifr_data = (caddr_t)&info;
+ info.cmd = ETHTOOL_GET_TS_INFO;
+ strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+ ret = ioctl(fd, SIOCETHTOOL, &ifr);
+ close(fd);
+
+ if (ret < 0)
+ return -1;
+
+ return info.phc_index;
+}
+
static int
eth_dev_start(struct rte_eth_dev *dev)
{
+ struct pmd_internals *internals = dev->data->dev_private;
uint16_t i;
if (dev->data->dev_conf.rxmode.offloads &
@@ -814,6 +883,25 @@ eth_dev_start(struct rte_eth_dev *dev)
AF_XDP_LOG_LINE(INFO,
"Registered mbuf timestamp field, offset: %d",
timestamp_dynfield_offset);
+
+ rc = eth_af_xdp_enable_hw_timestamping(internals->if_name);
+ if (rc < 0) {
+ AF_XDP_LOG_LINE(WARNING,
+ "Could not enable HW timestamping on %s: %s",
+ internals->if_name, strerror(-rc));
+ }
+
+ int phc_index = eth_af_xdp_get_ptp_index(internals->if_name);
+ if (phc_index >= 0) {
+ char ptp_dev[32];
+ snprintf(ptp_dev, sizeof(ptp_dev), "/dev/ptp%d", phc_index);
+ internals->ptp_fd = open(ptp_dev, O_RDONLY);
+ if (internals->ptp_fd >= 0) {
+ AF_XDP_LOG_LINE(INFO,
+ "Opened PTP device %s for read_clock",
+ ptp_dev);
+ }
+ }
}
dev->data->dev_link.link_status = RTE_ETH_LINK_UP;
@@ -829,6 +917,7 @@ eth_dev_start(struct rte_eth_dev *dev)
static int
eth_dev_stop(struct rte_eth_dev *dev)
{
+ struct pmd_internals *internals = dev->data->dev_private;
uint16_t i;
dev->data->dev_link.link_status = RTE_ETH_LINK_DOWN;
@@ -837,6 +926,11 @@ eth_dev_stop(struct rte_eth_dev *dev)
dev->data->tx_queue_state[i] = RTE_ETH_QUEUE_STATE_STOPPED;
}
+ if (internals->ptp_fd >= 0) {
+ close(internals->ptp_fd);
+ internals->ptp_fd = -1;
+ }
+
return 0;
}
@@ -1170,6 +1264,11 @@ eth_dev_close(struct rte_eth_dev *dev)
}
}
+ if (internals->ptp_fd >= 0) {
+ close(internals->ptp_fd);
+ internals->ptp_fd = -1;
+ }
+
out:
rte_free(dev->process_private);
@@ -2039,6 +2138,31 @@ eth_dev_promiscuous_disable(struct rte_eth_dev *dev)
return eth_dev_change_flags(internals->if_name, 0, ~IFF_PROMISC);
}
+/*
+ * In Linux, dynamic POSIX clock IDs from file descriptors (such as /dev/ptpX)
+ * are encoded with CLOCKFD (3) in the lower 3 bits and ~fd in the upper bits.
+ * As this is not defined in user-space UAPI headers, define the macro here.
+ */
+#define CLOCKFD 3
+#define FD_TO_CLOCKID(fd) ((clockid_t)(~(unsigned int)(fd) << 3 | CLOCKFD))
+
+static int
+eth_af_xdp_read_clock(struct rte_eth_dev *dev, uint64_t *timestamp)
+{
+ struct pmd_internals *internals = dev->data->dev_private;
+ struct timespec ts;
+
+ if (internals->ptp_fd < 0)
+ return -ENOTSUP;
+
+ clockid_t clkid = FD_TO_CLOCKID(internals->ptp_fd);
+ if (clock_gettime(clkid, &ts) < 0)
+ return -1;
+
+ *timestamp = rte_timespec_to_ns(&ts);
+ return 0;
+}
+
static const struct eth_dev_ops ops = {
.dev_start = eth_dev_start,
.dev_stop = eth_dev_stop,
@@ -2054,6 +2178,7 @@ static const struct eth_dev_ops ops = {
.stats_get = eth_stats_get,
.stats_reset = eth_stats_reset,
.get_monitor_addr = eth_get_monitor_addr,
+ .read_clock = eth_af_xdp_read_clock,
};
/* AF_XDP Device Plugin option works in unprivileged
@@ -2075,6 +2200,7 @@ static const struct eth_dev_ops ops_afxdp_dp = {
.stats_get = eth_stats_get,
.stats_reset = eth_stats_reset,
.get_monitor_addr = eth_get_monitor_addr,
+ .read_clock = eth_af_xdp_read_clock,
};
/** parse busy_budget argument */
@@ -2399,6 +2525,7 @@ init_internals(struct rte_vdev_device *dev, const char *if_name,
internals->rx_timestamp_offset = rx_timestamp_offset;
internals->rx_timestamp_valid_offset = rx_timestamp_valid_offset;
internals->rx_timestamp_valid_mask = (uint8_t)rx_timestamp_valid_mask;
+ internals->ptp_fd = -1;
if (xdp_get_channels_info(if_name, &internals->max_queue_cnt,
&internals->configured_queue_cnt)) {
--
2.55.0.141.g00534a21ce-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v3 0/2] net/af_xdp: add Rx timestamping and read_clock support
2026-06-23 21:53 [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support Mark Blasko
2026-06-23 22:06 ` Stephen Hemminger
2026-07-10 22:10 ` [PATCH v2 0/2] net/af_xdp: add Rx timestamping and read_clock support Mark Blasko
@ 2026-07-21 12:11 ` Mark Blasko
2026-07-21 12:11 ` [PATCH v3 1/2] net/af_xdp: add af_xdp rx metadata and dynamic timestamping support Mark Blasko
2026-07-21 12:11 ` [PATCH v3 2/2] net/af_xdp: add read_clock support to AF_XDP PMD Mark Blasko
2 siblings, 2 replies; 15+ messages in thread
From: Mark Blasko @ 2026-07-21 12:11 UTC (permalink / raw)
To: dev; +Cc: ciara.loftus, mtahhan, joshwash, jtranoleary, blasko
This patch series introduces support for dynamic RX timestamping and
clock querying in the AF_XDP Poll Mode Driver.
The first patch introduces three new vdev devargs to specify
layout-agnostic metadata offsets and bitmasks for extracting hardware
RX timestamps from XDP metadata into the mbuf dynamic timestamp field.
The second patch implements the read_clock ethdev operation, querying
ethtool for the interface's PTP Hardware Clock index at start and using
clock_gettime to query the NIC hardware clock time.
---
v3:
- Patch 1:
- Add PMD documentation and release notes entry.
- Move dynamic mbuf timestamp field registration to eth_dev_start().
- Replace 64-bit pointer casting with memcpy.
- Add devargs validation and support auto-base integer parsing.
- Patch 2:
- Add documentation and release notes entry.
- Add PTP file descriptor cleanup on device start, stop, and close.
- Return -errno on clock_gettime() failure and fix PTP open error logging.
v2:
- Patch 1:
- Replace static metadata struct assumption with configurable vdev devargs
for layout-agnostic timestamp offset extraction and validity verification.
- Patch 2:
- New patch introduced in v2 to support read_clock ethdev operation.
---
Mark Blasko (2):
net/af_xdp: add af_xdp rx metadata and dynamic timestamping support
net/af_xdp: add read_clock support to AF_XDP PMD
doc/guides/nics/af_xdp.rst | 42 ++++
doc/guides/rel_notes/release_26_07.rst | 7 +
drivers/net/af_xdp/rte_eth_af_xdp.c | 279 ++++++++++++++++++++++++-
3 files changed, 322 insertions(+), 6 deletions(-)
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 1/2] net/af_xdp: add af_xdp rx metadata and dynamic timestamping support
2026-07-21 12:11 ` [PATCH v3 0/2] net/af_xdp: add Rx timestamping and read_clock support Mark Blasko
@ 2026-07-21 12:11 ` Mark Blasko
2026-07-26 17:01 ` Stephen Hemminger
2026-07-21 12:11 ` [PATCH v3 2/2] net/af_xdp: add read_clock support to AF_XDP PMD Mark Blasko
1 sibling, 1 reply; 15+ messages in thread
From: Mark Blasko @ 2026-07-21 12:11 UTC (permalink / raw)
To: dev, Ciara Loftus, Maryam Tahhan; +Cc: joshwash, jtranoleary, blasko
Enable dynamic RX timestamping in the AF_XDP Poll Mode Driver using
layout-agnostic metadata offsets and validity verification.
This introduces three new vdev devargs to describe the metadata layout:
1. xdp_meta_rx_ts_offset: byte offset of the RX timestamp in metadata.
2. xdp_meta_valid_hint_offset: byte offset of the validity flag byte.
3. xdp_meta_rx_ts_valid_mask: bitmask to verify timestamp validity.
If validation parameters are configured, the PMD verifies the validity
bit at the target offset on a per-packet basis before copying the
timestamp to the standard EAL dynamic field. Otherwise, the timestamp
is assumed valid if metadata space is available.
Signed-off-by: Mark Blasko <blasko@google.com>
Reviewed-by: Joshua Washington <joshwash@google.com>
---
v3:
- Add PMD documentation in doc/guides/nics/af_xdp.rst.
- Add release notes entry in doc/guides/rel_notes/release_26_07.rst.
- Move dynamic mbuf timestamp field registration to eth_dev_start(),
conditioned on RTE_ETH_RX_OFFLOAD_TIMESTAMP.
- Replace 64-bit pointer casting with memcpy to fix ARM unaligned access
faults.
- Add devargs validation (validity mask <= 255) and support base 0
integer parsing.
v2:
- Replace static metadata struct assumption with layout-agnostic devargs
(xdp_meta_rx_ts_offset, xdp_meta_valid_hint_offset, and
xdp_meta_rx_ts_valid_mask).
- Support per-packet timestamp validity verification via metadata hint
flag.
- Reject PMD probe if validity mask is zero when validity offset
is set.
---
doc/guides/nics/af_xdp.rst | 30 ++++
doc/guides/rel_notes/release_26_07.rst | 6 +
drivers/net/af_xdp/rte_eth_af_xdp.c | 192 ++++++++++++++++++++++++-
3 files changed, 222 insertions(+), 6 deletions(-)
diff --git a/doc/guides/nics/af_xdp.rst b/doc/guides/nics/af_xdp.rst
index 8bd17f04ae..367a1b0507 100644
--- a/doc/guides/nics/af_xdp.rst
+++ b/doc/guides/nics/af_xdp.rst
@@ -212,6 +212,36 @@ the AF_XDP PMD configures it internally to the `AF_XDP Device Plugin for Kuberne
--vdev=net_af_xdp0,use_pinned_map=1,dp_path="/tmp/afxdp_dp/<<interface name>>/xsks_map"
+xdp_meta_rx_ts_offset
+~~~~~~~~~~~~~~~~~~~~~
+
+The ``xdp_meta_rx_ts_offset`` argument specifies the byte offset of the 64-bit RX
+timestamp within the XDP metadata headroom area (prepended before packet data).
+
+.. code-block:: console
+
+ --vdev net_af_xdp,iface=ens786f1,xdp_meta_rx_ts_offset=8
+
+xdp_meta_valid_hint_offset
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``xdp_meta_valid_hint_offset`` argument specifies the byte offset of the validity
+flag byte within the XDP metadata headroom area.
+
+.. code-block:: console
+
+ --vdev net_af_xdp,iface=ens786f1,xdp_meta_rx_ts_offset=8,xdp_meta_valid_hint_offset=4
+
+xdp_meta_rx_ts_valid_mask
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``xdp_meta_rx_ts_valid_mask`` argument specifies the bitmask (in hex or decimal) used
+to verify timestamp validity at ``xdp_meta_valid_hint_offset``.
+
+.. code-block:: console
+
+ --vdev net_af_xdp,iface=ens786f1,xdp_meta_rx_ts_offset=8,xdp_meta_valid_hint_offset=4,xdp_meta_rx_ts_valid_mask=0x1
+
Limitations
-----------
diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index 8b1bdada1a..8af0b8d615 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -135,6 +135,12 @@ New Features
Added network driver for the LinkData network adapters.
+* **Updated AF_XDP PMD.**
+
+ * Added support for RX metadata hardware timestamping via vdev devargs
+ ``xdp_meta_rx_ts_offset``, ``xdp_meta_valid_hint_offset``, and
+ ``xdp_meta_rx_ts_valid_mask``.
+
* **Updated Google gve driver.**
* Added hardware timestamping support on DQO queues.
diff --git a/drivers/net/af_xdp/rte_eth_af_xdp.c b/drivers/net/af_xdp/rte_eth_af_xdp.c
index 2cdb533276..e047870530 100644
--- a/drivers/net/af_xdp/rte_eth_af_xdp.c
+++ b/drivers/net/af_xdp/rte_eth_af_xdp.c
@@ -15,6 +15,7 @@
#include <linux/if_link.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>
+#include <linux/net_tstamp.h>
#include "af_xdp_deps.h"
#include <rte_ethdev.h>
@@ -62,6 +63,9 @@
#define PF_XDP AF_XDP
#endif
+static int timestamp_dynfield_offset = -1;
+static uint64_t timestamp_dynflag;
+
RTE_LOG_REGISTER_DEFAULT(af_xdp_logtype, NOTICE);
#define RTE_LOGTYPE_NET_AF_XDP af_xdp_logtype
@@ -144,6 +148,10 @@ struct pkt_rx_queue {
struct pollfd fds[1];
int xsk_queue_idx;
int busy_budget;
+ bool rx_timestamp_enabled;
+ int rx_timestamp_offset;
+ int rx_timestamp_valid_offset;
+ uint8_t rx_timestamp_valid_mask;
};
struct tx_stats {
@@ -183,6 +191,9 @@ struct pmd_internals {
struct pkt_rx_queue *rx_queues;
struct pkt_tx_queue *tx_queues;
+ int rx_timestamp_offset;
+ int rx_timestamp_valid_offset;
+ uint8_t rx_timestamp_valid_mask;
};
struct pmd_process_private {
@@ -200,6 +211,9 @@ struct pmd_process_private {
#define ETH_AF_XDP_USE_PINNED_MAP_ARG "use_pinned_map"
#define ETH_AF_XDP_DP_PATH_ARG "dp_path"
#define ETH_AF_XDP_MODE_ARG "mode"
+#define ETH_AF_XDP_RX_TIMESTAMP_OFFSET_ARG "xdp_meta_rx_ts_offset"
+#define ETH_AF_XDP_RX_TIMESTAMP_VALID_OFFSET_ARG "xdp_meta_valid_hint_offset"
+#define ETH_AF_XDP_RX_TIMESTAMP_VALID_MASK_ARG "xdp_meta_rx_ts_valid_mask"
/* Define different modes for af_xdp prog to attach */
#define ETH_AF_XDP_DRV_MODE_ARG "drv"
@@ -232,6 +246,9 @@ static const char * const valid_arguments[] = {
ETH_AF_XDP_USE_PINNED_MAP_ARG,
ETH_AF_XDP_DP_PATH_ARG,
ETH_AF_XDP_MODE_ARG,
+ ETH_AF_XDP_RX_TIMESTAMP_OFFSET_ARG,
+ ETH_AF_XDP_RX_TIMESTAMP_VALID_OFFSET_ARG,
+ ETH_AF_XDP_RX_TIMESTAMP_VALID_MASK_ARG,
NULL
};
@@ -398,6 +415,29 @@ af_xdp_rx_zc(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
rte_pktmbuf_pkt_len(bufs[i]) = len;
rte_pktmbuf_data_len(bufs[i]) = len;
+
+ if (rxq->rx_timestamp_enabled &&
+ timestamp_dynfield_offset >= 0) {
+ /*
+ * Copy timestamp if validity offset is not defined or
+ * flag is valid.
+ */
+ if (rxq->rx_timestamp_valid_offset < 0 ||
+ (*(uint8_t *)((char *)rte_pktmbuf_mtod(bufs[i], void *) -
+ rxq->rx_timestamp_valid_offset) &
+ rxq->rx_timestamp_valid_mask)) {
+ uint64_t ts;
+ memcpy(&ts,
+ (char *)rte_pktmbuf_mtod(bufs[i], void *) -
+ rxq->rx_timestamp_offset,
+ sizeof(ts));
+ *RTE_MBUF_DYNFIELD(bufs[i],
+ timestamp_dynfield_offset,
+ uint64_t *) = ts;
+ bufs[i]->ol_flags |= timestamp_dynflag;
+ }
+ }
+
rx_bytes += len;
}
@@ -457,6 +497,27 @@ af_xdp_rx_cp(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
len = desc->len;
pkt = xsk_umem__get_data(rxq->umem->mz->addr, addr);
+ if (rxq->rx_timestamp_enabled &&
+ timestamp_dynfield_offset >= 0) {
+ /*
+ * Copy timestamp if validity offset is not defined or
+ * flag is valid.
+ */
+ if (rxq->rx_timestamp_valid_offset < 0 ||
+ (*(uint8_t *)((char *)pkt -
+ rxq->rx_timestamp_valid_offset) &
+ rxq->rx_timestamp_valid_mask)) {
+ uint64_t ts;
+ memcpy(&ts,
+ (char *)pkt - rxq->rx_timestamp_offset,
+ sizeof(ts));
+ *RTE_MBUF_DYNFIELD(mbufs[i],
+ timestamp_dynfield_offset,
+ uint64_t *) = ts;
+ mbufs[i]->ol_flags |= timestamp_dynflag;
+ }
+ }
+
rte_memcpy(rte_pktmbuf_mtod(mbufs[i], void *), pkt, len);
rte_ring_enqueue(umem->buf_ring, (void *)addr);
rte_pktmbuf_pkt_len(mbufs[i]) = len;
@@ -738,11 +799,80 @@ eth_af_xdp_tx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
#endif
}
+static int
+eth_af_xdp_enable_hw_timestamping(const char *if_name)
+{
+ struct hwtstamp_config config = {0};
+ struct ifreq ifr = {0};
+ int fd, ret;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return -1;
+
+ ifr.ifr_data = (caddr_t)&config;
+ strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+ ret = ioctl(fd, SIOCGHWTSTAMP, &ifr);
+ if (ret == 0) {
+ if (config.rx_filter != HWTSTAMP_FILTER_NONE) {
+ close(fd);
+ return 0;
+ }
+ }
+
+ config.flags = 0;
+ config.tx_type = HWTSTAMP_TX_OFF;
+ config.rx_filter = HWTSTAMP_FILTER_ALL;
+
+ ret = ioctl(fd, SIOCSHWTSTAMP, &ifr);
+ close(fd);
+
+ if (ret < 0)
+ return -errno;
+
+ if (config.rx_filter == HWTSTAMP_FILTER_NONE)
+ return -ENOTSUP;
+
+ return 0;
+}
+
static int
eth_dev_start(struct rte_eth_dev *dev)
{
+ struct pmd_internals *internals = dev->data->dev_private;
uint16_t i;
+ if (dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP) {
+ int rc;
+
+ if (internals->rx_timestamp_offset < 0) {
+ AF_XDP_LOG_LINE(ERR,
+ "Timestamp offload requested but xdp_meta_rx_ts_offset parameter not configured");
+ return -EINVAL;
+ }
+
+ rc = rte_mbuf_dyn_rx_timestamp_register(×tamp_dynfield_offset,
+ ×tamp_dynflag);
+ if (rc) {
+ AF_XDP_LOG_LINE(ERR,
+ "Failed to register mbuf timestamp field");
+ return rc;
+ }
+ AF_XDP_LOG_LINE(INFO,
+ "Registered mbuf timestamp field, offset: %d",
+ timestamp_dynfield_offset);
+
+ rc = eth_af_xdp_enable_hw_timestamping(internals->if_name);
+ if (rc < 0) {
+ AF_XDP_LOG_LINE(ERR,
+ "Could not enable HW timestamping on %s: %s",
+ internals->if_name, strerror(-rc));
+ return rc;
+ }
+ }
+
dev->data->dev_link.link_status = RTE_ETH_LINK_UP;
for (i = 0; i < dev->data->nb_rx_queues; i++) {
dev->data->rx_queue_state[i] = RTE_ETH_QUEUE_STATE_STARTED;
@@ -870,6 +1000,8 @@ eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
dev_info->max_rx_queues = internals->queue_cnt;
dev_info->max_tx_queues = internals->queue_cnt;
+ dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_TIMESTAMP;
+
dev_info->min_mtu = RTE_ETHER_MIN_MTU;
#if defined(XDP_UMEM_UNALIGNED_CHUNK_FLAG)
dev_info->max_rx_pktlen = getpagesize() -
@@ -1873,7 +2005,12 @@ eth_rx_queue_setup(struct rte_eth_dev *dev,
process_private->rxq_xsk_fds[rx_queue_id] = rxq->fds[0].fd;
rxq->port = dev->data->port_id;
-
+ rxq->rx_timestamp_offset = internals->rx_timestamp_offset;
+ rxq->rx_timestamp_valid_offset = internals->rx_timestamp_valid_offset;
+ rxq->rx_timestamp_valid_mask = internals->rx_timestamp_valid_mask;
+ rxq->rx_timestamp_enabled = (rxq->rx_timestamp_offset >= 0) &&
+ !!(dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP);
dev->data->rx_queues[rx_queue_id] = rxq;
return 0;
@@ -2023,7 +2160,7 @@ parse_integer_arg(const char *key __rte_unused,
int *i = (int *)extra_args;
char *end;
- *i = strtol(value, &end, 10);
+ *i = strtol(value, &end, 0);
if (*i < 0) {
AF_XDP_LOG_LINE(ERR, "Argument has to be positive.");
return -EINVAL;
@@ -2146,7 +2283,9 @@ static int
parse_parameters(struct rte_kvargs *kvlist, char *if_name, int *start_queue,
int *queue_cnt, int *shared_umem, char *prog_path,
int *busy_budget, int *force_copy, int *use_cni,
- int *use_pinned_map, char *dp_path, uint32_t *xdp_mode)
+ int *use_pinned_map, char *dp_path, uint32_t *xdp_mode,
+ int *rx_timestamp_offset, int *rx_timestamp_valid_offset,
+ int *rx_timestamp_valid_mask)
{
int ret;
@@ -2209,6 +2348,21 @@ parse_parameters(struct rte_kvargs *kvlist, char *if_name, int *start_queue,
if (ret < 0)
goto free_kvlist;
+ ret = rte_kvargs_process(kvlist, ETH_AF_XDP_RX_TIMESTAMP_OFFSET_ARG,
+ &parse_integer_arg, rx_timestamp_offset);
+ if (ret < 0)
+ goto free_kvlist;
+
+ ret = rte_kvargs_process(kvlist, ETH_AF_XDP_RX_TIMESTAMP_VALID_OFFSET_ARG,
+ &parse_integer_arg, rx_timestamp_valid_offset);
+ if (ret < 0)
+ goto free_kvlist;
+
+ ret = rte_kvargs_process(kvlist, ETH_AF_XDP_RX_TIMESTAMP_VALID_MASK_ARG,
+ &parse_integer_arg, rx_timestamp_valid_mask);
+ if (ret < 0)
+ goto free_kvlist;
+
free_kvlist:
rte_kvargs_free(kvlist);
return ret;
@@ -2248,7 +2402,9 @@ static struct rte_eth_dev *
init_internals(struct rte_vdev_device *dev, const char *if_name,
int start_queue_idx, int queue_cnt, int shared_umem,
const char *prog_path, int busy_budget, int force_copy,
- int use_cni, int use_pinned_map, const char *dp_path, uint32_t xdp_mode)
+ int use_cni, int use_pinned_map, const char *dp_path, uint32_t xdp_mode,
+ int rx_timestamp_offset, int rx_timestamp_valid_offset,
+ int rx_timestamp_valid_mask)
{
const char *name = rte_vdev_device_name(dev);
const unsigned int numa_node = dev->device.numa_node;
@@ -2281,6 +2437,9 @@ init_internals(struct rte_vdev_device *dev, const char *if_name,
internals->use_pinned_map = use_pinned_map;
internals->mode_flag = XDP_FLAGS_UPDATE_IF_NOEXIST | xdp_mode;
strlcpy(internals->dp_path, dp_path, PATH_MAX);
+ internals->rx_timestamp_offset = rx_timestamp_offset;
+ internals->rx_timestamp_valid_offset = rx_timestamp_valid_offset;
+ internals->rx_timestamp_valid_mask = (uint8_t)rx_timestamp_valid_mask;
if (xdp_get_channels_info(if_name, &internals->max_queue_cnt,
&internals->configured_queue_cnt)) {
@@ -2474,6 +2633,9 @@ rte_pmd_af_xdp_probe(struct rte_vdev_device *dev)
int use_pinned_map = 0;
uint32_t xdp_mode = 0;
char dp_path[PATH_MAX] = {'\0'};
+ int rx_timestamp_offset = -1;
+ int rx_timestamp_valid_offset = -1;
+ int rx_timestamp_valid_mask = 0;
struct rte_eth_dev *eth_dev = NULL;
const char *name = rte_vdev_device_name(dev);
@@ -2517,11 +2679,28 @@ rte_pmd_af_xdp_probe(struct rte_vdev_device *dev)
if (parse_parameters(kvlist, if_name, &xsk_start_queue_idx,
&xsk_queue_cnt, &shared_umem, prog_path,
&busy_budget, &force_copy, &use_cni, &use_pinned_map,
- dp_path, &xdp_mode) < 0) {
+ dp_path, &xdp_mode, &rx_timestamp_offset,
+ &rx_timestamp_valid_offset, &rx_timestamp_valid_mask) < 0) {
AF_XDP_LOG_LINE(ERR, "Invalid kvargs value");
return -EINVAL;
}
+ if (rx_timestamp_valid_mask > UINT8_MAX) {
+ AF_XDP_LOG_LINE(ERR, "Validity mask must fit in a single byte (0-%u)", UINT8_MAX);
+ return -EINVAL;
+ }
+
+ if (rx_timestamp_valid_offset >= 0) {
+ if (rx_timestamp_offset < 0) {
+ AF_XDP_LOG_LINE(ERR, "Timestamp offset must be configured when validity offset is configured");
+ return -EINVAL;
+ }
+ if (rx_timestamp_valid_mask == 0) {
+ AF_XDP_LOG_LINE(ERR, "Validity mask cannot be zero when validity offset is configured");
+ return -EINVAL;
+ }
+ }
+
if (use_cni && use_pinned_map) {
AF_XDP_LOG_LINE(ERR, "When '%s' parameter is used, '%s' parameter is not valid",
ETH_AF_XDP_USE_CNI_ARG, ETH_AF_XDP_USE_PINNED_MAP_ARG);
@@ -2585,7 +2764,8 @@ rte_pmd_af_xdp_probe(struct rte_vdev_device *dev)
eth_dev = init_internals(dev, if_name, xsk_start_queue_idx,
xsk_queue_cnt, shared_umem, prog_path,
busy_budget, force_copy, use_cni, use_pinned_map,
- dp_path, xdp_mode);
+ dp_path, xdp_mode, rx_timestamp_offset,
+ rx_timestamp_valid_offset, rx_timestamp_valid_mask);
if (eth_dev == NULL) {
AF_XDP_LOG_LINE(ERR, "Failed to init internals");
return -1;
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v3 2/2] net/af_xdp: add read_clock support to AF_XDP PMD
2026-07-21 12:11 ` [PATCH v3 0/2] net/af_xdp: add Rx timestamping and read_clock support Mark Blasko
2026-07-21 12:11 ` [PATCH v3 1/2] net/af_xdp: add af_xdp rx metadata and dynamic timestamping support Mark Blasko
@ 2026-07-21 12:11 ` Mark Blasko
1 sibling, 0 replies; 15+ messages in thread
From: Mark Blasko @ 2026-07-21 12:11 UTC (permalink / raw)
To: dev, Ciara Loftus, Maryam Tahhan; +Cc: joshwash, jtranoleary, blasko
Implement the ethdev read_clock operation in the AF_XDP Poll Mode
Driver. This allows DPDK applications to query the current time of the
NIC hardware clock.
At device start, the PMD queries ethtool for the interface's PTP
Hardware Clock index and opens the corresponding PTP device node. The
read_clock operation queries the current time via clock_gettime.
Signed-off-by: Mark Blasko <blasko@google.com>
Reviewed-by: Joshua Washington <joshwash@google.com>
---
v3:
- Add feature description and PTP hardware clock prerequisites in
doc/guides/nics/af_xdp.rst.
- Add release notes entry in doc/guides/rel_notes/release_26_07.rst.
- Add PTP file descriptor cleanup in eth_dev_start(), eth_dev_stop(),
and eth_dev_close().
- Return -errno on clock_gettime() failure.
- Add strerror(errno) error logging on PTP device open failure.
v2:
- New patch introduced in v2 to support read_clock ethdev operation.
---
doc/guides/nics/af_xdp.rst | 12 ++++
doc/guides/rel_notes/release_26_07.rst | 1 +
drivers/net/af_xdp/rte_eth_af_xdp.c | 87 ++++++++++++++++++++++++++
3 files changed, 100 insertions(+)
diff --git a/doc/guides/nics/af_xdp.rst b/doc/guides/nics/af_xdp.rst
index 367a1b0507..3ef02f90a2 100644
--- a/doc/guides/nics/af_xdp.rst
+++ b/doc/guides/nics/af_xdp.rst
@@ -37,6 +37,9 @@ Prerequisites
header is used to determine the kernel version at compile time.
* A kernel with version 5.4 or later is required for 32-bit OS.
* The busy polling feature requires kernel version >= v5.11.
+* The ``read_clock`` feature requires a network interface with PTP
+ Hardware Clock support (capable of exposing a ``/dev/ptpX`` device via
+ ethtool ``ETHTOOL_GET_TS_INFO``).
Options
@@ -242,6 +245,15 @@ to verify timestamp validity at ``xdp_meta_valid_hint_offset``.
--vdev net_af_xdp,iface=ens786f1,xdp_meta_rx_ts_offset=8,xdp_meta_valid_hint_offset=4,xdp_meta_rx_ts_valid_mask=0x1
+read_clock
+~~~~~~~~~~
+
+The PMD supports querying the underlying PTP hardware clock time via
+``rte_eth_read_clock()``. When the timestamp offload
+``RTE_ETH_RX_OFFLOAD_TIMESTAMP`` is enabled, the PMD automatically discovers
+the hardware PHC index via ethtool and opens the PTP character device
+(``/dev/ptpX``).
+
Limitations
-----------
diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index 8af0b8d615..326c35243b 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -140,6 +140,7 @@ New Features
* Added support for RX metadata hardware timestamping via vdev devargs
``xdp_meta_rx_ts_offset``, ``xdp_meta_valid_hint_offset``, and
``xdp_meta_rx_ts_valid_mask``.
+ * Added ``read_clock`` operation to query the PTP hardware clock.
* **Updated Google gve driver.**
diff --git a/drivers/net/af_xdp/rte_eth_af_xdp.c b/drivers/net/af_xdp/rte_eth_af_xdp.c
index e047870530..b9a5bfe74f 100644
--- a/drivers/net/af_xdp/rte_eth_af_xdp.c
+++ b/drivers/net/af_xdp/rte_eth_af_xdp.c
@@ -5,6 +5,8 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
+#include <fcntl.h>
+#include <time.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/un.h>
@@ -29,6 +31,7 @@
#include <dev_driver.h>
#include <rte_eal.h>
#include <rte_ether.h>
+#include <rte_time.h>
#include <rte_lcore.h>
#include <rte_log.h>
#include <rte_memory.h>
@@ -194,6 +197,7 @@ struct pmd_internals {
int rx_timestamp_offset;
int rx_timestamp_valid_offset;
uint8_t rx_timestamp_valid_mask;
+ int ptp_fd;
};
struct pmd_process_private {
@@ -837,6 +841,30 @@ eth_af_xdp_enable_hw_timestamping(const char *if_name)
return 0;
}
+static int
+eth_af_xdp_get_ptp_index(const char *if_name)
+{
+ struct ethtool_ts_info info = {0};
+ struct ifreq ifr = {0};
+ int fd, ret;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return -1;
+
+ ifr.ifr_data = (caddr_t)&info;
+ info.cmd = ETHTOOL_GET_TS_INFO;
+ strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+ ret = ioctl(fd, SIOCETHTOOL, &ifr);
+ close(fd);
+
+ if (ret < 0)
+ return -1;
+
+ return info.phc_index;
+}
+
static int
eth_dev_start(struct rte_eth_dev *dev)
{
@@ -871,6 +899,26 @@ eth_dev_start(struct rte_eth_dev *dev)
internals->if_name, strerror(-rc));
return rc;
}
+
+ int phc_index = eth_af_xdp_get_ptp_index(internals->if_name);
+ if (phc_index >= 0) {
+ char ptp_dev[32];
+ snprintf(ptp_dev, sizeof(ptp_dev), "/dev/ptp%d", phc_index);
+ if (internals->ptp_fd >= 0) {
+ close(internals->ptp_fd);
+ internals->ptp_fd = -1;
+ }
+ internals->ptp_fd = open(ptp_dev, O_RDONLY);
+ if (internals->ptp_fd >= 0) {
+ AF_XDP_LOG_LINE(INFO,
+ "Opened PTP device %s for read_clock",
+ ptp_dev);
+ } else {
+ AF_XDP_LOG_LINE(WARNING,
+ "Failed to open PTP device %s for read_clock: %s",
+ ptp_dev, strerror(errno));
+ }
+ }
}
dev->data->dev_link.link_status = RTE_ETH_LINK_UP;
@@ -886,6 +934,7 @@ eth_dev_start(struct rte_eth_dev *dev)
static int
eth_dev_stop(struct rte_eth_dev *dev)
{
+ struct pmd_internals *internals = dev->data->dev_private;
uint16_t i;
dev->data->dev_link.link_status = RTE_ETH_LINK_DOWN;
@@ -894,6 +943,11 @@ eth_dev_stop(struct rte_eth_dev *dev)
dev->data->tx_queue_state[i] = RTE_ETH_QUEUE_STATE_STOPPED;
}
+ if (internals->ptp_fd >= 0) {
+ close(internals->ptp_fd);
+ internals->ptp_fd = -1;
+ }
+
return 0;
}
@@ -1227,6 +1281,11 @@ eth_dev_close(struct rte_eth_dev *dev)
}
}
+ if (internals->ptp_fd >= 0) {
+ close(internals->ptp_fd);
+ internals->ptp_fd = -1;
+ }
+
out:
rte_free(dev->process_private);
@@ -2096,6 +2155,31 @@ eth_dev_promiscuous_disable(struct rte_eth_dev *dev)
return eth_dev_change_flags(internals->if_name, 0, ~IFF_PROMISC);
}
+/*
+ * In Linux, dynamic POSIX clock IDs from file descriptors (such as /dev/ptpX)
+ * are encoded with CLOCKFD (3) in the lower 3 bits and ~fd in the upper bits.
+ * As this is not defined in user-space UAPI headers, define the macro here.
+ */
+#define CLOCKFD 3
+#define FD_TO_CLOCKID(fd) ((clockid_t)(~(unsigned int)(fd) << 3 | CLOCKFD))
+
+static int
+eth_af_xdp_read_clock(struct rte_eth_dev *dev, uint64_t *timestamp)
+{
+ struct pmd_internals *internals = dev->data->dev_private;
+ struct timespec ts;
+
+ if (internals->ptp_fd < 0)
+ return -ENOTSUP;
+
+ clockid_t clkid = FD_TO_CLOCKID(internals->ptp_fd);
+ if (clock_gettime(clkid, &ts) < 0)
+ return -errno;
+
+ *timestamp = rte_timespec_to_ns(&ts);
+ return 0;
+}
+
static const struct eth_dev_ops ops = {
.dev_start = eth_dev_start,
.dev_stop = eth_dev_stop,
@@ -2111,6 +2195,7 @@ static const struct eth_dev_ops ops = {
.stats_get = eth_stats_get,
.stats_reset = eth_stats_reset,
.get_monitor_addr = eth_get_monitor_addr,
+ .read_clock = eth_af_xdp_read_clock,
};
/* AF_XDP Device Plugin option works in unprivileged
@@ -2132,6 +2217,7 @@ static const struct eth_dev_ops ops_afxdp_dp = {
.stats_get = eth_stats_get,
.stats_reset = eth_stats_reset,
.get_monitor_addr = eth_get_monitor_addr,
+ .read_clock = eth_af_xdp_read_clock,
};
/** parse busy_budget argument */
@@ -2440,6 +2526,7 @@ init_internals(struct rte_vdev_device *dev, const char *if_name,
internals->rx_timestamp_offset = rx_timestamp_offset;
internals->rx_timestamp_valid_offset = rx_timestamp_valid_offset;
internals->rx_timestamp_valid_mask = (uint8_t)rx_timestamp_valid_mask;
+ internals->ptp_fd = -1;
if (xdp_get_channels_info(if_name, &internals->max_queue_cnt,
&internals->configured_queue_cnt)) {
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v3 1/2] net/af_xdp: add af_xdp rx metadata and dynamic timestamping support
2026-07-21 12:11 ` [PATCH v3 1/2] net/af_xdp: add af_xdp rx metadata and dynamic timestamping support Mark Blasko
@ 2026-07-26 17:01 ` Stephen Hemminger
0 siblings, 0 replies; 15+ messages in thread
From: Stephen Hemminger @ 2026-07-26 17:01 UTC (permalink / raw)
To: Mark Blasko; +Cc: dev, Ciara Loftus, Maryam Tahhan, joshwash, jtranoleary
On Tue, 21 Jul 2026 12:11:27 +0000
Mark Blasko <blasko@google.com> wrote:
> +xdp_meta_rx_ts_offset
> +~~~~~~~~~~~~~~~~~~~~~
> +
> +The ``xdp_meta_rx_ts_offset`` argument specifies the byte offset of the 64-bit RX
> +timestamp within the XDP metadata headroom area (prepended before packet data).
> +
> +.. code-block:: console
> +
> + --vdev net_af_xdp,iface=ens786f1,xdp_meta_rx_ts_offset=8
> +
> +xdp_meta_valid_hint_offset
> +~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +The ``xdp_meta_valid_hint_offset`` argument specifies the byte offset of the validity
> +flag byte within the XDP metadata headroom area.
> +
> +.. code-block:: console
> +
> + --vdev net_af_xdp,iface=ens786f1,xdp_meta_rx_ts_offset=8,xdp_meta_valid_hint_offset=4
> +
> +xdp_meta_rx_ts_valid_mask
> +~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +The ``xdp_meta_rx_ts_valid_mask`` argument specifies the bitmask (in hex or decimal) used
> +to verify timestamp validity at ``xdp_meta_valid_hint_offset``.
> +
> +.. code-block:: console
> +
> + --vdev net_af_xdp,iface=ens786f1,xdp_meta_rx_ts_offset=8,xdp_meta_valid_hint_offset=4,xdp_meta_rx_ts_valid_mask=0x1
> +
Awkward to have so many new metadata devargs, but I guess if XDP has no metadata standard there
is no better way. Maybe combine into one devarg if all are required?
> + if (rxq->rx_timestamp_enabled &&
> + timestamp_dynfield_offset >= 0) {
You could simplify this if rx_timestamp_enabled was not set unless timestamp_dynfield_offset was known.
> @@ -398,6 +415,29 @@ af_xdp_rx_zc(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
>
> rte_pktmbuf_pkt_len(bufs[i]) = len;
> rte_pktmbuf_data_len(bufs[i]) = len;
> +
> + if (rxq->rx_timestamp_enabled &&
> + timestamp_dynfield_offset >= 0) {
> + /*
> + * Copy timestamp if validity offset is not defined or
> + * flag is valid.
> + */
> + if (rxq->rx_timestamp_valid_offset < 0 ||
> + (*(uint8_t *)((char *)rte_pktmbuf_mtod(bufs[i], void *) -
> + rxq->rx_timestamp_valid_offset) &
Use rte_pktmbuf_mtod_offset() here and below.
More findings from detailed AI review
Review of [PATCH v3 0/2] net/af_xdp: rx timestamping and read_clock
Verified against main at 38f72e5 (26.11.0-rc0). Driver hunks apply
cleanly; the release notes hunk does not (see 1/2 Warning 6). Both
patches build with -Dwerror=true, and 1/2 builds standalone, so bisect
is fine. I also compiled the copy-mode branch by undefining
XDP_UMEM_UNALIGNED_CHUNK_FLAG -- that path is clean too.
The overall approach is sound: XDP metadata does sit immediately below
the packet data in the umem chunk in both zero-copy and copy mode, so
reading backwards from the data pointer is the right mechanism. The
problems below are about validating the offsets and about where the
PTP fd lives.
Patch 1/2: net/af_xdp: add af_xdp rx metadata and dynamic timestamping
Error 1: metadata offsets are unvalidated, giving an out-of-bounds read
in the Rx fast path.
xdp_meta_rx_ts_offset and xdp_meta_valid_hint_offset are only checked
for >= 0 (parse_integer_arg rejects negatives; probe checks the mask
and the offset/mask consistency). Nothing bounds them from above,
and both are used as raw backward pointer arithmetic:
memcpy(&ts, (char *)pkt - rxq->rx_timestamp_offset, sizeof(ts));
The window that actually exists is XDP_PACKET_HEADROOM (256) plus the
umem headroom. In copy mode xdp_umem_configure() sets
frame_headroom = 0 and fills the ring with chunk-aligned addresses
(i * ETH_AF_XDP_FRAME_SIZE), so for chunk 0 any offset above 256
reads before umem->mz->addr -- outside the memzone. In zero-copy
mode an oversized offset walks back through RTE_PKTMBUF_HEADROOM into
the rte_mbuf header and then past the chunk base.
The kernel caps metadata at 255 bytes in bpf_xdp_adjust_meta(), so a
hard upper bound is available. Reject at probe:
if (rx_timestamp_offset > XDP_PACKET_HEADROOM) ...
if (rx_timestamp_valid_offset > XDP_PACKET_HEADROOM) ...
Error 2: xdp_meta_rx_ts_offset of 0-7 silently reads packet data as a
timestamp.
The read is 8 bytes ending at (data - offset + 8), so the offset must
be at least sizeof(uint64_t) for the read to stay in the metadata
area. offset=0 is accepted today (parse_integer_arg only rejects < 0,
and eth_dev_start() only rejects < 0), and the PMD then reports the
first 8 bytes of the Ethernet header as a hardware timestamp with the
dynflag set. Require >= 8 for the timestamp offset and >= 1 for the
valid-hint offset.
Warning 3: changing parse_integer_arg() from base 10 to base 0 silently
changes six existing devargs.
parse_integer_arg() is shared by start_queue, queue_count,
shared_umem, force_copy, use_cni and use_pinned_map. Confirmed
empirically: strtol("08", &end, 10) is 8, strtol("08", &end, 0) is 0
with "8" left in end. end is assigned but never checked, so this is a
silent misparse rather than an error. Only the validity mask needs
hex; please add a separate parse function for it rather than changing
the shared one, or check *end == '\0' in the same patch.
Warning 4: rx_offload_capa advertises a capability the device may not
have.
dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_TIMESTAMP is
unconditional, but eth_dev_start() returns -EINVAL when
xdp_meta_rx_ts_offset was not supplied. eth_dev_configure() accepts
the offload, so the application only discovers the mismatch at start.
Advertise it only when internals->rx_timestamp_offset >= 0. Also use
|= rather than = so a later offload addition does not clobber this,
and set rx_queue_offload_capa as well -- features.rst lists both under
"Timestamp offload".
Warning 5: doc/guides/nics/features/af_xdp.ini is not updated. Add
"Timestamp offload = Y".
Warning 6: the release notes entry targets doc/guides/rel_notes/
release_26_07.rst. Main is now 26.11.0-rc0 and 26.07 is closed -- the
hunk no longer applies. Move the entry to release_26_11.rst.
Warning 7: eth_af_xdp_enable_hw_timestamping() clobbers another
application's Tx timestamping configuration.
The function reads the current config with SIOCGHWTSTAMP, then
unconditionally sets config.tx_type = HWTSTAMP_TX_OFF before
SIOCSHWTSTAMP. If ptp4l (or anything else) had Tx timestamping on
while rx_filter was NONE, this turns it off underneath them. Preserve
tx_type from the SIOCGHWTSTAMP result.
Related: the hwtstamp config is never restored on dev_stop or
dev_close, so the interface is left in HWTSTAMP_FILTER_ALL after the
DPDK application exits. Worth either restoring the saved config or
documenting the side effect explicitly.
Info 8: on socket() failure the function returns -1, and the caller logs
strerror(-rc), which prints "Operation not permitted". Return -errno.
Info 9: the new ioctl helpers use (caddr_t) casts for ifr_data. The
existing xdp_get_channels_info() in this file uses (void *). caddr_t is
a BSD-ism; please match the surrounding code.
Info 10: ops_afxdp_dp (the unprivileged AF_XDP device-plugin path)
shares eth_dev_start, and SIOCSHWTSTAMP needs CAP_NET_ADMIN. Requesting
the timestamp offload in that mode will fail the start with -EPERM.
Worth a line in af_xdp.rst.
Patch 2/2: net/af_xdp: add read_clock support to AF_XDP PMD
Error 1: ptp_fd is stored in pmd_internals, which is dev_private and
therefore shared memory, but a file descriptor is process-local.
This driver already has struct pmd_process_private for exactly this
reason -- rxq_xsk_fds[] lives there, and afxdp_mp_request_fds() /
afxdp_mp_send_fds() pass those descriptors between primary and
secondary over SCM_RIGHTS. af_xdp.ini declares "Multiprocess aware =
Y", so this is not hypothetical. Two concrete consequences:
- A secondary calling rte_eth_read_clock() derives a clockid from a
descriptor number that is only meaningful in the primary. It
either fails or, worse, reads whatever the secondary happens to
have open at that number.
- A secondary calling rte_eth_dev_stop() runs
close(internals->ptp_fd) on an unrelated descriptor in its own
table, then stores -1 into the shared field, so the primary leaks
its real PTP fd and read_clock stops working there.
Move ptp_fd into pmd_process_private, and if secondary read_clock is
meant to work, extend the existing IPC to pass it.
Warning 2: read_clock is only functional when
RTE_ETH_RX_OFFLOAD_TIMESTAMP is enabled, since the PHC is opened inside
that branch of eth_dev_start(). That couples two independent features:
an application that only wants the PHC time must also enable Rx
timestamp offload, which (per 1/2) reprograms the NIC's hwtstamp filter.
Opening the PHC unconditionally at start would decouple them.
Warning 3: features matrix -- as with 1/2, af_xdp.ini needs "Timestamp
offload = Y". features.rst lists read_clock as "[related] eth_dev_ops"
under that entry, so the one key covers both patches.
Warning 4: in af_xdp.rst the read_clock subsection is placed under
"Options", where every other ~~~ subsection is a devarg name.
read_clock is a feature, not a devarg -- it belongs in its own section.
Info 5: CLOCKFD and FD_TO_CLOCKID are defined unconditionally with no
driver prefix and no #ifndef guard. I checked the arithmetic against
the kernel's posix-timers.h definition across a range of descriptor
values and it agrees, so this is only a namespace point -- consider
AF_XDP_FD_TO_CLOCKID or an #ifndef.
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-07-26 17:01 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-23 21:53 [PATCH] net/af_xdp: add Rx metadata and dynamic timestamping support Mark Blasko
2026-06-23 22:06 ` Stephen Hemminger
2026-06-29 0:50 ` Mark Blasko
2026-06-29 17:38 ` Stephen Hemminger
2026-06-29 19:10 ` Joshua Washington
2026-06-29 20:02 ` Stephen Hemminger
2026-06-29 20:03 ` Stephen Hemminger
2026-06-30 0:41 ` Joshua Washington
2026-07-10 22:10 ` [PATCH v2 0/2] net/af_xdp: add Rx timestamping and read_clock support Mark Blasko
2026-07-10 22:10 ` [PATCH v2 1/2] net/af_xdp: add af_xdp rx metadata and dynamic timestamping support Mark Blasko
2026-07-10 22:10 ` [PATCH v2 2/2] net/af_xdp: add read_clock support to AF_XDP PMD Mark Blasko
2026-07-21 12:11 ` [PATCH v3 0/2] net/af_xdp: add Rx timestamping and read_clock support Mark Blasko
2026-07-21 12:11 ` [PATCH v3 1/2] net/af_xdp: add af_xdp rx metadata and dynamic timestamping support Mark Blasko
2026-07-26 17:01 ` Stephen Hemminger
2026-07-21 12:11 ` [PATCH v3 2/2] net/af_xdp: add read_clock support to AF_XDP PMD Mark Blasko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox