Implementation of RFC 4898 Extended TCP Statistics (Web10G)
Subsystems (3)
PATCH 3/3 Implementation of RFC 4898 Extended TCP Statistics (Web10G)
This patch set is the union of the previous two patches. Applying this patch to the net-next kernel (commit f96fe22) provides full functionality. The DLKM and API found at
https://sourceforge.net/projects/tcpestats/files/ will allow interested parties to test out our implementation from a user perspective.
As note - to enable tcp_estats in the kernel the net.ipv4.tcp_estats must be set. To enable all statistics set net.ipv4.tcp_estats=127
--- include/linux/tcp.h | 8 + include/net/tcp.h | 1 + include/net/tcp_estats.h | 376 +++++++++++++++++++++++ include/uapi/linux/tcp.h | 6 +- net/ipv4/Kconfig | 25 ++ net/ipv4/Makefile | 1 + net/ipv4/sysctl_net_ipv4.c | 14 + net/ipv4/tcp.c | 21 +- net/ipv4/tcp_cong.c | 3 + net/ipv4/tcp_estats.c | 736 +++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_htcp.c | 1 + net/ipv4/tcp_input.c | 116 ++++++- net/ipv4/tcp_ipv4.c | 10 + net/ipv4/tcp_output.c | 61 +++- net/ipv4/tcp_timer.c | 3 + net/ipv6/tcp_ipv6.c | 7 + 16 files changed, 1368 insertions(+), 21 deletions(-) create mode 100644 include/net/tcp_estats.h create mode 100644 net/ipv4/tcp_estats.c
include/linux/tcp.h +8 -0
@@ -126,6 +126,10 @@ static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req)return (struct tcp_request_sock *)req; } +#ifdef CONFIG_TCP_ESTATS +struct tcp_estats ; +#endif +struct tcp_sock {/* inet_connection_sock has to be the first member of tcp_sock */ struct inet_connection_sock inet_conn;
@@ -309,6 +313,10 @@ struct tcp_sock {struct tcp_md5sig_info __rcu *md5sig_info;#endif +#ifdef CONFIG_TCP_ESTATS +struct tcp_estats *tcp_stats; +#endif +/* TCP fastopen related information */ struct tcp_fastopen_request *fastopen_req;/* fastopen_rsk points to request_sock that resulted in this big
include/net/tcp.h +1 -0
@@ -43,6 +43,7 @@#include <net/tcp_states.h> #include <net/inet_ecn.h> #include <net/dst.h> +#include <net/tcp_estats.h> #include <linux/seq_file.h> #include <linux/memcontrol.h>
include/net/tcp_estats.h new file +376 -0
376 lines not on this page · raw message
include/uapi/linux/tcp.h +5 -1
@@ -186,9 +186,13 @@ struct tcp_info {__u32 tcpi_rcv_space;__u32 tcpi_total_retrans; -__u64 tcpi_pacing_rate;__u64 tcpi_max_pacing_rate; + +#ifdef CONFIG_TCP_ESTATS +/* RFC 4898 extended stats Info */ +__u32 tcpi_estats_cid; +#endif };/* for TCP_MD5SIG socket option */
net/ipv4/Kconfig +25 -0
@@ -680,3 +680,28 @@ config TCP_MD5SIG on the Internet. If unsure, say N. + +config TCP_ESTATS + bool "TCP: Extended TCP statistics (RFC4898) MIB" + ---help--- + RFC 4898 specifies a number of extended statistics for TCP. This + data can be accessed using netlink. See http://www.web10g.org for + more details. + +if TCP_ESTATS + +config TCP_ESTATS_STRICT_ELAPSEDTIME + bool "TCP: ESTATS strict ElapsedSecs/Msecs counters" + depends on TCP_ESTATS + default n + ---help--- + Elapsed time since beginning of connection. + RFC4898 defines ElapsedSecs/Msecs as being updated via ktime_get + at each protocol event (sending or receiving of a segment); + as this can be a performance hit, leaving this config option off + will update elapsed based on on the jiffies counter instead. + Set to Y for strict conformance with the MIB. + + If unsure, say N. + +endif
net/ipv4/Makefile +1 -0
@@ -36,6 +36,7 @@ obj-$(CONFIG_INET_TUNNEL) += tunnel4.o obj-$(CONFIG_INET_XFRM_MODE_TRANSPORT) += xfrm4_mode_transport.o obj-$(CONFIG_INET_XFRM_MODE_TUNNEL) += xfrm4_mode_tunnel.o obj-$(CONFIG_IP_PNP) += ipconfig.o +obj-$(CONFIG_TCP_ESTATS) += tcp_estats.o obj-$(CONFIG_NETFILTER) += netfilter.o netfilter/ obj-$(CONFIG_INET_DIAG) += inet_diag.o obj-$(CONFIG_INET_TCP_DIAG) += tcp_diag.o
net/ipv4/sysctl_net_ipv4.c +14 -0
@@ -42,6 +42,11 @@ static int tcp_syn_retries_max = MAX_TCP_SYNCNT;static int ip_ping_group_range_min[] = {0 ,0 };static int ip_ping_group_range_max[] = {GID_T_MAX ,GID_T_MAX }; +/* Extended statistics (RFC4898). */ +#ifdef CONFIG_TCP_ESTATS +int sysctl_tcp_estats__read_mostly ; +#endif /* CONFIG_TCP_ESTATS */ +/* Update system visible IP port range */ static void set_local_port_range (struct net *net,int range[2 ]) {
@@ -767,6 +772,15 @@ static struct ctl_table ipv4_table[] = { .proc_handler = proc_dointvec_minmax, .extra1 = &one }, +#ifdef CONFIG_TCP_ESTATS + { + .procname = "tcp_estats", + .data = &sysctl_tcp_estats, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, +#endif /* CONFIG TCP ESTATS */ { } };
net/ipv4/tcp.c +20 -1
@@ -418,6 +418,10 @@ void tcp_init_sock(struct sock *sk) sk->sk_sndbuf = sysctl_tcp_wmem[1 ]; sk->sk_rcvbuf = sysctl_tcp_rmem[1 ]; +#ifdef CONFIG_TCP_ESTATS + tp->tcp_stats =NULL ; +#endif +local_bh_disable ();sock_update_memcg (sk);sk_sockets_allocated_inc (sk);
@@ -972,6 +976,9 @@ wait_for_memory:tcp_push (sk, flags & ~MSG_MORE , mss_now,TCP_NAGLE_PUSH , size_goal); +if (copied) +TCP_ESTATS_UPDATE (tp,tcp_estats_update_writeq (sk)); +if ((err =sk_stream_wait_memory (sk, &timeo)) !=0 ) goto do_error;
@@ -1264,9 +1271,11 @@ new_segment: wait_for_sndbuf:set_bit (SOCK_NOSPACE , &sk->sk_socket->flags); wait_for_memory: -if (copied) +if (copied) {tcp_push (sk, flags & ~MSG_MORE , mss_now,TCP_NAGLE_PUSH , size_goal); +TCP_ESTATS_UPDATE (tp,tcp_estats_update_writeq (sk)); + }if ((err =sk_stream_wait_memory (sk, &timeo)) !=0 ) goto do_error;
@@ -1658,6 +1667,8 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, *seq, TCP_SKB_CB(skb)->seq, tp->rcv_nxt, flags); } +TCP_ESTATS_UPDATE (tp,tcp_estats_update_recvq (sk)); +/* Well, if we have backlog, try to process it now yet. */ if (copied >= target && !sk->sk_backlog.tail)
@@ -2684,6 +2695,11 @@ void tcp_get_info(const struct sock *sk, struct tcp_info *info) sk->sk_pacing_rate : ~0ULL; info->tcpi_max_pacing_rate = sk->sk_max_pacing_rate != ~0U ? sk->sk_max_pacing_rate : ~0ULL ; + +#ifdef CONFIG_TCP_ESTATS + info->tcpi_estats_cid = (tp->tcp_stats && tp->tcp_stats->tcpe_cid >0 ) + ? tp->tcp_stats->tcpe_cid :0 ; +#endif }EXPORT_SYMBOL_GPL (tcp_get_info);
@@ -3101,6 +3117,9 @@ void __init tcp_init(void) tcp_hashinfo.ehash_mask + 1, tcp_hashinfo.bhash_size);tcp_metrics_init (); +BUG_ON (tcp_register_congestion_control (&tcp_reno) !=0 ); +tcp_estats_init (); +tcp_tasklet_init (); }
net/ipv4/tcp_cong.c +3 -0
@@ -295,6 +295,8 @@ void tcp_slow_start(struct tcp_sock *tp, u32 acked) {u32 cwnd = tp->snd_cwnd + acked; +TCP_ESTATS_VAR_INC (tp, stack_table, SlowStart); +if (cwnd > tp->snd_ssthresh) cwnd = tp->snd_ssthresh +1 ; tp->snd_cwnd =min (cwnd, tp->snd_cwnd_clamp);
@@ -304,6 +306,7 @@ EXPORT_SYMBOL_GPL(tcp_slow_start);/* In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd (or alternative w) */ void tcp_cong_avoid_ai (struct tcp_sock *tp,u32 w) { +TCP_ESTATS_VAR_INC (tp, stack_table, CongAvoid);if (tp->snd_cwnd_cnt >= w) {if (tp->snd_cwnd < tp->snd_cwnd_clamp) tp->snd_cwnd++;
net/ipv4/tcp_estats.c new file +736 -0
736 lines not on this page · raw message
net/ipv4/tcp_htcp.c +1 -0
@@ -251,6 +251,7 @@ static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 acked) tp->snd_cwnd_cnt += ca->pkts_acked; ca->pkts_acked =1 ; +TCP_ESTATS_VAR_INC (tp, stack_table, CongAvoid); } }
net/ipv4/tcp_input.c +105 -11
366 lines not on this page · raw message
net/ipv4/tcp_ipv4.c +10 -0
@@ -1310,6 +1310,8 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,if (!newsk) goto exit_nonewsk; +tcp_estats_create (newsk,TCP_ESTATS_ADDRTYPE_IPV4 ,TCP_ESTATS_INACTIVE ); + newsk->sk_gso_type =SKB_GSO_TCPV4 ;inet_sk_rx_dst_set (newsk, skb);
@@ -1670,6 +1672,8 @@ process: skb->dev =NULL ;bh_lock_sock_nested (sk); +TCP_ESTATS_UPDATE ( +tcp_sk (sk),tcp_estats_update_segrecv (tcp_sk (sk), skb)); ret =0 ;if (!sock_owned_by_user (sk)) {if (!tcp_prequeue (sk, skb))
@@ -1680,6 +1684,8 @@ process:NET_INC_STATS_BH (net,LINUX_MIB_TCPBACKLOGDROP ); goto discard_and_relse; } +TCP_ESTATS_UPDATE ( +tcp_sk (sk),tcp_estats_update_finish_segrecv (tcp_sk (sk)));bh_unlock_sock (sk);sock_put (sk);
@@ -1809,6 +1815,8 @@ static int tcp_v4_init_sock(struct sock *sk)tcp_sk (sk)->af_specific = &tcp_sock_ipv4_specific;#endif +tcp_estats_create (sk,TCP_ESTATS_ADDRTYPE_IPV4 ,TCP_ESTATS_ACTIVE ); +return 0 ; }
@@ -1842,6 +1850,8 @@ void tcp_v4_destroy_sock(struct sock *sk)if (inet_csk (sk)->icsk_bind_hash)inet_put_port (sk); +tcp_estats_destroy (sk); +BUG_ON (tp->fastopen_rsk !=NULL );/* If socket is aborted during connect operation */
net/ipv4/tcp_output.c +53 -8
178 lines not on this page · raw message
net/ipv4/tcp_timer.c +3 -0
@@ -477,6 +477,9 @@ out_reset_timer: icsk->icsk_rto =min (icsk->icsk_rto <<1 ,TCP_RTO_MAX ); }inet_csk_reset_xmit_timer (sk,ICSK_TIME_RETRANS , icsk->icsk_rto,TCP_RTO_MAX ); + +TCP_ESTATS_UPDATE (tp,tcp_estats_update_timeout (sk)); +if (retransmits_timed_out (sk, sysctl_tcp_retries1 +1 ,0 ,0 ))__sk_dst_reset (sk);
net/ipv6/tcp_ipv6.c +7 -0
@@ -1131,6 +1131,8 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,if (newsk ==NULL ) goto out_nonewsk; +tcp_estats_create (newsk,TCP_ESTATS_ADDRTYPE_IPV6 ,TCP_ESTATS_INACTIVE ); +/* * No need to charge this sock to the relevant IPv6 refcnt debug socks * count here, tcp_create_openreq_child now does this for us, see the
@@ -1463,6 +1465,8 @@ process: skb->dev =NULL ;bh_lock_sock_nested (sk); +TCP_ESTATS_UPDATE ( +tcp_sk (sk),tcp_estats_update_segrecv (tcp_sk (sk), skb)); ret =0 ;if (!sock_owned_by_user (sk)) {if (!tcp_prequeue (sk, skb))
@@ -1473,6 +1477,8 @@ process:NET_INC_STATS_BH (net,LINUX_MIB_TCPBACKLOGDROP ); goto discard_and_relse; } +TCP_ESTATS_UPDATE ( +tcp_sk (sk),tcp_estats_update_finish_segrecv (tcp_sk (sk)));bh_unlock_sock (sk);sock_put (sk);
@@ -1661,6 +1667,7 @@ static int tcp_v6_init_sock(struct sock *sk)#ifdef CONFIG_TCP_MD5SIG tcp_sk (sk)->af_specific = &tcp_sock_ipv6_specific;#endif +tcp_estats_create (sk,TCP_ESTATS_ADDRTYPE_IPV6 ,TCP_ESTATS_ACTIVE );return 0 ; }
Signature · 1 line
1.9.3