tcp: export data delivery rate
authorYuchung Cheng <ycheng@google.com>
Tue, 20 Sep 2016 03:39:16 +0000 (23:39 -0400)
committerDavid S. Miller <davem@davemloft.net>
Wed, 21 Sep 2016 04:23:00 +0000 (00:23 -0400)
This commit export two new fields in struct tcp_info:

  tcpi_delivery_rate: The most recent goodput, as measured by
    tcp_rate_gen(). If the socket is limited by the sending
    application (e.g., no data to send), it reports the highest
    measurement instead of the most recent. The unit is bytes per
    second (like other rate fields in tcp_info).

  tcpi_delivery_rate_app_limited: A boolean indicating if the goodput
    was measured when the socket's throughput was limited by the
    sending application.

This delivery rate information can be useful for applications that
want to know the current throughput the TCP connection is seeing,
e.g. adaptive bitrate video streaming. It can also be very useful for
debugging or troubleshooting.

Signed-off-by: Van Jacobson <vanj@google.com>
Signed-off-by: Neal Cardwell <ncardwell@google.com>
Signed-off-by: Yuchung Cheng <ycheng@google.com>
Signed-off-by: Nandita Dukkipati <nanditad@google.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Soheil Hassas Yeganeh <soheil@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/tcp.h
include/uapi/linux/tcp.h
net/ipv4/tcp.c
net/ipv4/tcp_rate.c

index fdcd00ffcb66c4ef94f89796d3c9b4fdb8dcd007..a17ae7b8521805847aa1c81920cafd863bc4a275 100644 (file)
@@ -213,7 +213,8 @@ struct tcp_sock {
                u8 reord;    /* reordering detected */
        } rack;
        u16     advmss;         /* Advertised MSS                       */
-       u8      unused;
+       u8      rate_app_limited:1,  /* rate_{delivered,interval_us} limited? */
+               unused:7;
        u8      nonagle     : 4,/* Disable Nagle algorithm?             */
                thin_lto    : 1,/* Use linear timeouts for thin streams */
                thin_dupack : 1,/* Fast retransmit on first dupack      */
@@ -271,6 +272,8 @@ struct tcp_sock {
        u32     app_limited;    /* limited until "delivered" reaches this val */
        struct skb_mstamp first_tx_mstamp;  /* start of window send phase */
        struct skb_mstamp delivered_mstamp; /* time we reached "delivered" */
+       u32     rate_delivered;    /* saved rate sample: packets delivered */
+       u32     rate_interval_us;  /* saved rate sample: time elapsed */
 
        u32     rcv_wnd;        /* Current receiver window              */
        u32     write_seq;      /* Tail(+1) of data held in tcp send buffer */
index 482898fc433a024c3f601e61a049c685b07eaec0..73ac0db487f87cfe3d205c57856ad2d9d5dd33cd 100644 (file)
@@ -167,6 +167,7 @@ struct tcp_info {
        __u8    tcpi_backoff;
        __u8    tcpi_options;
        __u8    tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4;
+       __u8    tcpi_delivery_rate_app_limited:1;
 
        __u32   tcpi_rto;
        __u32   tcpi_ato;
@@ -211,6 +212,8 @@ struct tcp_info {
        __u32   tcpi_min_rtt;
        __u32   tcpi_data_segs_in;      /* RFC4898 tcpEStatsDataSegsIn */
        __u32   tcpi_data_segs_out;     /* RFC4898 tcpEStatsDataSegsOut */
+
+       __u64   tcpi_delivery_rate;
 };
 
 /* for TCP_MD5SIG socket option */
index 2250f891f93163d407874f86362a3c4f27a5c6d6..f253e5019d227461551b2dd3ba384e85a5e2a287 100644 (file)
@@ -2712,7 +2712,7 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info)
 {
        const struct tcp_sock *tp = tcp_sk(sk); /* iff sk_type == SOCK_STREAM */
        const struct inet_connection_sock *icsk = inet_csk(sk);
-       u32 now = tcp_time_stamp;
+       u32 now = tcp_time_stamp, intv;
        unsigned int start;
        int notsent_bytes;
        u64 rate64;
@@ -2802,6 +2802,15 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info)
        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;
+
+       info->tcpi_delivery_rate_app_limited = tp->rate_app_limited ? 1 : 0;
+       rate = READ_ONCE(tp->rate_delivered);
+       intv = READ_ONCE(tp->rate_interval_us);
+       if (rate && intv) {
+               rate64 = (u64)rate * tp->mss_cache * USEC_PER_SEC;
+               do_div(rate64, intv);
+               put_unaligned(rate64, &info->tcpi_delivery_rate);
+       }
 }
 EXPORT_SYMBOL_GPL(tcp_get_info);
 
index 52ff84be59abbc874639dae641663e5ca61a0f11..9be1581a5a08c36f4544fbdabedd9741fb266a1e 100644 (file)
@@ -149,12 +149,22 @@ void tcp_rate_gen(struct sock *sk, u32 delivered, u32 lost,
         * for connections suffer heavy or prolonged losses.
         */
        if (unlikely(rs->interval_us < tcp_min_rtt(tp))) {
-               rs->interval_us = -1;
                if (!rs->is_retrans)
                        pr_debug("tcp rate: %ld %d %u %u %u\n",
                                 rs->interval_us, rs->delivered,
                                 inet_csk(sk)->icsk_ca_state,
                                 tp->rx_opt.sack_ok, tcp_min_rtt(tp));
+               rs->interval_us = -1;
+               return;
+       }
+
+       /* Record the last non-app-limited or the highest app-limited bw */
+       if (!rs->is_app_limited ||
+           ((u64)rs->delivered * tp->rate_interval_us >=
+            (u64)tp->rate_delivered * rs->interval_us)) {
+               tp->rate_delivered = rs->delivered;
+               tp->rate_interval_us = rs->interval_us;
+               tp->rate_app_limited = rs->is_app_limited;
        }
 }