git: f8f05fe48d0f - main - tcp: collect and report more statistics on the TCP host cache

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Fri, 18 Sep 2026 22:02:20 UTC
The branch main has been updated by glebius:

URL: https://cgit.FreeBSD.org/src/commit/?id=f8f05fe48d0fe2538dad4c2d0f8e26ea3407b1f7

commit f8f05fe48d0fe2538dad4c2d0f8e26ea3407b1f7
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2026-09-18 21:56:54 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2026-09-18 21:56:54 +0000

    tcp: collect and report more statistics on the TCP host cache
    
    Reviewed by:            tuexen
    Differential Revision:  https://reviews.freebsd.org/D59451
---
 sys/netinet/in_kdtrace.c    | 12 ++++++++---
 sys/netinet/in_kdtrace.h    | 12 ++++++++---
 sys/netinet/tcp_hostcache.c | 12 +++++++----
 sys/netinet/tcp_var.h       | 13 +++++++++---
 usr.bin/netstat/inet.c      | 49 ++++++++++++++++++++++++++++++---------------
 5 files changed, 69 insertions(+), 29 deletions(-)

diff --git a/sys/netinet/in_kdtrace.c b/sys/netinet/in_kdtrace.c
index de2a98ce541c..917e6b8be137 100644
--- a/sys/netinet/in_kdtrace.c
+++ b/sys/netinet/in_kdtrace.c
@@ -259,9 +259,6 @@ MIB_PROBE_TCP(tcps_pawsdrop);
 MIB_PROBE_TCP(tcps_predack);
 MIB_PROBE_TCP(tcps_preddat);
 MIB_PROBE_TCP(tcps_pcbackemiss);
-MIB_PROBE_TCP(tcps_cachedrtt);
-MIB_PROBE_TCP(tcps_cachedrttvar);
-MIB_PROBE_TCP(tcps_cachedssthresh);
 MIB_PROBE_TCP(tcps_usedrtt);
 MIB_PROBE_TCP(tcps_usedrttvar);
 MIB_PROBE_TCP(tcps_usedssthresh);
@@ -289,8 +286,17 @@ MIB_PROBE_TCP(tcps_sc_recvcookie);
 MIB_PROBE_TCP(tcps_sc_spurcookie);
 MIB_PROBE_TCP(tcps_sc_failcookie);
 
+MIB_PROBE_TCP(tcps_hc_hits);
+MIB_PROBE_TCP(tcps_hc_misses);
 MIB_PROBE_TCP(tcps_hc_added);
 MIB_PROBE_TCP(tcps_hc_bucketoverflow);
+MIB_PROBE_TCP(tcps_hc_allocfail);
+MIB_PROBE_TCP(tcps_cachedrtt);
+MIB_PROBE_TCP(tcps_cachedrttvar);
+MIB_PROBE_TCP(tcps_cachedssthresh);
+MIB_PROBE_TCP(tcps_cachedcwnd);
+MIB_PROBE_TCP(tcps_cachedsendpipe);
+MIB_PROBE_TCP(tcps_cachedrecvpipe);
 
 MIB_PROBE_TCP(tcps_finwait2_drops);
 
diff --git a/sys/netinet/in_kdtrace.h b/sys/netinet/in_kdtrace.h
index a203b660d777..08fab3949b5c 100644
--- a/sys/netinet/in_kdtrace.h
+++ b/sys/netinet/in_kdtrace.h
@@ -251,9 +251,6 @@ SDT_PROBE_DECLARE(mib, tcp, count, tcps_pawsdrop);
 SDT_PROBE_DECLARE(mib, tcp, count, tcps_predack);
 SDT_PROBE_DECLARE(mib, tcp, count, tcps_preddat);
 SDT_PROBE_DECLARE(mib, tcp, count, tcps_pcbcachemiss);
-SDT_PROBE_DECLARE(mib, tcp, count, tcps_cachedrtt);
-SDT_PROBE_DECLARE(mib, tcp, count, tcps_cachedrttvar);
-SDT_PROBE_DECLARE(mib, tcp, count, tcps_cachedssthresh);
 SDT_PROBE_DECLARE(mib, tcp, count, tcps_usedrtt);
 SDT_PROBE_DECLARE(mib, tcp, count, tcps_usedrttvar);
 SDT_PROBE_DECLARE(mib, tcp, count, tcps_usedssthresh);
@@ -281,8 +278,17 @@ SDT_PROBE_DECLARE(mib, tcp, count, tcps_sc_recvcookie);
 SDT_PROBE_DECLARE(mib, tcp, count, tcps_sc_spurcookie);
 SDT_PROBE_DECLARE(mib, tcp, count, tcps_sc_failcookie);
 
+SDT_PROBE_DECLARE(mib, tcp, count, tcps_hc_hits);
+SDT_PROBE_DECLARE(mib, tcp, count, tcps_hc_misses);
 SDT_PROBE_DECLARE(mib, tcp, count, tcps_hc_added);
 SDT_PROBE_DECLARE(mib, tcp, count, tcps_hc_bucketoverflow);
+SDT_PROBE_DECLARE(mib, tcp, count, tcps_hc_allocfail);
+SDT_PROBE_DECLARE(mib, tcp, count, tcps_cachedrtt);
+SDT_PROBE_DECLARE(mib, tcp, count, tcps_cachedrttvar);
+SDT_PROBE_DECLARE(mib, tcp, count, tcps_cachedssthresh);
+SDT_PROBE_DECLARE(mib, tcp, count, tcps_cachedcwnd);
+SDT_PROBE_DECLARE(mib, tcp, count, tcps_cachedsendpipe);
+SDT_PROBE_DECLARE(mib, tcp, count, tcps_cachedrecvpipe);
 
 SDT_PROBE_DECLARE(mib, tcp, count, tcps_finwait2_drops);
 
diff --git a/sys/netinet/tcp_hostcache.c b/sys/netinet/tcp_hostcache.c
index 7a5e2ef095ec..d3bac8f7f11d 100644
--- a/sys/netinet/tcp_hostcache.c
+++ b/sys/netinet/tcp_hostcache.c
@@ -362,8 +362,11 @@ tcp_hc_lookup(const struct in_conninfo *inc)
 #ifdef	TCP_HC_COUNTERS
 		hc_entry->hc_hits++;
 #endif
-	} else
+		TCPSTAT_INC(tcps_hc_hits);
+	} else {
 		smr_exit(V_tcp_hostcache.smr);
+		TCPSTAT_INC(tcps_hc_misses);
+	}
 
 	return (hc_entry);
 }
@@ -527,6 +530,7 @@ tcp_hc_update(const struct in_conninfo *inc, struct tcp_hc_metrics *hcm)
 		hc_entry = uma_zalloc_smr(V_tcp_hostcache.zone, M_NOWAIT);
 		if (hc_entry == NULL) {
 			THC_UNLOCK(hc_head);
+			TCPSTAT_INC(tcps_hc_allocfail);
 			return;
 		}
 
@@ -584,7 +588,7 @@ tcp_hc_update(const struct in_conninfo *inc, struct tcp_hc_metrics *hcm)
 			v = ((uint64_t)hc_entry->hc_cwnd +
 			    (uint64_t)hcm->hc_cwnd) / 2;
 		atomic_store_32(&hc_entry->hc_cwnd, v);
