Commit a44d6eac authored by Martin KaFai Lau's avatar Martin KaFai Lau Committed by David S. Miller

tcp: Add RFC4898 tcpEStatsPerfDataSegsOut/In

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 a9d99ce2 ("tcp: fix tcpi_segs_in after connection establishment")
Signed-off-by: default avatarMartin 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: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent efc21d95
...@@ -158,6 +158,9 @@ struct tcp_sock { ...@@ -158,6 +158,9 @@ struct tcp_sock {
u32 segs_in; /* RFC4898 tcpEStatsPerfSegsIn u32 segs_in; /* RFC4898 tcpEStatsPerfSegsIn
* total number of segments in. * 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 rcv_nxt; /* What we want to receive next */
u32 copied_seq; /* Head of yet unread data */ u32 copied_seq; /* Head of yet unread data */
u32 rcv_wup; /* rcv_nxt on last window update sent */ u32 rcv_wup; /* rcv_nxt on last window update sent */
...@@ -165,6 +168,9 @@ struct tcp_sock { ...@@ -165,6 +168,9 @@ struct tcp_sock {
u32 segs_out; /* RFC4898 tcpEStatsPerfSegsOut u32 segs_out; /* RFC4898 tcpEStatsPerfSegsOut
* The total number of segments sent. * The total number of segments sent.
*/ */
u32 data_segs_out; /* RFC4898 tcpEStatsPerfDataSegsOut
* total number of data segments sent.
*/
u64 bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked u64 bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked
* sum(delta(snd_una)), or how many bytes * sum(delta(snd_una)), or how many bytes
* were acked. * were acked.
......
...@@ -1840,4 +1840,14 @@ static inline int tcp_inq(struct sock *sk) ...@@ -1840,4 +1840,14 @@ static inline int tcp_inq(struct sock *sk)
return answ; 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 */ #endif /* _TCP_H */
...@@ -199,6 +199,8 @@ struct tcp_info { ...@@ -199,6 +199,8 @@ struct tcp_info {
__u32 tcpi_notsent_bytes; __u32 tcpi_notsent_bytes;
__u32 tcpi_min_rtt; __u32 tcpi_min_rtt;
__u32 tcpi_data_segs_in; /* RFC4898 tcpEStatsDataSegsIn */
__u32 tcpi_data_segs_out; /* RFC4898 tcpEStatsDataSegsOut */
}; };
/* for TCP_MD5SIG socket option */ /* for TCP_MD5SIG socket option */
......
...@@ -2715,6 +2715,8 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) ...@@ -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_notsent_bytes = max(0, notsent_bytes);
info->tcpi_min_rtt = tcp_min_rtt(tp); 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); EXPORT_SYMBOL_GPL(tcp_get_info);
......
...@@ -140,6 +140,14 @@ void tcp_fastopen_add_skb(struct sock *sk, struct sk_buff *skb) ...@@ -140,6 +140,14 @@ void tcp_fastopen_add_skb(struct sock *sk, struct sk_buff *skb)
return; return;
skb_dst_drop(skb); 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_pull(skb, tcp_hdrlen(skb));
skb_set_owner_r(skb, sk); skb_set_owner_r(skb, sk);
......
...@@ -1650,7 +1650,7 @@ int tcp_v4_rcv(struct sk_buff *skb) ...@@ -1650,7 +1650,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
sk_incoming_cpu_update(sk); sk_incoming_cpu_update(sk);
bh_lock_sock_nested(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; ret = 0;
if (!sock_owned_by_user(sk)) { if (!sock_owned_by_user(sk)) {
if (!tcp_prequeue(sk, skb)) if (!tcp_prequeue(sk, skb))
......
...@@ -812,7 +812,7 @@ int tcp_child_process(struct sock *parent, struct sock *child, ...@@ -812,7 +812,7 @@ int tcp_child_process(struct sock *parent, struct sock *child,
int ret = 0; int ret = 0;
int state = child->sk_state; 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)) { if (!sock_owned_by_user(child)) {
ret = tcp_rcv_state_process(child, skb); ret = tcp_rcv_state_process(child, skb);
/* Wakeup parent, send SIGIO */ /* Wakeup parent, send SIGIO */
......
...@@ -1003,8 +1003,10 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, ...@@ -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)) if (likely(tcb->tcp_flags & TCPHDR_ACK))
tcp_event_ack_sent(sk, tcp_skb_pcount(skb)); 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); 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) if (after(tcb->end_seq, tp->snd_nxt) || tcb->seq == tcb->end_seq)
TCP_ADD_STATS(sock_net(sk), TCP_MIB_OUTSEGS, TCP_ADD_STATS(sock_net(sk), TCP_MIB_OUTSEGS,
......
...@@ -1443,7 +1443,7 @@ static int tcp_v6_rcv(struct sk_buff *skb) ...@@ -1443,7 +1443,7 @@ static int tcp_v6_rcv(struct sk_buff *skb)
sk_incoming_cpu_update(sk); sk_incoming_cpu_update(sk);
bh_lock_sock_nested(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; ret = 0;
if (!sock_owned_by_user(sk)) { if (!sock_owned_by_user(sk)) {
if (!tcp_prequeue(sk, skb)) if (!tcp_prequeue(sk, skb))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment