git: aacbd4dd578e - stable/13 - sctp: Implement sctp_inpcb_bind_locked()

Mark Johnston markj at FreeBSD.org
Wed Sep 8 12:41:51 UTC 2021


The branch stable/13 has been updated by markj:

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

commit aacbd4dd578e472b116814a4b7dcb27850e37df9
Author:     Mark Johnston <markj at FreeBSD.org>
AuthorDate: 2021-09-01 14:04:47 +0000
Commit:     Mark Johnston <markj at FreeBSD.org>
CommitDate: 2021-09-08 12:41:16 +0000

    sctp: Implement sctp_inpcb_bind_locked()
    
    This will be used by sctp_listen() to avoid dropping locks when
    performing an implicit bind.  No functional change intended.
    
    Reviewed by:    tuexen
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit 457abbb85794ad8b28d11a7cd44260eabdf3114d)
---
 sys/netinet/sctp_pcb.c | 53 +++++++++++++++++++++++++++++++++-----------------
 sys/netinet/sctp_pcb.h |  3 +++
 2 files changed, 38 insertions(+), 18 deletions(-)

diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c
index fb3b9fe6bdb8..3e517889d171 100644
--- a/sys/netinet/sctp_pcb.c
+++ b/sys/netinet/sctp_pcb.c
@@ -2801,14 +2801,19 @@ sctp_remove_laddr(struct sctp_laddr *laddr)
 	SCTP_DECR_LADDR_COUNT();
 }
 
-/* sctp_ifap is used to bypass normal local address validation checks */
+/*
+ * Bind the socket, with the PCB and global info locks held.  Note, if a
+ * socket address is specified, the PCB lock may be dropped and re-acquired.
+ *
+ * sctp_ifap is used to bypass normal local address validation checks.
+ */
 int
-sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
+sctp_inpcb_bind_locked(struct sctp_inpcb *inp, struct sockaddr *addr,
     struct sctp_ifa *sctp_ifap, struct thread *td)
 {
 	/* bind a ep to a socket address */
 	struct sctppcbhead *head;
-	struct sctp_inpcb *inp, *inp_tmp;
+	struct sctp_inpcb *inp_tmp;
 	struct inpcb *ip_inp;
 	int port_reuse_active = 0;
 	int bindall;
@@ -2821,8 +2826,11 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
 	error = 0;
 	lport = 0;
 	bindall = 1;
-	inp = (struct sctp_inpcb *)so->so_pcb;
-	ip_inp = (struct inpcb *)so->so_pcb;
+	ip_inp = &inp->ip_inp.inp;
+
+	SCTP_INP_INFO_WLOCK_ASSERT();
+	SCTP_INP_WLOCK_ASSERT(inp);
+
 #ifdef SCTP_DEBUG
 	if (addr) {
 		SCTPDBG(SCTP_DEBUG_PCB1, "Bind called port: %d\n",
@@ -2831,8 +2839,6 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
 		SCTPDBG_ADDR(SCTP_DEBUG_PCB1, addr);
 	}
 #endif
-	SCTP_INP_INFO_WLOCK();
-	SCTP_INP_WLOCK(inp);
 	if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) == 0) {
 		error = EINVAL;
 		/* already did a bind, subsequent binds NOT allowed ! */
@@ -2925,20 +2931,16 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
 	vrf_id = inp->def_vrf_id;
 
 	if (lport) {
-		/* increase our count due to the unlock we do */
-		SCTP_INP_INCR_REF(inp);
-
 		/*
 		 * Did the caller specify a port? if so we must see if an ep
 		 * already has this one bound.
 		 */
 		/* got to be root to get at low ports */
-		if (ntohs(lport) < IPPORT_RESERVED) {
-			if ((error = priv_check(td, PRIV_NETINET_RESERVEDPORT)) != 0) {
-				SCTP_INP_DECR_REF(inp);
-				goto out;
-			}
+		if (ntohs(lport) < IPPORT_RESERVED &&
+		    (error = priv_check(td, PRIV_NETINET_RESERVEDPORT)) != 0) {
+			goto out;
 		}
+		SCTP_INP_INCR_REF(inp);
 		SCTP_INP_WUNLOCK(inp);
 		if (bindall) {
 			vrf_id = inp->def_vrf_id;
@@ -2962,10 +2964,11 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
 					port_reuse_active = 1;
 					goto continue_anyway;
 				}
+				SCTP_INP_WLOCK(inp);
 				SCTP_INP_DECR_REF(inp);
 				error = EADDRINUSE;
 				SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error);
-				goto out_inp_unlocked;
+				goto out;
 			}
 		} else {
 			inp_tmp = sctp_pcb_findep(addr, 0, 1, vrf_id);
@@ -2988,10 +2991,11 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
 					port_reuse_active = 1;
 					goto continue_anyway;
 				}
+				SCTP_INP_WLOCK(inp);
 				SCTP_INP_DECR_REF(inp);
 				error = EADDRINUSE;
 				SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error);
-				goto out_inp_unlocked;
+				goto out;
 			}
 		}
 continue_anyway:
@@ -3201,8 +3205,21 @@ continue_anyway:
 	    ("%s: inp %p is already bound", __func__, inp));
 	inp->sctp_flags &= ~SCTP_PCB_FLAGS_UNBOUND;
 out:
+	return (error);
+}
+
+int
+sctp_inpcb_bind(struct socket *so, struct sockaddr *addr,
+    struct sctp_ifa *sctp_ifap, struct thread *td)
+{
+	struct sctp_inpcb *inp;
+	int error;
+
+	inp = so->so_pcb;
+	SCTP_INP_INFO_WLOCK();
+	SCTP_INP_WLOCK(inp);
+	error = sctp_inpcb_bind_locked(inp, addr, sctp_ifap, td);
 	SCTP_INP_WUNLOCK(inp);
-out_inp_unlocked:
 	SCTP_INP_INFO_WUNLOCK();
 	return (error);
 }
diff --git a/sys/netinet/sctp_pcb.h b/sys/netinet/sctp_pcb.h
index c978e8c72b42..e14c9f39356c 100644
--- a/sys/netinet/sctp_pcb.h
+++ b/sys/netinet/sctp_pcb.h
@@ -526,6 +526,9 @@ struct sctp_inpcb *sctp_pcb_findep(struct sockaddr *, int, int, uint32_t);
 int
 sctp_inpcb_bind(struct socket *, struct sockaddr *,
     struct sctp_ifa *, struct thread *);
+int
+sctp_inpcb_bind_locked(struct sctp_inpcb *, struct sockaddr *,
+    struct sctp_ifa *, struct thread *);
 
 struct sctp_tcb *
 sctp_findassociation_addr(struct mbuf *, int,


More information about the dev-commits-src-all mailing list