-		/* TCPSTAT_INC(tcps_cachedcwnd); */
+		TCPSTAT_INC(tcps_cachedcwnd);
 	}
 	if (hcm->hc_sendpipe != 0) {
 		if (hc_entry->hc_sendpipe == 0)
@@ -593,7 +597,7 @@ tcp_hc_update(const struct in_conninfo *inc, struct tcp_hc_metrics *hcm)
 			v = ((uint64_t)hc_entry->hc_sendpipe +
 			    (uint64_t)hcm->hc_sendpipe) / 2;
 		atomic_store_32(&hc_entry->hc_sendpipe, v);
-		/* TCPSTAT_INC(tcps_cachedsendpipe); */
+		TCPSTAT_INC(tcps_cachedsendpipe);
 	}
 	if (hcm->hc_recvpipe != 0) {
 		if (hc_entry->hc_recvpipe == 0)
@@ -602,7 +606,7 @@ tcp_hc_update(const struct in_conninfo *inc, struct tcp_hc_metrics *hcm)
 			v = ((uint64_t)hc_entry->hc_recvpipe +
 			    (uint64_t)hcm->hc_recvpipe) / 2;
 		atomic_store_32(&hc_entry->hc_recvpipe, v);
-		/* TCPSTAT_INC(tcps_cachedrecvpipe); */
+		TCPSTAT_INC(tcps_cachedrecvpipe);
 	}
 
 	/*
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
index 39a5cca1d635..8400824761ed 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -1022,9 +1022,6 @@ struct	tcpstat {
 	uint64_t tcps_predack;		/* times hdr predict ok for acks */
 	uint64_t tcps_preddat;		/* times hdr predict ok for data pkts */
 	uint64_t tcps_pcbcachemiss;
-	uint64_t tcps_cachedrtt;	/* times cached RTT in route updated */
-	uint64_t tcps_cachedrttvar;	/* times cached rttvar updated */
-	uint64_t tcps_cachedssthresh;	/* times cached ssthresh updated */
 	uint64_t tcps_usedrtt;		/* times RTT initialized from route */
 	uint64_t tcps_usedrttvar;	/* times RTTVAR initialized from rt */
 	uint64_t tcps_usedssthresh;	/* times ssthresh initialized from rt*/
@@ -1052,8 +1049,18 @@ struct	tcpstat {
 	uint64_t tcps_sc_spurcookie;	/* SYN cookie spurious, rejected */
 	uint64_t tcps_sc_failcookie;	/* SYN cookie failed, rejected */
 
+	/* TCP host cache */
+	uint64_t tcps_hc_hits;
+	uint64_t tcps_hc_misses;
 	uint64_t tcps_hc_added;		/* entry added to hostcache */
 	uint64_t tcps_hc_bucketoverflow;/* hostcache per bucket limit hit */
+	uint64_t tcps_hc_allocfail;	/* hostcache zone full */
+	uint64_t tcps_cachedrtt;	/* times cached RTT in route updated */
+	uint64_t tcps_cachedrttvar;	/* times cached rttvar updated */
+	uint64_t tcps_cachedssthresh;	/* times cached ssthresh updated */
+	uint64_t tcps_cachedcwnd;	/* times cached congestion window */
+	uint64_t tcps_cachedsendpipe;	/* times cached send buffer size */
+	uint64_t tcps_cachedrecvpipe;	/* times cached recv buffer size */
 
 	uint64_t tcps_finwait2_drops;    /* Drop FIN_WAIT_2 connection after time limit */
 
diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c
index ddc9a2fdd2f4..07e3e952a289 100644
--- a/usr.bin/netstat/inet.c
+++ b/usr.bin/netstat/inet.c
@@ -612,6 +612,8 @@ tcp_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
 	xo_emit(m, (uintmax_t )tcpstat.f, plural(tcpstat.f))
 #define	p1a(f, m) if (tcpstat.f || sflag <= 1)				\
 	xo_emit(m, (uintmax_t )tcpstat.f)
+#define	p1b(f, m) if (tcpstat.f || sflag <= 1) \
+	xo_emit(m, (uintmax_t )tcpstat.f, plurales(tcpstat.f))
 #define	p2(f1, f2, m) if (tcpstat.f1 || tcpstat.f2 || sflag <= 1)	\
 	xo_emit(m, (uintmax_t )tcpstat.f1, plural(tcpstat.f1),		\
 	    (uintmax_t )tcpstat.f2, plural(tcpstat.f2))
@@ -714,23 +716,9 @@ tcp_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
 	    "{N:/ignored RSTs in the window%s}\n");
 	p(tcps_connects, "\t{:connections-established/%ju} "
 	    "{N:/connection%s established (including accepts)}\n");
