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 Reviewed-by: Willem de Bruijn --- drivers/net/ethernet/engleder/tsnep_main.c | 8 +++++--- drivers/net/ethernet/intel/igc/igc_main.c | 8 +++++--- include/linux/netdevice.h | 21 ++++++++++++++------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c index b118407c30e87..7ae697fe51cf6 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_RAW) 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..c233e78f474f1 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_RAW) 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..1c1c7dcb8e801 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_RAW = 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_RAW; 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