Index: kern/uipc_socket.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.227
diff -u -r1.227 uipc_socket.c
--- kern/uipc_socket.c	18 Feb 2005 00:52:17 -0000	1.227
+++ kern/uipc_socket.c	18 Feb 2005 15:49:13 -0000
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2004 The FreeBSD Foundation
- * Copyright (c) 2004-2005 Robert Watson
+ * Copyright (c) 2004-2005 Robert N. M. Watson
  * Copyright (c) 1982, 1986, 1988, 1990, 1993
  *	The Regents of the University of California.  All rights reserved.
  *
@@ -275,6 +275,18 @@
 	mtx_unlock(&so_global_mtx);
 }
 
+/*
+ * solisten() transitions a socket from a non-listening state to a listening
+ * state, but can also be used to update the listen queue depth on an
+ * existing listen socket.  The protocol will call back into the sockets
+ * layer using solisten_proto_check() and solisten_proto() to check and set
+ * socket-layer listen state.  Call backs are used so that the protocol can
+ * acquire both protocol and socket layer locks in whatever order is reuiqred
+ * by the protocol.
+ *
+ * Protocol implementors are advised to hold the socket lock across the
+ * socket-layer test and set to avoid races at the socket layer.
+ */
 int
 solisten(so, backlog, td)
 	struct socket *so;
@@ -283,28 +295,44 @@
 {
 	int error;
 
-	/*
-	 * XXXRW: Ordering issue here -- perhaps we need to set
-	 * SO_ACCEPTCONN before the call to pru_listen()?
-	 * XXXRW: General atomic test-and-set concerns here also.
-	 */
-	if (so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING |
-			    SS_ISDISCONNECTING))
-		return (EINVAL);
 	error = (*so->so_proto->pr_usrreqs->pru_listen)(so, td);
 	if (error)
 		return (error);
-	ACCEPT_LOCK();
-	SOCK_LOCK(so);
-	so->so_options |= SO_ACCEPTCONN;
-	SOCK_UNLOCK(so);
+
+	/*
+	 * XXXRW: The following state adjustment should occur in
+	 * solisten_proto(), but we don't currently pass the backlog request
+	 * to the protocol via pru_listen().
+	 */
 	if (backlog < 0 || backlog > somaxconn)
 		backlog = somaxconn;
 	so->so_qlimit = backlog;
-	ACCEPT_UNLOCK();
 	return (0);
 }
 
+int
+solisten_proto_check(so)
+	struct socket *so;
+{
+
+	SOCK_LOCK_ASSERT(so);
+
+	if (so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING |
+	    SS_ISDISCONNECTING))
+		return (EINVAL);
+	return (0);
+}
+
+void
+solisten_proto(so)
+	struct socket *so;
+{
+
+	SOCK_LOCK_ASSERT(so);
+
+	so->so_options |= SO_ACCEPTCONN;
+}
+
 /*
  * Attempt to free a socket.  This should really be sotryfree().
  *
Index: kern/uipc_usrreq.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/uipc_usrreq.c,v
retrieving revision 1.146
diff -u -r1.146 uipc_usrreq.c
--- kern/uipc_usrreq.c	6 Jan 2005 23:35:40 -0000	1.146
+++ kern/uipc_usrreq.c	17 Feb 2005 11:37:41 -0000
@@ -1,5 +1,5 @@
 /*-
- * Copyright 2004 Robert N. M. Watson
+ * Copyright 2004-2005 Robert N. M. Watson
  * Copyright (c) 1982, 1986, 1989, 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
  *
@@ -124,7 +124,7 @@
 static void    unp_discard(struct file *);
 static void    unp_freerights(struct file **, int);
 static int     unp_internalize(struct mbuf **, struct thread *);
-static int     unp_listen(struct unpcb *, struct thread *);
+static int     unp_listen(struct socket *, struct unpcb *, struct thread *);
 
 static int
 uipc_abort(struct socket *so)
@@ -284,7 +284,7 @@
 		UNP_UNLOCK();
 		return (EINVAL);
 	}
-	error = unp_listen(unp, td);
+	error = unp_listen(so, unp, td);
 	UNP_UNLOCK();
 	return (error);
 }
@@ -1730,17 +1730,25 @@
 }
 
 static int
-unp_listen(unp, td)
+unp_listen(so, unp, td)
+	struct socket *so;
 	struct unpcb *unp;
 	struct thread *td;
 {
+	int error;
+
 	UNP_LOCK_ASSERT();
 
-	/*
-	 * XXXRW: Why populate the local peer cred with our own credential?
-	 */
+	SOCK_LOCK(so);
+	error = solisten_proto_check(so);
+	if (error != 0) {
+		SOCK_UNLOCK(so);
+		return (error);
+	}
 	cru2x(td->td_ucred, &unp->unp_peercred);
 	unp->unp_flags |= UNP_HAVEPCCACHED;
