net-timestamp: add key to disambiguate concurrent datagrams
authorWillem de Bruijn <willemb@google.com>
Tue, 5 Aug 2014 02:11:47 +0000 (22:11 -0400)
committerDavid S. Miller <davem@davemloft.net>
Tue, 5 Aug 2014 23:35:54 +0000 (16:35 -0700)
Datagrams timestamped on transmission can coexist in the kernel stack
and be reordered in packet scheduling. When reading looped datagrams
from the socket error queue it is not always possible to unique
correlate looped data with original send() call (for application
level retransmits). Even if possible, it may be expensive and complex,
requiring packet inspection.

Introduce a data-independent ID mechanism to associate timestamps with
send calls. Pass an ID alongside the timestamp in field ee_data of
sock_extended_err.

The ID is a simple 32 bit unsigned int that is associated with the
socket and incremented on each send() call for which software tx
timestamp generation is enabled.

The feature is enabled only if SOF_TIMESTAMPING_OPT_ID is set, to
avoid changing ee_data for existing applications that expect it 0.
The counter is reset each time the flag is reenabled. Reenabling
does not change the ID of already submitted data. It is possible
to receive out of order IDs if the timestamp stream is not quiesced
first.

Signed-off-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/skbuff.h
include/net/sock.h
include/uapi/linux/net_tstamp.h
net/core/skbuff.c
net/core/sock.c
net/ipv4/ip_output.c
net/ipv6/ip6_output.c

index 477f0f60db45a84295a394a9c7b714f7f19eeb03..0e35b3af7317389b0093346f7f5334c0c4e86731 100644 (file)
@@ -278,6 +278,7 @@ struct skb_shared_info {
        unsigned short  gso_type;
        struct sk_buff  *frag_list;
        struct skb_shared_hwtstamps hwtstamps;
+       u32             tskey;
        __be32          ip6_frag_id;
 
        /*
index a21129716aae48df0f2e5c0ad918555f530612a9..52fe0bc5598a175f69da42203592c26f74befa80 100644 (file)
@@ -280,6 +280,7 @@ struct cg_proto;
   *    @sk_timer: sock cleanup timer
   *    @sk_stamp: time stamp of last packet received
   *    @sk_tsflags: SO_TIMESTAMPING socket options
+  *    @sk_tskey: counter to disambiguate concurrent tstamp requests
   *    @sk_socket: Identd and reporting IO signals
   *    @sk_user_data: RPC layer private data
   *    @sk_frag: cached page frag
@@ -414,6 +415,7 @@ struct sock {
        struct timer_list       sk_timer;
        ktime_t                 sk_stamp;
        u16                     sk_tsflags;
+       u32                     sk_tskey;
        struct socket           *sk_socket;
        void                    *sk_user_data;
        struct page_frag        sk_frag;
index f53879c0f590f6a10c891f3716dfc060aefe4bc2..1e861d2e1a315df6829db23971df02c9e0952bb3 100644 (file)
@@ -20,9 +20,11 @@ enum {
        SOF_TIMESTAMPING_SOFTWARE = (1<<4),
        SOF_TIMESTAMPING_SYS_HARDWARE = (1<<5),
        SOF_TIMESTAMPING_RAW_HARDWARE = (1<<6),
-       SOF_TIMESTAMPING_MASK =
-       (SOF_TIMESTAMPING_RAW_HARDWARE - 1) |
-       SOF_TIMESTAMPING_RAW_HARDWARE
+       SOF_TIMESTAMPING_OPT_ID = (1<<7),
+
+       SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_ID,
+       SOF_TIMESTAMPING_MASK = (SOF_TIMESTAMPING_LAST - 1) |
+                                SOF_TIMESTAMPING_LAST
 };
 
 /**
index c9f68802e99295254cb474cb08abcb2034784cbf..0df4f1d14c5a98a104b19958f1449de8c3090739 100644 (file)
@@ -3522,6 +3522,8 @@ void skb_tstamp_tx(struct sk_buff *orig_skb,
        serr->ee.ee_errno = ENOMSG;
        serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING;
        serr->ee.ee_info = SCM_TSTAMP_SND;
+       if (sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)
+               serr->ee.ee_data = skb_shinfo(skb)->tskey;
 
        err = sock_queue_err_skb(sk, skb);
 
index 47c9377e14b9982af3b61f31eca23c7f1a4953c5..1e0f1c63ad6b33122dad40ce614da0f975720f10 100644 (file)
@@ -848,6 +848,9 @@ set_rcvbuf:
                        ret = -EINVAL;
                        break;
                }
+               if (val & SOF_TIMESTAMPING_OPT_ID &&
+                   !(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID))
+                       sk->sk_tskey = 0;
                sk->sk_tsflags = val;
                if (val & SOF_TIMESTAMPING_RX_SOFTWARE)
                        sock_enable_timestamp(sk,
index b16556836d66a63981826dd3625419f3173e24ce..215af2b155cb1946ff5f4b4d7d0fff19291573b8 100644 (file)
@@ -855,11 +855,15 @@ static int __ip_append_data(struct sock *sk,
        unsigned int maxfraglen, fragheaderlen, maxnonfragsize;
        int csummode = CHECKSUM_NONE;
        struct rtable *rt = (struct rtable *)cork->dst;
+       u32 tskey = 0;
 
        skb = skb_peek_tail(queue);
 
        exthdrlen = !skb ? rt->dst.header_len : 0;
        mtu = cork->fragsize;
+       if (cork->tx_flags & SKBTX_ANY_SW_TSTAMP &&
+           sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)
+               tskey = sk->sk_tskey++;
 
        hh_len = LL_RESERVED_SPACE(rt->dst.dev);
 
@@ -976,6 +980,8 @@ alloc_new_skb:
                        /* only the initial fragment is time stamped */
                        skb_shinfo(skb)->tx_flags = cork->tx_flags;
                        cork->tx_flags = 0;
+                       skb_shinfo(skb)->tskey = tskey;
+                       tskey = 0;
 
                        /*
                         *      Find where to start putting bytes.
index f5dafe609f8b7a7b8cfa1d105079fc45f2d9b707..315a55d66079cb7129dbfe183e31bcceffd63f99 100644 (file)
@@ -1157,6 +1157,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
        int err;
        int offset = 0;
        __u8 tx_flags = 0;
+       u32 tskey = 0;
 
        if (flags&MSG_PROBE)
                return 0;
@@ -1272,8 +1273,12 @@ emsgsize:
                }
        }
 
-       if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_RAW)
+       if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_RAW) {
                sock_tx_timestamp(sk, &tx_flags);
+               if (tx_flags & SKBTX_ANY_SW_TSTAMP &&
+                   sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)
+                       tskey = sk->sk_tskey++;
+       }
 
        /*
         * Let's try using as much space as possible.
@@ -1397,6 +1402,8 @@ alloc_new_skb:
                        /* Only the initial fragment is time stamped */
                        skb_shinfo(skb)->tx_flags = tx_flags;
                        tx_flags = 0;
+                       skb_shinfo(skb)->tskey = tskey;
+                       tskey = 0;
 
                        /*
                         *      Find where to start putting bytes