--- //depot/vendor/freebsd/src/sys/kern/uipc_usrreq.c	2005/02/21 22:00:52
+++ //depot/user/rwatson/proto/src/sys/kern/uipc_usrreq.c	2005/02/23 11:03:39
@@ -1,5 +1,28 @@
 /*-
  * Copyright 2004-2005 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
  * Copyright (c) 1982, 1986, 1989, 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
  *
@@ -83,38 +106,104 @@
 static ino_t	unp_ino;		/* prototype for fake inode numbers */
 
 /*
- * Currently, UNIX domain sockets are protected by a single subsystem lock,
- * which covers global data structures and variables, the contents of each
- * per-socket unpcb structure, and the so_pcb field in sockets attached to
- * the UNIX domain.  This provides for a moderate degree of paralellism, as
- * receive operations on UNIX domain sockets do not need to acquire the
- * subsystem lock.  Finer grained locking to permit send() without acquiring
- * a global lock would be a logical next step.
+ * Locking and synchronization:
+ *
+ * A global UNIX domain socket mutex protects all global variables in the
+ * implementation, as well as the linked lists tracking the set of allocated
+ * UNIX domain sockets.  These variables/fields may be read lockless using
+ * atomic operations if stale values are permissible; otherwise the global
+ * mutex is required to read or read-modify-write.  The global mutex also
+ * serves to prevent deadlock when multiple PCB locks may be acquired at once
+ * (see below).  Finally, the global mutex protects uncounted references from
+ * vnodes to sockets bound to those vnodes: to safely dereference the
+ * v_socket pointer, the global mutex must be held while a full reference is
+ * acquired.
+ *
+ * UNIX domain sockets each have one unpcb PCB associated with them from
+ * pru_attach() to pru_detach() via the so_pcb pointer.  The validity of that
+ * reference is an invariant for the lifetime of the socket, so no lock is
+ * required to dereference the so_pcb pointer if a valid socket reference is
+ * held.
+ *
+ * Each PCB has a back-pointer to its socket, unp_socket.  This pointer may
+ * only be safely dereferenced as long as a valid reference to the PCB is
+ * held.  Typically, this reference will be from the socket, or from another
+ * PCB when the referring PCB's lock is held (in order that the reference not
+ * be invalidated during use).
+ *
+ * Fields of PCBs are locked using a per-unpcb lock, unp_mtx.  Individual
+ * atomic reads without the lock may be performed "lockless", but more
+ * complex reads and read-modify-writes require the mutex to be held.  No
+ * lock order is defined between PCB locks -- multiple PCB locks may be
+ * acquired at the same time only when holding the global UNIX domain socket
+ * mutex, which prevents deadlocks.  To prevent inter-PCB references from
+ * becoming invalid, the lock protecting the reference must be held for the
+ * lifetime of use of the reference.
+ */
+
+static struct mtx unp_global_mtx;
+
+#define UNP_GLOBAL_INIT()		mtx_init(&unp_global_mtx,	\
+					    "unp_global_mtx", NULL, MTX_DEF)
+#define UNP_GLOBAL_LOCK()		mtx_lock(&unp_global_mtx)
+#define UNP_GLOBAL_UNLOCK()		mtx_unlock(&unp_global_mtx)
+#define UNP_GLOBAL_UNLOCK_ASSERT()	mtx_assert(&unp_global_mtx, MA_NOTOWNED)
+#define UNP_GLOBAL_LOCK_ASSERT()	mtx_assert(&unp_global_mtx, MA_OWNED)
+
+#define UNP_PCB_INIT(unp)		mtx_init(&(unp)->unp_mtx,       \
+					    "unp_mtx", "unp_mtx",	\
+					    MTX_DUPOK|MTX_DEF|MTX_RECURSE)
+#define UNP_PCB_DESTROY(unp)		mtx_destroy(&(unp)->unp_mtx)
+#define UNP_PCB_LOCK(unp)		mtx_lock(&(unp)->unp_mtx)
+#define UNP_PCB_UNLOCK(unp)		mtx_unlock(&(unp)->unp_mtx)
+#define UNP_PCB_LOCK_ASSERT(unp)	mtx_assert(&(unp)->unp_mtx, MA_OWNED)
+
+/*
+ * Both send and receive buffers are allocated PIPSIZ bytes of buffering for
+ * stream sockets, although the total for sender and receiver is actually
+ * only PIPSIZ.
  *
- * The UNIX domain socket lock preceds all socket layer locks, including the
- * socket lock and socket buffer lock, permitting UNIX domain socket code to
- * call into socket support routines without releasing its locks.
+ * Datagram sockets really use the sendspace as the maximum datagram size,
+ * and don't really want to reserve the sendspace.  Their recvspace should
+ * be large enough for at least one max-size datagram plus address.
+ */
+#ifndef PIPSIZ
+#define	PIPSIZ	8192
+#endif
+static u_long	unpst_sendspace = PIPSIZ;
+static u_long	unpst_recvspace = PIPSIZ;
+static u_long	unpdg_sendspace = 2*1024;	/* really max datagram size */
+static u_long	unpdg_recvspace = 4*1024;
+
+/*
+ * unp_defer is thread-local during garbage collection, and does not require
+ * explicit synchronization.  unp_gcing prevents other threads from entering
+ * garbage collection, and perhaps should be an sx lock instead.
  *
- * Some caution is required in areas where the UNIX domain socket code enters
- * VFS in order to create or find rendezvous points.  This results in
- * dropping of the UNIX domain socket subsystem lock, acquisition of the
- * Giant lock, and potential sleeping.  This increases the chances of races,
- * and exposes weaknesses in the socket->protocol API by offering poor
- * failure modes.
+ * unp_rights is a count of file descriptors "in flight".
  */
-static struct mtx unp_mtx;
-#define	UNP_LOCK_INIT() \
-	mtx_init(&unp_mtx, "unp", NULL, MTX_DEF)
-#define	UNP_LOCK()		mtx_lock(&unp_mtx)
-#define	UNP_UNLOCK()		mtx_unlock(&unp_mtx)
-#define	UNP_LOCK_ASSERT()	mtx_assert(&unp_mtx, MA_OWNED)
-#define	UNP_UNLOCK_ASSERT()	mtx_assert(&unp_mtx, MA_NOTOWNED)
+static int	unp_defer, unp_gcing;
+static int	unp_rights;
+
+SYSCTL_DECL(_net_local_stream);
+SYSCTL_INT(_net_local_stream, OID_AUTO, sendspace, CTLFLAG_RW,
+	   &unpst_sendspace, 0, "");
+SYSCTL_INT(_net_local_stream, OID_AUTO, recvspace, CTLFLAG_RW,
+	   &unpst_recvspace, 0, "");
+SYSCTL_DECL(_net_local_dgram);
+SYSCTL_INT(_net_local_dgram, OID_AUTO, maxdgram, CTLFLAG_RW,
+	   &unpdg_sendspace, 0, "");
+SYSCTL_INT(_net_local_dgram, OID_AUTO, recvspace, CTLFLAG_RW,
+	   &unpdg_recvspace, 0, "");
+SYSCTL_DECL(_net_local);
+SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD, &unp_rights, 0, "");
 