+	solisten_proto(so);
+	SOCK_UNLOCK(so);
 	return (0);
 }
 
Index: netatm/atm_cm.c
===================================================================
RCS file: /home/ncvs/src/sys/netatm/atm_cm.c,v
retrieving revision 1.32
diff -u -r1.32 atm_cm.c
--- netatm/atm_cm.c	7 Jan 2005 01:45:36 -0000	1.32
+++ netatm/atm_cm.c	17 Feb 2005 11:10:12 -0000
@@ -524,6 +524,7 @@
  * atm_cm_release().
  *
  * Arguments:
+ *	so	optional socket pointer -- if present, will set listen state
  *	epp	pointer to endpoint definition structure
  *	token	endpoint's listen instance token
  *	ap	pointer to listening connection attributes
@@ -535,7 +536,8 @@
  *
  */
 int
-atm_cm_listen(epp, token, ap, copp)
+atm_cm_listen(so, epp, token, ap, copp)
+	struct socket	*so;
 	Atm_endpoint	*epp;
 	void		*token;
 	Atm_attributes	*ap;
@@ -718,7 +720,13 @@
 	/*
 	 * Now try to register the listening connection
 	 */
+	if (so != NULL)
+		SOCK_LOCK(so);
 	s = splnet();
+	if (so != NULL)
+		err = solisten_proto_check(so);
+	if (err)
+		goto donex;
 	if (atm_cm_match(cop->co_lattr, NULL) != NULL) {
 		/*
 		 * Can't have matching listeners
@@ -728,9 +736,13 @@
 	}
 	cop->co_state = COS_LISTEN;
 	LINK2TAIL(cop, Atm_connection, atm_listen_queue, co_next);
+	if (so != NULL)
+		solisten_proto(so);
 
 donex:
 	(void) splx(s);
+	if (so != NULL)
+		SOCK_UNLOCK(so);
 
 done:
 	if (err) {
Index: netatm/atm_socket.c
===================================================================
RCS file: /home/ncvs/src/sys/netatm/atm_socket.c,v
retrieving revision 1.22
diff -u -r1.22 atm_socket.c
--- netatm/atm_socket.c	7 Jan 2005 01:45:36 -0000	1.22
+++ netatm/atm_socket.c	16 Feb 2005 13:45:30 -0000
@@ -350,7 +350,7 @@
 	/*
 	 * Start listening for incoming calls
 	 */
-	return (atm_cm_listen(epp, atp, &atp->atp_attr, &atp->atp_conn));
+	return (atm_cm_listen(so, epp, atp, &atp->atp_attr, &atp->atp_conn));
 }
 
 
Index: netatm/atm_var.h
===================================================================
RCS file: /home/ncvs/src/sys/netatm/atm_var.h,v
retrieving revision 1.25
diff -u -r1.25 atm_var.h
--- netatm/atm_var.h	7 Jan 2005 01:45:36 -0000	1.25
+++ netatm/atm_var.h	16 Feb 2005 13:45:30 -0000
@@ -81,8 +81,8 @@
 	/* atm_cm.c */
 int		atm_cm_connect(Atm_endpoint *, void *, Atm_attributes *,
 			Atm_connection **);
-int		atm_cm_listen(Atm_endpoint *, void *, Atm_attributes *,
-			Atm_connection **);
+int		atm_cm_listen(struct socket *, Atm_endpoint *, void *,
+			Atm_attributes *, Atm_connection **);
 int		atm_cm_addllc(Atm_endpoint *, void *, struct attr_llc *,
 			Atm_connection *, Atm_connection **);
 int		atm_cm_addparty(Atm_connection *, int, struct t_atm_sap *);
Index: netatm/ipatm/ipatm_load.c
===================================================================
RCS file: /home/ncvs/src/sys/netatm/ipatm/ipatm_load.c,v
retrieving revision 1.20
diff -u -r1.20 ipatm_load.c
--- netatm/ipatm/ipatm_load.c	7 Jan 2005 01:45:37 -0000	1.20
+++ netatm/ipatm/ipatm_load.c	16 Feb 2005 13:45:30 -0000
@@ -522,8 +522,8 @@
 		/*
 		 * Now start listening
 		 */
