git: fd01798fc48d - main - mbuf: add mc_split() that works on two struct mchain

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

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

commit fd01798fc48d2e1dbf0eb42aaa97838515bc5268
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: add mc_split() that works on two struct mchain
    
    It preserves tail points and all length/memory accounting, so that caller
    doesn't need to do any extra traversals.  It doesn't respect M_PKTHDR but
    it may be improved if needed.  It respects M_EOR, though.  First consumer
    will be the new unix(4) SOCK_STREAM and SOCK_SEQPACKET.
    
    Also provide much more simple mc_concat() that glues two chains back.
    
    Reviewed by:            markj
    Differentail Revision:  https://reviews.freebsd.org/D44148
---
 sys/kern/uipc_mbuf.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 sys/sys/mbuf.h       | 11 +++++++++
 2 files changed, 77 insertions(+)

diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c
index 5125ad902c34..2593238d5445 100644
--- a/sys/kern/uipc_mbuf.c
+++ b/sys/kern/uipc_mbuf.c
@@ -1108,6 +1108,72 @@ extpacket:
 	m->m_next = NULL;
 	return (n);
 }
+
+/*
+ * Partition mchain in two pieces, keeping len0 bytes in head and transferring
+ * remainder to tail.  In case of failure, both chains to be left untouched.
+ * M_EOR is observed correctly.
+ * Resulting mbufs might be read-only.
+ */
+int
+mc_split(struct mchain *head, struct mchain *tail, u_int len0, int wait)
+{
+	struct mbuf *m, *n;
+	u_int len, mlen, remain;
+
+	MPASS(!(mc_first(head)->m_flags & M_PKTHDR));
+	MBUF_CHECKSLEEP(wait);
+
+	mlen = 0;
+	len = len0;
+	STAILQ_FOREACH(m, &head->mc_q, m_stailq) {
+		mlen += MSIZE;
+		if (m->m_flags & M_EXT)
+			mlen += m->m_ext.ext_size;
+		if (len > m->m_len)
+			len -= m->m_len;
+		else
+			break;
+	}
+	if (__predict_false(m == NULL)) {
+		*tail = MCHAIN_INITIALIZER(tail);
+		return (0);
+	}
+	remain = m->m_len - len;
+	if (remain > 0) {
+		if (__predict_false((n = m_get(wait, m->m_type)) == NULL))
+			return (ENOMEM);
+		m_align(n, remain);
+		if (m->m_flags & M_EXT) {
+			n->m_data = m->m_data + len;
+			mb_dupcl(n, m);
+		} else
+			bcopy(mtod(m, char *) + len, mtod(n, char *), remain);
+	}
+
+	/* XXXGL: need STAILQ_SPLIT */
+	STAILQ_FIRST(&tail->mc_q) = STAILQ_NEXT(m, m_stailq);
+	tail->mc_q.stqh_last = head->mc_q.stqh_last;
+	tail->mc_len = head->mc_len - len0;
+	tail->mc_mlen = head->mc_mlen - mlen;
+	if (remain > 0) {
+		MPASS(n->m_len == 0);
+		mc_prepend(tail, n);
+		n->m_len = remain;
+		m->m_len -= remain;
+		if (m->m_flags & M_EOR) {
+			m->m_flags &= ~M_EOR;
+			n->m_flags |= M_EOR;
+		}
+	}
+	head->mc_q.stqh_last = &STAILQ_NEXT(m, m_stailq);
+	STAILQ_NEXT(m, m_stailq) = NULL;
+	head->mc_len = len0;
+	head->mc_mlen = mlen;
+
+	return (0);
+}
+
 /*
  * Routine to copy from device local memory into mbufs.
  * Note that `off' argument is offset into first mbuf of target chain from
diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h
index 823b56295c2f..a5af2291123b 100644
--- a/sys/sys/mbuf.h
+++ b/sys/sys/mbuf.h
@@ -1775,6 +1775,15 @@ mc_append(struct mchain *mc, struct mbuf *m)
 	mc_inc(mc, m);
 }
 
+static inline void
+mc_concat(struct mchain *head, struct mchain *tail)
+{
+	STAILQ_CONCAT(&head->mc_q, &tail->mc_q);
+	head->mc_len += tail->mc_len;
+	head->mc_mlen += tail->mc_mlen;
+	tail->mc_len = tail->mc_mlen = 0;
+}
+
 /*
  * Note: STAILQ_REMOVE() is expensive. mc_remove_after() needs to be provided
  * as long as there consumers that would benefit from it.
@@ -1786,6 +1795,8 @@ mc_remove(struct mchain *mc, struct mbuf *m)
 	mc_dec(mc, m);
 }
 
+int mc_split(struct mchain *, struct mchain *, u_int, int);
+
 #ifdef _SYS_TIMESPEC_H_
 static inline void
 mbuf_tstmp2timespec(struct mbuf *m, struct timespec *ts)