git: 346495824627 - main - unix/dgram: add a specific send method - uipc_sosend_dgram()

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
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,