Messages in this thread Patch in this message | | | Date | Thu, 19 Jul 2012 12:37:22 +0200 | Subject | Re: resurrecting tcphealth | From | "Piotr Sawuk" <> |
| |
On Mo, 16.07.2012, 17:24, Christoph Paasch wrote:
> You should do jiffies_to_msecs(tp->srtt) >> 3. > > The RTT is already exposed by tcp_info anyway... (see tcp_get_info() - where > you also see the bitshift)
thanks a lot. rtt is output for completion's sake, it helps in diagnosis. here my hopefully final version. it comes with tcp_info interface too:
diff -rub A/include/linux/tcp.h B/include/linux/tcp.h --- A/include/linux/tcp.h 2012-07-15 00:40:28.000000000 +0200 +++ B/include/linux/tcp.h 2012-07-18 13:03:50.000000000 +0200 @@ -183,6 +183,12 @@ __u32 tcpi_rcv_space;
__u32 tcpi_total_retrans; + + /* TCP Health */ + __u32 tcpi_dup_acks; + __u32 tcpi_dup_pkts; + __u32 tcpi_acks; + __u32 tcpi_pkts; };
/* for TCP_MD5SIG socket option */ @@ -492,6 +498,17 @@ * contains related tcp_cookie_transactions fields. */ struct tcp_cookie_values *cookie_values; + +#ifdef CONFIG_TCPHEALTH + /* + * TCP health monitoring counters. + */ + __u32 dup_acks_sent; + __u32 dup_pkts_recv; + __u32 acks_sent; + __u32 pkts_recv; + __u32 last_ack_sent; /* Sequence number of the last ack we sent. */ +#endif };
static inline struct tcp_sock *tcp_sk(const struct sock *sk) diff -rub A/net/ipv4/Kconfig B/net/ipv4/Kconfig --- A/net/ipv4/Kconfig 2012-07-15 00:40:28.000000000 +0200 +++ B/net/ipv4/Kconfig 2012-07-16 20:47:54.000000000 +0200 @@ -619,6 +619,28 @@ default "reno" if DEFAULT_RENO default "cubic"
+config TCPHEALTH + bool "TCP client-side health-statistics (/proc/net/tcphealth)" + default n + ---help--- + TCP Health Monitoring (established connections only): + -Duplicate ACKs indicate there could be lost or reordered packets + on the connection. + -Duplicate Packets Received signal a slow and badly inefficient + connection. + -RttEst estimates how long future packets will take on a round trip + over the connection. + + Additionally you get total amount of sent ACKs and received Packets. + All these values are displayed seperately for each connection. + If you are running a dedicated server you wont need this. + Duplicate ACKs refers only to those sent upon receiving a Packet. + A server most likely doesn't receive much Packets to count. + Hence for a server these statistics wont be meaningful. + especially since they are split into individual connections. + + If you plan to investigate why some download is slow, say Y. + config TCP_MD5SIG bool "TCP: MD5 Signature Option support (RFC2385) (EXPERIMENTAL)" depends on EXPERIMENTAL diff -rub A/net/ipv4/tcp.c B/net/ipv4/tcp.c --- A/net/ipv4/tcp.c 2012-07-15 00:40:28.000000000 +0200 +++ B/net/ipv4/tcp.c 2012-07-18 13:04:08.000000000 +0200 @@ -2723,6 +2723,13 @@ info->tcpi_rcv_space = tp->rcvq_space.space;
info->tcpi_total_retrans = tp->total_retrans; + +#ifdef TCPHEALTH + tcpi_dup_acks = tp->dup_acks_sent; + tcpi_dup_pkts = tp->dup_pkts_recv; + tcpi_acks = tp->acks_sent; + tcpi_pkts = tp->pkts_recv; +#endif } EXPORT_SYMBOL_GPL(tcp_get_info);
diff -rub A/net/ipv4/tcp_input.c B/net/ipv4/tcp_input.c --- A/net/ipv4/tcp_input.c 2012-07-15 00:40:28.000000000 +0200 +++ B/net/ipv4/tcp_input.c 2012-07-16 20:47:54.000000000 +0200 @@ -4492,6 +4492,11 @@ }
if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) { +#ifdef CONFIG_TCPHEALTH + /* Course-Grained Timeout caused retransmit inefficiency- + * this packet has been received twice. */ + tp->dup_pkts_recv++; +#endif SOCK_DEBUG(sk, "ofo packet was already received\n"); __skb_unlink(skb, &tp->out_of_order_queue); __kfree_skb(skb); @@ -4824,6 +4829,12 @@ return; }
+#ifdef CONFIG_TCPHEALTH + /* A packet is a "duplicate" if it contains bytes we have already received. */ + if (before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) + tp->dup_pkts_recv++; +#endif + if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) { /* A retransmit, 2nd most common case. Force an immediate ack. */ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_DELAYEDACKLOST); @@ -5535,6 +5546,12 @@
tp->rx_opt.saw_tstamp = 0;
+#ifdef CONFIG_TCPHEALTH + /* + * total per-connection packet arrivals. + */ + tp->pkts_recv++; +#endif /* pred_flags is 0xS?10 << 16 + snd_wnd * if header_prediction is to be made * 'S' will always be tp->tcp_header_len >> 2 diff -rub A/net/ipv4/tcp_ipv4.c B/net/ipv4/tcp_ipv4.c --- A/net/ipv4/tcp_ipv4.c 2012-07-15 00:40:28.000000000 +0200 +++ B/net/ipv4/tcp_ipv4.c 2012-07-18 11:56:32.000000000 +0200 @@ -2500,6 +2500,68 @@ return 0; }
+#ifdef CONFIG_TCPHEALTH +/* + * Output /proc/net/tcphealth + */ +#define LINESZ 128 + +int tcp_health_seq_show(struct seq_file *seq, void *v) +{ + int len, tab; + struct tcp_iter_state *st; + + if (v == SEQ_START_TOKEN) { + seq_printf(seq, + "id Local Address Remote Address RttEst(ms) AcksSent " + "DupAcksSent PktsRecv DupPktsRecv\n"); + goto out; + } + + /* Loop through established TCP connections */ + st = seq->private; + + + if (st->state == TCP_SEQ_STATE_ESTABLISHED) + { + const struct tcp_sock *tp = tcp_sk(v); + const struct inet_sock *inet = inet_sk(v); + + seq_printf(seq, "%d: %pI4:%u%n", + st->num, + &inet->inet_rcv_saddr, + ntohs(inet->inet_sport), + &tab + ); + len = 3 + 21; /* 3 is minimum length for "%d: " */ + if (tab < len) seq_printf(seq, "%*s", len - tab, ""); + else len = tab; + seq_printf(seq, " %pI4:%u%n", + &inet->inet_daddr, + ntohs(inet->inet_dport), + &tab + ); + tab += len; + len = 5 + 21 + 22; /* is num ever > 999? */ + if (tab < len) seq_printf(seq, "%*s", len - tab, ""); + else len = tab; + seq_printf(seq, " %8u %8lu %8lu %8lu %8lu%n", + jiffies_to_msecs(tp->srtt)>>3, + (unsigned long)tp->acks_sent, + (unsigned long)tp->dup_acks_sent, + (unsigned long)tp->pkts_recv, + (unsigned long)tp->dup_pkts_recv, + &tab + ); + + seq_printf(seq, "%*s\n", LINESZ - 1 - len - tab, ""); + } + +out: + return 0; +} +#endif /* CONFIG_TCPHEALTH */ + static const struct file_operations tcp_afinfo_seq_fops = { .owner = THIS_MODULE, .open = tcp_seq_open, @@ -2508,6 +2570,17 @@ .release = seq_release_net };
+#ifdef CONFIG_TCPHEALTH +static struct tcp_seq_afinfo tcphealth_seq_afinfo = { + .name = "tcphealth", + .family = AF_INET, + .seq_fops = &tcp_afinfo_seq_fops, + .seq_ops = { + .show = tcp_health_seq_show, + }, +}; +#endif + static struct tcp_seq_afinfo tcp4_seq_afinfo = { .name = "tcp", .family = AF_INET, @@ -2519,12 +2592,20 @@
static int __net_init tcp4_proc_init_net(struct net *net) { - return tcp_proc_register(net, &tcp4_seq_afinfo); + int ret = tcp_proc_register(net, &tcp4_seq_afinfo); +#ifdef CONFIG_TCPHEALTH + if(ret == 0) + ret = tcp_proc_register(net, &tcphealth_seq_afinfo); +#endif + return ret; }
static void __net_exit tcp4_proc_exit_net(struct net *net) { tcp_proc_unregister(net, &tcp4_seq_afinfo); +#ifdef CONFIG_TCPHEALTH + tcp_proc_unregister(net, &tcphealth_seq_afinfo); +#endif }
static struct pernet_operations tcp4_net_ops = { diff -rub A/net/ipv4/tcp_output.c B/net/ipv4/tcp_output.c --- A/net/ipv4/tcp_output.c 2012-07-15 00:40:28.000000000 +0200 +++ B/net/ipv4/tcp_output.c 2012-07-16 20:47:54.000000000 +0200 @@ -2772,8 +2772,19 @@ skb_reserve(buff, MAX_TCP_HEADER); tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK);
+#ifdef CONFIG_TCPHEALTH + /* If the rcv_nxt has not advanced since sending our last ACK, this is a duplicate. */ + if (tcp_sk(sk)->rcv_nxt == tcp_sk(sk)->last_ack_sent) + tcp_sk(sk)->dup_acks_sent++; + /* Record the total number of acks sent on this connection. */ + tcp_sk(sk)->acks_sent++; +#endif + /* Send it off, this clears delayed acks for us. */ TCP_SKB_CB(buff)->when = tcp_time_stamp; +#ifdef CONFIG_TCPHEALTH + tcp_sk(sk)->last_ack_sent = tcp_sk(sk)->rcv_nxt; +#endif tcp_transmit_skb(sk, buff, 0, GFP_ATOMIC); }
| |