-static int     unp_attach(struct socket *);
-static void    unp_detach(struct unpcb *);
-static int     unp_bind(struct unpcb *,struct sockaddr *, struct thread *);
+static int     uipc_detach(struct socket *so);
+static int     uipc_shutdown(struct socket *so);
 static int     unp_connect(struct socket *,struct sockaddr *, struct thread *);
-static int     unp_connect2(struct socket *so, struct socket *so2);
+static int     unp_connect2(struct socket *so, struct unpcb *unp1,
+		    struct socket *so2, struct unpcb *unp2);
+static int     unp_detach(struct socket *so, struct unpcb *unp);
 static void    unp_disconnect(struct unpcb *);
 static void    unp_shutdown(struct unpcb *);
 static void    unp_drop(struct unpcb *, int);
@@ -124,22 +213,20 @@
 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 socket *, struct unpcb *, struct thread *);
 
 static int
 uipc_abort(struct socket *so)
 {
 	struct unpcb *unp;
 
-	UNP_LOCK();
-	unp = sotounpcb(so);
-	if (unp == NULL) {
-		UNP_UNLOCK();
-		return (EINVAL);
-	}
+	KASSERT(so->so_pcb != NULL, ("uipc_abort: so->so_pcb == NULL"));
+
+	unp = so->so_pcb;
+	UNP_GLOBAL_LOCK();
+	UNP_PCB_LOCK(unp);
 	unp_drop(unp, ECONNABORTED);
-	unp_detach(unp);
-	UNP_UNLOCK_ASSERT();
+	unp_detach(so, unp);
+	UNP_GLOBAL_UNLOCK_ASSERT();
 	ACCEPT_LOCK();
 	SOCK_LOCK(so);
 	sotryfree(so);
@@ -149,56 +236,175 @@
 static int
 uipc_accept(struct socket *so, struct sockaddr **nam)
 {
-	struct unpcb *unp;
-	const struct sockaddr *sa;
+	struct unpcb *unp, *unp2;
+
+	KASSERT(so->so_pcb != NULL, ("uipc_accept: so->so_pcb == NULL"));
 
-	/*
-	 * Pass back name of connected socket,
-	 * if it was bound and we are still connected
-	 * (our peer may have closed already!).
-	 */
 	*nam = malloc(sizeof(struct sockaddr_un), M_SONAME, M_WAITOK);
-	UNP_LOCK();
 	unp = sotounpcb(so);
-	if (unp == NULL) {
-		UNP_UNLOCK();
-		free(*nam, M_SONAME);
-		*nam = NULL;
-		return (EINVAL);
-	}
-	if (unp->unp_conn != NULL && unp->unp_conn->unp_addr != NULL)
-		sa = (struct sockaddr *) unp->unp_conn->unp_addr;
-	else
-		sa = &sun_noname;
-	bcopy(sa, *nam, sa->sa_len);
-	UNP_UNLOCK();
+	UNP_GLOBAL_LOCK();
+	UNP_PCB_LOCK(unp);
+	unp2 = unp->unp_conn;
+	if (unp2 != NULL && unp2->unp_addr != NULL) {
+		UNP_PCB_LOCK(unp2);
+		bcopy(unp2->unp_addr, *nam, unp2->unp_addr->sun_len);
+		UNP_PCB_UNLOCK(unp2);
+	} else
+		bcopy(&sun_noname, *nam, sun_noname.sa_len);
+	UNP_PCB_UNLOCK(unp);
+	UNP_GLOBAL_UNLOCK();
 	return (0);
 }
 
 static int
 uipc_attach(struct socket *so, int proto, struct thread *td)
 {
-	struct unpcb *unp = sotounpcb(so);
+	struct unpcb *unp;
+	int error;
+
+	KASSERT(so->so_pcb == NULL, ("uipc_attach: so->so_pcb != NULL"));
+
+	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
+		switch (so->so_type) {
+
+		case SOCK_STREAM:
+			error = soreserve(so, unpst_sendspace, unpst_recvspace);
+			break;
+
+		case SOCK_DGRAM:
+			error = soreserve(so, unpdg_sendspace, unpdg_recvspace);
+			break;
+
+		default:
+			panic("unp_attach");
+		}
+		if (error)
+			return (error);
+	}
+	unp = uma_zalloc(unp_zone, M_WAITOK | M_ZERO);
+	if (unp == NULL)
+		return (ENOBUFS);
+	UNP_PCB_INIT(unp);
+	LIST_INIT(&unp->unp_refs);
+	unp->unp_socket = so;
+	so->so_pcb = unp;
+
+	UNP_GLOBAL_LOCK();
+	unp->unp_gencnt = ++unp_gencnt;
+	unp_count++;
+	LIST_INSERT_HEAD(so->so_type == SOCK_DGRAM ? &unp_dhead
+			 : &unp_shead, unp, unp_link);
+	UNP_GLOBAL_UNLOCK();
 
-	if (unp != NULL)
-		return (EISCONN);
-	return (unp_attach(so));
+	return (0);
 }
 
 static int
 uipc_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
 {
+	struct sockaddr_un *soun = (struct sockaddr_un *)nam;
+	struct vnode *vp;
+	struct mount *mp;
+	struct vattr vattr;
+	int error, namelen;
+	struct nameidata nd;
 	struct unpcb *unp;
-	int error;
+	char *buf;
+
+	KASSERT(so->so_pcb != NULL, ("uipc_bind: so->so_pcb == NULL"));
+
+	namelen = soun->sun_len - offsetof(struct sockaddr_un, sun_path);
+	if (namelen <= 0)
+		return (EINVAL);
 
-	UNP_LOCK();
+	/*
+	 * Do a lockless check of unp->unp_vnode to sanity check up front
+	 * before doing a lot of work.  We have to do the atomic test-and-set
+	 * later with locks held, once we've allocated memory, traversed file
+	 * system paths, etc.
+	 */
 	unp = sotounpcb(so);
-	if (unp == NULL) {
-		UNP_UNLOCK();
+	if (unp->unp_vnode != NULL)
 		return (EINVAL);
+
+	buf = malloc(namelen + 1, M_TEMP, M_WAITOK);
+	strlcpy(buf, soun->sun_path, namelen + 1);
+
+	mtx_lock(&Giant);
+restart:
+	mtx_assert(&Giant, MA_OWNED);
+	/*
+	 * SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's
+	 */
+	NDINIT(&nd, CREATE, NOFOLLOW | LOCKPARENT | SAVENAME, UIO_SYSSPACE,
+	    buf, td);
+	error = namei(&nd);
+	if (error)
+		goto done;
+	vp = nd.ni_vp;
+	if (vp != NULL || vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) {
+		NDFREE(&nd, NDF_ONLY_PNBUF);
+		if (nd.ni_dvp == vp)
+			vrele(nd.ni_dvp);
+		else
+			vput(nd.ni_dvp);
+		if (vp != NULL) {
+			vrele(vp);
+			error = EADDRINUSE;
+			goto done;
+		}
+		error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH);
+		if (error)
+			goto done;
+		goto restart;
 	}
-	error = unp_bind(unp, nam, td);
-	UNP_UNLOCK();
+	VATTR_NULL(&vattr);
+	vattr.va_type = VSOCK;
+	vattr.va_mode = (ACCESSPERMS & ~td->td_proc->p_fd->fd_cmask);
+#ifdef MAC
+	error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd,
+	    &vattr);
+#endif
+	if (error == 0) {
+		VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE);
+		error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
+	}
+	NDFREE(&nd, NDF_ONLY_PNBUF);
+	vput(nd.ni_dvp);
+	if (error) {
+		vn_finished_write(mp);
+		goto done;
+	}
+	vp = nd.ni_vp;
+	/*
+	 * XXXRW: But not LOCKLEAF above?
+	 */
+	ASSERT_VOP_LOCKED(vp, "unp_bind");
+	soun = (struct sockaddr_un *)sodupsockaddr(nam, M_WAITOK);
+
+	/*
+	 * Now actually bind the vnode and socket together.  This requires
+	 * the global lock so that test-and-set of v_socket is safe.
+	 */
+	UNP_GLOBAL_LOCK();
+	UNP_PCB_LOCK(unp);
+	if (unp->unp_vnode != NULL || vp->v_socket != NULL) {
+		/*
+		 * XXXRW: Add complicated stuff here to back out the
+		 * transaction if we raced, which didn't exist in the old
+		 * code either.  Return (EINVAL) or (EADDRINUSE).
+		 */
+	}
+	vp->v_socket = unp->unp_socket;
+	unp->unp_vnode = vp;
+	unp->unp_addr = soun;
+	UNP_PCB_UNLOCK(unp);
+	UNP_GLOBAL_UNLOCK();
+	VOP_UNLOCK(vp, 0, td);
+	vn_finished_write(mp);
+done:
+	mtx_unlock(&Giant);
+	free(buf, M_TEMP);
 	return (error);
 }
 
