git: 8b4d0bec4311 - main - inpcb: make in_pcbbind() acquire the hash lock internally

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Sun, 12 Apr 2026 18:35:36 UTC
The branch main has been updated by glebius:

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

commit 8b4d0bec43116f5d4d1fba89d1b81f1d05805147
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2026-04-12 18:33:20 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2026-04-12 18:33:20 +0000

    inpcb: make in_pcbbind() acquire the hash lock internally
    
    Reviewed by:            markj
    Differential Revision:  https://reviews.freebsd.org/D55970
---
 sys/netinet/in_pcb.c       |  9 +++++++--
 sys/netinet/tcp_usrreq.c   | 10 ----------
 sys/netinet/udp_usrreq.c   |  6 ------
 sys/netinet6/in6_pcb.c     | 15 +++++++++++----
 sys/netinet6/udp6_usrreq.c |  4 ----
 5 files changed, 18 insertions(+), 26 deletions(-)

diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index f1053f6abe03..6c5b7869d945 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -727,22 +727,27 @@ in_pcbbind(struct inpcb *inp, struct sockaddr_in *sin, int flags,
 	KASSERT(sin == NULL || sin->sin_len == sizeof(struct sockaddr_in),
 	    ("%s: invalid address length for %p", __func__, sin));
 	INP_WLOCK_ASSERT(inp);
-	INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);
 
 	if (inp->inp_lport != 0 || inp->inp_laddr.s_addr != INADDR_ANY)
 		return (EINVAL);
 	anonport = sin == NULL || sin->sin_port == 0;
+
+	INP_HASH_WLOCK(inp->inp_pcbinfo);
 	error = in_pcbbind_setup(inp, sin, &inp->inp_laddr.s_addr,
 	    &inp->inp_lport, flags, cred);
-	if (error)
+	if (error) {
+		INP_HASH_WUNLOCK(inp->inp_pcbinfo);
 		return (error);
+	}
 	if (__predict_false((error = in_pcbinshash(inp)) != 0)) {
+		INP_HASH_WUNLOCK(inp->inp_pcbinfo);
 		MPASS(inp->inp_socket->so_options & SO_REUSEPORT_LB);
 		inp->inp_laddr.s_addr = INADDR_ANY;
 		inp->inp_lport = 0;
 		inp->inp_flags &= ~INP_BOUNDFIB;
 		return (error);
 	}
+	INP_HASH_WUNLOCK(inp->inp_pcbinfo);
 	if (anonport)
 		inp->inp_flags |= INP_ANONPORT;
 	return (0);
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index ce13f1a9cefe..72100a0fb9d9 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -256,10 +256,8 @@ tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
 		error = EAFNOSUPPORT;
 		goto out;
 	}
-	INP_HASH_WLOCK(&V_tcbinfo);
 	error = in_pcbbind(inp, sinp, V_tcp_bind_all_fibs ? 0 : INPBIND_FIB,
 	    td->td_ucred);
-	INP_HASH_WUNLOCK(&V_tcbinfo);
 out:
 	tcp_bblog_pru(tp, PRU_BIND, error);
 	TCP_PROBE2(debug__user, tp, PRU_BIND);
@@ -305,7 +303,6 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
 		goto out;
 	}
 
-	INP_HASH_WLOCK(&V_tcbinfo);
 	inp->inp_vflag &= ~INP_IPV4;
 	inp->inp_vflag |= INP_IPV6;
 #ifdef INET
@@ -318,20 +315,17 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
 			in6_sin6_2_sin(&sin, sin6);
 			if (IN_MULTICAST(ntohl(sin.sin_addr.s_addr))) {
 				error = EAFNOSUPPORT;
-				INP_HASH_WUNLOCK(&V_tcbinfo);
 				goto out;
 			}
 			inp->inp_vflag |= INP_IPV4;
 			inp->inp_vflag &= ~INP_IPV6;
 			error = in_pcbbind(inp, &sin, 0, td->td_ucred);
-			INP_HASH_WUNLOCK(&V_tcbinfo);
 			goto out;
 		}
 	}
 #endif
 	error = in6_pcbbind(inp, sin6, V_tcp_bind_all_fibs ? 0 : INPBIND_FIB,
 	    td->td_ucred);
-	INP_HASH_WUNLOCK(&V_tcbinfo);
 out:
 	if (error != 0)
 		inp->inp_vflag = vflagsav;
@@ -368,10 +362,8 @@ tcp_usr_listen(struct socket *so, int backlog, struct thread *td)
 		goto out;
 	}
 	if (inp->inp_lport == 0) {
-		INP_HASH_WLOCK(&V_tcbinfo);
 		error = in_pcbbind(inp, NULL,
 		    V_tcp_bind_all_fibs ? 0 : INPBIND_FIB, td->td_ucred);
-		INP_HASH_WUNLOCK(&V_tcbinfo);
 	}
 	if (error == 0) {
 		tcp_state_change(tp, TCPS_LISTEN);
@@ -426,7 +418,6 @@ tcp6_usr_listen(struct socket *so, int backlog, struct thread *td)
 		SOCK_UNLOCK(so);
 		goto out;
 	}
-	INP_HASH_WLOCK(&V_tcbinfo);
 	if (inp->inp_lport == 0) {
 		inp->inp_vflag &= ~INP_IPV4;
 		if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0)
