Index: sys/netinet/tcp_hostcache.c =================================================================== --- sys/netinet/tcp_hostcache.c +++ sys/netinet/tcp_hostcache.c @@ -121,6 +121,7 @@ static struct hc_metrics *tcp_hc_lookup(struct in_conninfo *); static struct hc_metrics *tcp_hc_insert(struct in_conninfo *); static int sysctl_tcp_hc_list(SYSCTL_HANDLER_ARGS); +static int sysctl_tcp_hc_histo(SYSCTL_HANDLER_ARGS); static int sysctl_tcp_hc_purgenow(SYSCTL_HANDLER_ARGS); static void tcp_hc_purge_internal(int); static void tcp_hc_purge(void *); @@ -168,6 +169,11 @@ 0, 0, sysctl_tcp_hc_list, "A", "List of all hostcache entries"); +SYSCTL_PROC(_net_inet_tcp_hostcache, OID_AUTO, histo, + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_MPSAFE, + 0, 0, sysctl_tcp_hc_histo, "A", + "Print a histogram of hostcache hashbucket utilization"); + SYSCTL_PROC(_net_inet_tcp_hostcache, OID_AUTO, purgenow, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0, sysctl_tcp_hc_purgenow, "I", @@ -692,6 +698,72 @@ return(error); } +/* + * Sysctl function: prints a histogram of the hostcache hashbucket + * utilization. + */ +static int +sysctl_tcp_hc_histo(SYSCTL_HANDLER_ARGS) +{ + const int linesize = 128; + struct sbuf sb; + int i, error, n; + int *histo_cnt, *histo_tailq; + int hch_length; + struct hc_metrics *hc_entry; + + if (jailed_without_vnet(curthread->td_ucred) != 0) + return (EPERM); + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) { + return(error); + } + + histo_cnt = (int *)malloc(sizeof(int) * V_tcp_hostcache.bucket_limit, M_TEMP, M_NOWAIT|M_ZERO); + if (histo_cnt == NULL) { + return(ENOMEM); + } + histo_tailq = (int *)malloc(sizeof(int) * V_tcp_hostcache.bucket_limit, M_TEMP, M_NOWAIT|M_ZERO); + if (histo_tailq == NULL) { + return(ENOMEM); + } + + /* Use a buffer for 16 lines */ + sbuf_new_for_sysctl(&sb, NULL, 16 * linesize, req); + + for (i = 0; i < V_tcp_hostcache.hashsize; i++) { + hch_length = V_tcp_hostcache.hashbase[i].hch_length; + if (hch_length < V_tcp_hostcache.bucket_limit) { + histo_cnt[hch_length]++; + } else { + sbuf_printf(&sb, "excessive bucket length at %u: %u\n", + i, hch_length); + } + n = 0; + THC_LOCK(&V_tcp_hostcache.hashbase[i].hch_mtx); + TAILQ_FOREACH(hc_entry, &V_tcp_hostcache.hashbase[i].hch_bucket, + rmx_q) { + n++; + } + THC_UNLOCK(&V_tcp_hostcache.hashbase[i].hch_mtx); + if (n < V_tcp_hostcache.bucket_limit) { + histo_tailq[n]++; + } else { + sbuf_printf(&sb, "excessive bucket length at %u: %u\n", + i, n); + } + } + + sbuf_printf(&sb, "\nLength\tCount\tList\n"); + for (i = 0; i < V_tcp_hostcache.bucket_limit; i++) { + sbuf_printf(&sb, "%u\t%u\t%u\n", i, histo_cnt[i], histo_tailq[i]); + } + error = sbuf_finish(&sb); + sbuf_delete(&sb); + return(error); +} + /* * Caller has to make sure the curvnet is set properly. */ @@ -714,8 +786,19 @@ } else hc_entry->rmx_expire -= V_tcp_hostcache.prune; } + if (all) + V_tcp_hostcache.hashbase[i].hch_length = 0; THC_UNLOCK(&V_tcp_hostcache.hashbase[i].hch_mtx); } + if (all && V_tcp_hostcache.cache_count != 0) { + for (i = 0; i < V_tcp_hostcache.hashsize; i++) + THC_LOCK(&V_tcp_hostcache.hashbase[i].hch_mtx); + V_tcp_hostcache.cache_count = 0; + for (--i; i >= 0 ; i--) + V_tcp_hostcache.cache_count += V_tcp_hostcache.hashbase[i].hch_length; + for (++i; i < V_tcp_hostcache.hashsize; i++) + THC_UNLOCK(&V_tcp_hostcache.hashbase[i].hch_mtx); + } } /*