@@ -209,51 +415,53 @@
 	int error;
 
 	KASSERT(td == curthread, ("uipc_connect: td != curthread"));
+	KASSERT(so->so_pcb != NULL, ("uipc_connect: so->so_pcb == NULL"));
 
-	UNP_LOCK();
 	unp = sotounpcb(so);
-	if (unp == NULL) {
-		UNP_UNLOCK();
-		return (EINVAL);
-	}
+
+	UNP_GLOBAL_LOCK();
+	UNP_PCB_LOCK(unp);
 	error = unp_connect(so, nam, td);
-	UNP_UNLOCK();
+	UNP_PCB_UNLOCK(unp);
+	UNP_GLOBAL_UNLOCK();
 	return (error);
 }
 
 int
 uipc_connect2(struct socket *so1, struct socket *so2)
 {
-	struct unpcb *unp;
+	struct unpcb *unp1, *unp2;
 	int error;
 
-	UNP_LOCK();
-	unp = sotounpcb(so1);
-	if (unp == NULL) {
-		UNP_UNLOCK();
-		return (EINVAL);
-	}
-	error = unp_connect2(so1, so2);
-	UNP_UNLOCK();
+	KASSERT(so1->so_pcb != NULL, ("uipc_connect2: so1->so_pcb == NULL"));
+	KASSERT(so2->so_pcb != NULL, ("uipc_connect2: so2->so_pcb == NULL"));
+
+	unp1 = sotounpcb(so1);
+	unp2 = sotounpcb(so2);
+	UNP_GLOBAL_LOCK();
+	UNP_PCB_LOCK(unp1);
+	UNP_PCB_LOCK(unp2);
+	error = unp_connect2(so1, unp1, so2, unp2);
+	UNP_PCB_UNLOCK(unp2);
+	UNP_PCB_UNLOCK(unp1);
+	UNP_GLOBAL_UNLOCK();
 	return (error);
 }
 
-/* control is EOPNOTSUPP */
-
 static int
 uipc_detach(struct socket *so)
 {
 	struct unpcb *unp;
+	int error;
+
+	KASSERT(so->so_pcb != NULL, ("uipc_detach: so->so_pcb == NULL"));
 
-	UNP_LOCK();
 	unp = sotounpcb(so);
-	if (unp == NULL) {
-		UNP_UNLOCK();
-		return (EINVAL);
-	}
-	unp_detach(unp);
-	UNP_UNLOCK_ASSERT();
-	return (0);
+	UNP_GLOBAL_LOCK();
+	UNP_PCB_LOCK(unp);
+	error = unp_detach(so, unp);
+	UNP_GLOBAL_UNLOCK_ASSERT();
+	return (error);
 }
 
 static int
@@ -261,14 +469,14 @@
 {
 	struct unpcb *unp;
 
-	UNP_LOCK();
+	KASSERT(so->so_pcb != NULL, ("uipc_disconnect: so->so_pcb == NULL"));
+
 	unp = sotounpcb(so);
-	if (unp == NULL) {
-		UNP_UNLOCK();
-		return (EINVAL);
-	}
+	UNP_GLOBAL_LOCK();
+	UNP_PCB_LOCK(unp);
 	unp_disconnect(unp);
-	UNP_UNLOCK();
+	UNP_PCB_UNLOCK(unp);
+	UNP_GLOBAL_UNLOCK();
 	return (0);
 }
 
@@ -276,17 +484,19 @@
 uipc_listen(struct socket *so, struct thread *td)
 {
 	struct unpcb *unp;
-	int error;
+
+	KASSERT(so->so_pcb != NULL, ("uipc_listen: so->so_pcb == NULL"));
 
-	UNP_LOCK();
 	unp = sotounpcb(so);
-	if (unp == NULL || unp->unp_vnode == NULL) {
-		UNP_UNLOCK();
+	UNP_PCB_LOCK(unp);
+	if (unp->unp_vnode == NULL) {
+		UNP_PCB_UNLOCK(unp);
 		return (EINVAL);
 	}
-	error = unp_listen(so, unp, td);
-	UNP_UNLOCK();
-	return (error);
+	cru2x(td->td_ucred, &unp->unp_peercred);
+	unp->unp_flags |= UNP_HAVEPCCACHED;
+	UNP_PCB_UNLOCK(unp);
+	return (0);
 }
 
 static int
@@ -295,11 +505,13 @@
 	struct unpcb *unp;
 	const struct sockaddr *sa;
 
+	KASSERT(so->so_pcb != NULL, ("uipc_peeraddr: so->so_pcb == NULL"));
+
 	*nam = malloc(sizeof(struct sockaddr_un), M_SONAME, M_WAITOK);
-	UNP_LOCK();
+	UNP_GLOBAL_LOCK();
 	unp = sotounpcb(so);
 	if (unp == NULL) {
-		UNP_UNLOCK();
+		UNP_GLOBAL_UNLOCK();
 		free(*nam, M_SONAME);
 		*nam = NULL;
 		return (EINVAL);
@@ -315,7 +527,7 @@
 		sa = &sun_noname;
 	}
 	bcopy(sa, *nam, sa->sa_len);
-	UNP_UNLOCK();
+	UNP_GLOBAL_UNLOCK();
 	return (0);
 }
 
@@ -326,12 +538,11 @@
 	struct socket *so2;
 	u_long newhiwat;
 
-	UNP_LOCK();
+	KASSERT(so->so_pcb != NULL, ("uipc_rcvd: so->so_pcb == NULL"));
+
 	unp = sotounpcb(so);
