From 40aeae521ac2fc64e29531bebc20b342c6e2004d Mon Sep 17 00:00:00 2001
From: Aaron LI <aly@aaronly.me>
Date: Mon, 30 Oct 2023 13:58:19 +0800
Subject: [PATCH 1/2] mbuf(9): Add m_copyback2() for a better m_copyback()

The existing m_copyback() may extends the mbuf chain if necessary, but
it doesn't return a value to indicate whether the allocation fails.  In
addition, it doesn't allow to specify the M_WAITOK/M_NOWAIT flag for
mbuf allocation.

Extend m_copyback() and name it m_copyback2() that has the 'how'
parameter to specify M_WAITOK/M_NOWAIT flag and return an error code to
indication the success/failure.

Reimplement the original m_copyback() using m_copyback2() with
how=M_NOWAIT.

The superseded m_copyback() is kept for compatibility.  Better to rename
the function if its semantic changed.

Referred-to: OpenBSD
---
 share/man/man9/Makefile |  1 +
 share/man/man9/mbuf.9   | 12 ++++++++++
 sys/kern/uipc_mbuf.c    | 53 +++++++++++++++++++++++++++++------------
 sys/sys/mbuf.h          |  1 +
 4 files changed, 52 insertions(+), 15 deletions(-)

diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile
index 21c32c73b0d..13d65b234b1 100644
--- a/share/man/man9/Makefile
+++ b/share/man/man9/Makefile
@@ -685,6 +685,7 @@ MLINKS+=mbuf.9 m_adj.9 \
 	mbuf.9 m_cat.9 \
 	mbuf.9 MCLGET.9 \
 	mbuf.9 m_copyback.9 \
+	mbuf.9 m_copyback2.9 \
 	mbuf.9 m_copydata.9 \
 	mbuf.9 m_copym.9 \
 	mbuf.9 m_copypacket.9 \
diff --git a/share/man/man9/mbuf.9 b/share/man/man9/mbuf.9
index c64a0bd447c..4a90a4bf58a 100644
--- a/share/man/man9/mbuf.9
+++ b/share/man/man9/mbuf.9
@@ -84,6 +84,8 @@
 .Fn m_copydata "const struct mbuf *mbuf" "int offset" "int len" "caddr_t buf"
 .Ft void
 .Fn m_copyback "struct mbuf *mbuf" "int offset" "int len" "caddr_t buf"
+.Ft int
+.Fn m_copyback2 "struct mbuf *mbuf" "int offset" "int len" "caddr_t buf" "int how"
 .Ft struct mbuf *
 .Fo m_devget
 .Fa "char *buf"
@@ -401,6 +403,16 @@ bytes, into the indicated buffer
 .Fa buf .
 .\"
 .It Fn m_copyback mbuf offset len buf
+.Em ( deprecated )
+It is equivalent to
+.Fn m_copyback2 mbuf offset len buf M_NOWAIT ,
+but without a return value indicating the possible mbuf allocation failure.
+.Sy Note :
+This function is deprecated; use
+.Fn m_copyback2
+instead.
+.\"
+.It Fn m_copyback2 mbuf offset len buf how
 Copy
 .Fa len
 bytes from the buffer
diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c
index 8f3e70392d1..95581210c2e 100644
--- a/sys/kern/uipc_mbuf.c
+++ b/sys/kern/uipc_mbuf.c
@@ -2328,32 +2328,35 @@ m_devpad(struct mbuf *m, int padto)
 /*
  * Copy data from a buffer back into the indicated mbuf chain,
  * starting "off" bytes from the beginning, extending the mbuf
- * chain if necessary.
+ * chain if necessary.  The mbuf needs to be properly initialized
+ * including the setting of m_len.
  */
-void
-m_copyback(struct mbuf *m0, int off, int len, caddr_t cp)
+int
+m_copyback2(struct mbuf *m0, int off, int len, caddr_t cp, int how)
 {
-	int mlen;
+	int mlen, totlen = 0, error = 0;
 	struct mbuf *m = m0, *n;
-	int totlen = 0;
 
 	if (m0 == NULL)
-		return;
+		return (0);
+
 	while (off > (mlen = m->m_len)) {
 		off -= mlen;
 		totlen += mlen;
 		if (m->m_next == NULL) {
-			n = m_getclr(M_NOWAIT, m->m_type);
-			if (n == NULL)
+			n = m_getclr(how, m->m_type);
+			if (n == NULL) {
+				error = ENOBUFS;
 				goto out;
+			}
 			n->m_len = min(MLEN, len + off);
 			m->m_next = n;
 		}
 		m = m->m_next;
 	}
 	while (len > 0) {
-		mlen = min (m->m_len - off, len);
-		bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen);
+		mlen = min(m->m_len - off, len);
+		bcopy(cp, mtod(m, char *) + off, mlen);
 		cp += mlen;
 		len -= mlen;
 		mlen += off;
@@ -2362,16 +2365,36 @@ m_copyback(struct mbuf *m0, int off, int len, caddr_t cp)
 		if (len == 0)
 			break;
 		if (m->m_next == NULL) {
-			n = m_get(M_NOWAIT, m->m_type);
-			if (n == NULL)
-				break;
+			n = m_get(how, m->m_type);
+			if (n == NULL) {
+				error = ENOBUFS;
+				goto out;
+			}
 			n->m_len = min(MLEN, len);
 			m->m_next = n;
 		}
 		m = m->m_next;
 	}
-out:	if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen))
-		m->m_pkthdr.len = totlen;
+
+out:
+	if ((m0->m_flags & M_PKTHDR) && (m0->m_pkthdr.len < totlen))
+		m0->m_pkthdr.len = totlen;
+
+	return (error);
+}
+
+/*
+ * Legacy interface; use m_copyback2() instead.
+ *
+ * NOTE:
+ * The mbuf chain will be extended if necessary.  However, this routine
+ * doesn't return any value to indicate the potential failure of mbuf
+ * allocation.
+ */
+void
+m_copyback(struct mbuf *m0, int off, int len, caddr_t cp)
+{
+	(void)m_copyback2(m0, off, len, cp, M_NOWAIT);
 }
 
 /*
diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h
index 4bff7867417..6120a83babd 100644
--- a/sys/sys/mbuf.h
+++ b/sys/sys/mbuf.h
@@ -501,6 +501,7 @@ int		m_append(struct mbuf *, int, c_caddr_t);
 void		 m_cat(struct mbuf *, struct mbuf *);
 u_int		 m_countm(struct mbuf *m, struct mbuf **lastm, u_int *mbcnt);
 void		 m_copyback(struct mbuf *, int, int, caddr_t);
+int		 m_copyback2(struct mbuf *, int, int, caddr_t, int);
 void		 m_copydata(const struct mbuf *, int, int, caddr_t);
 struct	mbuf	*m_copym(const struct mbuf *, int, int, int);
 struct	mbuf	*m_copypacket(struct mbuf *, int);
-- 
2.40.1

