Skip to contentSkip to conversation

Add software rx timestamp for TCP.

PATCH In review Mike Maloney 3 participants 6 messages Aug 22, 2017 – Aug 24, 2017

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

v2 cover Add software rx timestamp for TCP.

From: Mike Maloney <[email protected]>

Add software rx timestamps for TCP, and a test to ensure consistency of behavior between IP, UDP, and TCP implementation.

Changes since v1:
-Initialize tss->ts[1] to 0 if caller requested any timestamps. -Fix test case to validate that tss->ts[1] is zero. -Fix tests to actually use a raw socket.
-Fix --tcp flag to work on the test.

Mike Maloney (2):
tcp: Extend SOF_TIMESTAMPING_RX_SOFTWARE to TCP recvmsg selftests/net: Add a test to validate behavior of rx timestamps

 include/net/tcp.h                                  |   9 +-
 net/ipv4/tcp.c                                     |  65 ++++
 net/ipv4/tcp_input.c                               |  35 +-
 net/ipv4/tcp_ipv4.c                                |   2 +
 net/ipv6/tcp_ipv6.c                                |   2 +
 .../selftests/networking/timestamping/.gitignore   |   1 +
 .../selftests/networking/timestamping/Makefile     |   4 +-
 .../networking/timestamping/rxtimestamp.c          | 389 +++++++++++++++++++++
 8 files changed, 501 insertions(+), 6 deletions(-)
 create mode 100644 tools/testing/selftests/networking/timestamping/rxtimestamp.c
Signature · 1 line
2.14.1.480.gb18f417b89-goog
#1 lore raw

PATCH v2 1/2 tcp: Extend SOF_TIMESTAMPING_RX_SOFTWARE to TCP recvmsg

From: Mike Maloney <[email protected]>

When SOF_TIMESTAMPING_RX_SOFTWARE is enabled for tcp sockets, return the timestamp corresponding to the highest sequence number data returned.

Previously the skb->tstamp is overwritten when a TCP packet is placed in the out of order queue. While the packet is in the ooo queue, save the timestamp in the TCB_SKB_CB. This space is shared with the gso_* options which are only used on the tx path, and a previously unused 4 byte hole.

When skbs are coalesced either in the sk_receive_queue or the out_of_order_queue always choose the timestamp of the appended skb to maintain the invariant of returning the timestamp of the last byte in the recvmsg buffer.

---
 include/net/tcp.h    |  9 +++++++-
 net/ipv4/tcp.c       | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 net/ipv4/tcp_input.c | 35 ++++++++++++++++++++++++----
 net/ipv4/tcp_ipv4.c  |  2 ++
 net/ipv6/tcp_ipv6.c  |  2 ++
 5 files changed, 108 insertions(+), 5 deletions(-)
include/net/tcp.h +8 -1
index afdab3781425..f26d20e9760d 100644
@@ -774,6 +774,12 @@ struct tcp_skb_cb {
 			u16	tcp_gso_segs;
 			u16	tcp_gso_size;
 		};
+
+		/* Used to stash the receive timestamp while this skb is in the
+		 * out of order queue, as skb->tstamp is overwritten by the
+		 * rbnode.
+		 */
+		ktime_t		swtstamp;
 	};
 	__u8		tcp_flags;	/* TCP header flags. (tcp[13])	*/
 