-	if (unp == NULL) {
-		UNP_UNLOCK();
-		return (EINVAL);
-	}
+	UNP_GLOBAL_LOCK();
+	UNP_PCB_LOCK(unp);
 	switch (so->so_type) {
 	case SOCK_DGRAM:
 		panic("uipc_rcvd DGRAM?");
@@ -344,8 +555,11 @@
 		SOCKBUF_LOCK(&so2->so_snd);
 		SOCKBUF_LOCK(&so->so_rcv);
 		/*
-		 * Adjust backpressure on sender
-		 * and wakeup any waiting to write.
+		 * Adjust backpressure on sender and wakeup any waiting to
+		 * write.
+		 *
+		 * XXXRW: I don't think the unp2 lock is required here, but
+		 * need to check.
 		 */
 		so2->so_snd.sb_mbmax += unp->unp_mbcnt - so->so_rcv.sb_mbcnt;
 		unp->unp_mbcnt = so->so_rcv.sb_mbcnt;
@@ -361,154 +575,202 @@
 	default:
 		panic("uipc_rcvd unknown socktype");
 	}
-	UNP_UNLOCK();
+	UNP_PCB_UNLOCK(unp);
+	UNP_GLOBAL_UNLOCK();
 	return (0);
 }
 
-/* pru_rcvoob is EOPNOTSUPP */
-
+/*
+ * XXXRW: In the below, it should not be necessary to hold more than one
+ * unpcb mutex at a time, so we shouldn't need the global locks except around
+ * connection operations.
+ */
 static int
-uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
-    struct mbuf *control, struct thread *td)
+unp_send_dgram(struct socket *so, struct unpcb *unp, int flags,
+    struct mbuf *m, struct sockaddr *nam, struct mbuf *control,
+    struct thread *td)
 {
-	int error = 0;
-	struct unpcb *unp;
+	const struct sockaddr *from;
 	struct socket *so2;
-	u_long newhiwat;
+	int error;
 
-	unp = sotounpcb(so);
-	if (unp == NULL) {
-		error = EINVAL;
-		goto release;
+	error = 0;
+	if (nam != NULL)
+		UNP_GLOBAL_LOCK();
+	UNP_PCB_LOCK(unp);
+	if (nam != NULL) {
+		if (unp->unp_conn != NULL) {
+			error = EISCONN;
+			goto out;
+		}
+		error = unp_connect(so, nam, td);
+		if (error)
+			goto out;
+	} else {
+		if (unp->unp_conn == NULL) {
+			error = ENOTCONN;
+			goto out;
+		}
+	}
+	so2 = unp->unp_conn->unp_socket;
+	if (unp->unp_addr != NULL)
+		from = (struct sockaddr *)unp->unp_addr;
+	else
+		from = &sun_noname;
+	SOCKBUF_LOCK(&so2->so_rcv);
+	if (sbappendaddr_locked(&so2->so_rcv, from, m, control)) {
+		sorwakeup_locked(so2);
+		m = NULL;
+		control = NULL;
+	} else {
+		SOCKBUF_UNLOCK(&so2->so_rcv);
+		error = ENOBUFS;
 	}
-	if (flags & PRUS_OOB) {
-		error = EOPNOTSUPP;
-		goto release;
+	if (nam != NULL)
+		unp_disconnect(unp);
+out:
+	UNP_PCB_UNLOCK(unp);
+	if (nam != NULL)
+		UNP_GLOBAL_UNLOCK();
+	if (control != NULL) {
+		unp_dispose(control);
+		m_freem(control);
 	}
+	if (m != NULL)
+		m_freem(m);
+	return (error);
+}
 
-	if (control != NULL && (error = unp_internalize(&control, td)))
-		goto release;
+static int
+unp_send_stream(struct socket *so, struct unpcb *unp, int flags,
+    struct mbuf *m, struct sockaddr *nam, struct mbuf *control,
+    struct thread *td)
+{
+	struct socket *so2;
+	u_long newhiwat;
+	int error;
 
-	UNP_LOCK();
-	unp = sotounpcb(so);
-	if (unp == NULL) {
-		UNP_UNLOCK();
-		error = EINVAL;
-		goto dispose_release;
-	}
+	error = 0;
+	if (nam != NULL)
+		UNP_GLOBAL_LOCK();
+	UNP_PCB_LOCK(unp);
 
-	switch (so->so_type) {
-	case SOCK_DGRAM:
-	{
-		const struct sockaddr *from;
-
+	/* Connect if not connected yet. */
+	/*
+	 * Note: A better implementation would complain
+	 * if not equal to the peer's address.
+	 */
+	if ((so->so_state & SS_ISCONNECTED) == 0) {
 		if (nam != NULL) {
-			if (unp->unp_conn != NULL) {
-				error = EISCONN;
-				break;
-			}
 			error = unp_connect(so, nam, td);
 			if (error)
-				break;
+				goto out;	/* XXX */
 		} else {
-			if (unp->unp_conn == NULL) {
-				error = ENOTCONN;
-				break;
-			}
+			error = ENOTCONN;
+			goto out;
 		}
-		so2 = unp->unp_conn->unp_socket;
-		if (unp->unp_addr != NULL)
-			from = (struct sockaddr *)unp->unp_addr;
-		else
-			from = &sun_noname;
-		SOCKBUF_LOCK(&so2->so_rcv);
-		if (sbappendaddr_locked(&so2->so_rcv, from, m, control)) {
-			sorwakeup_locked(so2);
+	}
+
+	SOCKBUF_LOCK(&so->so_snd);
+	if (so->so_snd.sb_state & SBS_CANTSENDMORE) {
+		SOCKBUF_UNLOCK(&so->so_snd);
+		error = EPIPE;
+		goto out;
+	}
+	if (unp->unp_conn == NULL)
+		panic("uipc_send_stream connected but no connection?");
+	so2 = unp->unp_conn->unp_socket;
+	SOCKBUF_LOCK(&so2->so_rcv);
+	/*
+	 * Send to paired receive port, and then reduce
+	 * send buffer hiwater marks to maintain backpressure.
+	 * Wake up readers only if delivery was successful.
+	 */
+	if (control != NULL) {
+		if (sbappendcontrol_locked(&so2->so_rcv, m, control)) {
+			control = NULL;
 			m = NULL;
-			control = NULL;
-		} else {
-			SOCKBUF_UNLOCK(&so2->so_rcv);
+		} else
 			error = ENOBUFS;
-		}
-		if (nam != NULL)
-			unp_disconnect(unp);
-		break;
+	} else {
+		sbappend_locked(&so2->so_rcv, m);
+		m = NULL;
 	}
-
-	case SOCK_STREAM:
-		/* Connect if not connected yet. */
-		/*
-		 * Note: A better implementation would complain
-		 * if not equal to the peer's address.
-		 */
-		if ((so->so_state & SS_ISCONNECTED) == 0) {
-			if (nam != NULL) {
-				error = unp_connect(so, nam, td);
-				if (error)
-					break;	/* XXX */
-			} else {
-				error = ENOTCONN;
-				break;
-			}
-		}
-
-		SOCKBUF_LOCK(&so->so_snd);
-		if (so->so_snd.sb_state & SBS_CANTSENDMORE) {
-			SOCKBUF_UNLOCK(&so->so_snd);
-			error = EPIPE;
-			break;
-		}
-		if (unp->unp_conn == NULL)
-			panic("uipc_send connected but no connection?");
-		so2 = unp->unp_conn->unp_socket;
-		SOCKBUF_LOCK(&so2->so_rcv);
-		/*
-		 * Send to paired receive port, and then reduce
-		 * send buffer hiwater marks to maintain backpressure.
-		 * Wake up readers.
-		 */
-		if (control != NULL) {
-			if (sbappendcontrol_locked(&so2->so_rcv, m, control))
-				control = NULL;
-		} else {
-			sbappend_locked(&so2->so_rcv, m);
-		}
+	if (m == NULL) {
 		so->so_snd.sb_mbmax -=
-			so2->so_rcv.sb_mbcnt - unp->unp_conn->unp_mbcnt;
+		    so2->so_rcv.sb_mbcnt - unp->unp_conn->unp_mbcnt;
 		unp->unp_conn->unp_mbcnt = so2->so_rcv.sb_mbcnt;
 		newhiwat = so->so_snd.sb_hiwat -
 		    (so2->so_rcv.sb_cc - unp->unp_conn->unp_cc);
-		(void)chgsbsize(so->so_cred->cr_uidinfo, &so->so_snd.sb_hiwat,
-		    newhiwat, RLIM_INFINITY);
+		(void)chgsbsize(so->so_cred->cr_uidinfo,
+		    &so->so_snd.sb_hiwat, newhiwat, RLIM_INFINITY);
+		unp->unp_conn->unp_cc = so2->so_rcv.sb_cc;
 		SOCKBUF_UNLOCK(&so->so_snd);
-		unp->unp_conn->unp_cc = so2->so_rcv.sb_cc;
 		sorwakeup_locked(so2);
-		m = NULL;
-		break;
+	} else {
+		SOCKBUF_UNLOCK(&so2->so_rcv);
+		SOCKBUF_UNLOCK(&so->so_snd);
+	}
+out:
+	UNP_PCB_UNLOCK(unp);
+	if (nam != NULL)
+		UNP_GLOBAL_UNLOCK();
+	if (control != NULL) {
+		unp_dispose(control);
+		m_freem(control);
+	}
+	if (m != NULL)
+		m_freem(m);
+	return (error);
+}
+
+/*
+ * XXXRW: Note: uipc_send() is structured to avoid needing any mutexes.
+ */
+static int
+uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
+    struct mbuf *control, struct thread *td)
+{
+	int error = 0;
+	struct unpcb *unp;
+
+	KASSERT(so->so_pcb != NULL, ("uipc_send: so_pcb is NULL"));
 
-	default:
-		panic("uipc_send unknown socktype");
+	error = 0;
+	unp = sotounpcb(so);
+	if (flags & PRUS_OOB) {
+		error = EOPNOTSUPP;
+		goto release;
+	}
+	if (control != NULL) {
+		error = unp_internalize(&control, td);
+		if (error != 0)
+			goto release;
 	}
-
 	/*
-	 * SEND_EOF is equivalent to a SEND followed by
-	 * a SHUTDOWN.
+	 * Mbufs are owned by unp_send_{dgram,stream}() once passed in, so
+	 * other than handling PRUS_EOF, we can just return.
 	 */
-	if (flags & PRUS_EOF) {
-		socantsendmore(so);
-		unp_shutdown(unp);
-	}
-	UNP_UNLOCK();
+	switch (so->so_type) {
+	case SOCK_DGRAM:
+		error = unp_send_dgram(so, unp, flags, m, nam, control, td);
+		break;
 
-dispose_release:
-	if (control != NULL && error != 0)
-		unp_dispose(control);
+	case SOCK_STREAM:
+		error = unp_send_stream(so, unp, flags, m, nam, control, td);
+		break;
 
+	default:
+		panic("uipc_send unknown socktype");
+	}
+	if (flags & PRUS_EOF)
+		uipc_shutdown(so);
+	return (error);
 release:
+	if (m != NULL)
+		m_freem(m);
 	if (control != NULL)
 		m_freem(control);
-	if (m != NULL)
-		m_freem(m);
 	return (error);
 }
 
@@ -518,12 +780,11 @@
 	struct unpcb *unp;
 	struct socket *so2;
 
-	UNP_LOCK();
+	KASSERT(so->so_pcb != NULL, ("uipc_sense: so->so_pcb == NULL"));
+
 	unp = sotounpcb(so);
-	if (unp == NULL) {
-		UNP_UNLOCK();
-		return (EINVAL);
-	}
+	UNP_GLOBAL_LOCK();
+	UNP_PCB_LOCK(unp);
 	sb->st_blksize = so->so_snd.sb_hiwat;
 	if (so->so_type == SOCK_STREAM && unp->unp_conn != NULL) {
 		so2 = unp->unp_conn->unp_socket;
@@ -533,7 +794,8 @@
 	if (unp->unp_ino == 0)
 		unp->unp_ino = (++unp_ino == 0) ? ++unp_ino : unp_ino;
 	sb->st_ino = unp->unp_ino;
-	UNP_UNLOCK();
+	UNP_PCB_UNLOCK(unp);
+	UNP_GLOBAL_UNLOCK();
 	return (0);
 }
 
@@ -542,15 +804,13 @@
 {
 	struct unpcb *unp;
 
-	UNP_LOCK();
+	KASSERT(so->so_pcb != NULL, ("uipc_shutdown: so->so_pcb == NULL"));
+
 	unp = sotounpcb(so);
-	if (unp == NULL) {
-		UNP_UNLOCK();
-		return (EINVAL);
-	}
 	socantsendmore(so);
+	UNP_PCB_LOCK(unp);
 	unp_shutdown(unp);
-	UNP_UNLOCK();
+	UNP_PCB_UNLOCK(unp);
 	return (0);
 }
 
@@ -560,21 +820,17 @@
 	struct unpcb *unp;
 	const struct sockaddr *sa;
 
+	KASSERT(so->so_pcb != NULL, ("uipc_sockaddr: so->so_pcb == NULL"));
+
 	*nam = malloc(sizeof(struct sockaddr_un), M_SONAME, M_WAITOK);
-	UNP_LOCK();
 	unp = sotounpcb(so);
-	if (unp == NULL) {
-		UNP_UNLOCK();
-		free(*nam, M_SONAME);
-		*nam = NULL;
-		return (EINVAL);
-	}
+	UNP_PCB_LOCK(unp);
 	if (unp->unp_addr != NULL)
 		sa = (struct sockaddr *) unp->unp_addr;
 	else
 		sa = &sun_noname;
 	bcopy(sa, *nam, sa->sa_len);
-	UNP_UNLOCK();
+	UNP_PCB_UNLOCK(unp);
 	return (0);
 }
 
@@ -606,15 +862,17 @@
 	struct xucred xu;
 	int error;
 
+	KASSERT(so->so_pcb != NULL, ("uipc_ctloutput: so->so_pcb == NULL"));
+
 	switch (sopt->sopt_dir) {
 	case SOPT_GET:
 		switch (sopt->sopt_name) {
 		case LOCAL_PEERCRED:
 			error = 0;
-			UNP_LOCK();
 			unp = sotounpcb(so);
+			UNP_PCB_LOCK(unp);
 			if (unp == NULL) {
-				UNP_UNLOCK();
+				UNP_PCB_UNLOCK(unp);
 				error = EINVAL;
 				break;
 			}
@@ -626,7 +884,7 @@
 				else
 					error = EINVAL;
 			}
-			UNP_UNLOCK();
+			UNP_PCB_UNLOCK(unp);
 			if (error == 0)
 				error = sooptcopyout(sopt, &xu, sizeof(xu));
 			break;
@@ -643,92 +901,20 @@
 	return (error);
 }
 
-/*
- * Both send and receive buffers are allocated PIPSIZ bytes of buffering
- * for stream sockets, although the total for sender and receiver is
- * actually only PIPSIZ.
- * Datagram sockets really use the sendspace as the maximum datagram size,
- * and don't really want to reserve the sendspace.  Their recvspace should
- * be large enough for at least one max-size datagram plus address.
- */
-#ifndef PIPSIZ
-#define	PIPSIZ	8192
-#endif
-static u_long	unpst_sendspace = PIPSIZ;
-static u_long	unpst_recvspace = PIPSIZ;
-static u_long	unpdg_sendspace = 2*1024;	/* really max datagram size */
-static u_long	unpdg_recvspace = 4*1024;
-
-static int	unp_rights;			/* file descriptors in flight */
-
-SYSCTL_DECL(_net_local_stream);
-SYSCTL_INT(_net_local_stream, OID_AUTO, sendspace, CTLFLAG_RW,
-	   &unpst_sendspace, 0, "");
-SYSCTL_INT(_net_local_stream, OID_AUTO, recvspace, CTLFLAG_RW,
-	   &unpst_recvspace, 0, "");
-SYSCTL_DECL(_net_local_dgram);
-SYSCTL_INT(_net_local_dgram, OID_AUTO, maxdgram, CTLFLAG_RW,
-	   &unpdg_sendspace, 0, "");
-SYSCTL_INT(_net_local_dgram, OID_AUTO, recvspace, CTLFLAG_RW,
-	   &unpdg_recvspace, 0, "");
-SYSCTL_DECL(_net_local);
-SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD, &unp_rights, 0, "");
-
 static int
-unp_attach(struct socket *so)
+unp_detach(struct socket *so, struct unpcb *unp)
 {
-	struct unpcb *unp;
-	int error;
+	struct vnode *vp;
 
-	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
-		switch (so->so_type) {
+	KASSERT(so->so_pcb != NULL, ("uipc_detach: so->so_pcb == NULL"));
 
-		case SOCK_STREAM:
-			error = soreserve(so, unpst_sendspace, unpst_recvspace);
-			break;
-
-		case SOCK_DGRAM:
-			error = soreserve(so, unpdg_sendspace, unpdg_recvspace);
-			break;
-
-		default:
-			panic("unp_attach");
-		}
-		if (error)
-			return (error);
-	}
-	unp = uma_zalloc(unp_zone, M_WAITOK | M_ZERO);
-	if (unp == NULL)
-		return (ENOBUFS);
-	LIST_INIT(&unp->unp_refs);
-	unp->unp_socket = so;
-	so->so_pcb = unp;
+	UNP_GLOBAL_LOCK_ASSERT();
+	UNP_PCB_LOCK_ASSERT(unp);
 
-	UNP_LOCK();
-	unp->unp_gencnt = ++unp_gencnt;
-	unp_count++;
-	LIST_INSERT_HEAD(so->so_type == SOCK_DGRAM ? &unp_dhead
-			 : &unp_shead, unp, unp_link);
-	UNP_UNLOCK();
-
-	return (0);
-}
-
-static void
-unp_detach(struct unpcb *unp)
-{
-	struct vnode *vp;
-
-	UNP_LOCK_ASSERT();
-
 	LIST_REMOVE(unp, unp_link);
 	unp->unp_gencnt = ++unp_gencnt;
 	--unp_count;
 	if ((vp = unp->unp_vnode) != NULL) {
-		/*
-		 * XXXRW: should v_socket be frobbed only while holding
-		 * Giant?
-		 */
 		unp->unp_vnode->v_socket = NULL;
 		unp->unp_vnode = NULL;
 	}
@@ -736,10 +922,18 @@
 		unp_disconnect(unp);
 	while (!LIST_EMPTY(&unp->unp_refs)) {
 		struct unpcb *ref = LIST_FIRST(&unp->unp_refs);
+		/*
+		 * Note that unp_drop() calls unp_disconnect() which then
+		 * grabs more locks.  It might be desirable to inline some
+		 * of that here.
+		 */
+		UNP_PCB_LOCK(ref);
 		unp_drop(ref, ECONNRESET);
+		UNP_PCB_UNLOCK(ref);
 	}
 	soisdisconnected(unp->unp_socket);
 	unp->unp_socket->so_pcb = NULL;
+	UNP_PCB_UNLOCK(unp);			/* Before calling unp_gc(). */
 	if (unp_rights) {
 		/*
 		 * Normally the receive buffer is flushed later,
@@ -751,107 +945,19 @@
 		sorflush(unp->unp_socket);
 		unp_gc();	/* Will unlock UNP. */
 	} else
-		UNP_UNLOCK();
-	UNP_UNLOCK_ASSERT();
+		UNP_GLOBAL_UNLOCK();
+	UNP_GLOBAL_UNLOCK_ASSERT();
 	if (unp->unp_addr != NULL)
 		FREE(unp->unp_addr, M_SONAME);
+	UNP_PCB_DESTROY(unp);
 	uma_zfree(unp_zone, unp);
 	if (vp) {
 		mtx_lock(&Giant);
 		vrele(vp);
 		mtx_unlock(&Giant);
 	}
-}
-
-static int
-unp_bind(struct unpcb *unp, struct sockaddr *nam, struct thread *td)
-{
-	struct sockaddr_un *soun = (struct sockaddr_un *)nam;
-	struct vnode *vp;
-	struct mount *mp;
-	struct vattr vattr;
-	int error, namelen;
-	struct nameidata nd;
-	char *buf;
-
-	UNP_LOCK_ASSERT();
-
-	/*
-	 * XXXRW: This test-and-set of unp_vnode is non-atomic; the
-	 * unlocked read here is fine, but the value of unp_vnode needs
-	 * to be tested again after we do all the lookups to see if the
-	 * pcb is still unbound?
-	 */
-	if (unp->unp_vnode != NULL)
-		return (EINVAL);
-
-	namelen = soun->sun_len - offsetof(struct sockaddr_un, sun_path);
-	if (namelen <= 0)
-		return (EINVAL);
-
-	UNP_UNLOCK();
-
-	buf = malloc(namelen + 1, M_TEMP, M_WAITOK);
-	strlcpy(buf, soun->sun_path, namelen + 1);
-
-	mtx_lock(&Giant);
-restart:
-	mtx_assert(&Giant, MA_OWNED);
-	NDINIT(&nd, CREATE, NOFOLLOW | LOCKPARENT | SAVENAME, UIO_SYSSPACE,
-	    buf, td);
-/* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */
-	error = namei(&nd);
-	if (error)
-		goto done;
-	vp = nd.ni_vp;
-	if (vp != NULL || vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) {
-		NDFREE(&nd, NDF_ONLY_PNBUF);
-		if (nd.ni_dvp == vp)
-			vrele(nd.ni_dvp);
-		else
-			vput(nd.ni_dvp);
-		if (vp != NULL) {
-			vrele(vp);
-			error = EADDRINUSE;
-			goto done;
-		}
-		error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH);
-		if (error)
-			goto done;
-		goto restart;
-	}
-	VATTR_NULL(&vattr);
-	vattr.va_type = VSOCK;
-	vattr.va_mode = (ACCESSPERMS & ~td->td_proc->p_fd->fd_cmask);
-#ifdef MAC
-	error = mac_check_vnode_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd,
-	    &vattr);
-#endif
-	if (error == 0) {
-		VOP_LEASE(nd.ni_dvp, td, td->td_ucred, LEASE_WRITE);
-		error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
-	}
-	NDFREE(&nd, NDF_ONLY_PNBUF);
-	vput(nd.ni_dvp);
-	if (error) {
-		vn_finished_write(mp);
-		goto done;
-	}
-	vp = nd.ni_vp;
-	ASSERT_VOP_LOCKED(vp, "unp_bind");
-	soun = (struct sockaddr_un *)sodupsockaddr(nam, M_WAITOK);
-	UNP_LOCK();
-	vp->v_socket = unp->unp_socket;
-	unp->unp_vnode = vp;
-	unp->unp_addr = soun;
-	UNP_UNLOCK();
-	VOP_UNLOCK(vp, 0, td);
-	vn_finished_write(mp);
-done:
-	mtx_unlock(&Giant);
-	free(buf, M_TEMP);
-	UNP_LOCK();
-	return (error);
+	UNP_GLOBAL_UNLOCK_ASSERT();
+	return (0);
 }
 
 static int
