tcp: Add RFC4898 tcpEStatsPerfDataSegsOut/In
authorMartin KaFai Lau <kafai@fb.com>
Mon, 14 Mar 2016 17:52:15 +0000 (10:52 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 14 Mar 2016 18:55:26 +0000 (14:55 -0400)
Per RFC4898, they count segments sent/received
containing a positive length data segment (that includes
retransmission segments carrying data).  Unlike
tcpi_segs_out/in, tcpi_data_segs_out/in excludes segments
carrying no data (e.g. pure ack).

The patch also updates the segs_in in tcp_fastopen_add_skb()
so that segs_in >= data_segs_in property is kept.

Together with retransmission data, tcpi_data_segs_out
gives a better signal on the rxmit rate.

v6: Rebase on the latest net-next

v5: Eric pointed out that checking skb->len is still needed in
tcp_fastopen_add_skb() because skb can carry a FIN without data.
Hence, instead of open coding segs_in and data_segs_in, tcp_segs_in()
helper is used.  Comment is added to the fastopen case to explain why
segs_in has to be reset and tcp_segs_in() has to be called before
__skb_pull().

v4: Add comment to the changes in tcp_fastopen_add_skb()
and also add remark on this case in the commit message.

v3: Add const modifier to the skb parameter in tcp_segs_in()

v2: Rework based on recent fix by Eric:
commit a9d99ce28ed3 ("tcp: fix tcpi_segs_in after connection establishment")

Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Cc: Chris Rapier <rapier@psc.edu>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Marcelo Ricardo Leitner <mleitner@redhat.com>
Cc: Neal Cardwell <ncardwell@google.com>
Cc: Yuchung Cheng <ycheng@google.com>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/tcp.h
include/net/tcp.h
include/uapi/linux/tcp.h
net/ipv4/tcp.c
net/ipv4/tcp_fastopen.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_minisocks.c
net/ipv4/tcp_output.c
net/ipv6/tcp_ipv6.c

index bcbf51da4e1e4ddbf1161edd0407423863635965..7be9b124235486a42a5e49f6686296b842d2429e 100644 (file)
@@ -158,6 +158,9 @@ struct tcp_sock {
        u32     segs_in;        /* RFC4898 tcpEStatsPerfSegsIn
                                 * total number of segments in.
                                 */
+       u32     data_segs_in;   /* RFC4898 tcpEStatsPerfDataSegsIn
+                                * total number of data segments in.
+                                */
        u32     rcv_nxt;        /* What we want to receive next         */
        u32     copied_seq;     /* Head of yet unread data              */
        u32     rcv_wup;        /* rcv_nxt on last window update sent   */
@@ -165,6 +168,9 @@ struct tcp_sock {
        u32     segs_out;       /* RFC4898 tcpEStatsPerfSegsOut
                                 * The total number of segments sent.
                                 */
+       u32     data_segs_out;  /* RFC4898 tcpEStatsPerfDataSegsOut
+                                * total number of data segments sent.
+                                */
        u64     bytes_acked;    /* RFC4898 tcpEStatsAppHCThruOctetsAcked
                                 * sum(delta(snd_una)), or how many bytes
                                 * were acked.
index 0302636af98c40869b4703891c128ff87c196416..c8dbd293daae0fefb323e2163b986e421d61a05b 100644 (file)
@@ -1840,4 +1840,14 @@ static inline int tcp_inq(struct sock *sk)
        return answ;
 }
 
+static inline void tcp_segs_in(struct tcp_sock *tp, const struct sk_buff *skb)
+{
+       u16 segs_in;
+
+       segs_in = max_t(u16, 1, skb_shinfo(skb)->gso_segs);
+       tp->segs_in += segs_in;
+       if (skb->len > tcp_hdrlen(skb))
+               tp->data_segs_in += segs_in;
+}
+
 #endif /* _TCP_H */
index fe95446e9abff381a794c667ebe1321d61d04913..53e8e3fe6b1b7b01600df3159be89db31e159179 100644 (file)
@@ -199,6 +199,8 @@ struct tcp_info {
 
        __u32   tcpi_notsent_bytes;
        __u32   tcpi_min_rtt;
+       __u32   tcpi_data_segs_in;      /* RFC4898 tcpEStatsDataSegsIn */
+       __u32   tcpi_data_segs_out;     /* RFC4898 tcpEStatsDataSegsOut */
 };
 
 /* for TCP_MD5SIG socket option */
index a265f00b9df948de44f2cfd9730b50924e2226d1..992b3103ec3eed6faa50d386a0f8971437736144 100644 (file)
@@ -2715,6 +2715,8 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info)
        info->tcpi_notsent_bytes = max(0, notsent_bytes);
 
        info->tcpi_min_rtt = tcp_min_rtt(tp);
+       info->tcpi_data_segs_in = tp->data_segs_in;
+       info->tcpi_data_segs_out = tp->data_segs_out;
 }
 EXPORT_SYMBOL_GPL(tcp_get_info);
 
index fdb286ddba04e548eb47c006ac9c334fa21d07ef..4fc0061bebf482e88aea7fbe3c78ac26a33a16c5 100644 (file)
@@ -140,6 +140,14 @@ void tcp_fastopen_add_skb(struct sock *sk, struct sk_buff *skb)
                return;
 
        skb_dst_drop(skb);
+       /* segs_in has been initialized to 1 in tcp_create_openreq_child().
+        * Hence, reset segs_in to 0 before calling tcp_segs_in()
+        * to avoid double counting.  Also, tcp_segs_in() expects
+        * skb->len to include the tcp_hdrlen.  Hence, it should
+        * be called before __skb_pull().
+        */
+       tp->segs_in = 0;
+       tcp_segs_in(tp, skb);
        __skb_pull(skb, tcp_hdrlen(skb));
        skb_set_owner_r(skb, sk);
 
index 4c8d58dfac9be15e1424d3bdad82ef6c7cd8b79c..0b02ef77370572c1c1f0646c7231a208f5bb654e 100644 (file)
@@ -1650,7 +1650,7 @@ process:
        sk_incoming_cpu_update(sk);
 
        bh_lock_sock_nested(sk);
-       tcp_sk(sk)->segs_in += max_t(u16, 1, skb_shinfo(skb)->gso_segs);
+       tcp_segs_in(tcp_sk(sk), skb);
        ret = 0;
        if (!sock_owned_by_user(sk)) {
                if (!tcp_prequeue(sk, skb))
index ae90e4b34bd3c656850a79131b22febfb41cb9ad..acb366dd61e6845b306ff19ed37287806a39a755 100644 (file)
@@ -812,7 +812,7 @@ int tcp_child_process(struct sock *parent, struct sock *child,
        int ret = 0;
        int state = child->sk_state;
 
-       tcp_sk(child)->segs_in += max_t(u16, 1, skb_shinfo(skb)->gso_segs);
+       tcp_segs_in(tcp_sk(child), skb);
        if (!sock_owned_by_user(child)) {
                ret = tcp_rcv_state_process(child, skb);
                /* Wakeup parent, send SIGIO */
index 7d2c7a400456bf036ec6b7a32eaf2657eed94378..7d2dc015cd19a64505c968df43c544adabb65e0e 100644 (file)
@@ -1003,8 +1003,10 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
        if (likely(tcb->tcp_flags & TCPHDR_ACK))
                tcp_event_ack_sent(sk, tcp_skb_pcount(skb));
 
-       if (skb->len != tcp_header_size)
+       if (skb->len != tcp_header_size) {
                tcp_event_data_sent(tp, sk);
+               tp->data_segs_out += tcp_skb_pcount(skb);
+       }
 
        if (after(tcb->end_seq, tp->snd_nxt) || tcb->seq == tcb->end_seq)
                TCP_ADD_STATS(sock_net(sk), TCP_MIB_OUTSEGS,
index 33f2820181f9512fc465a6af7559c3d33f539a57..9c16565b70cc56b9b796f1b98ab8399831f65a42 100644 (file)
@@ -1443,7 +1443,7 @@ process:
        sk_incoming_cpu_update(sk);
 
        bh_lock_sock_nested(sk);
-       tcp_sk(sk)->segs_in += max_t(u16, 1, skb_shinfo(skb)->gso_segs);
+       tcp_segs_in(tcp_sk(sk), skb);
        ret = 0;
        if (!sock_owned_by_user(sk)) {
                if (!tcp_prequeue(sk, skb))