-		if ((err = atm_cm_listen(&ipatm_endpt, (void *)(intptr_t)i,
-				&ipatm_listeners[i].attr,
+		if ((err = atm_cm_listen(NULL, &ipatm_endpt,
+				(void *)(intptr_t)i, &ipatm_listeners[i].attr,
 				&ipatm_listeners[i].conn)) != 0)
 			goto done;
 	}
Index: netgraph/bluetooth/socket/ng_btsocket_l2cap.c
===================================================================
RCS file: /home/ncvs/src/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c,v
retrieving revision 1.15
diff -u -r1.15 ng_btsocket_l2cap.c
--- netgraph/bluetooth/socket/ng_btsocket_l2cap.c	7 Jan 2005 01:45:44 -0000	1.15
+++ netgraph/bluetooth/socket/ng_btsocket_l2cap.c	16 Feb 2005 14:10:46 -0000
@@ -2409,13 +2409,28 @@
 ng_btsocket_l2cap_listen(struct socket *so, struct thread *td)
 {
 	ng_btsocket_l2cap_pcb_p	pcb = so2l2cap_pcb(so);
+	int error;
 
-	if (pcb == NULL)
-		return (EINVAL);
-	if (ng_btsocket_l2cap_node == NULL) 
-		return (EINVAL);
-
-	return ((pcb->psm == 0)? EDESTADDRREQ : 0);
+	SOCK_LOCK(so);
+	error = solisten_proto_check(so);
+	if (error != 0)
+		goto out;
+	if (pcb == NULL) {
+		error = EINVAL;
+		goto out;
+	}
+	if (ng_btsocket_l2cap_node == NULL) {
+		error = EINVAL;
+		goto out;
+	}
+	if (pcb->psm == 0) {
+		error = EDESTADDRREQ;
+		goto out;
+	}
+	solisten_proto(so);
+out:
+	SOCK_UNLOCK(so);
+	return (error);
 } /* ng_btsocket_listen */
 
 /*
Index: netgraph/bluetooth/socket/ng_btsocket_rfcomm.c
===================================================================
RCS file: /home/ncvs/src/sys/netgraph/bluetooth/socket/ng_btsocket_rfcomm.c,v
retrieving revision 1.14
diff -u -r1.14 ng_btsocket_rfcomm.c
--- netgraph/bluetooth/socket/ng_btsocket_rfcomm.c	7 Jan 2005 01:45:44 -0000	1.14
+++ netgraph/bluetooth/socket/ng_btsocket_rfcomm.c	16 Feb 2005 13:45:30 -0000
@@ -800,6 +800,7 @@
 	ng_btsocket_rfcomm_session_p	 s = NULL;
 	struct socket			*l2so = NULL;
 	int				 error;
+	int				 socreate_error;
 
 	if (pcb == NULL)
 		return (EINVAL);
@@ -816,14 +817,18 @@
 	 * session.
 	 */
 
-	error = socreate(PF_BLUETOOTH, &l2so, SOCK_SEQPACKET,
+	socreate_error = socreate(PF_BLUETOOTH, &l2so, SOCK_SEQPACKET,
 			BLUETOOTH_PROTO_L2CAP, td->td_ucred, td);
 
-	/* 
-	 * Look for the session in LISTENING state. There can only be one.
+	/*
+	 * Transition the socket and session into the LISTENING state.  Check
+	 * for collisions first, as there can only be one.
 	 */
-
 	mtx_lock(&ng_btsocket_rfcomm_sessions_mtx);
+	SOCK_LOCK(so);
+	error = solisten_proto_check(so);
+	if (error != 0)
+		goto out;
 
 	LIST_FOREACH(s, &ng_btsocket_rfcomm_sessions, next)
 		if (s->state == NG_BTSOCKET_RFCOMM_SESSION_LISTENING)
@@ -835,10 +840,9 @@
 		 * L2CAP socket. If l2so == NULL then error has the error code 
 		 * from socreate()
 		 */
-
 		if (l2so == NULL) {
-			mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
-			return (error);
+			error = socreate_error;
+			goto out;
 		}
 
 		/* 
@@ -848,21 +852,24 @@
 		 * XXX FIXME Note that currently there is no way to adjust MTU
 		 * for the default session.
 		 */
-
 		error = ng_btsocket_rfcomm_session_create(&s, l2so,
 					NG_HCI_BDADDR_ANY, NULL, td);
-		if (error != 0) {
-			mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
-			soclose(l2so);
-
-			return (error);
-		}
-	} else if (l2so != NULL)
-		soclose(l2so); /* we don't need new L2CAP socket */
-
+		if (error != 0)
+			goto out;
+		l2so = NULL;
+	}
+	solisten_proto(so);
 	mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
-
-	return (0);
+out:
+	SOCK_UNLOCK(so);
+	mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
+	/*
+	 * If we still have an l2so reference here, it's unneeded, so release
+	 * it.
+	 */
+	if (l2so != NULL)
+		soclose(l2so);
+	return (error);
 } /* ng_btsocket_listen */
 
 /*
Index: netinet/tcp_usrreq.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_usrreq.c,v
retrieving revision 1.114
diff -u -r1.114 tcp_usrreq.c
--- netinet/tcp_usrreq.c	14 Feb 2005 07:37:50 -0000	1.114
+++ netinet/tcp_usrreq.c	16 Feb 2005 13:45:30 -0000
@@ -302,10 +302,15 @@
 	const int inirw = INI_WRITE;
 
 	COMMON_START();
-	if (inp->inp_lport == 0)
+	SOCK_LOCK(so);
+	error = solisten_proto_check(so);
+	if (error == 0 && inp->inp_lport == 0)
 		error = in_pcbbind(inp, (struct sockaddr *)0, td->td_ucred);
-	if (error == 0)
+	if (error == 0) {
 		tp->t_state = TCPS_LISTEN;
+		solisten_proto(so);
+	}
+	SOCK_UNLOCK(so);
 	COMMON_END(PRU_LISTEN);
 }
 
@@ -319,14 +324,19 @@
 	const int inirw = INI_WRITE;
 
 	COMMON_START();
-	if (inp->inp_lport == 0) {
+	SOCK_LOCK(so);
+	error = solisten_proto_check(so);
+	if (error == 0 && inp->inp_lport == 0) {
 		inp->inp_vflag &= ~INP_IPV4;
 		if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0)
 			inp->inp_vflag |= INP_IPV4;
 		error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred);
 	}
-	if (error == 0)
+	if (error == 0) {
 		tp->t_state = TCPS_LISTEN;
+		solisten_proto(so);
+	}
+	SOCK_UNLOCK(so);
 	COMMON_END(PRU_LISTEN);
 }
 #endif /* INET6 */