@@ -434,7 +425,6 @@ tcp6_usr_listen(struct socket *so, int backlog, struct thread *td)
 		error = in6_pcbbind(inp, NULL,
 		    V_tcp_bind_all_fibs ? 0 : INPBIND_FIB, td->td_ucred);
 	}
-	INP_HASH_WUNLOCK(&V_tcbinfo);
 	if (error == 0) {
 		tcp_state_change(tp, TCPS_LISTEN);
 		solisten_proto(so, backlog);
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index e425af92048d..3e8011542753 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -1356,10 +1356,8 @@ udp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
 				.sin_len = sizeof(struct sockaddr_in),
 			};
 
-			INP_HASH_WLOCK(pcbinfo);
 			error = in_pcbbind(inp, &wild, V_udp_bind_all_fibs ?
 			    0 : INPBIND_FIB, td->td_ucred);
-			INP_HASH_WUNLOCK(pcbinfo);
 			if (error)
 				goto release;
 			lport = inp->inp_lport;
@@ -1599,11 +1597,9 @@ static int
 udp_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
 {
 	struct inpcb *inp;
-	struct inpcbinfo *pcbinfo;
 	struct sockaddr_in *sinp;
 	int error;
 
-	pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("udp_bind: inp == NULL"));
 
@@ -1622,10 +1618,8 @@ udp_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
 		return (EINVAL);
 
 	INP_WLOCK(inp);
-	INP_HASH_WLOCK(pcbinfo);
 	error = in_pcbbind(inp, sinp, V_udp_bind_all_fibs ? 0 : INPBIND_FIB,
 	    td->td_ucred);
-	INP_HASH_WUNLOCK(pcbinfo);
 	INP_WUNLOCK(inp);
 	return (error);
 }
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
index 09a62a53e054..c4469136fc90 100644
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -297,7 +297,6 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, int flags,
 	int error, fib, lookupflags, sooptions;
 
 	INP_WLOCK_ASSERT(inp);
-	INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);
 
 	if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))
 		return (EINVAL);
@@ -310,6 +309,7 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, int flags,
 		if ((error = prison_local_ip6(cred, &inp->in6p_laddr,
 		    ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0))) != 0)
 			return (error);
+		INP_HASH_WLOCK(inp->inp_pcbinfo);
 	} else {
 		KASSERT(sin6->sin6_family == AF_INET6,
 		    ("%s: invalid address family for %p", __func__, sin6));
@@ -326,11 +326,14 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, int flags,
 		fib = (flags & INPBIND_FIB) != 0 ? inp->inp_inc.inc_fibnum :
 		    RT_ALL_FIBS;
 
+		INP_HASH_WLOCK(inp->inp_pcbinfo);
 		/* See if this address/port combo is available. */
-		error = in6_pcbbind_avail(inp, sin6, fib, sooptions, lookupflags,
-		    cred);
-		if (error != 0)
+		error = in6_pcbbind_avail(inp, sin6, fib, sooptions,
+		    lookupflags, cred);
+		if (error != 0) {
+			INP_HASH_WUNLOCK(inp->inp_pcbinfo);
 			return (error);
+		}
 
 		lport = sin6->sin6_port;
 		inp->in6p_laddr = sin6->sin6_addr;
@@ -339,6 +342,7 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, int flags,
 		inp->inp_flags |= INP_BOUNDFIB;
 	if (lport == 0) {
 		if ((error = in6_pcbsetport(&inp->in6p_laddr, inp, cred)) != 0) {
+			INP_HASH_WUNLOCK(inp->inp_pcbinfo);
 			/* Undo an address bind that may have occurred. */
 			inp->inp_flags &= ~INP_BOUNDFIB;
 			inp->in6p_laddr = in6addr_any;
@@ -347,12 +351,15 @@ in6_pcbbind(struct inpcb *inp, struct sockaddr_in6 *sin6, int flags,
 	} else {
 		inp->inp_lport = lport;
 		if (in_pcbinshash(inp) != 0) {
+			INP_HASH_WUNLOCK(inp->inp_pcbinfo);
 			inp->inp_flags &= ~INP_BOUNDFIB;
 			inp->in6p_laddr = in6addr_any;
 			inp->inp_lport = 0;
 			return (EAGAIN);
 		}
 	}
+	INP_HASH_WUNLOCK(inp->inp_pcbinfo);
+
 	return (0);
 }
 
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
index 715f43f0d47c..6e37586f16e4 100644
--- a/sys/netinet6/udp6_usrreq.c
+++ b/sys/netinet6/udp6_usrreq.c
@@ -1046,11 +1046,9 @@ udp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
 {
 	struct sockaddr_in6 *sin6_p;
 	struct inpcb *inp;
-	struct inpcbinfo *pcbinfo;
 	int error;
 	u_char vflagsav;
 
-	pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("udp6_bind: inp == NULL"));
 
@@ -1062,7 +1060,6 @@ udp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
 	sin6_p = (struct sockaddr_in6 *)nam;
 
 	INP_WLOCK(inp);
-	INP_HASH_WLOCK(pcbinfo);
 	vflagsav = inp->inp_vflag;
 	inp->inp_vflag &= ~INP_IPV4;
 	inp->inp_vflag |= INP_IPV6;
@@ -1091,7 +1088,6 @@ out:
 #endif
 	if (error != 0)
 		inp->inp_vflag = vflagsav;
-	INP_HASH_WUNLOCK(pcbinfo);
 	INP_WUNLOCK(inp);
 	return (error);
 }