@@ -866,14 +972,18 @@
 	char buf[SOCK_MAXADDRLEN];
 	struct sockaddr *sa;
 
-	UNP_LOCK_ASSERT();
 	unp = sotounpcb(so);
+	UNP_GLOBAL_LOCK_ASSERT();
+	UNP_PCB_LOCK_ASSERT(unp);
 
 	len = nam->sa_len - offsetof(struct sockaddr_un, sun_path);
 	if (len <= 0)
 		return (EINVAL);
+
 	strlcpy(buf, soun->sun_path, len + 1);
-	UNP_UNLOCK();
+	UNP_PCB_UNLOCK(unp);
+	UNP_GLOBAL_UNLOCK();
+
 	sa = malloc(sizeof(struct sockaddr_un), M_SONAME, M_WAITOK);
 	mtx_lock(&Giant);
 	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, buf, td);
@@ -895,12 +1005,15 @@
 	if (error)
 		goto bad;
 	mtx_unlock(&Giant);
-	UNP_LOCK();
+
+	/*
+	 * XXXRW: This section has some locking issues.  In particular:
+	 * - Shouldn't we acquire a reference to so2 so it doesn't evaporate
+	 *   if the socket is closed (detached from v_socket) while we drop
+	 *   locks and/or sleep?
+	 */
 	unp = sotounpcb(so);