@@ -790,7 +796,8 @@ struct tcp_skb_cb {
 	__u8		ip_dsfield;	/* IPv4 tos or IPv6 dsfield	*/
 	__u8		txstamp_ack:1,	/* Record TX timestamp for ack? */
 			eor:1,		/* Is skb MSG_EOR marked? */
-			unused:6;
+			has_rxtstamp:1,	/* SKB has a RX timestamp	*/
+			unused:5;
 	__u32		ack_seq;	/* Sequence number ACK'd	*/
 	union {
 		struct {
net/ipv4/tcp.c +65 -0

95 lines not on this page · raw message

net/ipv4/tcp_input.c +31 -4
index ddc854728a60..66abcbf6f381 100644
@@ -4246,9 +4246,15 @@ static void tcp_sack_remove(struct tcp_sock *tp)
 	tp->rx_opt.num_sacks = num_sacks;
 }
 
+enum tcp_queue {
+	OOO_QUEUE,
+	RCV_QUEUE,
+};
+
 /**
  * tcp_try_coalesce - try to merge skb to prior one
  * @sk: socket
+ * @dest: destination queue
  * @to: prior buffer
  * @from: buffer to add in queue
  * @fragstolen: pointer to boolean
@@ -4260,6 +4266,7 @@ static void tcp_sack_remove(struct tcp_sock *tp)
  * Returns true if caller should free @from instead of queueing it
  */
 static bool tcp_try_coalesce(struct sock *sk,
+			     enum tcp_queue dest,
 			     struct sk_buff *to,
 			     struct sk_buff *from,
 			     bool *fragstolen)
@@ -4281,6 +4288,15 @@ static bool tcp_try_coalesce(struct sock *sk,
 	TCP_SKB_CB(to)->end_seq = TCP_SKB_CB(from)->end_seq;
 	TCP_SKB_CB(to)->ack_seq = TCP_SKB_CB(from)->ack_seq;
 	TCP_SKB_CB(to)->tcp_flags |= TCP_SKB_CB(from)->tcp_flags;
+
+	if (TCP_SKB_CB(from)->has_rxtstamp) {
+		TCP_SKB_CB(to)->has_rxtstamp = true;
+		if (dest == OOO_QUEUE)
+			TCP_SKB_CB(to)->swtstamp = TCP_SKB_CB(from)->swtstamp;
+		else
+			to->tstamp = from->tstamp;
+	}
+
 	return true;
 }
 
@@ -4315,6 +4331,9 @@ static void tcp_ofo_queue(struct sock *sk)
 		}
 		p = rb_next(p);
 		rb_erase(&skb->rbnode, &tp->out_of_order_queue);
+		/* Replace tstamp which was stomped by rbnode */
+		if (TCP_SKB_CB(skb)->has_rxtstamp)
+			skb->tstamp = TCP_SKB_CB(skb)->swtstamp;
 
 		if (unlikely(!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt))) {
 			SOCK_DEBUG(sk, "ofo packet was already received\n");
@@ -4326,7 +4345,8 @@ static void tcp_ofo_queue(struct sock *sk)
 			   TCP_SKB_CB(skb)->end_seq);
 
 		tail = skb_peek_tail(&sk->sk_receive_queue);
-		eaten = tail && tcp_try_coalesce(sk, tail, skb, &fragstolen);
+		eaten = tail && tcp_try_coalesce(sk, RCV_QUEUE,
+						 tail, skb, &fragstolen);
 		tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq);
 		fin = TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN;
 		if (!eaten)
@@ -4380,6 +4400,10 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
 		return;
 	}
 
+	/* Stash tstamp to avoid being stomped on by rbnode */
+	if (TCP_SKB_CB(skb)->has_rxtstamp)
+		TCP_SKB_CB(skb)->swtstamp = skb->tstamp;
+
 	inet_csk_schedule_ack(sk);
 
 	NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFOQUEUE);
@@ -4405,7 +4429,8 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
 	/* In the typical case, we are adding an skb to the end of the list.
 	 * Use of ooo_last_skb avoids the O(Log(N)) rbtree lookup.
 	 */
-	if (tcp_try_coalesce(sk, tp->ooo_last_skb, skb, &fragstolen)) {
+	if (tcp_try_coalesce(sk, OOO_QUEUE, tp->ooo_last_skb,
+			     skb, &fragstolen)) {
 coalesce_done:
 		tcp_grow_window(sk, skb);
 		kfree_skb_partial(skb, fragstolen);
@@ -4455,7 +4480,8 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
 				__kfree_skb(skb1);
 				goto merge_right;
 			}
-		} else if (tcp_try_coalesce(sk, skb1, skb, &fragstolen)) {
+		} else if (tcp_try_coalesce(sk, OOO_QUEUE, skb1,
+					    skb, &fragstolen)) {
 			goto coalesce_done;
 		}
 		p = &parent->rb_right;
@@ -4506,7 +4532,8 @@ static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int
 
 	__skb_pull(skb, hdrlen);
 	eaten = (tail &&
-		 tcp_try_coalesce(sk, tail, skb, fragstolen)) ? 1 : 0;
+		 tcp_try_coalesce(sk, RCV_QUEUE, tail,
+				  skb, fragstolen)) ? 1 : 0;
 	tcp_rcv_nxt_update(tcp_sk(sk), TCP_SKB_CB(skb)->end_seq);
 	if (!eaten) {
 		__skb_queue_tail(&sk->sk_receive_queue, skb);
net/ipv4/tcp_ipv4.c +2 -0
index 5af8b809dfbc..a63486afa7a7 100644
@@ -1637,6 +1637,8 @@ int tcp_v4_rcv(struct sk_buff *skb)
 	TCP_SKB_CB(skb)->tcp_tw_isn = 0;
 	TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph);
 	TCP_SKB_CB(skb)->sacked	 = 0;
+	TCP_SKB_CB(skb)->has_rxtstamp =
+			skb->tstamp || skb_hwtstamps(skb)->hwtstamp;
 
 lookup:
 	sk = __inet_lookup_skb(&tcp_hashinfo, skb, __tcp_hdrlen(th), th->source,
net/ipv6/tcp_ipv6.c +2 -0
index d79a1af3252e..abba3bc2a3d9 100644
@@ -1394,6 +1394,8 @@ static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr,
 	TCP_SKB_CB(skb)->tcp_tw_isn = 0;
 	TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr);
 	TCP_SKB_CB(skb)->sacked = 0;
+	TCP_SKB_CB(skb)->has_rxtstamp =
+			skb->tstamp || skb_hwtstamps(skb)->hwtstamp;
 }
 
 static int tcp_v6_rcv(struct sk_buff *skb)
Signature · 1 line
2.14.1.480.gb18f417b89-goog
Acked-by on v2 1/2
#2 lore raw

Re: [PATCH v2 net-next 1/2] tcp: Extend SOF_TIMESTAMPING_RX_SOFTWARE to TCP recvmsg

Quoted 11 lines · Mike Maloney

From: Mike Maloney <[email protected]>

When SOF_TIMESTAMPING_RX_SOFTWARE is enabled for tcp sockets, return the timestamp corresponding to the highest sequence number data returned.

Previously the skb->tstamp is overwritten when a TCP packet is placed in the out of order queue. While the packet is in the ooo queue, save the timestamp in the TCB_SKB_CB. This space is shared with the gso_* options which are only used on the tx path, and a previously unused 4 byte hole.

Quoted 6 lines

When skbs are coalesced either in the sk_receive_queue or the out_of_order_queue always choose the timestamp of the appended skb to maintain the invariant of returning the timestamp of the last byte in the recvmsg buffer.

#3 lore raw

PATCH v2 2/2 selftests/net: Add a test to validate behavior of rx timestamps

In reply to Mike Maloney

From: Mike Maloney <[email protected]>

Validate the behavior of the combination of various timestamp socket options, and ensure consistency across ip, udp, and tcp.

---
 .../selftests/networking/timestamping/.gitignore   |   1 +
 .../selftests/networking/timestamping/Makefile     |   4 +-
 .../networking/timestamping/rxtimestamp.c          | 389 +++++++++++++++++++++
 3 files changed, 393 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/networking/timestamping/rxtimestamp.c
tools/testing/selftests/networking/timestamping/.gitignore +1 -0
index 9e69e982fb38..d9355035e746 100644
@@ -1,3 +1,4 @@
 timestamping
+rxtimestamp
 txtimestamp
 hwtstamp_config
tools/testing/selftests/networking/timestamping/Makefile +3 -1
index ccbb9edbbbb9..92fb8ee917c5 100644
@@ -1,4 +1,6 @@
-TEST_PROGS := hwtstamp_config timestamping txtimestamp
+CFLAGS += -I../../../../../usr/include
+
+TEST_PROGS := hwtstamp_config rxtimestamp timestamping txtimestamp
 
 all: $(TEST_PROGS)
 
tools/testing/selftests/networking/timestamping/rxtimestamp.c new file +389 -0

389 lines not on this page · raw message

Signature · 1 line
2.14.1.480.gb18f417b89-goog
Acked-by on v2 2/2
#4 lore raw

Re: [PATCH v2 net-next 2/2] selftests/net: Add a test to validate behavior of rx timestamps

Quoted 6 lines · Mike Maloney

From: Mike Maloney <[email protected]>

Validate the behavior of the combination of various timestamp socket options, and ensure consistency across ip, udp, and tcp.

Thanks for adding these tests.

For tcp, it may be useful to eventually also add packetdrill tests to validate the more subtle semantics, such as that the timestamp is returned only when reading the last byte of the skb.

tools/testing/selftests/networking/timestamping/rxtimestamp.c line 315

Quoted 4 lines
tools/testing/selftests/networking/timestamping/rxtimestamp.c new file +4 -0
+       do_send(src);
+
+       if (s.type == SOCK_RAW)
+               read_size += 20;  /* for IP header */

No need to revise just for this, but if submitting a v3:

please use sizeof(struct iphdr). And maybe test PF_INET6, too.

maintainer
#5 lore raw
In reply to Mike Maloney

From: Mike Maloney <[email protected]>
Date: Tue, 22 Aug 2017 17:08:47 -0400

Quoted 10 lines

From: Mike Maloney <[email protected]>

Add software rx timestamps for TCP, and a test to ensure consistency of behavior between IP, UDP, and TCP implementation.

Changes since v1:
-Initialize tss->ts[1] to 0 if caller requested any timestamps. -Fix test case to validate that tss->ts[1] is zero. -Fix tests to actually use a raw socket.
-Fix --tcp flag to work on the test.

Series applied, thanks.