The branch main has been updated by glebius:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=3464958246273971a7f1e8e56773a589032c3ee4

commit 3464958246273971a7f1e8e56773a589032c3ee4
Author:     Gleb Smirnoff <[email protected]>
AuthorDate: 2022-06-24 16:09:10 +0000
Commit:     Gleb Smirnoff <[email protected]>
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,

Reply via email to