-	p(tcps_usedrtt, "\t\t{:connections-hostcache-rtt/%ju} "
-	    "{N:/time%s used RTT from hostcache}\n");
-	p(tcps_usedrttvar, "\t\t{:connections-hostcache-rttvar/%ju} "
-	    "{N:/time%s used RTT variance from hostcache}\n");
-	p(tcps_usedssthresh, "\t\t{:connections-hostcache-ssthresh/%ju} "
-	    "{N:/time%s used slow-start threshold from hostcache}\n");
 	p2(tcps_closed, tcps_drops, "\t{:connections-closed/%ju} "
 	    "{N:/connection%s closed (including} "
 	    "{:connection-drops/%ju} {N:/drop%s})\n");
-	p(tcps_cachedrtt, "\t\t{:connections-updated-rtt-on-close/%ju} "
-	    "{N:/connection%s updated cached RTT on close}\n");
-	p(tcps_cachedrttvar, "\t\t"
-	    "{:connections-updated-variance-on-close/%ju} "
-	    "{N:/connection%s updated cached RTT variance on close}\n");
-	p(tcps_cachedssthresh, "\t\t"
-	    "{:connections-updated-ssthresh-on-close/%ju} "
-	    "{N:/connection%s updated cached ssthresh on close}\n");
 	p(tcps_conndrops, "\t{:embryonic-connections-dropped/%ju} "
 	    "{N:/embryonic connection%s dropped}\n");
 	p2(tcps_rttupdated, tcps_segstimed, "\t{:segments-updated-rtt/%ju} "
@@ -800,8 +788,37 @@ tcp_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
 
 	p3(tcps_hc_added, "\t{:entries-added/%ju} "
 	    "{N:/hostcache entr%s added}\n");
-	p1a(tcps_hc_bucketoverflow, "\t\t{:buffer-overflows/%ju} "
-	    "{N:/bucket overflow}\n");
+	p(tcps_hc_hits, "\t\t{:cache-hits/%ju} "
+	    "{N:/cache lookup hit%s}\n");
+	p1b(tcps_hc_misses, "\t\t{:cache-misses/%ju} "
+	    "{N:/cache lookup miss%s}\n");
+	p(tcps_hc_bucketoverflow, "\t\t{:bucket-overflows/%ju} "
+	    "{N:/bucket overflow%s}\n");
+	p(tcps_hc_allocfail, "\t\t{:allocation-failures/%ju} "
+	    "{N:/allocation failure%s}\n");
+	p(tcps_cachedrtt, "\t\t{:connections-updated-rtt-on-close/%ju} "
+	    "{N:/connection%s updated cached RTT on close}\n");
+	p(tcps_usedrtt, "\t\t{:connections-hostcache-rtt/%ju} "
+	    "{N:/time%s used RTT from hostcache}\n");
+	p(tcps_cachedrttvar, "\t\t"
+	    "{:connections-updated-variance-on-close/%ju} "
+	    "{N:/connection%s updated cached RTT variance on close}\n");
+	p(tcps_usedrttvar, "\t\t{:connections-hostcache-rttvar/%ju} "
+	    "{N:/time%s used RTT variance from hostcache}\n");
+	p(tcps_cachedssthresh, "\t\t"
+	    "{:connections-updated-ssthresh-on-close/%ju} "
+	    "{N:/connection%s updated cached ssthresh on close}\n");
+	p(tcps_usedssthresh, "\t\t{:connections-hostcache-ssthresh/%ju} "
+	    "{N:/time%s used slow-start threshold from hostcache}\n");
+	p(tcps_cachedcwnd, "\t\t"
+	    "{:connections-updated-cwnd-on-close/%ju} "
+	    "{N:/connection%s updated cached congestion windows on close}\n");
+	p(tcps_cachedsendpipe, "\t\t"
+	    "{:connections-updated-send-space-on-close/%ju} "
+	    "{N:/connection%s updated cached send buffer space on close}\n");
+	p(tcps_cachedrecvpipe, "\t\t"
+	    "{:connections-updated-recv-space-on-close/%ju} "
+	    "{N:/connection%s updated cached recv buffer space on close}\n");
 
 	xo_close_container("hostcache");