summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/kern/uipc_usrreq.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/kern/uipc_usrreq.c')
-rw-r--r--freebsd/sys/kern/uipc_usrreq.c157
1 files changed, 88 insertions, 69 deletions
diff --git a/freebsd/sys/kern/uipc_usrreq.c b/freebsd/sys/kern/uipc_usrreq.c
index 688682d4..c1885ed6 100644
--- a/freebsd/sys/kern/uipc_usrreq.c
+++ b/freebsd/sys/kern/uipc_usrreq.c
@@ -376,33 +376,32 @@ unp_pcb_lock2(struct unpcb *unp, struct unpcb *unp2)
}
static __noinline void
-unp_pcb_owned_lock2_slowpath(struct unpcb *unp, struct unpcb **unp2p, int *freed)
-
+unp_pcb_owned_lock2_slowpath(struct unpcb *unp, struct unpcb **unp2p,
+ int *freed)
{
struct unpcb *unp2;
unp2 = *unp2p;
- unp_pcb_hold((unp2));
- UNP_PCB_UNLOCK((unp));
- UNP_PCB_LOCK((unp2));
- UNP_PCB_LOCK((unp));
- *freed = unp_pcb_rele((unp2));
+ unp_pcb_hold(unp2);
+ UNP_PCB_UNLOCK(unp);
+ UNP_PCB_LOCK(unp2);
+ UNP_PCB_LOCK(unp);
+ *freed = unp_pcb_rele(unp2);
if (*freed)
*unp2p = NULL;
}
-#define unp_pcb_owned_lock2(unp, unp2, freed) do { \
- freed = 0; \
- UNP_PCB_LOCK_ASSERT((unp)); \
- UNP_PCB_UNLOCK_ASSERT((unp2)); \
- MPASS(unp != unp2); \
- if (__predict_true(UNP_PCB_TRYLOCK((unp2)))) \
- break; \
- else if ((uintptr_t)(unp2) > (uintptr_t)(unp)) \
- UNP_PCB_LOCK((unp2)); \
- else { \
- unp_pcb_owned_lock2_slowpath((unp), &(unp2), &freed); \
- } \
+#define unp_pcb_owned_lock2(unp, unp2, freed) do { \
+ freed = 0; \
+ UNP_PCB_LOCK_ASSERT(unp); \
+ UNP_PCB_UNLOCK_ASSERT(unp2); \
+ MPASS((unp) != (unp2)); \
+ if (__predict_true(UNP_PCB_TRYLOCK(unp2))) \
+ break; \
+ else if ((uintptr_t)(unp2) > (uintptr_t)(unp)) \
+ UNP_PCB_LOCK(unp2); \
+ else \
+ unp_pcb_owned_lock2_slowpath((unp), &(unp2), &freed); \
} while (0)
@@ -992,21 +991,19 @@ uipc_disconnect(struct socket *so)
UNP_PCB_UNLOCK(unp);
return (0);
}
- if (unp == unp2) {
- if (unp_pcb_rele(unp) == 0)
+ if (__predict_true(unp != unp2)) {
+ unp_pcb_owned_lock2(unp, unp2, freed);
+ if (__predict_false(freed)) {
UNP_PCB_UNLOCK(unp);
+ return (0);
+ }
+ unp_pcb_hold(unp2);
}
- unp_pcb_owned_lock2(unp, unp2, freed);
- if (__predict_false(freed)) {
- UNP_PCB_UNLOCK(unp);
- return (0);
- }
- unp_pcb_hold(unp2);
unp_pcb_hold(unp);
unp_disconnect(unp, unp2);
if (unp_pcb_rele(unp) == 0)
UNP_PCB_UNLOCK(unp);
- if (unp_pcb_rele(unp2) == 0)
+ if ((unp != unp2) && unp_pcb_rele(unp2) == 0)
UNP_PCB_UNLOCK(unp2);
return (0);
}
@@ -1305,16 +1302,22 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
control = unp_addsockcred(td, control);
#endif /* __rtems__ */
}
+
/*
- * Send to paired receive port, and then reduce send buffer
- * hiwater marks to maintain backpressure. Wake up readers.
+ * Send to paired receive port and wake up readers. Don't
+ * check for space available in the receive buffer if we're
+ * attaching ancillary data; Unix domain sockets only check
+ * for space in the sending sockbuf, and that check is
+ * performed one level up the stack. At that level we cannot
+ * precisely account for the amount of buffer space used
+ * (e.g., because control messages are not yet internalized).
*/
switch (so->so_type) {
case SOCK_STREAM:
if (control != NULL) {
- if (sbappendcontrol_locked(&so2->so_rcv, m,
- control))
- control = NULL;
+ sbappendcontrol_locked(&so2->so_rcv, m,
+ control);
+ control = NULL;
} else
sbappend_locked(&so2->so_rcv, m, flags);
break;
@@ -1323,14 +1326,8 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
const struct sockaddr *from;
from = &sun_noname;
- /*
- * Don't check for space available in so2->so_rcv.
- * Unix domain sockets only check for space in the
- * sending sockbuf, and that check is performed one
- * level up the stack.
- */
if (sbappendaddr_nospacecheck_locked(&so2->so_rcv,
- from, m, control))
+ from, m, control))
control = NULL;
break;
}
@@ -1396,14 +1393,21 @@ uipc_ready(struct socket *so, struct mbuf *m, int count)
unp = sotounpcb(so);
- UNP_LINK_RLOCK();
+ UNP_PCB_LOCK(unp);
if ((unp2 = unp->unp_conn) == NULL) {
- UNP_LINK_RUNLOCK();
- for (int i = 0; i < count; i++)
- m = m_free(m);
- return (ECONNRESET);
+ UNP_PCB_UNLOCK(unp);
+ goto error;
+ }
+ if (unp != unp2) {
+ if (UNP_PCB_TRYLOCK(unp2) == 0) {
+ unp_pcb_hold(unp2);
+ UNP_PCB_UNLOCK(unp);
+ UNP_PCB_LOCK(unp2);
+ if (unp_pcb_rele(unp2))
+ goto error;
+ } else
+ UNP_PCB_UNLOCK(unp);
}
- UNP_PCB_LOCK(unp2);
so2 = unp2->unp_socket;
SOCKBUF_LOCK(&so2->so_rcv);
@@ -1413,9 +1417,12 @@ uipc_ready(struct socket *so, struct mbuf *m, int count)
SOCKBUF_UNLOCK(&so2->so_rcv);
UNP_PCB_UNLOCK(unp2);
- UNP_LINK_RUNLOCK();
return (error);
+ error:
+ for (int i = 0; i < count; i++)
+ m = m_free(m);
+ return (ECONNRESET);
}
static int
@@ -1778,24 +1785,8 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam,
sa = NULL;
}
- /*
- * The connector's (client's) credentials are copied from its
- * process structure at the time of connect() (which is now).
- */
- cru2x(td->td_ucred, &unp3->unp_peercred);
- unp3->unp_flags |= UNP_HAVEPC;
+ unp_copy_peercred(td, unp3, unp, unp2);
- /*
- * The receiver's (server's) credentials are copied from the
- * unp_peercred member of socket on which the former called
- * listen(); uipc_listen() cached that process's credentials
- * at that time so we can use them now.
- */
- memcpy(&unp->unp_peercred, &unp2->unp_peercred,
- sizeof(unp->unp_peercred));
- unp->unp_flags |= UNP_HAVEPC;
- if (unp2->unp_flags & UNP_WANTCRED)
- unp3->unp_flags |= UNP_WANTCRED;
UNP_PCB_UNLOCK(unp2);
unp2 = unp3;
unp_pcb_owned_lock2(unp2, unp, freed);
@@ -1838,6 +1829,27 @@ bad:
return (error);
}
+/*
+ * Set socket peer credentials at connection time.
+ *
+ * The client's PCB credentials are copied from its process structure. The
+ * server's PCB credentials are copied from the socket on which it called
+ * listen(2). uipc_listen cached that process's credentials at the time.
+ */
+void
+unp_copy_peercred(struct thread *td, struct unpcb *client_unp,
+ struct unpcb *server_unp, struct unpcb *listen_unp)
+{
+ cru2x(td->td_ucred, &client_unp->unp_peercred);
+ client_unp->unp_flags |= UNP_HAVEPC;
+
+ memcpy(&server_unp->unp_peercred, &listen_unp->unp_peercred,
+ sizeof(server_unp->unp_peercred));
+ server_unp->unp_flags |= UNP_HAVEPC;
+ if (listen_unp->unp_flags & UNP_WANTCRED)
+ client_unp->unp_flags |= UNP_WANTCRED;
+}
+
static int
unp_connect2(struct socket *so, struct socket *so2, int req)
{
@@ -2026,7 +2038,7 @@ unp_pcblist(SYSCTL_HANDLER_ARGS)
if (freeunp == 0 && unp->unp_gencnt <= gencnt) {
xu->xu_len = sizeof *xu;
- xu->xu_unpp = unp;
+ xu->xu_unpp = (uintptr_t)unp;
/*
* XXX - need more locking here to protect against
* connect/disconnect races for SMP.
@@ -2043,10 +2055,10 @@ unp_pcblist(SYSCTL_HANDLER_ARGS)
unp->unp_conn->unp_addr->sun_len);
else
bzero(&xu->xu_caddr, sizeof(xu->xu_caddr));
- xu->unp_vnode = unp->unp_vnode;
- xu->unp_conn = unp->unp_conn;
- xu->xu_firstref = LIST_FIRST(&unp->unp_refs);
- xu->xu_nextref = LIST_NEXT(unp, unp_reflink);
+ xu->unp_vnode = (uintptr_t)unp->unp_vnode;
+ xu->unp_conn = (uintptr_t)unp->unp_conn;
+ xu->xu_firstref = (uintptr_t)LIST_FIRST(&unp->unp_refs);
+ xu->xu_nextref = (uintptr_t)LIST_NEXT(unp, unp_reflink);
xu->unp_gencnt = unp->unp_gencnt;
sotoxsocket(unp->unp_socket, &xu->xu_socket);
UNP_PCB_UNLOCK(unp);
@@ -2220,6 +2232,13 @@ unp_externalize(struct mbuf *control, struct mbuf **controlp, int flags)
&fdep[i]->fde_caps);
unp_externalize_fp(fdep[i]->fde_file);
}
+
+ /*
+ * The new type indicates that the mbuf data refers to
+ * kernel resources that may need to be released before
+ * the mbuf is freed.
+ */
+ m_chtype(*controlp, MT_EXTCONTROL);
FILEDESC_XUNLOCK(fdesc);
free(fdep[0], M_FILECAPS);
} else {