git: 91bff7d7ed46 - main - Revert "inpcb: add single peer local port caching"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 23 Oct 2026 18:56:25 UTC
The branch main has been updated by glebius:
URL: https://cgit.FreeBSD.org/src/commit/?id=91bff7d7ed461360bc992dcd411703db6cd6d2a1
commit 91bff7d7ed461360bc992dcd411703db6cd6d2a1
Author: Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2026-10-23 18:56:06 +0000
Commit: Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2026-10-23 18:56:06 +0000
Revert "inpcb: add single peer local port caching"
This reverts commit 289082428c764ed282623cc6f7ee9b7448fddad1.
Requested by: markj
---
sys/netinet/in_pcb.c | 244 +----------------------------------------------
sys/netinet/in_pcb.h | 2 +-
sys/netinet/in_pcb_var.h | 1 -
sys/netinet6/in6_pcb.c | 1 -
4 files changed, 2 insertions(+), 246 deletions(-)
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index 35eb4614e2c6..d72fb4cce587 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -726,226 +726,6 @@ out:
#endif
}
-VNET_DEFINE_STATIC(bool, lport_cache_enable) = true;
-#define V_lport_cache_enable VNET(lport_cache_enable)
-SYSCTL_BOOL(_net_inet_ip_portrange, OID_AUTO, lport_cache,
- CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(lport_cache_enable), 0,
- "Enable local port cache for peers that have many outgoing connections to");
-
-static MALLOC_DEFINE(M_LPORTS, "inp_lports", "inpcb lport cache");
-#define PORTBITS (IPPORT_MAX + 1)
-BITSET_DEFINE(lportbits, PORTBITS);
-struct lport_cache {
- struct lportbits lports;
- struct lport_cache *successor;
- uint64_t age;
- u_int refcount;
-#ifdef INVARIANTS
- union in_dependaddr faddr;
- uint16_t fport;
-#endif
-};
-
-/*
- * Remove one reference from cache, potentially freeing a chain.
- */
-static struct lport_cache *
-lport_cache_release(struct lport_cache *cache)
-{
-
- while (cache != NULL && refcount_release(&cache->refcount)) {
- struct lport_cache *next;
-
- next = atomic_load_ptr(&cache->successor);
- free(cache, M_LPORTS);
- cache = next;
- }
-
- return (cache);
-}
-
-static inline void
-lport_acquire(struct lportbits *lports, uint16_t port)
-{
- BIT_SET_ATOMIC(PORTBITS, port, lports);
-}
-
-static inline void
-lport_release(struct lportbits *lports, uint16_t port)
-{
- BIT_CLR_ATOMIC(PORTBITS, port, lports);
-}
-
-static inline bool
-lport_check(struct lportbits *lports, uint16_t port)
-{
- return (BIT_ISSET(PORTBITS, port, lports));
-}
-
-static inline void
-lport_merge(struct lportbits *dst, struct lportbits *src)
-{
- BIT_OR_ATOMIC(PORTBITS, dst, src);
-}
-
-/*
- * Return inp->inp_lport_cache, potentially promoting it.
- */
-static struct lport_cache *
-in_pcb_lport_cache(struct inpcb *inp)
-{
- struct lport_cache *cache, *next;
-
- INP_WLOCK_ASSERT(inp);
-
- if ((cache = inp->inp_lport_cache) != NULL)
- while ((next = atomic_load_ptr(&cache->successor)) != NULL) {
- MPASS(next != cache);
- inp->inp_lport_cache = next;
- if (refcount_release(&cache->refcount))
- free(cache, M_LPORTS);
- else
- refcount_acquire(&next->refcount);
- cache = next;
- }
-
- return (inp->inp_lport_cache);
-}
-
-static void
-in_pcb_cache_lport(struct inpcb *new, struct inpcb *old)
-{
- struct lport_cache *cache;
-
- INP_WLOCK_ASSERT(new);
- INP_WLOCK_ASSERT(old);
-
- /*
- * When conflicted with a wildcard bound inpcb, we could actually cache
- * the port number, but do not attach the cache to the wildcard inpcb.
- * This would complicate this function, making each iteration more
- * expensive. With the expectation that not so many ports in the
- * anonymous range are occupied by wildcard inpcbs, usually zero, seems
- * it would be more effective to not cover this case and have just tiny
- * increase in attempts rather then making each attempt heavier.
- */
-#ifdef INET6
- if (old->inp_vflag & INP_IPV6) {
- if (IN6_IS_ADDR_UNSPECIFIED(&old->in6p_faddr))
- goto out;
- } else
-#endif
- if (in_nullhost(old->inp_faddr))
- goto out;
-
- (void)in_pcb_lport_cache(new);
- (void)in_pcb_lport_cache(old);
-
- if (new->inp_lport_cache == NULL &&
- old->inp_lport_cache == NULL) {
- static uint64_t age;
-
- cache = malloc(sizeof(*cache), M_LPORTS, M_ZERO | M_NOWAIT);
- if (__predict_false(cache == NULL))
- goto out;
- refcount_init(&cache->refcount, 2);
- cache->age = atomic_fetchadd_64(&age, 1);
-#ifdef INVARIANTS
- cache->faddr = old->inp_inc.inc_ie.ie_dependfaddr;
- cache->fport = old->inp_fport;
-#endif
- lport_acquire(&cache->lports, ntohs(old->inp_lport));
- new->inp_lport_cache = old->inp_lport_cache = cache;
- } else if (new->inp_lport_cache == old->inp_lport_cache) {
- /* Race: old has grabbed the port after our lport_check(). */
- } else if (new->inp_lport_cache == NULL) {
- cache = old->inp_lport_cache;
- MPASS(memcmp(&old->inp_inc.inc_ie.ie_dependfaddr,
- &cache->faddr, sizeof(union in_dependaddr)) == 0);
- MPASS(old->inp_fport == cache->fport);
- refcount_acquire(&cache->refcount);
- new->inp_lport_cache = cache;
- } else if (old->inp_lport_cache == NULL) {
- cache = new->inp_lport_cache;
- MPASS(memcmp(&old->inp_inc.inc_ie.ie_dependfaddr,
- &cache->faddr, sizeof(union in_dependaddr)) == 0);
- MPASS(old->inp_fport == cache->fport);
- lport_acquire(&cache->lports, ntohs(old->inp_lport));
- refcount_acquire(&cache->refcount);
- old->inp_lport_cache = cache;
- } else {
- struct lport_cache *second;
-
- /*
- * We can't use caches refcounts here, as it makes it possible
- * for two competing caches to be linked against each other by
- * two parallel threads. We use age speculating that an older
- * cache usually holds more bits.
- */
- if (new->inp_lport_cache->age < old->inp_lport_cache->age) {
- cache = new->inp_lport_cache;
- second = old->inp_lport_cache;
- MPASS(cache != second);
- old->inp_lport_cache = cache;
- } else {
- cache = old->inp_lport_cache;
- second = new->inp_lport_cache;
- MPASS(cache != second);
- new->inp_lport_cache = cache;
- }
- MPASS(memcmp(&old->inp_inc.inc_ie.ie_dependfaddr,
- &cache->faddr, sizeof(union in_dependaddr)) == 0);
- MPASS(old->inp_fport == cache->fport);
- MPASS(memcmp(&second->faddr, &cache->faddr,
- sizeof(union in_dependaddr)) == 0);
- MPASS(second->fport == cache->fport);
- lport_merge(&cache->lports, &second->lports);
- refcount_acquire(&cache->refcount);
- /*
- * Now both inpcbs and winning cache are all set, and tricky
- * part is to dereference 'second'. Three possibilities here:
- * 1) our reference was the last and we free it 2) we were not
- * the last so make it point at the winner 3) we lost the race
- * to other thread that was doing the same.
- */
- if (lport_cache_release(second) == second &&
- atomic_cmpset_ptr((uintptr_t *)&second->successor,
- (uintptr_t)NULL, (uintptr_t)cache))
- refcount_acquire(&cache->refcount);
- }
-out:
- INP_WUNLOCK(old);
-}
-
-void
-in_pcb_lport_cache_free(struct inpcb *inp)
-{
- struct lport_cache *cache = inp->inp_lport_cache;
-
- INP_WLOCK_ASSERT(inp);
-
- /* Free possible chain of caches, where we were the last reference. */
- while (cache != NULL && refcount_release(&cache->refcount)) {
- struct lport_cache *next;
-
- MPASS(memcmp(&inp->inp_inc.inc_ie.ie_dependfaddr,
- &cache->faddr, sizeof(union in_dependaddr)) == 0);
- MPASS(inp->inp_fport == cache->fport);
- next = atomic_load_ptr(&cache->successor);
- free(cache, M_LPORTS);
- cache = next;
- }
- /* Walk through the remaining chain and clear our bit. */
- while (cache != NULL) {
- MPASS(memcmp(&inp->inp_inc.inc_ie.ie_dependfaddr,
- &cache->faddr, sizeof(union in_dependaddr)) == 0);
- MPASS(inp->inp_fport == cache->fport);
- lport_release(&cache->lports, ntohs(inp->inp_lport));
- cache = atomic_load_ptr(&cache->successor);
- }
- inp->inp_lport_cache = NULL;
-}
-
/*
* Assign a local port like in_pcb_lport(), but also used with connect()
* and a foreign address and port. If fsa is non-NULL, choose a local port
@@ -967,11 +747,8 @@ in_pcb_lport_dest(struct inpcbinfo_ctx *ipictx, struct inpcb *inp,
#ifdef INET6
const struct in6_addr *laddr6, *faddr6;
#endif
- const bool use_cache = V_lport_cache_enable && INP_WLOCKED(inp) &&
- fsa != NULL;
INP_LOCK_ASSERT(inp);
- MPASS(inp->inp_lport_cache == NULL);
if (inp->inp_flags & INP_HIGHPORT) {
first = V_ipport_hifirstauto; /* sysctl */
@@ -1037,11 +814,6 @@ in_pcb_lport_dest(struct inpcbinfo_ctx *ipictx, struct inpcb *inp,
.pcbinfo = ipictx->pcbinfo
};
struct inpcb *tmpinp = NULL;
- struct lport_cache *cache;
-
- if ((cache = in_pcb_lport_cache(inp)) != NULL &&
- lport_check(&cache->lports, port))
- goto next;
lport = htons(port);
@@ -1060,9 +832,6 @@ in_pcb_lport_dest(struct inpcbinfo_ctx *ipictx, struct inpcb *inp,
M_NODOM, RT_ALL_FIBS);
}
#endif
- if (use_cache && tmpinp != NULL &&
- inp_trylock(tmpinp, INPLOOKUP_WLOCKPCB))
- in_pcb_cache_lport(inp, tmpinp);
} else {
#ifdef INET6
if ((inp->inp_vflag & INP_IPV6) != 0) {
@@ -1095,22 +864,13 @@ in_pcb_lport_dest(struct inpcbinfo_ctx *ipictx, struct inpcb *inp,
break;
}
inpcbinfo_ctx_release(&tmpctx);
-next:
++port;
if (port < first || port > last)
port = first;
}
- if (count == 0) { /* completely used? */
- if (inp->inp_lport_cache != NULL) {
- lport_cache_release(inp->inp_lport_cache);
- inp->inp_lport_cache = NULL;
- }
+ if (count == 0) /* completely used? */
return (EADDRNOTAVAIL);
- }
-
- if (inp->inp_lport_cache != NULL)
- lport_acquire(&inp->inp_lport_cache->lports, ntohs(lport));
*lportp = lport;
@@ -1752,7 +1512,6 @@ in_pcbdisconnect(struct inpcb *inp)
if (inp->inp_flags & INP_UNCONNECTED)
return;
- in_pcb_lport_cache_free(inp);
in_pcbremhash(inp);
IPI_LOCK(inp->inp_pcbinfo);
CK_LIST_INSERT_HEAD(&inp->inp_pcbinfo->ipi_list_unconn.head, inp,
@@ -2180,7 +1939,6 @@ in_pcbfree(struct inpcb *inp)
inp->inp_socket->so_pcb = NULL;
inp->inp_socket = NULL;
- in_pcb_lport_cache_free(inp);
RO_INVALIDATE_CACHE(&inp->inp_route);
#ifdef MAC
mac_inpcb_destroy(inp);
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
index 5a6bbfd3b5b2..0d889feca7ed 100644
--- a/sys/netinet/in_pcb.h
+++ b/sys/netinet/in_pcb.h
@@ -382,7 +382,7 @@ struct inpcb {
};
CK_LIST_ENTRY(inpcb) inp_portlist; /* (r:e/w:h) port list */
uint64_t inp_gencnt; /* (c) generation count */
- struct lport_cache *inp_lport_cache;
+ void *spare_ptr; /* Spare pointer. */
rt_gen_t inp_rt_cookie; /* generation for route entry */
union { /* cached L3 information */
struct route inp_route;
diff --git a/sys/netinet/in_pcb_var.h b/sys/netinet/in_pcb_var.h
index 2c6c342409dd..de9a09c6b2ca 100644
--- a/sys/netinet/in_pcb_var.h
+++ b/sys/netinet/in_pcb_var.h
@@ -131,7 +131,6 @@ void in_pcbrehash(struct inpcb *, struct inpcbinfo_ctx *);
void in_pcbremhash(struct inpcb *);
struct inpcblbgroup *in_pcblbgroup_find(struct inpcb *inp,
struct lbgroupbucket **bucket);
-void in_pcb_lport_cache_free(struct inpcb *);
/*
* Load balance groups used for the SO_REUSEPORT_LB socket option. Each group
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
index e45fc0f3d832..05c04be5dd80 100644
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -565,7 +565,6 @@ in6_pcbdisconnect(struct inpcb *inp)
if (inp->inp_flags & INP_UNCONNECTED)
return;
- in_pcb_lport_cache_free(inp);
in_pcbremhash(inp);
IPI_LOCK(inp->inp_pcbinfo);
CK_LIST_INSERT_HEAD(&inp->inp_pcbinfo->ipi_list_unconn.head, inp,