Page MenuHomeFreeBSD

D61341.1794325838.diff
No OneTemporary

Authored By
Unknown
Size
7 KB
Referenced Files
None
Subscribers
None

D61341.1794325838.diff

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

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.1794325838.diff (7 KB)

Event Timeline