git: aba79b0f4a3f - main - mbuf: provide mc_uiotomc() a function to copy from uio(9) to mchain

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Mon, 08 Apr 2024 20:29:47 UTC
The branch main has been updated by glebius:

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

commit aba79b0f4a3f69f070ace6effd5700d65226bd20
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2024-04-08 20:16:51 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2024-04-08 20:16:51 +0000

    mbuf: provide mc_uiotomc() a function to copy from uio(9) to mchain
    
    Implement m_uiotombuf() as a wrapper around mc_uiotomc().  The M_EXTPG is
    left untouched.  The m_uiotombuf() is left as a compat KPI.  New code
    should use either mc_uiotomc() or m_uiotombuf_nomap().
    
    Reviewed by:            markj, tuexen
    Differential Revision:  https://reviews.freebsd.org/D44150
---
 sys/kern/uipc_mbuf.c | 126 +++++++++++++++++++++++++++++++++------------------
 sys/sys/mbuf.h       |   1 +
 2 files changed, 83 insertions(+), 44 deletions(-)

diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c
index 2593238d5445..f5f6db752883 100644
--- a/sys/kern/uipc_mbuf.c
+++ b/sys/kern/uipc_mbuf.c
@@ -1938,65 +1938,103 @@ failed:
 
 /*
  * Copy the contents of uio into a properly sized mbuf chain.
+ * A compat KPI.  Users are recommended to use direct calls to backing
+ * functions.
  */
 struct mbuf *
-m_uiotombuf(struct uio *uio, int how, int len, int align, int flags)
+m_uiotombuf(struct uio *uio, int how, int len, int lspace, int flags)
 {
-	struct mbuf *m, *mb;
-	int error, length;
-	ssize_t total;
-	int progress = 0;
 
-	if (flags & M_EXTPG)
-		return (m_uiotombuf_nomap(uio, how, len, align, flags));
+	if (flags & M_EXTPG) {
+		/* XXX: 'lspace' magically becomes maxseg! */
+		return (m_uiotombuf_nomap(uio, how, len, lspace, flags));
+	} else if (__predict_false(uio->uio_resid + len == 0)) {
+		struct mbuf *m;
 
-	/*
-	 * len can be zero or an arbitrary large value bound by
-	 * the total data supplied by the uio.
-	 */
-	if (len > 0)
-		total = (uio->uio_resid < len) ? uio->uio_resid : len;
+		/*
+		 * m_uiotombuf() is known to return zero length buffer, keep
+		 * this compatibility. mc_uiotomc() won't do that.
+		 */
+		if (flags & M_PKTHDR) {
+			m = m_gethdr(how, MT_DATA);
+			m->m_pkthdr.memlen = MSIZE;
+		} else
+			m = m_get(how, MT_DATA);
+		if (m != NULL)
+			m->m_data += lspace;
+		return (m);
+	} else {
+		struct mchain mc;
+		int error;
+
+		error = mc_uiotomc(&mc, uio, len, lspace, how, flags);
+		if (__predict_true(error == 0)) {
+			if (flags & M_PKTHDR) {
+				mc_first(&mc)->m_pkthdr.len = mc.mc_len;
+				mc_first(&mc)->m_pkthdr.memlen = mc.mc_mlen;
+			}
+			return (mc_first(&mc));
+		} else
+			return (NULL);
+	}
+}
+
+/*
+ * Copy the contents of uio into a properly sized mbuf chain.
+ * In case of failure state of mchain is inconsistent.
+ * @param length Limit copyout length.  If 0 entire uio_resid is copied.
+ * @param lspace Provide leading space in the first mbuf in the chain.
+ */
+int
+mc_uiotomc(struct mchain *mc, struct uio *uio, u_int length, u_int lspace,
+    int how, int flags)
+{
+	struct mbuf *mb;
+	u_int total;
+	int error;
+
+	MPASS(lspace < MHLEN);
+	MPASS(UINT_MAX - lspace >= length);
+	MPASS(uio->uio_rw == UIO_WRITE);
+	MPASS(uio->uio_resid >= 0);
+
+	if (length > 0) {
+		if (uio->uio_resid > length) {
+			total = length;
+			flags &= ~M_EOR;
+		} else
+			total = uio->uio_resid;
+	} else if (__predict_false(uio->uio_resid + lspace > UINT_MAX))
+		return (EOVERFLOW);
 	else
 		total = uio->uio_resid;
 
-	/*
-	 * The smallest unit returned by m_getm2() is a single mbuf
-	 * with pkthdr.  We can't align past it.
-	 */
-	if (align >= MHLEN)
-		return (NULL);
+	if (__predict_false(total + lspace == 0)) {
+		*mc = MCHAIN_INITIALIZER(mc);
+		return (0);
+	}
 
-	/*
-	 * Give us the full allocation or nothing.
-	 * If len is zero return the smallest empty mbuf.
-	 */
-	m = m_getm2(NULL, max(total + align, 1), how, MT_DATA, flags);
-	if (m == NULL)
-		return (NULL);
-	m->m_data += align;
+	error = mc_get(mc, total + lspace, how, MT_DATA, flags);
+	if (__predict_false(error))
+		return (error);
+	mc_first(mc)->m_data += lspace;
 
 	/* Fill all mbufs with uio data and update header information. */
-	for (mb = m; mb != NULL; mb = mb->m_next) {
-		length = min(M_TRAILINGSPACE(mb), total - progress);
-
-		error = uiomove(mtod(mb, void *), length, uio);
-		if (error) {
-			m_freem(m);
-			return (NULL);
-		}
+	STAILQ_FOREACH(mb, &mc->mc_q, m_stailq) {
+		u_int mlen;
 
-		mb->m_len = length;
-		progress += length;
-		if (flags & M_PKTHDR) {
-			m->m_pkthdr.len += length;
-			m->m_pkthdr.memlen += MSIZE;
-			if (mb->m_flags & M_EXT)
-				m->m_pkthdr.memlen += mb->m_ext.ext_size;
+		mlen = min(M_TRAILINGSPACE(mb), total - mc->mc_len);
+		error = uiomove(mtod(mb, void *), mlen, uio);
+		if (__predict_false(error)) {
+			mc_freem(mc);
+			return (error);
 		}
+		mb->m_len = mlen;
+		mc->mc_len += mlen;
 	}
-	KASSERT(progress == total, ("%s: progress != total", __func__));
+	MPASS(mc->mc_len == total);
 
-	return (m);
+	return (0);
 }
 
 /*
diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h
index 76c0005ae963..e13ba39c27aa 100644
--- a/sys/sys/mbuf.h
+++ b/sys/sys/mbuf.h
@@ -1797,6 +1797,7 @@ mc_remove(struct mchain *mc, struct mbuf *m)
 
 int mc_get(struct mchain *, u_int, int, short, int);
 int mc_split(struct mchain *, struct mchain *, u_int, int);
+int mc_uiotomc(struct mchain *, struct uio *, u_int, u_int, int, int);
 
 #ifdef _SYS_TIMESPEC_H_
 static inline void