The branch main has been updated by glebius:

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

commit aba79b0f4a3f69f070ace6effd5700d65226bd20
Author:     Gleb Smirnoff <gleb...@freebsd.org>
AuthorDate: 2024-04-08 20:16:51 +0000
Commit:     Gleb Smirnoff <gleb...@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

Reply via email to