Skip to content

Commit

Permalink
net-timestamp: add key to disambiguate concurrent datagrams
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Willem de Bruijn authored and David S. Miller committed Aug 5, 2014
1 parent b9f40e2 commit 09c2d25
Show file tree
Hide file tree
Showing 7 changed files with 27 additions and 4 deletions.
1 change: 1 addition & 0 deletions include/linux/skbuff.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/*
Expand Down
2 changes: 2 additions & 0 deletions include/net/sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 5 additions & 3 deletions include/uapi/linux/net_tstamp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

/**
Expand Down
2 changes: 2 additions & 0 deletions net/core/skbuff.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 3 additions & 0 deletions net/core/sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,9 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
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,
Expand Down
6 changes: 6 additions & 0 deletions net/ipv4/ip_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -976,6 +980,8 @@ static int __ip_append_data(struct sock *sk,
/* 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.
Expand Down
9 changes: 8 additions & 1 deletion net/ipv6/ip6_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1272,8 +1273,12 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
}
}

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.
Expand Down Expand Up @@ -1397,6 +1402,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
/* 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
Expand Down

0 comments on commit 09c2d25

Please sign in to comment.