From nobody Tue Apr 09 20:19:34 2024 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4VDckZ6STvz5Fw0M; Tue, 9 Apr 2024 20:19:34 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4VDckZ672Nz50rt; Tue, 9 Apr 2024 20:19:34 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1712693974; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=HTsC+k8WGYhpzFo3ni6OlBxnLBFpxhZixDeQZy/bWVo=; b=whHg5Adyhk5cGeTttLSKZT+wU6825ZNzP41LNMld3N44+wC+NcjipRMlkbXjjBmku9DSFX CbefBoWxOm6mcGlFwhxppS3BwP9q0FMQUXvJ35jPM/3DWZQPMzPRLO4vmBYHNQNzB5IA+z jbSYt8rxJ0RYwSSnUaV22PQU+K85DyoBQTJ0ulkP9SOK5ucXt8skUwT5EXuTIw1MRZWYoQ 86Ji04hkuHUKFEfHUVp6LIIWn06+Mlc+HdNYpGuTIaWzWKHvLVE8dZa9TODHZchcVgnIHe RQ9hBEdZnxbTPYioArSJoQwyF44Qjj0dOPLwgSWAkI7TctlBo+r+xbLh5lGjIw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1712693974; a=rsa-sha256; cv=none; b=myK5EahlAfjcM6ZsQ6BR1jdyMtxQ3DKXY8/G6gfREP9VFI5Uo1Mlh84olIyJUvr8ygAKVo 09VDCE2nyMuEpo9WST7j5muPKmE1bW1lFT3hGeYwLqpJEOi8rgRl1XK4SVuCJkRyjZoL5i PmHWJYU3U9Xs0NcGKLqALfgeODiBbL5LreoTn1599utpSdF461e43W4+xow+fIQUVZO6Lm SVHXu28Cy/JolKIioFvobQo2UFYGMCZMNSER8qdcG6I2eulqyw2nvu8fEGLa7gmYgV9stp ScTOreR86jHBp2DeDX54XsLoO/tBVi24YhmDq5XcgRJl6AqA5uCrJevo6iRocQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1712693974; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=HTsC+k8WGYhpzFo3ni6OlBxnLBFpxhZixDeQZy/bWVo=; b=ReOx2XNDYlpOr6Kob7zVpsRAAI6q2Xpryzfi6mDQ1UsjPR2S2nW1IE7lOq9VDE9Ddwb/sX O8WBdAi8c26x5A1Mpy5J5GECFcpd6spL2n3gcJc1NlB9vod9vC8ptwYo2n2F/fHZhqlpTE P7jAvmavhWXjWSmT3PvLSeJHSQmnaA4SJ/mkPVO1UbRXyFhDqvvVj9m3tvLQ5aSWaDpADb aeB1q9EzQlGt5kW9Tw1ryxPU2ojjph9HTQ2udXMKQ/coBe8/PxSswJ8kqOSNNQo3273NO0 u+CVBipu/q5psPEGoj7H1TOzMLHLb76+/shhgUnAiZeOqxcBq3YKHD340/xFdg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4VDckZ5Ytpz17qw; Tue, 9 Apr 2024 20:19:34 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 439KJYgo064399; Tue, 9 Apr 2024 20:19:34 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 439KJYU9064396; Tue, 9 Apr 2024 20:19:34 GMT (envelope-from git) Date: Tue, 9 Apr 2024 20:19:34 GMT Message-Id: <202404092019.439KJYU9064396@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Gleb Smirnoff Subject: git: 5716d902ae1d - main - Revert "unix: new implementation of unix/stream & unix/seqpacket" List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: dev-commits-src-main+owner@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: glebius X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 5716d902ae1d2dbd396f7808c12d7b0d285f2c9d Auto-Submitted: auto-generated The branch main has been updated by glebius: URL: https://cgit.FreeBSD.org/src/commit/?id=5716d902ae1d2dbd396f7808c12d7b0d285f2c9d commit 5716d902ae1d2dbd396f7808c12d7b0d285f2c9d Author: Gleb Smirnoff AuthorDate: 2024-04-09 20:15:16 +0000 Commit: Gleb Smirnoff CommitDate: 2024-04-09 20:15:47 +0000 Revert "unix: new implementation of unix/stream & unix/seqpacket" The regressions in aio(4) and kernel RPC aren't a 5 minute problem. This reverts commit d80a97def9a1db6f07f5d2e68f7ad62b27918947. This reverts commit d1cbb17a873c787a527316bbb27551e97d5ad30c. This reverts commit fb8a8333b481cc4256d0b3f0b5b4feaa4594e01f. --- sys/kern/uipc_usrreq.c | 959 +++++++++++++++++-------------------------------- sys/sys/sockbuf.h | 7 - 2 files changed, 320 insertions(+), 646 deletions(-) diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index 4a1c480c43fa..6e83e2be6f05 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -5,7 +5,7 @@ * The Regents of the University of California. All Rights Reserved. * Copyright (c) 2004-2009 Robert N. M. Watson All Rights Reserved. * Copyright (c) 2018 Matthew Macy - * Copyright (c) 2022-2024 Gleb Smirnoff + * Copyright (c) 2022 Gleb Smirnoff * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -141,14 +141,11 @@ static struct timeout_task unp_gc_task; static struct task unp_defer_task; /* - * SOCK_STREAM and SOCK_SEQPACKET unix(4) sockets fully bypass the send buffer, - * however the notion of send buffer still makes sense with them. Its size is - * the amount of space that a send(2) syscall may copyin(9) before checking - * with the receive buffer of a peer. Although not linked anywhere yet, - * pointed to by a stack variable, effectively it is a buffer that needs to be - * sized. + * Both send and receive buffers are allocated PIPSIZ bytes of buffering for + * stream sockets, although the total for sender and receiver is actually + * only PIPSIZ. * - * SOCK_DGRAM sockets really use the sendspace as the maximum datagram size, + * Datagram sockets really use the sendspace as the maximum datagram size, * and don't really want to reserve the sendspace. Their recvspace should be * large enough for at least one max-size datagram plus address. */ @@ -159,7 +156,7 @@ static u_long unpst_sendspace = PIPSIZ; static u_long unpst_recvspace = PIPSIZ; static u_long unpdg_maxdgram = 8*1024; /* support 8KB syslog msgs */ static u_long unpdg_recvspace = 16*1024; -static u_long unpsp_sendspace = PIPSIZ; +static u_long unpsp_sendspace = PIPSIZ; /* really max datagram size */ static u_long unpsp_recvspace = PIPSIZ; static SYSCTL_NODE(_net, PF_LOCAL, local, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, @@ -303,12 +300,13 @@ static void unp_gc(__unused void *, int); static void unp_scan(struct mbuf *, void (*)(struct filedescent **, int)); static void unp_discard(struct file *); static void unp_freerights(struct filedescent **, int); -static int unp_internalize(struct mbuf *, struct mchain *, - struct thread *); +static int unp_internalize(struct mbuf **, struct thread *, + struct mbuf **, u_int *, u_int *); static void unp_internalize_fp(struct file *); static int unp_externalize(struct mbuf *, struct mbuf **, int); static int unp_externalize_fp(struct file *); -static void unp_addsockcred(struct thread *, struct mchain *, int); +static struct mbuf *unp_addsockcred(struct thread *, struct mbuf *, + int, struct mbuf **, u_int *, u_int *); static void unp_process_defers(void * __unused, int); static void @@ -451,7 +449,6 @@ uipc_attach(struct socket *so, int proto, struct thread *td) case SOCK_STREAM: sendspace = unpst_sendspace; recvspace = unpst_recvspace; - STAILQ_INIT(&so->so_rcv.sb_mbq); break; case SOCK_DGRAM: @@ -469,7 +466,6 @@ uipc_attach(struct socket *so, int proto, struct thread *td) case SOCK_SEQPACKET: sendspace = unpsp_sendspace; recvspace = unpsp_recvspace; - STAILQ_INIT(&so->so_rcv.sb_mbq); break; default: @@ -801,10 +797,6 @@ uipc_detach(struct socket *so) taskqueue_enqueue_timeout(taskqueue_thread, &unp_gc_task, -1); switch (so->so_type) { - case SOCK_STREAM: - case SOCK_SEQPACKET: - MPASS(STAILQ_EMPTY(&so->so_rcv.sb_mbq)); - break; case SOCK_DGRAM: /* * Everything should have been unlinked/freed by unp_dispose() @@ -860,10 +852,6 @@ uipc_listen(struct socket *so, int backlog, struct thread *td) error = solisten_proto_check(so); if (error == 0) { cru2xt(td, &unp->unp_peercred); - (void)chgsbsize(so->so_cred->cr_uidinfo, &so->so_snd.sb_hiwat, - 0, RLIM_INFINITY); - (void)chgsbsize(so->so_cred->cr_uidinfo, &so->so_rcv.sb_hiwat, - 0, RLIM_INFINITY); solisten_proto(so, backlog); } SOCK_UNLOCK(so); @@ -897,562 +885,187 @@ uipc_peeraddr(struct socket *so, struct sockaddr *ret) return (0); } -/* - * pr_sosend() called with mbuf instead of uio is a kernel thread. NFS, - * netgraph(4) and other subsystems can call into socket code. The - * function will condition the mbuf so that it can be safely put onto socket - * buffer and calculate its char count and mbuf count. - * - * Note: we don't support receiving control data from a kernel thread. Our - * pr_sosend methods have MPASS() to check that. This may change. - */ -static void -uipc_reset_kernel_mbuf(struct mbuf *m, struct mchain *mc) -{ - - M_ASSERTPKTHDR(m); - - 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; - - mc_init_m(mc, m); - MPASS(m->m_pkthdr.len == mc->mc_len); -} - -#ifdef SOCKBUF_DEBUG -static inline void -uipc_stream_sbcheck(struct sockbuf *sb) +static int +uipc_rcvd(struct socket *so, int flags) { - struct mbuf *d; - u_int dcc, dctl, dmbcnt; - - dcc = dctl = dmbcnt = 0; - STAILQ_FOREACH(d, &sb->sb_mbq, m_stailq) { - if (d->m_type == MT_CONTROL) - dctl += d->m_len; - else if (d->m_type == MT_DATA) - dcc += d->m_len; - else - MPASS(0); - dmbcnt += MSIZE; - if (d->m_flags & M_EXT) - dmbcnt += d->m_ext.ext_size; - if (d->m_stailq.stqe_next == NULL) - MPASS(sb->sb_mbq.stqh_last == &d->m_stailq.stqe_next); - } - MPASS(dcc == sb->sb_acc); - MPASS(dcc == sb->sb_ccc); - MPASS(dctl == sb->sb_ctl); - MPASS(dmbcnt == sb->sb_mbcnt); -} -#define UIPC_STREAM_SBCHECK(sb) uipc_stream_sbcheck(sb) -#else -#define UIPC_STREAM_SBCHECK(sb) do {} while (0) -#endif + struct unpcb *unp, *unp2; + struct socket *so2; + u_int mbcnt, sbcc; -/* - * uipc_stream_sbspace() returns how much a writer can send, limited by char - * count or mbuf memory use, whatever ends first. - * - * XXXGL: sb_mbcnt may overcommit sb_mbmax in case if previous write observed - * 'space < mbspace', but mchain allocated to hold 'space' bytes of data ended - * up with 'mc_mlen > mbspace'. A typical scenario would be a full buffer with - * writer trying to push in a large write, and a slow reader, that reads just - * a few bytes at a time. In that case writer will keep creating new mbufs - * with mc_split(). These mbufs will carry little chars, but will all point at - * the same cluster, thus each adding cluster size to sb_mbcnt. This means we - * will count same cluster many times potentially underutilizing socket buffer. - * We aren't optimizing towards ineffective readers. Classic socket buffer had - * the same "feature". - */ -static inline u_int -uipc_stream_sbspace(struct sockbuf *sb) -{ - u_int space, mbspace; + unp = sotounpcb(so); + KASSERT(unp != NULL, ("%s: unp == NULL", __func__)); + KASSERT(so->so_type == SOCK_STREAM || so->so_type == SOCK_SEQPACKET, + ("%s: socktype %d", __func__, so->so_type)); - MPASS(sb->sb_hiwat >= sb->sb_ccc + sb->sb_ctl); - space = sb->sb_hiwat - sb->sb_ccc - sb->sb_ctl; - if (__predict_true(sb->sb_mbmax >= sb->sb_mbcnt)) - mbspace = sb->sb_mbmax - sb->sb_mbcnt; - else + /* + * Adjust backpressure on sender and wakeup any waiting to write. + * + * The unp lock is acquired to maintain the validity of the unp_conn + * pointer; no lock on unp2 is required as unp2->unp_socket will be + * static as long as we don't permit unp2 to disconnect from unp, + * which is prevented by the lock on unp. We cache values from + * so_rcv to avoid holding the so_rcv lock over the entire + * transaction on the remote so_snd. + */ + SOCKBUF_LOCK(&so->so_rcv); + mbcnt = so->so_rcv.sb_mbcnt; + sbcc = sbavail(&so->so_rcv); + SOCKBUF_UNLOCK(&so->so_rcv); + /* + * There is a benign race condition at this point. If we're planning to + * clear SB_STOP, but uipc_send is called on the connected socket at + * this instant, it might add data to the sockbuf and set SB_STOP. Then + * we would erroneously clear SB_STOP below, even though the sockbuf is + * full. The race is benign because the only ill effect is to allow the + * sockbuf to exceed its size limit, and the size limits are not + * strictly guaranteed anyway. + */ + UNP_PCB_LOCK(unp); + unp2 = unp->unp_conn; + if (unp2 == NULL) { + UNP_PCB_UNLOCK(unp); return (0); - - return (min(space, mbspace)); + } + so2 = unp2->unp_socket; + SOCKBUF_LOCK(&so2->so_snd); + if (sbcc < so2->so_snd.sb_hiwat && mbcnt < so2->so_snd.sb_mbmax) + so2->so_snd.sb_flags &= ~SB_STOP; + sowwakeup_locked(so2); + UNP_PCB_UNLOCK(unp); + return (0); } static int -uipc_sosend_stream_or_seqpacket(struct socket *so, struct sockaddr *addr, - struct uio *uio, struct mbuf *m, struct mbuf *c, int flags, - struct thread *td) +uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, + struct mbuf *control, struct thread *td) { struct unpcb *unp, *unp2; struct socket *so2; - struct sockbuf *sb; - struct mchain mc, cmc; - ssize_t resid, sent; - bool nonblock, eor; + u_int mbcnt, sbcc; int error; - MPASS((uio != NULL && m == NULL) || (m != NULL && uio == NULL)); - MPASS(m == NULL || c == NULL); - - if (__predict_false(flags & MSG_OOB)) - return (EOPNOTSUPP); - - nonblock = (so->so_state & SS_NBIO) || - (flags & (MSG_DONTWAIT | MSG_NBIO)); - eor = flags & MSG_EOR; + unp = sotounpcb(so); + KASSERT(unp != NULL, ("%s: unp == NULL", __func__)); + KASSERT(so->so_type == SOCK_STREAM || so->so_type == SOCK_SEQPACKET, + ("%s: socktype %d", __func__, so->so_type)); - mc = MCHAIN_INITIALIZER(&mc); - cmc = MCHAIN_INITIALIZER(&cmc); - sent = 0; + error = 0; + if (flags & PRUS_OOB) { + error = EOPNOTSUPP; + goto release; + } + if (control != NULL && + (error = unp_internalize(&control, td, NULL, NULL, NULL))) + goto release; - if (m == NULL) { - if (c != NULL && (error = unp_internalize(c, &cmc, td))) + unp2 = NULL; + if ((so->so_state & SS_ISCONNECTED) == 0) { + if (nam != NULL) { + if ((error = unp_connect(so, nam, td)) != 0) + goto out; + } else { + error = ENOTCONN; goto out; - /* - * Optimization for a case when our send fits into the receive - * buffer - do the copyin before taking any locks, sized to our - * send buffer. Later copyins will also take into account - * space in the peer's receive buffer. - */ - resid = uio->uio_resid; - error = mc_uiotomc(&mc, uio, so->so_snd.sb_hiwat, 0, M_WAITOK, - eor ? M_EOR : 0); - if (__predict_false(error)) - goto out2; - } else - uipc_reset_kernel_mbuf(m, &mc); - - error = SOCK_IO_SEND_LOCK(so, SBLOCKWAIT(flags)); - if (error) - goto out2; + } + } - unp = sotounpcb(so); UNP_PCB_LOCK(unp); - unp2 = unp_pcb_lock_peer(unp); - if (__predict_false(so->so_error != 0)) { - error = so->so_error; - so->so_error = 0; - UNP_PCB_UNLOCK(unp); - if (unp2 != NULL) - UNP_PCB_UNLOCK(unp2); - goto out3; - } - if (__predict_false(unp2 == NULL)) { - /* - * Different error code for a previously connected socket and - * a never connected one. The SS_ISDISCONNECTED is set in the - * unp_soisdisconnected() and is synchronized by the pcb lock. - */ - error = so->so_state & SS_ISDISCONNECTED ? EPIPE : ENOTCONN; + if ((unp2 = unp_pcb_lock_peer(unp)) == NULL) { UNP_PCB_UNLOCK(unp); - goto out3; + error = ENOTCONN; + goto out; + } else if (so->so_snd.sb_state & SBS_CANTSENDMORE) { + unp_pcb_unlock_pair(unp, unp2); + error = EPIPE; + goto out; } UNP_PCB_UNLOCK(unp); - + if ((so2 = unp2->unp_socket) == NULL) { + UNP_PCB_UNLOCK(unp2); + error = ENOTCONN; + goto out; + } + SOCKBUF_LOCK(&so2->so_rcv); if (unp2->unp_flags & UNP_WANTCRED_MASK) { /* * Credentials are passed only once on SOCK_STREAM and * SOCK_SEQPACKET (LOCAL_CREDS => WANTCRED_ONESHOT), or * forever (LOCAL_CREDS_PERSISTENT => WANTCRED_ALWAYS). */ - unp_addsockcred(td, &cmc, unp2->unp_flags); + control = unp_addsockcred(td, control, unp2->unp_flags, NULL, + NULL, NULL); unp2->unp_flags &= ~UNP_WANTCRED_ONESHOT; } /* - * Cycle through the data to send and available space in the peer's - * receive buffer. Put a reference on the peer socket, so that it - * doesn't get freed while we sbwait(). If peer goes away, we will - * observe the SBS_CANTRCVMORE and our sorele() will finalize peer's - * socket destruction. + * 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). */ - so2 = unp2->unp_socket; - soref(so2); - UNP_PCB_UNLOCK(unp2); - sb = &so2->so_rcv; - while (mc.mc_len + cmc.mc_len > 0) { - struct mchain mcnext = MCHAIN_INITIALIZER(&mcnext); - u_int space; + switch (so->so_type) { + case SOCK_STREAM: + if (control != NULL) { + sbappendcontrol_locked(&so2->so_rcv, + m->m_len > 0 ? m : NULL, control, flags); + control = NULL; + } else + sbappend_locked(&so2->so_rcv, m, flags); + break; - SOCK_RECVBUF_LOCK(so2); -restart: - UIPC_STREAM_SBCHECK(sb); - if (__predict_false(cmc.mc_len > sb->sb_hiwat)) { - SOCK_RECVBUF_UNLOCK(so2); - error = EMSGSIZE; - goto out4; - } - if (__predict_false(sb->sb_state & SBS_CANTRCVMORE)) { - SOCK_RECVBUF_UNLOCK(so2); - error = EPIPE; - goto out4; - } - /* - * Wait on the peer socket receive buffer until we have enough - * space to put at least control. The data is a stream and can - * be put partially, but control is really a datagram. - */ - space = uipc_stream_sbspace(sb); - if (space < sb->sb_lowat || space < cmc.mc_len) { - if (nonblock) { - SOCK_RECVBUF_UNLOCK(so2); - error = EWOULDBLOCK; - goto out4; - } - if ((error = sbwait(so2, SO_RCV)) != 0) { - SOCK_RECVBUF_UNLOCK(so2); - goto out4; - } else - goto restart; - } - MPASS(space >= cmc.mc_len); - space -= cmc.mc_len; - if (space == 0) { - /* There is space only to send control. */ - MPASS(!STAILQ_EMPTY(&cmc.mc_q)); - mcnext = mc; - mc = MCHAIN_INITIALIZER(&mc); - } else if (space < mc.mc_len) { - /* Not enough space. */ - if (__predict_false(mc_split(&mc, &mcnext, space, - M_NOWAIT) == ENOMEM)) { - /* - * If allocation failed use M_WAITOK and merge - * the chain back. Next time mc_split() will - * easily split at the same place. Only if we - * race with setsockopt(SO_RCVBUF) shrinking - * sb_hiwat can this happen more than once. - */ - SOCK_RECVBUF_UNLOCK(so2); - (void)mc_split(&mc, &mcnext, space, M_WAITOK); - mc_concat(&mc, &mcnext); - SOCK_RECVBUF_LOCK(so2); - goto restart; - } - MPASS(mc.mc_len == space); - } - if (!STAILQ_EMPTY(&cmc.mc_q)) { - STAILQ_CONCAT(&sb->sb_mbq, &cmc.mc_q); - sb->sb_ctl += cmc.mc_len; - sb->sb_mbcnt += cmc.mc_mlen; - cmc.mc_len = 0; - } - sent += mc.mc_len; - sb->sb_acc += mc.mc_len; - sb->sb_ccc += mc.mc_len; - sb->sb_mbcnt += mc.mc_mlen; - STAILQ_CONCAT(&sb->sb_mbq, &mc.mc_q); - UIPC_STREAM_SBCHECK(sb); - space = uipc_stream_sbspace(sb); - sorwakeup_locked(so2); - mc = mcnext; - if (STAILQ_EMPTY(&mc.mc_q) && - uio != NULL && uio->uio_resid > 0) { - /* - * Copyin sum of peer's receive buffer space and our - * sb_hiwat, which is our virtual send buffer size. - * See comment above unpst_sendspace declaration. - * We are reading sb_hiwat locklessly, cause a) we - * don't care about an application that does send(2) - * and setsockopt(2) racing internally, and for an - * application that does this in sequence we will see - * the correct value cause sbsetopt() uses buffer lock - * and we also have already acquired it at least once. - */ - error = mc_uiotomc(&mc, uio, space + - atomic_load_int(&so->so_snd.sb_hiwat), 0, M_WAITOK, - eor ? M_EOR : 0); - if (__predict_false(error)) - goto out4; - } + case SOCK_SEQPACKET: + if (sbappendaddr_nospacecheck_locked(&so2->so_rcv, + &sun_noname, m, control)) + control = NULL; + break; } - MPASS(STAILQ_EMPTY(&mc.mc_q)); - - td->td_ru.ru_msgsnd++; -out4: - sorele(so2); -out3: - SOCK_IO_SEND_UNLOCK(so); -out2: - if (!mc_empty(&cmc)) - unp_scan(mc_first(&cmc), unp_freerights); -out: - mc_freem(&mc); - mc_freem(&cmc); - - if (uio != NULL) - uio->uio_resid = resid - sent; - - return (error); -} - -static int -uipc_soreceive_stream_or_seqpacket(struct socket *so, struct sockaddr **psa, - struct uio *uio, struct mbuf **mp0, struct mbuf **controlp, int *flagsp) -{ - struct sockbuf *sb = &so->so_rcv; - struct mbuf *control, *m, *first, *last, *next; - u_int ctl, space, datalen, mbcnt, lastlen; - int error, flags; - bool nonblock, waitall, peek; - - MPASS(mp0 == NULL); - - if (psa != NULL) - *psa = NULL; - if (controlp != NULL) - *controlp = NULL; - - flags = flagsp != NULL ? *flagsp : 0; - nonblock = (so->so_state & SS_NBIO) || - (flags & (MSG_DONTWAIT | MSG_NBIO)); - peek = flags & MSG_PEEK; - waitall = (flags & MSG_WAITALL) && !peek; + mbcnt = so2->so_rcv.sb_mbcnt; + sbcc = sbavail(&so2->so_rcv); + if (sbcc) + sorwakeup_locked(so2); + else + SOCKBUF_UNLOCK(&so2->so_rcv); /* - * This check may fail only on a socket that never went through - * connect(2). We can check this locklessly, cause: a) for a new born - * socket we don't care about applications that may race internally - * between connect(2) and recv(2), and b) for a dying socket if we - * miss update by unp_sosidisconnected(), we would still get the check - * correct. For dying socket we would observe SBS_CANTRCVMORE later. + * The PCB lock on unp2 protects the SB_STOP flag. Without it, + * it would be possible for uipc_rcvd to be called at this + * point, drain the receiving sockbuf, clear SB_STOP, and then + * we would set SB_STOP below. That could lead to an empty + * sockbuf having SB_STOP set */ - if (__predict_false((atomic_load_short(&so->so_state) & - (SS_ISCONNECTED|SS_ISDISCONNECTED)) == 0)) - return (ENOTCONN); - - error = SOCK_IO_RECV_LOCK(so, SBLOCKWAIT(flags)); - if (__predict_false(error)) - return (error); - -restart: - SOCK_RECVBUF_LOCK(so); - UIPC_STREAM_SBCHECK(sb); - while (sb->sb_acc < sb->sb_lowat && - (sb->sb_ctl == 0 || controlp == NULL)) { - if (so->so_error) { - error = so->so_error; - if (!peek) - so->so_error = 0; - SOCK_RECVBUF_UNLOCK(so); - SOCK_IO_RECV_UNLOCK(so); - return (error); - } - if (sb->sb_state & SBS_CANTRCVMORE) { - SOCK_RECVBUF_UNLOCK(so); - SOCK_IO_RECV_UNLOCK(so); - return (0); - } - if (nonblock) { - SOCK_RECVBUF_UNLOCK(so); - SOCK_IO_RECV_UNLOCK(so); - return (EWOULDBLOCK); - } - error = sbwait(so, SO_RCV); - if (error) { - SOCK_RECVBUF_UNLOCK(so); - SOCK_IO_RECV_UNLOCK(so); - return (error); - } - } - - MPASS(STAILQ_FIRST(&sb->sb_mbq)); - MPASS(sb->sb_acc > 0 || sb->sb_ctl > 0); - - mbcnt = 0; - ctl = 0; - first = STAILQ_FIRST(&sb->sb_mbq); - if (first->m_type == MT_CONTROL) { - control = first; - STAILQ_FOREACH_FROM(first, &sb->sb_mbq, m_stailq) { - if (first->m_type != MT_CONTROL) - break; - ctl += first->m_len; - mbcnt += MSIZE; - if (first->m_flags & M_EXT) - mbcnt += first->m_ext.ext_size; - } - } else - control = NULL; - + SOCKBUF_LOCK(&so->so_snd); + if (sbcc >= so->so_snd.sb_hiwat || mbcnt >= so->so_snd.sb_mbmax) + so->so_snd.sb_flags |= SB_STOP; + SOCKBUF_UNLOCK(&so->so_snd); + UNP_PCB_UNLOCK(unp2); + m = NULL; +out: /* - * Find split point for the next copyout. On exit from the loop: - * last == NULL - socket to be flushed - * last != NULL - * lastlen > last->m_len - uio to be filled, last to be adjusted - * lastlen == 0 - MT_CONTROL or M_EOR encountered + * PRUS_EOF is equivalent to pr_send followed by pr_shutdown. */ - space = uio->uio_resid; - datalen = 0; - for (m = first, last = NULL; m != NULL; m = STAILQ_NEXT(m, m_stailq)) { - if (m->m_type != MT_DATA) { - last = m; - lastlen = 0; - break; - } - if (space >= m->m_len) { - space -= m->m_len; - datalen += m->m_len; - mbcnt += MSIZE; - if (m->m_flags & M_EXT) - mbcnt += m->m_ext.ext_size; - if (m->m_flags & M_EOR) { - last = STAILQ_NEXT(m, m_stailq); - lastlen = 0; - flags |= MSG_EOR; - break; - } - } else { - datalen += space; - last = m; - lastlen = space; - break; - } - } - - UIPC_STREAM_SBCHECK(sb); - if (!peek) { - if (last == NULL) - STAILQ_INIT(&sb->sb_mbq); - else { - STAILQ_FIRST(&sb->sb_mbq) = last; - MPASS(last->m_len > lastlen); - last->m_len -= lastlen; - last->m_data += lastlen; - } - MPASS(sb->sb_acc >= datalen); - sb->sb_acc -= datalen; - sb->sb_ccc -= datalen; - MPASS(sb->sb_ctl >= ctl); - sb->sb_ctl -= ctl; - MPASS(sb->sb_mbcnt >= mbcnt); - sb->sb_mbcnt -= mbcnt; - UIPC_STREAM_SBCHECK(sb); - /* Mind the name. We are waking writer here, not reader. */ - sorwakeup_locked(so); - } else - SOCK_RECVBUF_UNLOCK(so); - - while (control != NULL && control->m_type == MT_CONTROL) { - if (!peek) { - struct mbuf *c; - - /* - * unp_externalize() failure must abort entire read(2). - * Such failure should also free the problematic - * control, so that socket is not left in a state - * where it can't progress forward with reading. - * Probability of such a failure is really low, so it - * is fine that we need to perform pretty complex - * operation here to reconstruct the buffer. - * XXXGL: unp_externalize() used to be - * dom_externalize() KBI and it frees whole chain, so - * we need to feed it with mbufs one by one. - */ - c = control; - control = STAILQ_NEXT(c, m_stailq); - STAILQ_NEXT(c, m_stailq) = NULL; - error = unp_externalize(c, controlp, flags); - if (__predict_false(error)) { - SOCK_RECVBUF_LOCK(so); - UIPC_STREAM_SBCHECK(sb); - MPASS(!(sb->sb_state & SBS_CANTRCVMORE)); - /* XXXGL: STAILQ_PREPEND */ - if (STAILQ_EMPTY(&sb->sb_mbq) && - control != NULL) - STAILQ_INSERT_HEAD(&sb->sb_mbq, - control, m_stailq); - else - STAILQ_FIRST(&sb->sb_mbq) = control; - sb->sb_ctl = sb->sb_acc = sb->sb_ccc = - sb->sb_mbcnt = 0; - STAILQ_FOREACH(m, &sb->sb_mbq, m_stailq) { - if (m->m_type == MT_DATA) { - sb->sb_acc += m->m_len; - sb->sb_ccc += m->m_len; - } else { - sb->sb_ctl += m->m_len; - } - sb->sb_mbcnt += MSIZE; - if (m->m_flags & M_EXT) - sb->sb_mbcnt += - m->m_ext.ext_size; - } - UIPC_STREAM_SBCHECK(sb); - SOCK_RECVBUF_UNLOCK(so); - SOCK_IO_RECV_UNLOCK(so); - return (error); - } - if (controlp != NULL) { - while (*controlp != NULL) - controlp = &(*controlp)->m_next; - } - } else { - /* - * XXXGL - * - * In MSG_PEEK case control is not externalized. This - * means we are leaking some kernel pointers to the - * userland. They are useless to a law-abiding - * application, but may be useful to a malware. This - * is what the historical implementation in the - * soreceive_generic() did. To be improved? - */ - if (controlp != NULL) { - *controlp = m_copym(control, 0, control->m_len, - M_WAITOK); - controlp = &(*controlp)->m_next; - } - control = STAILQ_NEXT(control, m_stailq); - } + if (flags & PRUS_EOF) { + UNP_PCB_LOCK(unp); + socantsendmore(so); + unp_shutdown(unp); + UNP_PCB_UNLOCK(unp); } + if (control != NULL && error != 0) + unp_scan(control, unp_freerights); - for (m = first; m != last; m = next) { - next = STAILQ_NEXT(m, m_stailq); - error = uiomove(mtod(m, char *), m->m_len, uio); - if (__predict_false(error)) { - SOCK_IO_RECV_UNLOCK(so); - if (!peek) - for (; m != last; m = next) { - next = STAILQ_NEXT(m, m_stailq); - m_free(m); - } - return (error); - } - if (!peek) - m_free(m); - } - if (last != NULL && lastlen > 0) { - if (!peek) { - MPASS(!(m->m_flags & M_PKTHDR)); - MPASS(last->m_data - M_START(last) >= lastlen); - error = uiomove(mtod(last, char *) - lastlen, - lastlen, uio); - } else - error = uiomove(mtod(last, char *), lastlen, uio); - if (__predict_false(error)) { - SOCK_IO_RECV_UNLOCK(so); - return (error); - } - } - if (waitall && !(flags & MSG_EOR) && uio->uio_resid > 0) - goto restart; - SOCK_IO_RECV_UNLOCK(so); - - if (flagsp != NULL) - *flagsp |= flags; - - uio->uio_td->td_ru.ru_msgrcv++; - - return (0); +release: + if (control != NULL) + m_freem(control); + /* + * In case of PRUS_NOTREADY, uipc_ready() is responsible + * for freeing memory. + */ + if (m != NULL && (flags & PRUS_NOTREADY) == 0) + m_freem(m); + return (error); } /* PF_UNIX/SOCK_DGRAM version of sbspace() */ @@ -1498,8 +1111,7 @@ uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, const struct sockaddr *from; struct socket *so2; struct sockbuf *sb; - struct mchain cmc = MCHAIN_INITIALIZER(&cmc); - struct mbuf *f; + struct mbuf *f, *clast; u_int cc, ctl, mbcnt; u_int dcc __diagused, dctl __diagused, dmbcnt __diagused; int error; @@ -1508,6 +1120,7 @@ uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, error = 0; f = NULL; + ctl = 0; if (__predict_false(flags & MSG_OOB)) { error = EOPNOTSUPP; @@ -1526,14 +1139,16 @@ uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, f = m_gethdr(M_WAITOK, MT_SONAME); cc = m->m_pkthdr.len; mbcnt = MSIZE + m->m_pkthdr.memlen; - if (c != NULL && (error = unp_internalize(c, &cmc, td))) + if (c != NULL && + (error = unp_internalize(&c, td, &clast, &ctl, &mbcnt))) goto out; } else { - struct mchain mc; + /* pr_sosend() with mbuf usually is a kernel thread. */ + + M_ASSERTPKTHDR(m); + if (__predict_false(c != NULL)) + panic("%s: control from a kernel thread", __func__); - uipc_reset_kernel_mbuf(m, &mc); - cc = mc.mc_len; - mbcnt = mc.mc_mlen; if (__predict_false(m->m_pkthdr.len > unpdg_maxdgram)) { error = EMSGSIZE; goto out; @@ -1542,6 +1157,22 @@ uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, error = ENOBUFS; 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; + + cc = m->m_pkthdr.len; + mbcnt = MSIZE; + for (struct mbuf *mb = m; mb != NULL; mb = mb->m_next) { + mbcnt += MSIZE; + if (mb->m_flags & M_EXT) + mbcnt += mb->m_ext.ext_size; + } } unp = sotounpcb(so); @@ -1593,7 +1224,8 @@ uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, } if (unp2->unp_flags & UNP_WANTCRED_MASK) - unp_addsockcred(td, &cmc, unp2->unp_flags); + c = unp_addsockcred(td, c, unp2->unp_flags, &clast, &ctl, + &mbcnt); if (unp->unp_addr != NULL) from = (struct sockaddr *)unp->unp_addr; else @@ -1601,21 +1233,25 @@ uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, f->m_len = from->sa_len; MPASS(from->sa_len <= MLEN); bcopy(from, mtod(f, void *), from->sa_len); + ctl += f->m_len; /* * Concatenate mbufs: from -> control -> data. * Save overall cc and mbcnt in "from" mbuf. */ - if (!STAILQ_EMPTY(&cmc.mc_q)) { - f->m_next = mc_first(&cmc); - mc_last(&cmc)->m_next = m; - /* XXXGL: This is dirty as well as rollback after ENOBUFS. */ - STAILQ_INIT(&cmc.mc_q); + if (c != NULL) { +#ifdef INVARIANTS + struct mbuf *mc; + + for (mc = c; mc->m_next != NULL; mc = mc->m_next); + MPASS(mc == clast); +#endif + f->m_next = c; + clast->m_next = m; + c = NULL; } else f->m_next = m; m = NULL; - ctl = f->m_len + cmc.mc_len; - mbcnt += cmc.mc_mlen; #ifdef INVARIANTS dcc = dctl = dmbcnt = 0; for (struct mbuf *mb = f; mb != NULL; mb = mb->m_next) { @@ -1681,7 +1317,7 @@ uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, soroverflow_locked(so2); error = ENOBUFS; if (f->m_next->m_type == MT_CONTROL) { - STAILQ_FIRST(&cmc.mc_q) = f->m_next; + c = f->m_next; f->m_next = NULL; } } @@ -1696,12 +1332,13 @@ uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, out3: SOCK_IO_SEND_UNLOCK(so); out2: - if (!mc_empty(&cmc)) - unp_scan(mc_first(&cmc), unp_freerights); + if (c) + unp_scan(c, unp_freerights); out: if (f) m_freem(f); - mc_freem(&cmc); + if (c) + m_freem(c); if (m) m_freem(m); @@ -1942,7 +1579,6 @@ uipc_soreceive_dgram(struct socket *so, struct sockaddr **psa, struct uio *uio, return (0); } -#if 0 /* No sendfile support. */ static bool uipc_ready_scan(struct socket *so, struct mbuf *m, int count, int *errorp) { @@ -2022,7 +1658,6 @@ uipc_ready(struct socket *so, struct mbuf *m, int count) } return (error); } -#endif static int uipc_sense(struct socket *so, struct stat *sb) @@ -2461,19 +2096,6 @@ unp_connect2(struct socket *so, struct socket *so2) } } -static void -unp_soisdisconnected(struct socket *so) -{ - SOCK_LOCK(so); - MPASS(!SOLISTENING(so)); - so->so_state |= SS_ISDISCONNECTED; - so->so_state &= ~SS_ISCONNECTED; - SOCK_RECVBUF_LOCK(so); - socantrcvmore_locked(so); - SOCK_UNLOCK(so); - wakeup(&so->so_timeo); /* XXXGL: is this needed? */ -} - *** 398 LINES SKIPPED ***