-	if (unp == NULL) {
-		error = EINVAL;
-		goto bad2;
-	}
+	UNP_GLOBAL_LOCK();
 	so2 = vp->v_socket;
 	if (so2 == NULL) {
 		error = ECONNREFUSED;
@@ -918,9 +1031,9 @@
 			 *     of the head and holding sleep locks across
 			 *     a (potentially) blocking malloc.
 			 */
-			UNP_UNLOCK();
+			UNP_GLOBAL_UNLOCK();
 			so3 = sonewconn(so2, 0);
-			UNP_LOCK();
+			UNP_GLOBAL_LOCK();
 		} else
 			so3 = NULL;
 		if (so3 == NULL) {
@@ -930,6 +1043,9 @@
 		unp = sotounpcb(so);
 		unp2 = sotounpcb(so2);
 		unp3 = sotounpcb(so3);
+		UNP_PCB_LOCK(unp);
+		UNP_PCB_LOCK(unp2);
+		UNP_PCB_LOCK(unp3);
 		if (unp2->unp_addr != NULL) {
 			bcopy(unp2->unp_addr, sa, unp2->unp_addr->sun_len);
 			unp3->unp_addr = (struct sockaddr_un *) sa;
@@ -947,7 +1063,7 @@
 		/*
 		 * The receiver's (server's) credentials are copied
 		 * from the unp_peercred member of socket on which the
-		 * former called listen(); unp_listen() cached that
+		 * former called listen(); uipc_listen() cached that
 		 * process's credentials at that time so we can use
 		 * them now.
 		 */
@@ -962,12 +1078,19 @@
 		mac_set_socket_peer_from_socket(so3, so);
 		SOCK_UNLOCK(so);
 #endif
-
+		UNP_PCB_UNLOCK(unp2);
+		unp2 = unp3;
 		so2 = so3;
+	} else {
+		unp2 = sotounpcb(so2);
+		UNP_PCB_LOCK(unp);
+		UNP_PCB_LOCK(unp2);
 	}
-	error = unp_connect2(so, so2);
+	error = unp_connect2(so, unp, so2, unp2);
+	UNP_PCB_UNLOCK(unp);
+	UNP_PCB_UNLOCK(unp2);
 bad2:
-	UNP_UNLOCK();
+	UNP_GLOBAL_UNLOCK();
 	mtx_lock(&Giant);
 bad:
 	mtx_assert(&Giant, MA_OWNED);
@@ -975,32 +1098,32 @@
 		vput(vp);
 	mtx_unlock(&Giant);
 	free(sa, M_SONAME);
-	UNP_LOCK();
+	UNP_GLOBAL_LOCK();
+	UNP_PCB_LOCK(unp);
 	return (error);
 }
 
 static int
