git: d1c4e56772f8 - stable/13 - netlink: make snl(3) scratch buffer growable

From: Alexander V. Chernikov <melifaro_at_FreeBSD.org>
Date: Sat, 08 Apr 2023 19:44:48 UTC
The branch stable/13 has been updated by melifaro:

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

commit d1c4e56772f804065d7f06c7a6b9d8accccb9d4a
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2023-03-08 11:17:43 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2023-04-08 19:05:34 +0000

    netlink: make snl(3) scratch buffer growable
    
    Differential Revision: https://reviews.freebsd.org/D38946
    MFC after:      2 weeks
    
    (cherry picked from commit 0679eb1f39aa798da6c4601bb06bab4c175c4b18)
---
 sys/netlink/netlink_snl.h | 90 ++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 69 insertions(+), 21 deletions(-)

diff --git a/sys/netlink/netlink_snl.h b/sys/netlink/netlink_snl.h
index d88808ea6d3b..3c63e3f95419 100644
--- a/sys/netlink/netlink_snl.h
+++ b/sys/netlink/netlink_snl.h
@@ -70,11 +70,31 @@
 #define	NL_ARRAY_LEN(_a)	(sizeof(_a) / sizeof((_a)[0]))
 
 struct linear_buffer {
-	char		*base;	/* Base allocated memory pointer */
-	uint32_t	offset;	/* Currently used offset */
-	uint32_t	size;	/* Total buffer size */
+	char			*base;	/* Base allocated memory pointer */
+	uint32_t		offset;	/* Currently used offset */
+	uint32_t		size;	/* Total buffer size */
+	struct linear_buffer	*next;	/* Buffer chaining */
 };
 
+static inline struct linear_buffer *
+lb_init(uint32_t size)
+{
+	struct linear_buffer *lb = calloc(1, size);
+
+	if (lb != NULL) {
+		lb->base = (char *)(lb + 1);
+		lb->size = size - sizeof(*lb);
+	}
+
+	return (lb);
+}
+
+static inline void
+lb_free(struct linear_buffer *lb)
+{
+	free(lb);
+}
+
 static inline char *
 lb_allocz(struct linear_buffer *lb, int len)
 {
@@ -101,7 +121,7 @@ struct snl_state {
 	size_t datalen;
 	uint32_t seq;
 	bool init_done;
-	struct linear_buffer lb;
+	struct linear_buffer *lb;
 };
 #define	SCRATCH_BUFFER_SIZE	1024
 
@@ -145,6 +165,45 @@ static const struct snl_hdr_parser _name = {		\
 }
 
 
+static inline void *
+snl_allocz(struct snl_state *ss, int len)
+{
+	void *data = lb_allocz(ss->lb, len);
+
+	if (data == NULL) {
+		uint32_t size = ss->lb->size * 2;
+
+		while (size < len + sizeof(struct linear_buffer))
+			size *= 2;
+
+		struct linear_buffer *lb = lb_init(size);
+
+		if (lb != NULL) {
+			lb->next = ss->lb;
+			ss->lb = lb;
+			data = lb_allocz(ss->lb, len);
+		}
+	}
+
+	return (data);
+}
+
+static inline void
+snl_clear_lb(struct snl_state *ss)
+{
+	struct linear_buffer *lb = ss->lb;
+
+	lb_clear(lb);
+	lb = lb->next;
+	ss->lb->next = NULL;
+	/* Remove all linear bufs except the largest one */
+	while (lb != NULL) {
+		struct linear_buffer *lb_next = lb->next;
+		lb_free(lb);
+		lb = lb_next;
+	}
+}
+
 static void
 snl_free(struct snl_state *ss)
 {
@@ -152,8 +211,10 @@ snl_free(struct snl_state *ss)
 		close(ss->fd);
 		if (ss->buf != NULL)
 			free(ss->buf);
-		if (ss->lb.base != NULL)
-			free(ss->lb.base);
+		if (ss->lb != NULL) {
+			snl_clear_lb(ss);
+			lb_free(ss->lb);
+		}
 	}
 }
 
@@ -181,9 +242,8 @@ snl_init(struct snl_state *ss, int netlink_family)
 		return (false);
 	}
 
-	ss->lb.size = SCRATCH_BUFFER_SIZE;
-	ss->lb.base = calloc(1, ss->lb.size);
-	if (ss->lb.base == NULL) {
+	ss->lb = lb_init(SCRATCH_BUFFER_SIZE);
+	if (ss->lb == NULL) {
 		snl_free(ss);
 		return (false);
 	}
@@ -191,18 +251,6 @@ snl_init(struct snl_state *ss, int netlink_family)
 	return (true);
 }
 
-static inline void *
-snl_allocz(struct snl_state *ss, int len)
-{
-	return (lb_allocz(&ss->lb, len));
-}
-
-static inline void
-snl_clear_lb(struct snl_state *ss)
-{
-	lb_clear(&ss->lb);
-}
-
 static inline bool
 snl_send(struct snl_state *ss, void *data, int sz)
 {