From patchwork Wed Oct 22 08:17:24 2008 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Ohly X-Patchwork-Id: 8140 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 F2D6DDDF53 for ; Wed, 12 Nov 2008 01:56:53 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756009AbYKKO4w (ORCPT ); Tue, 11 Nov 2008 09:56:52 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1755932AbYKKO4v (ORCPT ); Tue, 11 Nov 2008 09:56:51 -0500 Received: from mga11.intel.com ([192.55.52.93]:23511 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755855AbYKKO4u (ORCPT ); Tue, 11 Nov 2008 09:56:50 -0500 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga102.fm.intel.com with ESMTP; 11 Nov 2008 06:51:24 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.33,584,1220252400"; d="scan'208";a="637896255" Received: from ecld0pohly.ikn.intel.com (HELO [172.28.75.199]) ([172.28.75.199]) by fmsmga001.fm.intel.com with ESMTP; 11 Nov 2008 06:57:27 -0800 In-Reply-To: <1226414697.17450.852.camel@ecld0pohly> References: <1226414697.17450.852.camel@ecld0pohly> From: Patrick Ohly Date: Wed, 22 Oct 2008 10:17:24 +0200 Subject: [RFC PATCH 02/13] extended semantic of sk_buff::tstamp: lowest bit marks hardware time stamps 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: <1226415407.31699.1.camel@ecld0pohly> Mime-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org If generated in hardware, then the driver must convert to system time before storing the transformed value in sk_buff with skb_hwtstamp_set(). If conversion back to the original hardware time stamp is desired, then the driver needs to implement the hwtstamp_raw() callback, which is called by skb_hwtstamp_raw(). The purpose of the new skb_* methods is the hiding of how hardware time stamps are really stored. Later they might be stored in an extra field instead of mangling the existing tstamp. Signed-off-by: Patrick Ohly --- include/linux/netdevice.h | 12 +++++++ include/linux/skbuff.h | 76 +++++++++++++++++++++++++++++++++++++++++++- net/core/skbuff.c | 32 +++++++++++++++++++ 3 files changed, 118 insertions(+), 2 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 488c56e..4da51cb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -749,6 +749,18 @@ struct net_device /* for setting kernel sock attribute on TCP connection setup */ #define GSO_MAX_SIZE 65536 unsigned int gso_max_size; + + /* hardware time stamping support */ +#define HAVE_HW_TIME_STAMP + /* Transforms skb->tstamp back to the original, raw hardware + * time stamp. The value must have been generated by the + * device. Implementing this is optional, but necessary for + * SO_TIMESTAMP_HARDWARE. + * + * Returns 1 if value could be retrieved, 0 otherwise. + */ + int (*hwtstamp_raw)(const struct sk_buff *skb, + struct timespec *stamp); }; #define to_net_dev(d) container_of(d, struct net_device, dev) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 9099237..0b3b36a 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -199,7 +199,10 @@ typedef unsigned char *sk_buff_data_t; * @next: Next buffer in list * @prev: Previous buffer in list * @sk: Socket we are owned by - * @tstamp: Time we arrived + * @tstamp: Time we arrived: usually generated by ktime_get_real() and + * 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. * @dev: Device we arrived on/are leaving by * @transport_header: Transport layer header * @network_header: Network layer header @@ -1524,23 +1527,52 @@ static inline void skb_copy_to_linear_data_offset(struct sk_buff *skb, 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) + return ktime_set(skb->tstamp.tv.sec, + skb->tstamp.tv.nsec & ~1); +#else + return (ktime_t) { .tv64 = skb->tstamp.tv64 & ~1UL }; +#endif +} + /** * skb_get_timestamp - get timestamp from a skb * @skb: skb to get stamp from * @stamp: pointer to struct timeval to store stamp in * * Timestamps are stored in the skb as offsets to a base timestamp. + * The lowest bit is set if and only if the time stamp was originally + * created by hardware when processing the packet. + * * This function converts the offset back to a struct timeval and stores * it in stamp. */ static inline void skb_get_timestamp(const struct sk_buff *skb, struct timeval *stamp) { - *stamp = ktime_to_timeval(skb->tstamp); + *stamp = ktime_to_timeval(skb_get_ktime(skb)); +} + +static inline void skb_get_timestampns(const struct sk_buff *skb, struct timespec *stamp) +{ + *stamp = ktime_to_timespec(skb_get_ktime(skb)); } static inline void __net_timestamp(struct sk_buff *skb) { skb->tstamp = ktime_get_real(); + + /* + * make sure that lowest bit is never set: it marks hardware + * time stamps + */ +#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 } static inline ktime_t net_timedelta(ktime_t t) @@ -1553,6 +1585,46 @@ static inline ktime_t net_invalid_timestamp(void) return ktime_set(0, 0); } +/** + * checks whether the time stamp value has been set (= non-zero) + * and really came from hardware + */ +static inline int skb_hwtstamp_available(const struct sk_buff *skb) +{ + return skb->tstamp.tv64 & 1; +} + +static inline void skb_hwtstamp_set(struct sk_buff *skb, + union ktime stamp) +{ +#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 + skb->tstamp.tv64 = stamp.tv64 | 1; +#endif +} + +/** + * Fills the timespec with the original, "raw" time stamp as generated + * by the hardware when it processed the packet and returns 1 if such + * a hardware time stamp is unavailable or cannot be inferred. Otherwise + * it returns 0; + */ +int skb_hwtstamp_raw(const struct sk_buff *skb, struct timespec *stamp); + +/** + * Fills the timespec with the hardware time stamp generated when the + * hardware processed the packet, transformed to system time. Beware + * that this transformation is not perfect: packet A received on + * interface 1 before packet B on interface 2 might have a higher + * transformed time stamp. + * + * Returns 1 if a transformed hardware time stamp is available, 0 + * otherwise. + */ +int skb_hwtstamp_transformed(const struct sk_buff *skb, struct timespec *stamp); + extern __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len); extern __sum16 __skb_checksum_complete(struct sk_buff *skb); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index ca1ccdf..7a95062 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #ifdef CONFIG_NET_CLS_ACT #include @@ -2323,6 +2324,37 @@ err: EXPORT_SYMBOL_GPL(skb_segment); +int skb_hwtstamp_raw(const struct sk_buff *skb, struct timespec *stamp) +{ + struct rtable *rt; + struct in_device *idev; + struct net_device *netdev; + + if (skb_hwtstamp_available(skb) && + (rt = skb->rtable) != NULL && + (idev = rt->idev) != NULL && + (netdev = idev->dev) != NULL && + netdev->hwtstamp_raw) { + return netdev->hwtstamp_raw(skb, stamp); + } else { + return 0; + } +} + +EXPORT_SYMBOL_GPL(skb_hwtstamp_raw); + +int skb_hwtstamp_transformed(const struct sk_buff *skb, struct timespec *stamp) +{ + if (skb_hwtstamp_available(skb)) { + skb_get_timestampns(skb, stamp); + return 1; + } else { + return 0; + } +} + +EXPORT_SYMBOL_GPL(skb_hwtstamp_transformed); + void __init skb_init(void) { skbuff_head_cache = kmem_cache_create("skbuff_head_cache",