From patchwork Fri Nov 7 09:26:01 2008 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Ohly X-Patchwork-Id: 8151 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by ozlabs.org (Postfix) with ESMTP id 857D5474CA for ; Wed, 12 Nov 2008 01:57:48 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756241AbYKKO5l (ORCPT ); Tue, 11 Nov 2008 09:57:41 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1756240AbYKKO5k (ORCPT ); Tue, 11 Nov 2008 09:57:40 -0500 Received: from mga02.intel.com ([134.134.136.20]:13371 "EHLO mga02.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756229AbYKKO5i (ORCPT ); Tue, 11 Nov 2008 09:57:38 -0500 Received: from orsmga001.jf.intel.com ([10.7.209.18]) by orsmga101.jf.intel.com with ESMTP; 11 Nov 2008 06:56:03 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.33,584,1220252400"; d="scan'208";a="461695558" Received: from ecld0pohly.ikn.intel.com (HELO [172.28.75.199]) ([172.28.75.199]) by orsmga001.jf.intel.com with ESMTP; 11 Nov 2008 06:56:24 -0800 In-Reply-To: <1226414697.17450.852.camel@ecld0pohly> References: <1226414697.17450.852.camel@ecld0pohly> From: Patrick Ohly Date: Fri, 7 Nov 2008 10:26:01 +0100 Subject: [RFC PATCH 13/13] skbuff: optionally store hardware time stamps in new field To: netdev@vger.kernel.org Cc: Octavian Purdila , Stephen Hemminger , Ingo Oeser , Andi Kleen , John Ronciak , Eric Dumazet , Oliver Hartkopp X-Mailer: Evolution 2.22.2 Message-Id: <1226415456.31699.12.camel@ecld0pohly> Mime-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Because of performance reasons, adding a new field to struct sk_buff was avoided. Hardware time stamps are stored in the existing field, but in order to not break other code, they must have been transformed to the system time base. To obtain the original hardware time stamp before the transformation, a network device driver must implement the inverse transformation. The clocksync code has no support for that yet and it would be difficult to implement 100% accurately (rounding errors, updated offset/skew values). Instead of implementing this inverse transformation, this patch adds another field for hardware time stamps. It is off by default and mainstream Linux distributions should leave it off (PTP time synchronization doesn't need it), but special distributions/users could enable it if needed without having to patch the mainline kernel source. Signed-off-by: Patrick Ohly --- drivers/net/igb/igb_main.c | 3 +- include/linux/skbuff.h | 48 ++++++++++++++++++++++++++++++++++--------- net/Kconfig | 16 ++++++++++++++ net/core/skbuff.c | 18 +++++++++++++-- 4 files changed, 71 insertions(+), 14 deletions(-) diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index b320fec..2fed508 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -4116,7 +4116,8 @@ send_up: clocksync_update(&adapter->sync, tstamp); skb_hwtstamp_set(skb, ns_to_ktime(clocksource_cyc2time(&adapter->clock, - tstamp))); + tstamp)), + ns_to_ktime(tstamp)); } if (staterr & E1000_RXDEXT_ERR_FRAME_ERR_MASK) { diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index bcca8fc..123711d 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -203,6 +203,7 @@ typedef unsigned char *sk_buff_data_t; * thus is recorded in system time. If the lowest bit is set, * then the value was originally generated by a different clock * in the receiving hardware and then transformed to system time. + * @hwtstamp: raw, unmodified hardware time stamp (optional) * @dev: Device we arrived on/are leaving by * @transport_header: Transport layer header * @network_header: Network layer header @@ -260,6 +261,9 @@ struct sk_buff { struct sock *sk; ktime_t tstamp; +#ifdef CONFIG_NET_SKBUFF_HWTSTAMPS + union ktime hwtstamp; +#endif struct net_device *dev; union { @@ -1530,11 +1534,15 @@ extern void skb_init(void); /** returns skb->tstamp without the bit which marks hardware time stamps */ static inline union ktime skb_get_ktime(const struct sk_buff *skb) { -#if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR) +#ifdef CONFIG_NET_SKBUFF_HWTSTAMPS + return skb->tstamp; +#else +# if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR) return ktime_set(skb->tstamp.tv.sec, skb->tstamp.tv.nsec & ~1); -#else +# else return (ktime_t) { .tv64 = skb->tstamp.tv64 & ~1UL }; +# endif #endif } @@ -1564,15 +1572,19 @@ static inline void __net_timestamp(struct sk_buff *skb) { skb->tstamp = ktime_get_real(); +#ifdef CONFIG_NET_SKBUFF_HWTSTAMPS + skb->hwtstamp.tv64 = 0; +#else /* * make sure that lowest bit is never set: it marks hardware * time stamps */ -#if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR) +# if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR) skb->tstamp.tv.sec = skb->tstamp.tv.sec / 2 * 2; -#else +# else skb->tstamp.tv64 = skb->tstamp.tv64 / 2 * 2; -#endif +# endif +#endif /* CONFIG_NET_SKBUFF_HWTSTAMPS */ } static inline ktime_t net_timedelta(ktime_t t) @@ -1591,18 +1603,34 @@ static inline ktime_t net_invalid_timestamp(void) */ static inline int skb_hwtstamp_available(const struct sk_buff *skb) { +#ifdef CONFIG_NET_SKBUFF_HWTSTAMPS + return skb->hwtstamp.tv64 != 0; +#else return skb->tstamp.tv64 & 1; +#endif } +/** + * skb_hwtstamp_set - stores a time stamp generated by hardware in the skb + * @skb: time stamp is stored here + * @stamp: hardware time stamp transformed to system time + * @hwtstamp: original, untransformed hardware time stamp + */ static inline void skb_hwtstamp_set(struct sk_buff *skb, - union ktime stamp) -{ -#if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR) + union ktime stamp, + union ktime hwtstamp) +{ +#ifdef CONFIG_NET_SKBUFF_HWTSTAMPS + skb->tstamp = stamp; + skb->hwtstamp = hwtstamp; +#else /* CONFIG_NET_SKBUFF_HWTSTAMPS */ +# if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR) skb->tstamp.tv.sec = stamp.tv.sec; skb->tstamp.tv.nsec = stamp.tv.nsec | 1; -#else +# else skb->tstamp.tv64 = stamp.tv64 | 1; -#endif +# endif +#endif /* CONFIG_NET_SKBUFF_HWTSTAMPS */ } /** diff --git a/net/Kconfig b/net/Kconfig index 7612cc8..b37b891 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -32,6 +32,22 @@ config NET_NS Allow user space to create what appear to be multiple instances of the network stack. +config NET_SKBUFF_HWTSTAMPS + bool "Additional hardware time stamp field in struct sk_buff" + default n + depends on EXPERIMENTAL + help + Increase the size of sk_buff by 64 bits to store a raw hardware + time stamp in addition to the system time stamp. This is only + necessary when a) there is a network device which supports + hardware time stamping and b) access to these raw, unmodified values + is required. + + Usually it is sufficient to convert the raw time stamps into system + time and store that in the existing time stamp value. Increasing + the size of sk_buff can have a performance impact, so if in doubt + say N here. + source "net/packet/Kconfig" source "net/unix/Kconfig" source "net/xfrm/Kconfig" diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 7d9f1dd..8b7960e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2326,6 +2326,12 @@ EXPORT_SYMBOL_GPL(skb_segment); int skb_hwtstamp_raw(const struct sk_buff *skb, struct timespec *stamp) { +#ifdef CONFIG_NET_SKBUFF_HWTSTAMPS + if (skb_hwtstamp_available(skb)) { + *stamp = ktime_to_timespec(skb->hwtstamp); + return 1; + } +#else struct rtable *rt; struct in_device *idev; struct net_device *netdev; @@ -2342,6 +2348,7 @@ int skb_hwtstamp_raw(const struct sk_buff *skb, struct timespec *stamp) return 1; } } +#endif return 0; } @@ -2592,14 +2599,19 @@ void skb_hwtstamp_tx(struct sk_buff *orig_skb, skb_hwtstamp_set(skb, dev->hwtstamp_raw2sys ? dev->hwtstamp_raw2sys(dev, stamp) : + stamp, stamp); } else { skb->tstamp = stamp; -#if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR) - skb->tstamp.tv.sec = skb->tstamp.tv.sec / 2 * 2; +#ifdef CONFIG_NET_SKBUFF_HWTSTAMPS + skb->hwtstamp.tv64 = 0; #else +# if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR) + skb->tstamp.tv.sec = skb->tstamp.tv.sec / 2 * 2; +# else skb->tstamp.tv64 = skb->tstamp.tv64 / 2 * 2; -#endif +# endif +#endif /* CONFIG_NET_SKBUFF_HWTSTAMPS */ } err = sock_queue_err_skb(sk, skb);