Index: netipx/spx_usrreq.c
===================================================================
RCS file: /home/ncvs/src/sys/netipx/spx_usrreq.c,v
retrieving revision 1.61
diff -u -r1.61 spx_usrreq.c
--- netipx/spx_usrreq.c	9 Jan 2005 05:31:16 -0000	1.61
+++ netipx/spx_usrreq.c	16 Feb 2005 13:45:30 -0000
@@ -1532,10 +1532,15 @@
 
 	IPX_LIST_LOCK();
 	IPX_LOCK(ipxp);
-	if (ipxp->ipxp_lport == 0)
+	SOCK_LOCK(so);
+	error = solisten_proto_check(so);
+	if (error == 0 && ipxp->ipxp_lport == 0)
 		error = ipx_pcbbind(ipxp, NULL, td);
-	if (error == 0)
+	if (error == 0) {
 		cb->s_state = TCPS_LISTEN;
+		solisten_proto(so);
+	}
+	SOCK_UNLOCK(so);
 	IPX_UNLOCK(ipxp);
 	IPX_LIST_UNLOCK();
 	return (error);
Index: sys/socketvar.h
===================================================================
RCS file: /home/ncvs/src/sys/sys/socketvar.h,v
retrieving revision 1.137
diff -u -r1.137 socketvar.h
--- sys/socketvar.h	30 Jan 2005 13:11:44 -0000	1.137
+++ sys/socketvar.h	16 Feb 2005 13:57:42 -0000
@@ -512,6 +512,8 @@
 void	soisdisconnected(struct socket *so);
 void	soisdisconnecting(struct socket *so);
 int	solisten(struct socket *so, int backlog, struct thread *td);
+void	solisten_proto(struct socket *so);
+int	solisten_proto_check(struct socket *so);
 struct socket *
 	sonewconn(struct socket *head, int connstatus);
 int	sooptcopyin(struct sockopt *sopt, void *buf, size_t len, size_t minlen);
