Subject: [PATCH net-next v2 1/2] net: extend ndo_get_tstamp for other timestamp types

From: Kevin Yang <[email-masked]>
Date: Wed, 21 Jan 2026 16:04:57 +0000

Network device hardware timestamps (hwtstamps) and the system's
clock (ktime) often originate from different clock domains.
This makes it hard to directly calculate the duration between
a hardware-timestamped event and a system-time event by simple
subtraction.

This patch extends ndo_get_tstamp to allow a netdev to provide
a hwtstamp into the system's CLOCK_REALTIME domain. This allows a
driver to either perform a conversion by estimating or, if the
clocks are kept synchronized, return the original timestamp directly.
Other clock domains, e.g. CLOCK_MONOTONIC_RAW can also be added when
a use surfaces.

This is useful for features that need to measure the delay between
a packet's hardware arrival/departure and a later software event.
For example, the TCP stack can use this to measure precise
packet receive delays, which is a requirement for the upcoming
TCP Swift [1] congestion control algorithm.

[1] Kumar, Gautam, et al. "Swift: Delay is simple and effective
for congestion control in the datacenter." Proceedings of the
Annual conference of the ACM Special Interest Group on Data
Communication on the applications, technologies, architectures,
and protocols for computer communication. 2020.

Signed-off-by: Kevin Yang <[email-masked]>
Reviewed-by: Willem de Bruijn <[email-masked]>
---
 Documentation/networking/timestamping.rst  | 21 +++++++++++++++++++++
 drivers/net/ethernet/engleder/tsnep_main.c |  8 +++++---
 drivers/net/ethernet/intel/igc/igc_main.c  |  8 +++++---
 include/linux/netdevice.h                  | 21 ++++++++++++++-------
 4 files changed, 45 insertions(+), 13 deletions(-)

diff --git a/Documentation/networking/timestamping.rst b/Documentation/networking/timestamping.rst
index 2162c4f2b28a6..05e3607b43551 100644
--- a/Documentation/networking/timestamping.rst
+++ b/Documentation/networking/timestamping.rst
@@ -671,6 +671,27 @@ Time stamps for outgoing packets are to be generated as follows:
   software time stamping and therefore could lead to unexpected deltas
   between time stamps.
 
+3.1.1 netdev_tstamp_type and ndo_get_tstamp
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``ndo_get_tstamp`` operation allows the stack to convert a hardware timestamp
+to a specific time domain or format. The ``type`` argument specifies the
+requested timestamp type:
+
+- ``NETDEV_TSTAMP_PHC``: The hardware timestamp in its PTP Hardware Clock
+  domain.
+- ``NETDEV_TSTAMP_CYCLE``: The hardware timestamp as a cycle counter.
+- ``NETDEV_TSTAMP_REALTIME``: The hardware timestamp converted to system time
+  (``CLOCK_REALTIME``).
+
+For ``NETDEV_TSTAMP_REALTIME``, the driver is responsible for converting the
+hardware timestamp to system time. In that case, ``ndo_get_tstamp`` does not
+provide an accuracy guarantee. A device might use a disciplined clock that is
+synchronized with ``CLOCK_REALTIME``. Or, the driver might estimate the system
+time. The accuracy primarily depends on the physical hardware and driver
+implementation.
+
+
 3.2 Special considerations for stacked PTP Hardware Clocks
 ----------------------------------------------------------
 
diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c
index b118407c30e87..d1c0cbaba06e7 100644
--- a/drivers/net/ethernet/engleder/tsnep_main.c
+++ b/drivers/net/ethernet/engleder/tsnep_main.c
@@ -2275,15 +2275,17 @@ static int tsnep_netdev_set_features(struct net_device *netdev,
 
 static ktime_t tsnep_netdev_get_tstamp(struct net_device *netdev,
 				       const struct skb_shared_hwtstamps *hwtstamps,
-				       bool cycles)
+				       enum netdev_tstamp_type type)
 {
 	struct tsnep_rx_inline *rx_inline = hwtstamps->netdev_data;
 	u64 timestamp;
 
-	if (cycles)
+	if (type == NETDEV_TSTAMP_CYCLE)
 		timestamp = __le64_to_cpu(rx_inline->counter);
-	else
+	else if (type == NETDEV_TSTAMP_PHC)
 		timestamp = __le64_to_cpu(rx_inline->timestamp);
+	else
+		return 0;
 
 	return ns_to_ktime(timestamp);
 }
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index 7aafa60ba0c86..3bbb9098c6fc4 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -6947,7 +6947,7 @@ int igc_xsk_wakeup(struct net_device *dev, u32 queue_id, u32 flags)
 
 static ktime_t igc_get_tstamp(struct net_device *dev,
 			      const struct skb_shared_hwtstamps *hwtstamps,
-			      bool cycles)
+			      enum netdev_tstamp_type type)
 {
 	struct igc_adapter *adapter = netdev_priv(dev);
 	struct igc_inline_rx_tstamps *tstamp;
@@ -6955,10 +6955,12 @@ static ktime_t igc_get_tstamp(struct net_device *dev,
 
 	tstamp = hwtstamps->netdev_data;
 
-	if (cycles)
+	if (type == NETDEV_TSTAMP_CYCLE)
 		timestamp = igc_ptp_rx_pktstamp(adapter, tstamp->timer1);
-	else
+	else if (type == NETDEV_TSTAMP_PHC)
 		timestamp = igc_ptp_rx_pktstamp(adapter, tstamp->timer0);
+	else
+		return 0;
 
 	return timestamp;
 }
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d99b0fbc1942a..bfbdeca150c03 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1062,6 +1062,12 @@ struct netdev_net_notifier {
 	struct notifier_block *nb;
 };
 
+enum netdev_tstamp_type {
+	NETDEV_TSTAMP_PHC = 0,
+	NETDEV_TSTAMP_CYCLE,
+	NETDEV_TSTAMP_REALTIME,
+};
+
 /*
  * This structure defines the management hooks for network devices.
  * The following hooks can be defined; unless noted otherwise, they are
@@ -1406,11 +1412,10 @@ struct netdev_net_notifier {
  *     Get the forwarding path to reach the real device from the HW destination address
  * ktime_t (*ndo_get_tstamp)(struct net_device *dev,
  *			     const struct skb_shared_hwtstamps *hwtstamps,
- *			     bool cycles);
- *	Get hardware timestamp based on normal/adjustable time or free running
- *	cycle counter. This function is required if physical clock supports a
- *	free running cycle counter.
- *
+ *			     enum netdev_tstamp_type type);
+ *	Get hardware timestamp based on the type requested, or return 0 if the
+ *	requested type is not supported. This function is required if physical
+ *	clock supports a free running cycle counter.
  * int (*ndo_hwtstamp_get)(struct net_device *dev,
  *			   struct kernel_hwtstamp_config *kernel_config);
  *	Get the currently configured hardware timestamping parameters for the
@@ -1661,7 +1666,7 @@ struct net_device_ops {
                                                          struct net_device_path *path);
 	ktime_t			(*ndo_get_tstamp)(struct net_device *dev,
 						  const struct skb_shared_hwtstamps *hwtstamps,
-						  bool cycles);
+						  enum netdev_tstamp_type type);
 	int			(*ndo_hwtstamp_get)(struct net_device *dev,
 						    struct kernel_hwtstamp_config *kernel_config);
 	int			(*ndo_hwtstamp_set)(struct net_device *dev,
@@ -5236,9 +5241,11 @@ static inline ktime_t netdev_get_tstamp(struct net_device *dev,
 					bool cycles)
 {
 	const struct net_device_ops *ops = dev->netdev_ops;
+	enum netdev_tstamp_type type = cycles ? NETDEV_TSTAMP_CYCLE :
+						NETDEV_TSTAMP_PHC;
 
 	if (ops->ndo_get_tstamp)
-		return ops->ndo_get_tstamp(dev, hwtstamps, cycles);
+		return ops->ndo_get_tstamp(dev, hwtstamps, type);
 
 	return hwtstamps->hwtstamp;
 }
-- 
2.52.0.457.g6b5491de43-goog