git: 346495824627 - main - unix/dgram: add a specific send method - uipc_sosend_dgram()
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 24 Jun 2022 16:10:28 UTC
The branch main has been updated by glebius: URL: https://cgit.FreeBSD.org/src/commit/?id=3464958246273971a7f1e8e56773a589032c3ee4 commit 3464958246273971a7f1e8e56773a589032c3ee4 Author: Gleb Smirnoff <glebius@FreeBSD.org> AuthorDate: 2022-06-24 16:09:10 +0000 Commit: Gleb Smirnoff <glebius@FreeBSD.org> CommitDate: 2022-06-24 16:09:10 +0000 unix/dgram: add a specific send method - uipc_sosend_dgram() This is first step towards splitting classic BSD socket implementation into separate classes. The first to be split is PF_UNIX/SOCK_DGRAM as it has most differencies to SOCK_STREAM sockets and to PF_INET sockets. Historically a protocol shall provide two methods for sendmsg(2): pru_sosend and pru_send. The former is a generic send method, e.g. sosend_generic() which would internally call the latter, uipc_send() in our case. There is one important exception, though, the sendfile(2) code will call pru_send directly. But sendfile doesn't work on SOCK_DGRAM, so we can do the trick. We will create socket class specific uipc_sosend_dgram() which will carry only important bits from sosend_generic() and uipc_send(). Reviewed by: markj Differential revision: https://reviews.freebsd.org/D35293 --- sys/kern/uipc_usrreq.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 3 deletions(-) diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index 16029e5c38c8..2e54ee827b8e 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -996,8 +996,7 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, unp = sotounpcb(so); KASSERT(unp != NULL, ("%s: unp == NULL", __func__)); - KASSERT(so->so_type == SOCK_STREAM || so->so_type == SOCK_DGRAM || - so->so_type == SOCK_SEQPACKET, + KASSERT(so->so_type == SOCK_STREAM || so->so_type == SOCK_SEQPACKET, ("%s: socktype %d", __func__, so->so_type)); error = 0; @@ -1173,6 +1172,146 @@ release: return (error); } +/* + * PF_UNIX/SOCK_DGRAM send + */ +static int +uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, + struct mbuf *m, struct mbuf *c, int flags, struct thread *td) +{ + struct unpcb *unp, *unp2; + const struct sockaddr *from; + struct socket *so2; + int error; + + MPASS((uio != NULL && m == NULL) || (m != NULL && uio == NULL)); + + error = 0; + + if (__predict_false(flags & MSG_OOB)) { + error = EOPNOTSUPP; + goto out; + } + if (m == NULL) { + if (__predict_false(uio->uio_resid > unpdg_maxdgram)) { + error = EMSGSIZE; + goto out; + } + m = m_uiotombuf(uio, M_WAITOK, 0, max_hdr, M_PKTHDR); + if (__predict_false(m == NULL)) { + error = EFAULT; + goto out; + } + if (c != NULL && (error = unp_internalize(&c, td))) + goto out; + } else { + /* pru_sosend() with mbuf usually is a kernel thread. */ + + M_ASSERTPKTHDR(m); + if (__predict_false(c != NULL)) + panic("%s: control from a kernel thread", __func__); + + if (__predict_false(m->m_pkthdr.len > unpdg_maxdgram)) { + error = EMSGSIZE; + goto out; + } + /* Condition the foreign mbuf to our standards. */ + m_clrprotoflags(m); + m_tag_delete_chain(m, NULL); + m->m_pkthdr.rcvif = NULL; + m->m_pkthdr.flowid = 0; + m->m_pkthdr.csum_flags = 0; + m->m_pkthdr.fibnum = 0; + m->m_pkthdr.rsstype = 0; + } + + unp = sotounpcb(so); + MPASS(unp); + + /* + * XXXGL: would be cool to fully remove so_snd out of the equation + * and avoid this lock, which is not only extraneous, but also being + * released, thus still leaving possibility for a race. We can easily + * handle SBS_CANTSENDMORE/SS_ISCONNECTED complement in unpcb, but it + * is more difficult to invent something to handle so_error. + */ + error = SOCK_IO_SEND_LOCK(so, SBLOCKWAIT(flags)); + if (error) + goto out2; + SOCKBUF_LOCK(&so->so_snd); + if (so->so_snd.sb_state & SBS_CANTSENDMORE) { + SOCK_SENDBUF_UNLOCK(so); + error = EPIPE; + goto out3; + } + if (so->so_error != 0) { + error = so->so_error; + so->so_error = 0; + SOCKBUF_UNLOCK(&so->so_snd); + goto out3; + } + if (((so->so_state & SS_ISCONNECTED) == 0) && addr == NULL) { + SOCKBUF_UNLOCK(&so->so_snd); + error = EDESTADDRREQ; + goto out3; + } + SOCKBUF_UNLOCK(&so->so_snd); + + if (addr != NULL && (error = unp_connect(so, addr, td))) + goto out3; + + UNP_PCB_LOCK(unp); + /* + * Because connect() and send() are non-atomic in a sendto() with a + * target address, it's possible that the socket will have disconnected + * before the send() can run. In that case return the slightly + * counter-intuitive but otherwise correct error that the socket is not + * connected. + */ + unp2 = unp_pcb_lock_peer(unp); + if (unp2 == NULL) { + UNP_PCB_UNLOCK(unp); + error = ENOTCONN; + goto out3; + } + + if (unp2->unp_flags & UNP_WANTCRED_MASK) + c = unp_addsockcred(td, c, unp2->unp_flags); + if (unp->unp_addr != NULL) + from = (struct sockaddr *)unp->unp_addr; + else + from = &sun_noname; + so2 = unp2->unp_socket; + SOCKBUF_LOCK(&so2->so_rcv); + if (sbappendaddr_locked(&so2->so_rcv, from, m, c)) { + sorwakeup_locked(so2); + m = c = NULL; + } else { + soroverflow_locked(so2); + error = (so->so_state & SS_NBIO) ? EAGAIN : ENOBUFS; + } + + if (addr != NULL) + unp_disconnect(unp, unp2); + else + unp_pcb_unlock_pair(unp, unp2); + + td->td_ru.ru_msgsnd++; + +out3: + SOCK_IO_SEND_UNLOCK(so); +out2: + if (c) + unp_scan(c, unp_freerights); +out: + if (c) + m_freem(c); + if (m) + m_freem(m); + + return (error); +} + static bool uipc_ready_scan(struct socket *so, struct mbuf *m, int count, int *errorp) { @@ -1314,7 +1453,7 @@ static struct pr_usrreqs uipc_usrreqs_dgram = { .pru_detach = uipc_detach, .pru_disconnect = uipc_disconnect, .pru_peeraddr = uipc_peeraddr, - .pru_send = uipc_send, + .pru_sosend = uipc_sosend_dgram, .pru_sense = uipc_sense, .pru_shutdown = uipc_shutdown, .pru_sockaddr = uipc_sockaddr,