-unp_connect2(struct socket *so, struct socket *so2)
+unp_connect2(struct socket *so1, struct unpcb *unp1, struct socket *so2,
+    struct unpcb *unp2)
 {
-	struct unpcb *unp = sotounpcb(so);
-	struct unpcb *unp2;
 
-	UNP_LOCK_ASSERT();
+	UNP_GLOBAL_LOCK_ASSERT();
+	UNP_PCB_LOCK_ASSERT(unp1);
+	UNP_PCB_LOCK_ASSERT(unp2);
 
-	if (so2->so_type != so->so_type)
+	if (so2->so_type != so1->so_type)
 		return (EPROTOTYPE);
-	unp2 = sotounpcb(so2);
-	unp->unp_conn = unp2;
-	switch (so->so_type) {
-
+	unp1->unp_conn = unp2;
+	switch (so1->so_type) {
 	case SOCK_DGRAM:
-		LIST_INSERT_HEAD(&unp2->unp_refs, unp, unp_reflink);
-		soisconnected(so);
+		LIST_INSERT_HEAD(&unp2->unp_refs, unp1, unp_reflink);
+		soisconnected(so1);
 		break;
 
 	case SOCK_STREAM:
-		unp2->unp_conn = unp;
-		soisconnected(so);
+		unp2->unp_conn = unp1;
+		soisconnected(so1);
 		soisconnected(so2);
 		break;
 
@@ -1013,16 +1136,19 @@
 static void
 unp_disconnect(struct unpcb *unp)
 {
-	struct unpcb *unp2 = unp->unp_conn;
+	struct unpcb *unp2;
 	struct socket *so;
 
-	UNP_LOCK_ASSERT();
+	UNP_GLOBAL_LOCK_ASSERT();
+	UNP_PCB_LOCK_ASSERT(unp);
 
+	unp2 = unp->unp_conn;
 	if (unp2 == NULL)
 		return;
 	unp->unp_conn = NULL;
+
+	UNP_PCB_LOCK(unp2);
 	switch (unp->unp_socket->so_type) {
-
 	case SOCK_DGRAM:
 		LIST_REMOVE(unp, unp_reflink);
 		so = unp->unp_socket;
@@ -1037,18 +1163,9 @@
 		soisdisconnected(unp2->unp_socket);
 		break;
 	}
+	UNP_PCB_UNLOCK(unp2);
 }
 
-#ifdef notdef
-void
-unp_abort(struct unpcb *unp)
-{
-
-	unp_detach(unp);
-	UNP_UNLOCK_ASSERT();
-}
-#endif
-
 /*
  * unp_pcblist() assumes that UNIX domain socket memory is never reclaimed
  * by the zone (UMA_ZONE_NOFREE), and as such potentially stale pointers
@@ -1087,10 +1204,10 @@
 	 * OK, now we're committed to doing something.
 	 */
 	xug = malloc(sizeof(*xug), M_TEMP, M_WAITOK);
-	UNP_LOCK();
+	UNP_GLOBAL_LOCK();
 	gencnt = unp_gencnt;
 	n = unp_count;
-	UNP_UNLOCK();
+	UNP_GLOBAL_UNLOCK();
 
 	xug->xug_len = sizeof *xug;
 	xug->xug_count = n;
@@ -1104,7 +1221,7 @@
 
 	unp_list = malloc(n * sizeof *unp_list, M_TEMP, M_WAITOK);
 
-	UNP_LOCK();
+	UNP_GLOBAL_LOCK();
 	for (unp = LIST_FIRST(head), i = 0; unp && i < n;
 	     unp = LIST_NEXT(unp, unp_link)) {
 		if (unp->unp_gencnt <= gencnt) {
@@ -1114,7 +1231,7 @@
 			unp_list[i++] = unp;
 		}
 	}
-	UNP_UNLOCK();
+	UNP_GLOBAL_UNLOCK();
 	n = i;			/* in case we lost some during malloc */
 
 	error = 0;
@@ -1172,7 +1289,7 @@
 {
 	struct socket *so;
 
-	UNP_LOCK_ASSERT();
+	UNP_PCB_LOCK_ASSERT(unp);
 
 	if (unp->unp_socket->so_type == SOCK_STREAM && unp->unp_conn &&
 	    (so = unp->unp_conn->unp_socket))
@@ -1182,22 +1299,16 @@
 static void
 unp_drop(struct unpcb *unp, int errno)
 {
-	struct socket *so = unp->unp_socket;
+	struct socket *so;
 
-	UNP_LOCK_ASSERT();
+	UNP_GLOBAL_LOCK_ASSERT();
+	UNP_PCB_LOCK_ASSERT(unp);
 
+	so = unp->unp_socket;
 	so->so_error = errno;
 	unp_disconnect(unp);
 }
 
-#ifdef notdef
-void
-unp_drain(void)
-{
-
-}
-#endif
-
 static void
 unp_freerights(struct file **rp, int fdcount)
 {
@@ -1231,7 +1342,7 @@
 	int f;
 	u_int newlen;
 
-	UNP_UNLOCK_ASSERT();
+	UNP_GLOBAL_UNLOCK_ASSERT();
 
 	error = 0;
 	if (controlp != NULL) /* controlp == NULL => free control messages */
@@ -1337,7 +1448,7 @@
 	LIST_INIT(&unp_dhead);
 	LIST_INIT(&unp_shead);
 
-	UNP_LOCK_INIT();
+	UNP_GLOBAL_INIT();
 }
 
 static int
@@ -1357,7 +1468,7 @@
 	int error, oldfds;
 	u_int newlen;
 
-	UNP_UNLOCK_ASSERT();
+	UNP_GLOBAL_UNLOCK_ASSERT();
 
 	error = 0;
 	*controlp = NULL;
@@ -1485,12 +1596,11 @@
 }
 
 /*
- * unp_defer is thread-local during garbage collection, and does not require
- * explicit synchronization.  unp_gcing prevents other threads from entering
- * garbage collection, and perhaps should be an sx lock instead.
+ * XXXRW: Note that unp_gcing prevents reentrance, but doesn't guarantee that
+ * the GC'ing required by a second thread will be performed by the first.
+ * This means that potentially some cleanup will be missed in the first pass
+ * and have to way for another triggering of unp_gc().
  */
-static int	unp_defer, unp_gcing;
-
 static void
 unp_gc(void)
 {
@@ -1501,15 +1611,15 @@
 	int nfiles_snap;
 	int nfiles_slack = 20;
 
-	UNP_LOCK_ASSERT();
+	UNP_GLOBAL_LOCK_ASSERT();
 
 	if (unp_gcing) {
-		UNP_UNLOCK();
+		UNP_GLOBAL_UNLOCK();
 		return;
 	}
 	unp_gcing = 1;
 	unp_defer = 0;
-	UNP_UNLOCK();
+	UNP_GLOBAL_UNLOCK();
 	/*
 	 * before going through all this, set all FDs to
 	 * be NOT defered and NOT externally accessible
@@ -1618,7 +1728,7 @@
 	 * we end up doing a (full) closef on the descriptor.  A closef on A
 	 * results in the following chain.  Closef calls soo_close, which
 	 * calls soclose.   Soclose calls first (through the switch
-	 * uipc_usrreq) unp_detach, which re-invokes unp_gc.  Unp_gc simply
+	 * uipc_usrreq) uipc_detach, which re-invokes unp_gc.  Unp_gc simply
 	 * returns because the previous instance had set unp_gcing, and
 	 * we return all the way back to soclose, which marks the socket
 	 * with SS_NOFDREF, and then calls sofree.  Sofree calls sorflush
@@ -1637,7 +1747,7 @@
 	 * it is a Unix domain socket anyhow.  After we destroy all the
 	 * rights carried in messages, we do a last closef to get rid
 	 * of our extra reference.  This is the last close, and the
-	 * unp_detach etc will shut down the socket.
+	 * uipc_detach etc will shut down the socket.
 	 *
 	 * 91/09/19, bsy@cs.cmu.edu
 	 */
@@ -1696,7 +1806,7 @@
 	free(extra_ref, M_TEMP);
 	unp_gcing = 0;
 
-	UNP_UNLOCK_ASSERT();
+	UNP_GLOBAL_UNLOCK_ASSERT();
 }
 
 void
@@ -1707,24 +1817,6 @@
 		unp_scan(m, unp_discard);
 }
 
-static int
-unp_listen(struct socket *so, struct unpcb *unp, struct thread *td)
-{
-	int error;
-
-	UNP_LOCK_ASSERT();
-
-	SOCK_LOCK(so);
-	error = solisten_proto_check(so);
-	if (error == 0) {
-		cru2x(td->td_ucred, &unp->unp_peercred);
-		unp->unp_flags |= UNP_HAVEPCCACHED;
-		solisten_proto(so);
-	}
-	SOCK_UNLOCK(so);
-	return (error);
-}
-
 static void
 unp_scan(struct mbuf *m0, void (*op)(struct file *))
 {
--- //depot/vendor/freebsd/src/sys/sys/unpcb.h	2005/01/07 02:32:16
+++ //depot/user/rwatson/proto/src/sys/sys/unpcb.h	2005/02/21 14:37:24
@@ -78,6 +78,7 @@
 	unp_gen_t unp_gencnt;		/* generation count of this instance */
 	int	unp_flags;		/* flags */
 	struct	xucred unp_peercred;	/* peer credentials, if applicable */
+	struct	mtx unp_mtx;		/* mutex */
 };
 
 /*
