Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177416537
D61341.1794325829.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
7 KB
Referenced Files
None
Subscribers
None
D61341.1794325829.diff
View Options
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -295,9 +295,9 @@
static int unp_connect_peer(struct socket *, struct unpcb *,
struct sockaddr **, struct thread *, bool);
static int unp_connectat_peer(struct thread *, int, const char *,
- struct socket **, struct mtx **, struct vnode **);
+ struct socket **);
static int unp_vnode_peer(struct vnode *, struct thread *,
- struct socket **, struct mtx **, struct vnode **);
+ struct socket **);
static void unp_connect2(struct socket *, struct socket *, bool);
static void unp_disconnect(struct unpcb *unp, struct unpcb *unp2);
static void unp_dispose(struct socket *so);
@@ -2955,8 +2955,6 @@
struct unpcb *unp;
char buf[SOCK_MAXADDRLEN];
struct sockaddr *sa;
- struct mtx *mtxp;
- struct vnode *vp;
int error;
bool connreq;
@@ -3008,36 +3006,17 @@
else
sa = NULL;
- /*
- * Find the peer socket we're connecting to, and connect to it.
- *
- * If the peer is bound to a name in the filesystem, then we hold
- * the vnode pool lock until the connection is established, so as to
- * avoid racing with a close of the peer listening socket. If the peer
- * is referenced by a file descriptor, then that reference prevents the
- * race, so no extra synchronization is needed.
- */
- error = unp_connectat_peer(td, fd, buf, &so2, &mtxp, &vp);
- if (error == 0) {
- error = unp_connect_peer(so, sotounpcb(so2), &sa, td,
- referenced_peerp != NULL);
- if (error == 0 && referenced_peerp != NULL) {
- *referenced_peerp = so2;
- so2 = NULL;
- }
-
- /*
- * Release references only after the pool lock is dropped in
- * order to avoid potential lock ordering issues.
- */
- if (mtxp != NULL) {
- mtx_unlock(mtxp);
- vput(vp);
- }
- if (so2 != NULL)
- sorele(so2);
- }
-
+ error = unp_connectat_peer(td, fd, buf, &so2);
+ if (error != 0)
+ goto out;
+ error = unp_connect_peer(so, sotounpcb(so2), &sa, td,
+ referenced_peerp != NULL);
+ /* Transfer the reference; the caller releases it after unlocking. */
+ if (error == 0 && referenced_peerp != NULL)
+ *referenced_peerp = so2;
+ else
+ sorele(so2);
+out:
free(sa, M_SONAME);
if (__predict_false(error)) {
UNP_PCB_LOCK(unp);
@@ -3087,7 +3066,7 @@
* descriptor in td_dupfd and fails with ENODEV, the same convention open(2)
* follows via dupfdopen() for /dev/fd. We honour it here and resolve that
* descriptor as the peer, so a plain connect(2) to /dev/fd/N reaches the
- * socket. Consumes the vnode reference.
+ * socket. Does not consume 'vp'.
*/
static int
unp_dupfd_peer(struct vnode *vp, struct thread *td, struct socket **so2p)
@@ -3100,14 +3079,13 @@
error = VOP_OPEN(vp, FREAD, td->td_ucred, td, NULL);
dupfd = td->td_dupfd;
td->td_dupfd = 0;
- if (error == ENODEV && dupfd >= 0) {
- error = unp_socket_fd_peer(td, dupfd, so2p);
- } else if (error == 0) {
+ if (error == ENODEV && dupfd >= 0)
+ return (unp_socket_fd_peer(td, dupfd, so2p));
+ if (error == 0) {
/* Not the dupfd convention: an openable node is not a peer. */
(void)VOP_CLOSE(vp, FREAD, td->td_ucred, td);
error = ECONNREFUSED;
}
- vput(vp);
return (error);
}
@@ -3121,21 +3099,16 @@
* /dev/fd/N pathname fdescfs names a descriptor
* ordinary pathname a bound socket looked up by path
*
- * The caller must release the returned socket with sorele(). If the mutex
- * and vnode pointers are filled, they must be released as well.
+ * The caller must release the returned socket with sorele().
*/
static int
unp_connectat_peer(struct thread *td, int fd, const char *buf,
- struct socket **so2p, struct mtx **mtxp, struct vnode **vpp)
+ struct socket **so2p)
{
struct nameidata nd;
cap_rights_t rights;
int error;
- *so2p = NULL;
- *mtxp = NULL;
- *vpp = NULL;
-
/*
* An empty sun_path means 'fd' names the peer directly. If it is a
* socket, it is the peer, so return success (or its error) with no
@@ -3158,10 +3131,10 @@
NDFREE_PNBUF(&nd);
/*
- * Find our peer socket. If it comes from a socket on the filesystem,
- * then we hold on to the vnode pool lock so as to interlock with a
- * close of the listening socket. If the peer comes to us via an fd,
- * then the fd reference itself keeps the peer stable.
+ * Dispatch on the resolved vnode, then drop it: for the socket cases
+ * the returned reference keeps the peer stable, so the caller holds
+ * no vnode lock across unp_connect_peer() (which matters for the
+ * return_locked datagram fast path).
*
* A synthetic descriptor node -- as fdescfs fabricates for a /dev/fd/N
* path -- carries no type of its own (VNON); opening it yields the
@@ -3176,7 +3149,8 @@
if (nd.ni_vp->v_type == VNON)
error = unp_dupfd_peer(nd.ni_vp, td, so2p);
else
- error = unp_vnode_peer(nd.ni_vp, td, so2p, mtxp, vpp);
+ error = unp_vnode_peer(nd.ni_vp, td, so2p);
+ vput(nd.ni_vp);
return (error);
}
@@ -3186,10 +3160,13 @@
* enforces the caller's authorization to reach the socket -- filesystem
* permission (VOP_ACCESS) and MAC (mac_vnode_check_open) -- which bare readers
* of the vnode->pcb binding, such as vfs_unp_reclaim(), deliberately skip.
+ *
+ * The returned reference keeps the peer stable for unp_connect_peer() once vp's
+ * per-vnode binding lock is dropped, so the caller must release it with
+ * sorele(). Does not consume 'vp'.
*/
static int
-unp_vnode_peer(struct vnode *vp, struct thread *td, struct socket **so2p,
- struct mtx **mtxp, struct vnode **vpp)
+unp_vnode_peer(struct vnode *vp, struct thread *td, struct socket **so2p)
{
struct mtx *vplock;
struct unpcb *unp2;
@@ -3197,43 +3174,34 @@
ASSERT_VOP_LOCKED(vp, __func__);
- if (vp->v_type != VSOCK) {
- error = ENOTSOCK;
- goto fail;
- }
+ if (vp->v_type != VSOCK)
+ return (ENOTSOCK);
#ifdef MAC
error = mac_vnode_check_open(td->td_ucred, vp, VWRITE | VREAD);
if (error != 0)
- goto fail;
+ return (error);
#endif
error = VOP_ACCESS(vp, VWRITE, td->td_ucred, td);
if (error != 0)
- goto fail;
+ return (error);
vplock = mtx_pool_find(unp_vp_mtxpool, vp);
mtx_lock(vplock);
VOP_UNP_CONNECT(vp, &unp2);
- if (unp2 == NULL) {
- mtx_unlock(vplock);
+ if (unp2 == NULL)
error = ECONNREFUSED;
- goto fail;
- }
- soref(*so2p = unp2->unp_socket);
- *mtxp = vplock;
- *vpp = vp;
- return (0);
-
-fail:
- vput(vp);
+ else
+ soref(*so2p = unp2->unp_socket);
+ mtx_unlock(vplock);
return (error);
}
/*
* Second half of connecting a unix socket: 'so' is our connecting socket,
* with UNP_CONNECTING set, and 'unp2' is the PCB of the peer named by the
- * caller, which must guarantee its stability (by holding a reference on the
- * peer socket, or the vnode lock plus unp_vp_mtxpool lock for a peer found
- * via VOP_UNP_CONNECT()).
+ * caller, which must guarantee its stability by holding a reference on the
+ * peer socket. That reference does not keep a listening peer from being
+ * closed; solisten_enqueue() handles that race.
*
* For connection-oriented sockets '*sap' points to a buffer to hold the
* listener's address; it is consumed (set to NULL) if used. On success
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 10, 3:50 PM (10 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41642371
Default Alt Text
D61341.1794325829.diff (7 KB)
Attached To
Mode
D61341: unix: Go back to not holding the vnode pool lock across connect
Attached
Detach File
Event Timeline