git: acb79b56b1eb - main - udp: make in_pcbbind_setup() acquire the hash lock internally

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

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

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

    udp: make in_pcbbind_setup() acquire the hash lock internally
    
    Reviewed by:            pouria, rrs, markj
    Differential Revision:  https://reviews.freebsd.org/D55973
---
 sys/netinet/in_pcb.c     | 99 +++++++++++++++++++++++++++---------------------
 sys/netinet/udp_usrreq.c |  4 --
 2 files changed, 56 insertions(+), 47 deletions(-)

diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index 69974d931e44..29214fbd2cf6 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -714,46 +714,6 @@ out:
 #endif
 }
 
-#ifdef INET
-int
-in_pcbbind(struct inpcb *inp, struct sockaddr_in *sin, int flags,
-    struct ucred *cred)
-{
-	int error;
-	bool anonport;
-
-	KASSERT(sin == NULL || sin->sin_family == AF_INET,
-	    ("%s: invalid address family for %p", __func__, sin));
-	KASSERT(sin == NULL || sin->sin_len == sizeof(struct sockaddr_in),
-	    ("%s: invalid address length for %p", __func__, sin));
-	INP_WLOCK_ASSERT(inp);
-
-	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) {
-		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);
-}
-#endif
-
 #if defined(INET) || defined(INET6)
 /*
  * Assign a local port like in_pcb_lport(), but also used with connect()
@@ -1016,9 +976,9 @@ in_pcbbind_avail(struct inpcb *inp, const struct in_addr laddr,
  *
  * On error, the values of *laddrp and *lportp are not changed.
  */
-int
-in_pcbbind_setup(struct inpcb *inp, struct sockaddr_in *sin, in_addr_t *laddrp,
-    u_short *lportp, int flags, struct ucred *cred)
+static int
+in_pcbbind_setup_locked(struct inpcb *inp, struct sockaddr_in *sin,
+    in_addr_t *laddrp, u_short *lportp, int flags, struct ucred *cred)
 {
 	struct socket *so = inp->inp_socket;
 	struct in_addr laddr;
@@ -1082,6 +1042,59 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr_in *sin, in_addr_t *laddrp,
 	return (0);
 }
 
+int
+in_pcbbind_setup(struct inpcb *inp, struct sockaddr_in *sin, in_addr_t *laddrp,
+    u_short *lportp, int flags, struct ucred *cred)
+{
+	int error;
+
+	INP_HASH_WLOCK(inp->inp_pcbinfo);
+	error = in_pcbbind_setup_locked(inp, sin, laddrp, lportp, flags, cred);
+	INP_HASH_WUNLOCK(inp->inp_pcbinfo);
+
+	return (error);
+}
+
+#ifdef INET
+int
+in_pcbbind(struct inpcb *inp, struct sockaddr_in *sin, int flags,
+    struct ucred *cred)
+{
+	int error;
+	bool anonport;
+
+	KASSERT(sin == NULL || sin->sin_family == AF_INET,
+	    ("%s: invalid address family for %p", __func__, sin));
+	KASSERT(sin == NULL || sin->sin_len == sizeof(struct sockaddr_in),
+	    ("%s: invalid address length for %p", __func__, sin));
+	INP_WLOCK_ASSERT(inp);
+
+	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_locked(inp, sin, &inp->inp_laddr.s_addr,
+	    &inp->inp_lport, flags, cred);
+	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);
+}
+#endif
+
 /*
  * Connect from a socket to a specified address.
  * Both address and port must be specified in argument sin.
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index a94e1ec813c4..b973b299ca56 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -1117,7 +1117,6 @@ udp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
 	int len, error = 0;
 	struct in_addr faddr, laddr;
 	struct cmsghdr *cm;
-	struct inpcbinfo *pcbinfo;
 	struct sockaddr_in *sin, src;
 	struct epoch_tracker et;
 	int cscov_partial = 0;
@@ -1289,7 +1288,6 @@ udp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
 		goto release;
 
 	pr = inp->inp_socket->so_proto->pr_protocol;
-	pcbinfo = udp_get_inpcbinfo(pr);
 
 	/*
 	 * If the IP_SENDSRCADDR control message was specified, override the
@@ -1310,10 +1308,8 @@ udp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
 			inp->inp_vflag |= INP_IPV4;
 			inp->inp_vflag &= ~INP_IPV6;
 		}
-		INP_HASH_WLOCK(pcbinfo);
 		error = in_pcbbind_setup(inp, &src, &laddr.s_addr, &lport,
 		    V_udp_bind_all_fibs ? 0 : INPBIND_FIB, td->td_ucred);
-		INP_HASH_WUNLOCK(pcbinfo);
 		if ((flags & PRUS_IPV6) != 0)
 			inp->inp_vflag = vflagsav;
 		if (error)